Super Mango manualPlay

Source Files

On this page: File map · main.c · game.h · Runtime core · Player · Levels · Start menu · Enemies · Hazards · Collectibles · Surfaces · Effects · System modules


File Map

src/
├── main.c                        CLI entry point; AppSession owns platform lifetime
├── game.h                        GameState struct; includes game_constants.h
├── game_assets.h                 GameAssets: the shared sprite and sound slots
├── game_constants.h              Window, world, floor and camera #defines (no other includes)
├── game_fwd.h                    `typedef struct GameState GameState;` for pointer-only headers
├── collectibles/
│   ├── coin.h / .c               Coin collectible: placement, AABB collection, render
│   ├── health_star.h / .c        Yellow/green/red star health pickups (one module)
│   └── last_star.h / .c          End-of-level star collectible
├── collision/
│   ├── collision_damage.h / .c   Damage checks against hazards and enemies
│   ├── floor_gap_collision.h / .c Sea-gap fall/death detection
│   └── game_collision.h / .c     s_damage_sources table, collision pass, pickups
├── core/
│   ├── app_session.h / .c        Heap-owned session: window/audio lifetime, menu/game routes, in-place replay
│   ├── game_profile.h / .c       Versioned player settings/results/Continue point and native/web persistence
│   ├── game_resume.h / .c        Capture a running level as a Continue point and resume it
│   ├── game_ghost.h / .c         Time trial: record each step, race the best run's ghost
│   ├── game_ghost_file.c         Ghost text format and its native file / browser storage
│   ├── game_inspector.h / .c     Simulation stepping, slow motion, tuning, status panel and F5 key help
│   ├── game_experiment.h / .c    Bounded capture/export/replay with level fingerprints
│   ├── game_random.h / .c        Reproducible unsigned PRNG for native/WASM
│   ├── debug.h / .c              Debug overlay: FPS/frame budget/memory, hitboxes, event log
│   ├── entity_utils.h / .c       Shared entity helper functions
│   ├── game_state.h / .c         GameState reset helpers
│   ├── game_window.h / .c        Screen-owned logical render target
│   ├── game_timing.h / .c        Fixed-step accumulator and frame clock
│   ├── game_lifecycle.c          `game_init` / `game_cleanup` implementation
│   ├── game_loop.c               Active-game frame runner (game_frame)
│   ├── game_update.h / .c        s_update_steps table: one fixed step, in order
│   ├── game_player_step.h / .c   Player update/collision step wrapper
│   ├── game_bouncepads.h / .c    Bouncepad list views and hit response
│   ├── game_float_platforms.h / .c Float-platform update helpers
│   ├── game_bridges.h / .c       Bridge update helpers
│   ├── game_checkpoint.h / .c    Checkpoint/respawn helpers
│   ├── game_camera.h / .c        Camera follow/lookahead helpers
│   ├── game_resources.h / .c     Shared sprite/sound tables (GameAssets) load/unload
│   ├── game_score.h / .c         Shared score and bonus-life helpers
│   ├── game_overlay.h / .c       Canonical pause/game-over/completion overlay state
│   ├── game_completion.h / .c    Last-star completion and next-phase flow
│   └── game_terminal.h / .c      Shared terminal action list, focus movement, labels, and routes
├── editor/
│   ├── editor_main.c             Standalone editor entry point
│   ├── editor.h / .c             Editor state, resource startup/cleanup and loop delegation
│   ├── canvas.h / .c             Zoomable editing canvas with horizontal and vertical camera scrolling
│   ├── palette.h / .c            Entity palette
│   ├── properties.h / .c         Property panels: one draw_<type>_properties function per entity type (picked from a table), plus level config
│   ├── asset_choices.h / .c      The files each asset dropdown offers (layer images, floor/platform tiles, music); a test checks them against the folders
│   ├── tools.h / .c              Selection and placement tools
│   ├── hit_test.h / .c           Entity rectangles and click hit-testing
│   ├── entity_meta.h / .c        One table row per entity type (names, capacity, LevelDef storage, placement preview) plus shared read/insert/remove of placements
│   ├── editor_frame.h / .c       Per-frame editor orchestration
│   ├── editor_events.h / .c      Semantic input dispatch
│   ├── dialog_choice.h / .c      Two- and three-button prompts drawn in the editor window
│   ├── editor_chrome.h / .c      Toolbar/status/panel chrome
│   ├── editor_panels.h / .c      Palette/properties panel rendering
│   ├── editor_layout.h / .c      Editor layout metrics
│   ├── editor_minimap.h / .c     Level overview strip in the status bar: entities by category, the view's frame, click/drag to scroll
│   ├── editor_shortcuts.h / .c   The shortcut table: toolbar tooltips and the F1 shortcut list drawn from it
│   ├── editor_textures.h / .c    Editor texture loading/cleanup
│   ├── editor_files.h / .c       Open/save, playtest copies, recent files; saving through a symlink keeps the link
│   ├── editor_recovery.h / .c    Autosave snapshots and crash recovery (find, offer, load and retire them)
│   ├── editor_session.h / .c     Dirty state, staged-edit/save decisions and document hashing
│   ├── editor_playtest.h / .c    Save and launch a playtest, poll it, stop (kill) it
│   ├── editor_campaign.h / .c    Campaign view: reorder, add, remove, rename and relink the levels/campaigns/main.toml entries
│   ├── editor_clipboard.h / .c   Copy/paste; a rail rider re-attaches to its rail by shape and position
│   ├── editor_validation.h / .c  Level validation report helpers
│   ├── editor_undo_apply.h / .c  Undo operation application
│   ├── file_dialog.h / .c        Native file dialogs
│   └── undo.h / .c               Compact history with owned config snapshots; `undo_push(stack, const Command *)`
├── shared/
│   ├── graphics.h / .c          Window and icon, sprite pivots and logical presentation; declares the texture handles
│   ├── texture.c                `texture_load`, `texture_own`, `texture_unload`: owned texture handles
│   ├── asset_cache.h / .c       Session's reference-counted cache of level files, player sheet, HUD icon, font, menu logo and sound
│   ├── geometry.h              Integer hitboxes and half-open intersection
│   ├── raymath_inline.h        raylib's raymath as static inline (Clamp and friends, project flags)
│   ├── audio.h / .c             Bounded sound voices, music streaming and device ownership
│   ├── text.h / .c              Borrowed built-in font, text measurement and (centred) drawing from its atlas
│   ├── platform.h / .c          Monotonic time, UTF-8 copying and OS preference/executable paths
│   ├── asset_root.h / .c        Move a foreign working folder to the one holding assets/ and levels/
│   ├── utf8.h                   Strict UTF-8 checks for loaded strings and typed text
│   ├── hex.h                    Fixed-width lowercase hex reader for ghost and resume values
│   ├── printf_format.h          PRINTF_FORMAT: lets GCC/Clang check printf-style format arguments
│   ├── ui.h / .c                 Immediate-mode widgets shared by editor and game settings
│   ├── serializer.h / .c         TOML save/load public API anchor
│   ├── serializer_emit.h / .c    TOML emission helpers; control bytes and DEL are written as \uXXXX escapes
│   ├── serializer_io.h / .c      UTF-8 path I/O, temp-file-then-rename saves, symlink checks
│   ├── serializer_load.c         `level_load_toml` staged parse orchestration
│   ├── serializer_load_header.h / .c        TOML header/meta and floor-gap parsing
│   ├── serializer_load_checkpoints.h / .c   Strict authored checkpoint parsing
│   ├── serializer_load_geometry.h / .c      Rails and platforms parsing
│   ├── serializer_load_collectibles.h / .c  Coin, star, last-star, next-phase parsing
│   ├── serializer_load_enemies.h / .c       Enemy placement parsing
│   ├── serializer_load_hazards.h / .c       Hazard placement parsing
│   ├── serializer_load_surfaces.h / .c      Surface placement parsing
│   ├── serializer_load_climbables.h / .c    Vine, ladder, rope parsing
│   ├── serializer_load_layers.h / .c        Background/fog/foreground layer parsing
│   ├── serializer_load_config.h / .c        Optional rule/config parsing
│   ├── serializer_parse.h / .c  Shared TOML parse utilities
│   ├── serializer_save.c        TOML save: write_level_toml calls one writer per section family
│   └── serializer_types.h / .c  Enum/string conversion helpers
├── effects/
│   ├── fog.h / .c                Atmospheric fog overlay: init, slide, spawn, render
│   ├── parallax.h / .c           Multi-layer scrolling background: init, tiled render, cleanup
│   └── water.h / .c              Animated water strip: init, scroll, tile render
├── entities/
│   ├── spider.h / .c             Spider enemy: ground patrol, animation, render
│   ├── jumping_spider.h / .c     Jumping spider: patrol, jump arcs, floor-gap awareness
│   ├── bird.h / .c               Bird enemy: sine-wave sky patrol, animation, render (shared by both birds)
│   ├── faster_bird.h / .c        `FasterBird` (a `typedef` of `Bird`) and its FBIRD_* tuning
│   ├── fish.h / .c               Fish enemy: patrol, random jump arcs, render (shared by both fish)
│   └── faster_fish.h / .c        `FasterFish` (a `typedef` of `Fish`) and its FFISH_* tuning
├── hazards/
│   ├── spike.h / .c              Static ground spike hazard rows
│   ├── spike_block.h / .c        Rail-riding rotating spike hazard
│   ├── spike_platform.h / .c     Elevated spike surface hazard
│   ├── circular_saw.h / .c       Fast rotating patrol saw hazard
│   ├── axe_trap.h / .c           Swinging/spinning axe hazard
│   └── blue_flame.h / .c         Blue/fire flame hazards: rise/flip/fall cycle
├── input/
│   ├── input_backend.h / .c     raylib device sampling, versioned binding translation and command queue
│   ├── game_bindings.c           Reserved-key/button and configurable binding validation
│   ├── game_input.h / .c         Keyboard/gamepad action masks and cross-screen release latch
│   ├── game_events.h / .c        Semantic input and terminal action dispatch
│   ├── game_replay.h / .c        Deterministic command/replay-mask injection
│   └── game_web_input.h / .c     Browser/WebAssembly stale-key repair
├── levels/
│   ├── level.h                   Shared level definitions
│   ├── level_loader.h / .c       s_level_loaders table: LevelDef into GameWorld
│   ├── level_path.h / .c         Level path normalization and directory helpers
│   ├── level_physics.h / .c      Level physics override/default helpers
│   ├── level_ref.h / .c          Shared levels/<name>.toml rule for next_phase, campaigns and profile keys
│   ├── level_resources.h / .c    A level's own files: background, floor, water, fog, music
│   ├── level_session.h / .c      Active LevelDef storage, initial load and phase transitions
│   ├── campaign_catalog.h / .c   v1 campaign manifest: loading, the campaign rules, and saving (shared with the editor)
│   ├── level_start.h / .c        Playtest start points (--start-x / --start-checkpoint), shared with the editor
│   ├── phase_transition.h / .c   next_phase resolution and progress helpers
│   └── level_validate.h / .c     LevelDef count, schema-range and geometry validation
├── player/
│   ├── player.h / .c             Public API + high-level glue
│   ├── player_internal.h         Private frame/hitbox/coyote constants
│   ├── player_lifecycle.c        Init/render/hitbox/reset/cleanup/default physics
│   ├── player_input.c            Keyboard/gamepad/climb input sampling
│   ├── player_motion.h / .c      Horizontal acceleration/friction
│   ├── player_jump.h / .c        Jump buffering, coyote time, jump cut
│   ├── player_climb.h / .c       Vine/ladder/rope grab and climb helpers
│   ├── player_surfaces.h / .c    Surface collision helpers
│   └── player_animation.h / .c   Animation state/frame selection
├── render/
│   ├── game_render.h / .c        s_render_layers table: the frame, back to front
│   └── render_overlay.c          Foreground/overlay render helpers
├── screens/
│   ├── settings_menu.h / .c      Keyboard/gamepad options, remapping, and profile status
│   ├── start_menu.h / .c         Start menu screen with logo
│   └── hud.h / .c                HUD renderer: hearts, lives counter, score text
└── surfaces/
    ├── platform.h / .c           One-way platform pillar init and 9-slice rendering
    ├── float_platform.h / .c     Hovering platform: static, crumble, and rail behaviours
    ├── bridge.h / .c             Tiled crumble walkway: per-brick fall timers, render
    ├── bouncepad.h / .c          Shared bouncepad mechanics (squash/release animation)
    ├── bouncepad_small.h         Green bouncepad placement helper
    ├── bouncepad_medium.h        Wood bouncepad placement helper
    ├── bouncepad_high.h          Red bouncepad placement helper
    ├── rail.h / .c               Rail path builder, bitmask tile render, position interpolation
    ├── vine.h / .c               Climbable vine decoration
    ├── ladder.h / .c             Climbable ladder decoration
    └── rope.h / .c               Climbable rope decoration

