The engine is small enough that the main data model can be described directly.
What matters most is not just which structs exist, but which ones own long-lived
state, which ones are temporary, and which modules are allowed to mutate them.
Runtime ownership
Global owner, local temporaries, and per-module mutation rules
The engine relies on a small set of core structs. `t_app` owns the runtime, temporary structs
stay local to parsing or rendering, and each subsystem only mutates the part of the state it
is responsible for.
t_config config owns
t_map map owns
t_player player
t_input input
void * mlx_ptr / win_ptr MLX
t_img frame MLX
t_img tex_no / tex_so / tex_we / tex_ea MLX
int win_w / win_h · floor_color / ceiling_color
double dir_x/y · plane_x/y
double delta_time · last_frame_time
t_bonus_ctx bonus bonus
mutation policy
render writes frame buffers, input writes player/camera, parsing writes config/map,
bonus writes `app->bonus.*` only
lifetime `init_app` → parsing → MLX init → loop → `free_app`
t_ray stack · one column · discarded after draw
double ray_dir_x / ray_dir_y
int map_x / map_y
double delta_x / delta_y
double side_x / side_y
int step_x / step_y
int side
double perp_dist
int draw_start / draw_end
lifetime = one rendered column
t_parse_headers stack · parse_file only
char ** lines
int line_count
int * map_start
const char * map_path
t_bfs_context heap temp · validation only
char ** lines / visited
int * qx / qy
t_size qcap / head / tail
int px / py / leak / alloc_fail
t_player spawn + movement state
double x / y
char orientation
t_input per-frame intent state
int forward / backward / left / right
int turn_left / turn_right
int mouse_dx / mouse_last_x / mouse_ready
t_img MLX image wrapper
void * img_ptr
char * addr
int bpp / line_len
int width / height / endian
t_bonus_ctx embedded in t_app
t_bonus_assets assets
t_bonus_retro retro
t_bonus_doors doors
t_bonus_levels levels
t_bonus_pickups / t_bonus_sprites pickups / sprites
t_bonus_stats / t_bonus_hud stats / hud
bonus subsystems mutate only their own sub-structs
renderwrites `frame.addr`, reads most of the rest
inputwrites `input`, `player.x/y`, and `dir/plane`
parsingwrites config strings and `map.grid`
bonuswrites only `app->bonus.*` and reads shared world state
The most important types in the current codebase are:
t_app
t_ray
t_map
t_player
t_input
t_img
- bonus runtime structs grouped under
t_bonus_ctx
Together they define ownership, per-frame state, and render-local working data.
t_app is the runtime owner.
It is:
- created on the stack in
main
- initialized by
init_app
- filled progressively during startup
- released by
free_app
That is why parsing, rendering, bonus systems, and cleanup all converge on the
same root object.
Not every struct should live for the whole runtime.
Examples of temporary data:
t_ray for one rendered column
- parse context structs used only during parsing
- BFS validation context used only during map closure checks
Examples of persistent runtime data:
- map storage
- player state
- input state
- MLX images
- bonus subsystem state
This distinction keeps ownership clear.
The codebase stays understandable because write access is effectively scoped by
subsystem:
- parsing writes config and final map storage
- validation writes the discovered player spawn before vector initialization
- input updates movement and camera state
- render writes frame buffers
- bonus modules mutate only bonus-owned runtime state
That keeps side effects localized and easier to explain.
This structure model matters during evaluation because it answers three
questions cleanly:
- who owns the data
- how long does it live
- who is allowed to mutate it
That is usually more useful than listing every field mechanically.
include/structs.h
include/structs_bonus.h
srcs/core/init_app.c
srcs/render/raycast.c
srcs/validation/validate_closed_setup.c