map-level representation Door symbols mark where runtime door entities must be created.
Doors extend the static .cub map with runtime entities. The map only marks
where a door exists; the bonus subsystem owns the mutable state used by
interaction, collision, raycasting, rendering, and minimap feedback.
Interactive doors
Door tiles are discovered from the map, but animation and passability are driven by
runtime door state under app->bonus.doors.
map-level representation Door symbols mark where runtime door entities must be created.
runtime door state Static map text is not rewritten; mutable behavior lives in door structs.
int x, y
int state
double open_progress // [0..1]
double timer state machine Interaction and timers drive deterministic transitions every frame.
if (state == OPENING)
open_progress += DOOR_SPEED * delta_time
if (state == CLOSING)
open_progress -= DOOR_SPEED * delta_time interactive passability simulation Use the interaction button like key E. The same progress value drives visual opening and passability.
State: CLOSED · progress: 0.00
passability query Movement and ray traversal stay coherent because both ask the door subsystem.
// offset = hit fraction inside the door tile [0..1]
return (offset < door->open_progress); runtime invariants These properties make doors safe to defend during evaluation.
Door-capable tiles come from BONUS_DOOR_SET, currently A and D. During
bonus initialization, each matching map tile is converted into one runtime door
entry under app->bonus.doors.
The original map.grid is not rewritten every frame. Instead, each door keeps
its own map position, state, open_progress, and timers. This separation keeps
the static map stable while still allowing animated movement and passability.
Door interaction is triggered from the bonus input path, typically with E.
When a valid door is found in interaction range, the door state machine changes
state and progresses over time using delta_time.
The state flow is:
closed becomes openingopen_progress increases until it reaches 1.0open can later become closingopen_progress decreases back to 0.0closedCollision and ray traversal do not rely on the raw map character alone. They query the door subsystem to decide whether the current door slice is blocking or passable.
This is the key invariant: movement, DDA hits, visual door opening, and minimap feedback all derive from the same runtime door progress. That prevents cases where the player can walk through a visually closed door or where the raycaster draws a door differently from the collision system.
The door subsystem is responsible for:
At runtime, door handling preserves:
open_progresssrcs_bonus/doors/doors_api.csrcs_bonus/doors/doors_logic.csrcs_bonus/doors/doors_query.csrcs_bonus/doors/doors_utils.csrcs/render/raycast.csrcs/render/raycast_draw.cinclude/structs_bonus.hinclude/defines_bonus.h