New .c files in src/ or recognized source subdirectories are picked up by Makefile wildcards. New source directories need Makefile wildcard, compile-rule, and clean-rule entries.

There is no level-to-C exporter any more. The editor once wrote levels out as C source under src/levels/exported/; that exporter was removed when levels became TOML-only, and nothing builds or reads that folder now. .gitignore still lists it so a leftover local copy cannot be committed by accident. The debugging practice files in labs/debugging/ are also outside the build; see Debugging C.


main.c

Role: Parses program arguments, constructs one AppSession, and returns the session result. AppSession owns raylib startup/shutdown and cross-screen transitions.

For a beginner’s reading order, continue through core/app_session.c, core/game_loop.c, render/game_render.c, then a small entity such as collectibles/coin.c. The editor’s parallel entry points are editor_main.c, editor.c, editor_frame.c and editor_events.c.

Responsibilities

  • Parse startup, profile, experiment and smoke flags; see the complete Controls reference, including --seed, --profile, --continue, --no-save and --experiment
  • When the working folder holds no assets/, make typed paths absolute and move to the executable’s folder (asset_root_enter, shared/asset_root.c); the editor’s editor_main.c does the same
  • Delegate window, input and audio initialization to session_create
  • Route to the start menu, sandbox, or direct TOML level mode through session_create()
  • Run session_run(); native callers then destroy the session, while the browser callback frees it after terminal cleanup

