This codebase is easiest to read as a layered architecture. Each layer owns a
clear responsibility, and control flow moves through them in a fixed order from
startup to shutdown.
System structure
Six layers around one runtime owner and one frame loop
This architecture is easiest to understand as layered responsibilities: bootstrap, parsing,
runtime state, input/update, rendering, then cleanup. The control flow crosses those layers in
a strict startup-to-shutdown order.
1 Bootstrap
entrypoint, CLI validation, startup sequencing, and loop setup
main.cinit_app.c
2 Parsing & Validation
config parsing, map loading, duplicate checks, and closure validation
parse_file.cparse_config.cparse_textures.cparse_colors.cparse_map.cvalidate_map*.c
3 Runtime State
`t_app`, player vectors, MLX handles, frame image, and bonus runtime ownership
structs.hinit_app.cinit_mlx.c
5 Raycasting & Render
background, DDA traversal, wall projection, texture draw, overlays, and final present
render_frame.craycast.craycast_draw.cdraw_vignette.c
6 Cleanup & Errors
centralized shutdown, guarded destruction, and explicit error reporting
shutdown.cerror.cmemory.c
2 init_app(&app) NULL init 3 bonus_levels_init(&app) bonus 4 parse_file(&app, path) parsing 5 init_player_vectors(&app) player 6 init_mlx(&app) MLX alloc 8 mlx_loop_hook(draw_frame) loop 9a timing + input + bonus update draw_frame 9b background + raycast + overlays draw_frame 9c vignette + HUD + mlx_put_image draw_frame 10 close_window -> free_app -> exit shutdown
parsing finishes before MLX init invalid maps never reach window or texture initialization
`t_app` is the runtime owner persistent state and resource cleanup converge on one root object
mandatory and bonus share one runtime flow bonus features integrate through modules and no-op safe fallbacks
`free_app(...)` covers all exits startup failure, runtime close, and fatal exits rely on the same cleanup model
one final image blit per frame render writes to buffers first, then presents once through MLX
The current codebase is organized around six practical layers:
- bootstrap
- parsing and validation
- runtime state
- input and update
- raycasting and rendering
- cleanup and errors
That split mirrors the execution order of the engine.
The bootstrap layer starts in main.c.
Its job is to:
- validate CLI arguments
- initialize the global app state
- prepare bonus level context
- resolve the active map path
- launch parsing and graphics initialization
- bind MLX hooks and enter the loop
The parsing layer transforms raw .cub text into validated runtime data.
It is responsible for:
- reading the file
- parsing required headers
- detecting duplicates or malformed values
- validating map symbols and enclosure
- producing the final map storage
This stage completes before MLX initialization starts.
The ownership root is t_app.
It contains:
- parsed config
- validated map
- player position and camera basis
- MLX handles and images
- mandatory textures
- bonus runtime state
That is why both rendering and cleanup can stay centralized.
Once startup succeeds, the runtime loop repeatedly:
- updates timing and input
- updates bonus systems
- renders world content
- draws overlays
- presents one final image
So the architecture is shaped both by file boundaries and by frame lifecycle.
The last layer centralizes shutdown and error handling.
That includes:
- explicit error reporting
- ordered MLX teardown
- map/config release
- bonus shutdown delegation
- normal close handling
That is what keeps partial init failures and runtime exits manageable.
srcs/core/main.c
srcs/parsing/
srcs/validation/
srcs/input/
srcs/render/
srcs/core/shutdown.c
srcs/tools/utils.c