Subsystem Init Order

Order Call Purpose
1 display_open / InitWindow One raylib window/context for the session
2 input_open Semantic input queue and canvas-scoped browser keyboard handlers
3 audio_open / InitAudioDevice Required audio device (no device means startup fails); screen assets load afterward

On failure at any step, all previously-succeeded subsystems are torn down before returning EXIT_FAILURE.


game.h

Role: Gameplay’s umbrella header: shared resource groups and GameState, plus the shared constants through game_constants.h. Gameplay modules use it; smaller shared helpers and editor modules also have their own focused headers.

Constants

The shared numbers live in src/game_constants.h, which includes nothing else. game.h includes it, so any file with game.h sees them; a file that needs only the numbers (most entity, hazard, surface and effect .c files) includes game_constants.h instead. Changing GameState, or one of the headers game.h pulls in for it, then does not recompile those files. See Constants Reference for full details.

Who includes game.h

Only files that read or write GameState fields: the gameplay .c files and the tests that build a GameState. A module header that merely passes GameState * around (game_camera.h, level_loader.h, game_render.h, …) includes game_fwd.h, a forward declaration of the name, instead; its .c file includes game.h itself. levels/level.h includes the entity headers that define its MAX_* limits rather than game.h, so the serializer, the editor and tools/level_check.c never see GameState. game_assets.h holds the shared sprite and sound slots, which AppSession and game_resources.c need without the rest. Touching game.h now recompiles 65 of the 231 objects of the headless game, editor and tests. An entity header such as spider.h still reaches about 160, because LevelDef needs its MAX_SPIDERS and almost everything reads levels.

#define WINDOW_TITLE  "Super Mango"
#define WINDOW_W      800
#define WINDOW_H      600
#define TARGET_FPS    60
#define GAME_W        400
#define GAME_H        300
#define TILE_SIZE     48
#define FLOOR_Y       (GAME_H - TILE_SIZE)   // = 252
#define GRAVITY       800.0f
#define WORLD_W       1600
#define FLOOR_GAP_W         32
#define MAX_FLOOR_GAPS      16
#define CAM_LOOKAHEAD_VX_FACTOR  0.20f
#define CAM_LOOKAHEAD_MAX  50.0f
#define CAM_SMOOTHING      8.0f
#define CAM_SNAP_THRESHOLD 0.5f

Includes

GameState is deliberately one struct that stores every entity array by value, so game.h includes exactly the headers whose structs or MAX_* constants that definition needs, plus game_constants.h — nothing else. Call-only helpers such as shared/platform.h (clock_millis, str_copy) are included by the .c files that use them. Inside, GameState is three named parts stored by value, world (the level being played), screen (overlays, routes, timing, input, tools) and assets (shared sprites and sounds), so gs->world.spiders[i] still leads straight to the data; the comment at the top of game.h explains the trade-off.

#include "shared/graphics.h"            // Texture2D, RenderTexture2D, IntRect
#include "shared/audio.h"               // MusicTrack pointer
#include "game_assets.h"                // GameAssets: shared sprites and sounds
#include "game_fwd.h"                   // the GameState typedef name
#include <stdint.h>                     // uint32_t, uint64_t fields
#include "player/player.h"              // Player struct
#include "surfaces/platform.h"          // Platform struct + MAX_PLATFORMS
#include "effects/water.h"              // Water struct
#include "effects/fog.h"                // FogSystem struct
#include "entities/spider.h"            // Spider struct + MAX_SPIDERS
#include "entities/fish.h"              // Fish struct + MAX_FISH
#include "collectibles/coin.h"          // Coin struct + MAX_COINS
#include "surfaces/vine.h"              // VineDecor struct + MAX_VINES
#include "surfaces/bouncepad.h"         // Bouncepad struct (shared mechanics)
#include "surfaces/bouncepad_small.h"   // Small bouncepad
#include "surfaces/bouncepad_medium.h"  // Medium bouncepad
#include "surfaces/bouncepad_high.h"    // High bouncepad
#include "screens/hud.h"                // Hud struct
#include "effects/parallax.h"           // ParallaxSystem
#include "surfaces/rail.h"              // Rail, RailTile
#include "hazards/spike_block.h"        // SpikeBlock
#include "surfaces/float_platform.h"    // FloatPlatform
#include "surfaces/bridge.h"            // Bridge
#include "entities/jumping_spider.h"    // JumpingSpider
#include "entities/bird.h"              // Bird
#include "entities/faster_bird.h"       // FasterBird
#include "collectibles/health_star.h"   // HealthStar (yellow/green/red)
#include "hazards/axe_trap.h"           // AxeTrap
#include "hazards/circular_saw.h"       // CircularSaw
#include "hazards/blue_flame.h"         // BlueFlame
#include "surfaces/ladder.h"            // LadderDecor
#include "surfaces/rope.h"              // RopeDecor
#include "entities/faster_fish.h"       // FasterFish
#include "collectibles/last_star.h"     // LastStar
#include "hazards/spike.h"              // SpikeRow
#include "hazards/spike_platform.h"     // SpikePlatform
#include "core/debug.h"                 // DebugOverlay

Function Declarations

int  game_init(GameState *gs);
void game_cleanup(GameState *gs);
int  game_load_next_phase(GameState *gs);
void game_complete_level(GameState *gs);

Runtime Core (core/app_session.c, core/game_lifecycle.c, core/game_loop.c, core/game_resources.c)

Role: app_session.c owns the app-level frame loop, window/audio lifetime, the shared sprites and sounds (GameAssets), the asset cache (AssetCache), menu/game swaps, in-place replay and shutdown. game_lifecycle.c owns active-game game_init / game_cleanup; game_loop.c owns game_frame; the shared sprite and sound tables and their load/unload live in game_resources.c, a level’s own files in levels/level_resources.c.

game_init(GameState *gs)

Creates all runtime resources:

  1. Screen-owned 400x300 render target in the existing session context (game_window_init)
  2. Shared textures for entities, hazards, collectibles and surfaces, plus sound effects: borrowed from the session, which loaded them with game_resources_load when its first game opened; only a GameState with no session loads its own here (owns_assets). Generated sounds come from tools/gen_sounds.py; the default water strip follows (water_init). From this step on, every file is acquired from gs->cache, the session’s asset cache, so a Replay finds it already loaded
  3. Player sprite and default physics (player_init)
  4. HUD (raylib’s built-in font, coin icon, borrowed star/player textures) and, with --debug, the debug overlay
  5. TOML level load from the selected campaign entry or direct --level path, then level-wide resources (level_resources_apply, held by gs->world): parallax, floor/platform tiles, foreground strip, fog, water, music and level contents; game_level_commit then trims the cache, unloading what the previous level left idle
  6. Optional --replay-script load
  7. Discover the first available raylib gamepad index

Returns 0 on success. If a required window, texture, level, or subsystem resource fails, it cleans up the partially initialized GameState and returns -1; the top-level runner reports EXIT_FAILURE.

game_frame(GameState *gs)

game_frame performs one frame: real frame time -> events -> 0..5 fixed 1/60 s update steps -> render. Real time feeds an accumulator (core/game_timing.c), so every step, live or replayed, has the same dt. AppSession is the only loop owner. See Architecture for routes and render order.

game_cleanup(GameState *gs)

Frees all resources in reverse init order.

The four tables

What happens to each kind of entity, and when, is listed in four tables. Each is a static const array of structs holding a function pointer, walked top to bottom by one loop, with one small function per row above it:

Table File Walked by A row
s_level_loaders levels/level_loader.c level_apply (every row), level_reset (the LOAD_EVERY_LIFE rows) { load_spiders, LOAD_EVERY_LIFE }
s_update_steps core/game_update.c game_update_active, once per fixed step, after the player has moved { "spiders", update_spiders }
s_damage_sources collision/game_collision.c game_collide, until the first hit { "spider", WORLD_COUNT(spider_count), spider_touches }
s_render_layers render/game_render.c game_render_frame, once per frame { 25, "spiders", render_spiders }

core/game_update.c holds StepInputs (the step time, the camera x and what the player landed on), one update_<things>() per row, the table, and game_update_active, which moves the player (core/game_player_step.c), samples authored checkpoints, tests the floor gaps, walks the table until it ends or the step costs a life, and moves the camera. Collision and the screen checkpoints are rows of the table. core/game_float_platforms.c (rider carry) and core/game_bridges.c (crumble start, debug log) keep the logic their rows call.

render/game_render.c holds one render_<layer>() per layer, the table, and game_render_frame, which clears the canvas, walks the table, then draws the pause/game-over/completion overlay and the settings panel on top and presents the frame. make docs-drift checks s_update_steps and s_render_layers against the Update Order and Render Order tables in Architecture.


Player Module (player/)

Role: Player character lifecycle, input, horizontal motion, jumps, climbables, surface collision, animation, rendering, hitbox, and reset. Public declarations live in player.h; private constants live in player_internal.h; implementation is split across focused .c files. See Player Module for the deep dive.

Key functions: player_init, player_apply_default_physics, player_handle_input, player_update, player_render, player_get_hitbox, player_reset, player_cleanup


levels/level.h, levels/level_loader.c, levels/level_physics.c, levels/level_validate.c, levels/phase_transition.c

Role: Level schema, TOML loading, authored-checkpoint validation, physics override application, phase switching, and count validation.

Key functions:

  • int level_load(GameState *gs, const LevelDef *def); – validate and copy a parsed level definition into runtime GameState; returns -1 without mutating current runtime state when runtime counts are invalid
  • level_apply(GameState *gs, const LevelDef *def) – the copy step alone, for a definition level_load_toml already validated; it cannot fail. The level session parses and validates a level once, checks its sprites, then swaps the heap-staged LevelDef in and applies it, so a failed Next Level never replaces the current level
  • level_reset(GameState *gs, const LevelDef *def) – restore mutable level state after death/retry; collected coins stay collected (Retry re-activates them)
  • s_level_loaders (static, in level_loader.c) – one row per placement array: its load_<things>() function and whether it runs once per level (LOAD_ONCE) or after every lost life too (LOAD_EVERY_LIFE). level_apply runs every row in order, level_reset only the LOAD_EVERY_LIFE ones
  • level_release_platform_tiles(GameState *gs) – release the platform tile textures to the asset cache; GameState.world.platform_tiles holds each distinct tile_path once and every platform naming it borrows that texture, so a level with 23 stone pillars decodes stone_platform.png once, and reloading the same level (Replay, F8) decodes nothing
  • level_load_toml(const char *path, LevelDef *def) – parse TOML into heap staging storage, run runtime validation, free TOML data, then assign the validated LevelDef to the caller. A LevelDef is about 16 KB; game load paths never keep one in a local variable (the browser stack is 64 KB), and level.h stops the build if it outgrows LEVEL_DEF_SIZE_BUDGET
  • level_apply_player_physics(Player *player, const LevelDef *def) – reset player movement tunables to engine defaults, then apply non-negative level overrides
  • level_validate_counts(const LevelDef *def, char *err, size_t err_size) – reject out-of-range array counts
  • phase_has_next, phase_next_path, phase_progress_save, phase_progress_restore – resolve level-completion next-phase paths and protect progress when staging phase transitions

LevelDef.checkpoints stores optional immutable CheckpointPlacement { x, y } records. The serializer requires finite numeric x and y; runtime validation enforces a maximum of MAX_CHECKPOINTS (99), unique in-world x coordinates strictly after the effective player start, and y coordinates in the logical canvas.

core/game_checkpoint.h / core/game_checkpoint.c

Resolves respawn state without mutating LevelDef. With authored records, the greatest crossed x coordinate becomes GameState.world.respawn_x / respawn_y before lethal collisions run. With no records, the legacy automatic screen-boundary checkpoint remains active; it walks left from the screen edge to the first column with solid floor and no static hazard, keeping the previous checkpoint when none is safe.

core/game_ghost.h / core/game_ghost.c / core/game_ghost_file.c

The time-trial ghost. game_ghost.c records one GhostSample (pixel position plus sprite cell and facing) after every fixed step (game_ghost_step, called from game_frame) and picks the best run’s sample for the same step (game_ghost_current; render_ghost in game_render.c draws it). game_ghost_take_run turns a finished attempt into a GameGhostTrack, refusing a continued run or one longer than GHOST_MAX_STEPS; Retry calls game_ghost_restart. game_ghost_file.c holds the strict TOML codec (game_ghost_decode / game_ghost_encode, fuzzed alongside the profile) and storage: a file next to the profile (game_ghost_file_path) or a browser localStorage entry. AppSession loads the best ghost when a level opens and saves a finished run that beats it.

core/game_resume.h / core/game_resume.c

Bridges gameplay and the profile’s one Continue point (GameResume, defined in game_profile.h). game_resume_capture copies the profile key, the level’s content hash (GameState.world.source_level_hash), respawn point, checkpoint index, score, lives, collected coins (one bit per coin) and level timer out of a running GameState. game_resume_apply refuses a point for another level, another version of the file, or a checkpoint, coin or position the level does not have; otherwise it restores those values and calls reset_current_level, the same reset a lost life uses, so the camera snaps there and enemies restart. AppSession decides when to record (new respawn point, pause, leaving part-way) and clear (level finished or lost) it.


screens/start_menu.h / screens/start_menu.c

Role: Start menu screen with start_menu_logo.png and a wrapped level list backed by the validated ordered catalog from levels/campaigns/main.toml. Each row shows the profile’s cleared mark, best time (m:ss.cc) and best coins (start_menu_level_row). Entries whose level file failed to load stay listed but greyed out, with their reason; Play refuses them. Its colours and button outlines come from start_menu_style, the settings panel’s settings_menu_style, so the high-contrast setting switches both; it draws its own buttons with those roles but keeps its own event loop rather than ui_button. The AppSession can reopen it after a terminal Level Select action without restarting the process.

Files it holds: start_menu_init creates the menu’s render target (owned by the menu) and acquires three things from the asset cache passed to start_menu_create(catalog, cache): the font handle, start_menu_logo.png and confirm_ui.qoa. start_menu_cleanup frees the render target, cuts a confirm sound that is still playing (sound_stop) and releases the three. AppSession keeps a reference of its own to each from the first menu until the session ends (session_keep_menu_files), so only the first menu of a session reads the two files; every later one, after Level Select, reads nothing. With cache == NULL (a menu made without a session, as tests do) the menu loads its own copies and frees them on cleanup.

Key functions: start_menu_create, start_menu_frame, start_menu_level_row, start_menu_format_time, start_menu_get_input_state, start_menu_close

core/game_terminal.h / core/game_terminal.c

Builds the single valid terminal-action list used by both rendering and input. Completion lists Next Level when a phase is pending, then Replay, Level Select, and Exit; final completion omits Next Level; game over lists Retry, Level Select, and Exit. Focus wraps through this list.

core/app_session.h / core/app_session.c

Owns one heap-allocated application session and its active menu or game screen. Without --level, it loads and retains the validated v1 campaign catalog; the menu uses the catalog’s names and ordered paths. A direct --level session bypasses the catalog. The first game it opens loads the shared sprites and sounds into session->assets; every game gets a copy of those pointers and the session unloads them once, in session_runtime_cleanup. It also owns session->cache, the asset cache every game acquires its level’s files from (and the player sheet, HUD icon and font), so Replay, Retry and going back into the same level read nothing from disk, and another level reads only the files it adds; session_runtime_cleanup clears the cache once. The start menu’s font, logo and confirm sound are in the same cache, with one reference held by the session itself so they stay loaded between menus. The session consumes explicit game routes after each rendered frame: next level stays in the current game; replay replaces the game with the same TOML path in place (native and browser alike); level select returns to the menu. It also keeps the profile’s Continue point current (session_track_resume), drops one whose level file changed before the menu opens, and applies it for the menu’s Continue and for --continue.


Enemy Modules (entities/)

entities/spider.h / entities/spider.c

Ground-patrol spider enemy with 3-frame walk animation. Reverses at patrol boundaries and respects sea gaps. Asset: spider.png.

entities/jumping_spider.h / entities/jumping_spider.c

Spider variant whose jumps are triggered by floor-gap edges, not a periodic timer. Asset: jumping_spider.png.

entities/bird.h / entities/bird.c

Slow sine-wave sky patrol bird. The flight, flap sound, hitbox and render code here serve both birds; each variant passes its tuning as a BirdSpec. Asset: bird.png.

entities/faster_bird.h / entities/faster_bird.c

Fast aggressive sine-wave sky patrol bird with tighter curves and quicker wing animation. It is a Bird with different BirdSpec tuning, run by the code in bird.c. Asset: faster_bird.png.

entities/fish.h / entities/fish.c

Jumping water enemy that patrols the bottom lane and leaps on random arcs. Asset: fish.png.

entities/faster_fish.h / entities/faster_fish.c

Fast fish variant with higher jumps and faster patrol speed. It is a Fish with different FishSpec tuning: update, render and hitbox code live once in fish.c (the same pattern as bird.c and faster_bird.c). Asset: faster_fish.png.


Hazard Modules (hazards/)

hazards/blue_flame.h / hazards/blue_flame.c

Erupting flame hazard from floor gaps. Cycles through waiting -> rising -> flipping (180 degree rotation at apex) -> falling. Blue and fire visual variants use blue_flame.png and fire_flame.png.

hazards/spike.h / hazards/spike.c

Static ground spike rows placed along the floor. Asset: spike.png.

hazards/spike_block.h / hazards/spike_block.c

Rail-riding rotating hazard (360 degrees/s spin). Travels along rail paths, pushes player on collision. Asset: spike_block.png.

hazards/spike_platform.h / hazards/spike_platform.c

Elevated spike surface hazard. 3-slice rendered. Asset: spike_platform.png.

hazards/circular_saw.h / hazards/circular_saw.c

Fast rotating patrol saw hazard (720 degrees/s). Patrols horizontally. Asset: circular_saw.png.

hazards/axe_trap.h / hazards/axe_trap.c

Swinging pendulum or spinning axe hazard. Two behaviour modes: swing (60 degree amplitude) and spin (180 degrees/s). Asset: axe_trap.png.


Collectible Modules (collectibles/)

collectibles/coin.h / collectibles/coin.c

Gold coin collectible. AABB pickup awards the level’s coin_score (100 by default); crossing score_per_life grants a bonus life. Asset: coin.png. coin_streak_pitch raises the pickup chime a semitone per quick pickup (CoinStreak, kept in GameScreen and fed only the world’s sim_steps).

collectibles/health_star.h / collectibles/health_star.c

Yellow, green and red star health pickups: one HealthStar struct, renderer and hitbox shared by the three GameState arrays (star_yellows, star_greens, star_reds); each restores one heart. The texture picks the colour. Assets: star_yellow.png, star_green.png, star_red.png.

collectibles/last_star.h / collectibles/last_star.c

End-of-level star collectible. Asset: last_star.png.


Surface Modules (surfaces/)

surfaces/platform.h / surfaces/platform.c

One-way pillar platforms built from 9-slice platform tiles. Player can jump through from below and land on top. Default asset: grass_platform.png.

surfaces/float_platform.h / surfaces/float_platform.c

Hovering surfaces with three modes: static, crumble (falls after 0.75s), and rail (follows a rail path). 3-slice rendered. Asset: float_platform.png.

surfaces/bridge.h / surfaces/bridge.c

Tiled crumble walkway. Each brick the player stands on falls BRIDGE_FALL_DELAY (0.2 s) after it is first touched; bricks the player never stands on stay. Asset: bridge.png.

surfaces/bouncepad.h / surfaces/bouncepad.c

Shared bouncepad mechanics: squash/release 3-frame animation.

surfaces/bouncepad_small.h

Green bouncepad – small jump height. Asset: bouncepad_small.png.

surfaces/bouncepad_medium.h

Wood bouncepad – medium jump height. Asset: bouncepad_medium.png.

surfaces/bouncepad_high.h

Red bouncepad – high jump height. Asset: bouncepad_high.png.

surfaces/rail.h / surfaces/rail.c

Rail path system. Builds closed-loop and open-line rail paths from tile definitions. 4x4 bitmask tileset for rendering. Used by spike blocks and float platforms. Asset: rail.png.

surfaces/vine.h / surfaces/vine.c

Climbable vine decoration. Player can grab, climb up/down, drift horizontally, and dismount with a jump. Assets: vine_green.png, vine_brown.png.

surfaces/ladder.h / surfaces/ladder.c

Climbable ladder decoration. Same climbing mechanics as vines. Asset: ladder.png.

surfaces/rope.h / surfaces/rope.c

Climbable rope decoration. Same climbing mechanics as vines. Asset: rope.png.


Environment Modules (effects/)

effects/water.h / effects/water.c

Animated scrolling water strip at the bottom of the screen. 8 frames tiled seamlessly. Asset: water.png.

effects/fog.h / effects/fog.c

Atmospheric fog overlay. Semi-transparent foreground layers slide across the screen with random direction, duration, and fade-in/out. Default assets: assets/sprites/foregrounds/fog_1.png, fog_2.png; volcanic levels can use fog_fire_1.png, fog_fire_2.png, and smoke.png.

effects/parallax.h / effects/parallax.c

Multi-layer scrolling background configured per TOML level. Current assets include blue-sky/cloud/glacial layers plus volcanic sky, mountains, and smoke layers under assets/sprites/backgrounds/. parallax_layer_offset is the scroll arithmetic on its own: cam_x × speed, wrapped into [0, tex_w) (never negative), so parallax_render only tiles from -offset.


System Modules

screens/hud.h / screens/hud.c

HUD renderer. Draws heart icons (health), player icon + lives counter, coin icon + score. Assets: star_yellow.png (hearts), hud_coins.png (coin icon), player.png (lives icon); text uses raylib’s built-in font.

core/debug.h / core/debug.c

Debug overlay (activated with --debug flag): collision hitbox visualization for all entities, the performance line, the one-line player readout and the event log. debug_draw_panel draws the small translucent text panels every debug readout shares, and debug_draw_box the box alone. The performance line shows the measured FPS, the frame interval in milliseconds (the time between rendered frames, not CPU utilization) and, where the OS reports it, resident memory in MB.

Shared raylib boundaries (shared/ and input/input_backend.*)

Read Learning purpose
shared/graphics.h, shared/graphics.c Window setup (including its icon), sprite pivots/flips and logical-canvas presentation
shared/texture.c Texture ownership: a malloc’d handle, NULL on failure, one owner unloads it
shared/asset_cache.h, shared/asset_cache.c A reference count: holders per file, idle entries, and why freeing waits for asset_cache_trim
shared/geometry.h Integer, half-open hitboxes; overflow-safe edge arithmetic
shared/raymath_inline.h Why raymath is included static inline, and when using it would change replay numbers
shared/audio.h, shared/audio.c Owned samples, borrowed alias data, per-play volume and music pumping
shared/text.h, shared/text.c Borrowing raylib’s built-in font and drawing glyphs from its one atlas texture
shared/ui.h, shared/ui.c Immediate-mode calls, stable widget IDs, staged edits and labels drawn from the font atlas
shared/platform.h, shared/platform.c Monotonic elapsed time, UTF-8 copying and caller-owned OS paths
shared/asset_root.h, shared/asset_root.c Why relative paths depend on the working folder, and how startup finds the asset root
shared/utf8.h What makes a UTF-8 byte sequence valid (overlong forms, surrogates, truncation)
shared/hex.h Why 64-bit values are stored as hex strings, and a reader that refuses what the writer never produces
shared/printf_format.h How a compiler attribute lets our own printf-style wrappers get format-string checks
input/input_backend.h, input/input_backend.c Ordered events versus held state, logical pointer mapping and saved-binding translation

core/entity_utils.h / core/entity_utils.c

Shared entity helper functions used across multiple modules.

core/game_score.h / core/game_score.c

Shared score-award helper. Adds points and grants one bonus life for every crossed score_per_life threshold while guarding invalid bonus-life cadence values.

levels/level.h

Shared level definitions and constants.

levels/level_loader.h / levels/level_loader.c

Copies a validated LevelDef into gs->world: one small load_<things>() per placement array, listed in the s_level_loaders table in the order they run.

levels/level_physics.h / levels/level_physics.c

Shared physics override/default helpers for player movement and camera lookahead.

levels/level_ref.h / levels/level_ref.c

level_ref_valid() defines the one level-reference rule shared by [last_star].next_phase, campaign manifest entries and profile result keys: a direct levels/<name>.toml child without subdirectories, Windows-reserved characters or device names. tools/validate_levels.py mirrors it.

levels/campaign_catalog.h / levels/campaign_catalog.c

The campaign manifest (levels/campaigns/main.toml) and its catalog. campaign_catalog_load() is what the start menu calls: campaign_catalog_read() parses the manifest (version, a nonempty list of valid, unrepeated levels/<name>.toml paths) and loads each listed level with campaign_entry_load(), then the order rule (each next_phase names the next entry, the last none) marks entries unavailable, and a catalog with nothing playable is refused. The editor’s Campaign view applies the same rules to its edited, in-memory catalog with campaign_catalog_check() and writes the list with campaign_manifest_write_temp(): the serializer’s serializer_write_temp() writes a temporary file next to the manifest, which the view installs only after every changed level file has been written the same way.

levels/level_start.h / levels/level_start.c

Resolves the game’s --start-x / --start-checkpoint request (the editor’s Playtest from here) against a LevelDef: level_ground_top_at() finds the highest surface a player dropped into a column lands on (floor unless over a gap, pillars, bridges, non-rail float platforms), and level_start_resolve() turns the request into a spawn point plus the authored checkpoint already behind it, or an error for an x outside the level, an x over a gap with nothing above it, or a missing checkpoint. level_session.c applies it after the first level loads; the editor links the same file to refuse a start before launching.

levels/level_validate.h / levels/level_validate.c

Bounds validation for LevelDef counts before runtime data is copied into GameState. level_validate.h shares the path-shape rules (level_path_has_parent_segment, level_path_has_control_char) with the editor’s own checks, and reports where an error is: every validator message starts with the TOML path of the bad value, level_issue_location_parse() reads it back into a LevelIssueLocation (path, index, field), and level_validate_runtime_at() returns it with the first error so the editor can make its messages clickable. level_load_toml_explained() (serializer) says why a load failed: the TOML syntax error, the schema error or every runtime error, each with its line (serializer_toml_line() follows a message’s TOML path through the parsed tree). level_validate_runtime_each() runs the same rules but calls back once per error instead of stopping at the first (the game keeps the single-error level_validate_runtime()); the editor lists them all. The derived positions both programs need (level_platform_top_y, level_axe_trap_y, level_circular_saw_y) are inline helpers in level.h.