td

FrameLifecycle
Login

FrameLifecycle

Home · Architecture · Combat · Extensions · Known gaps · Source index

Frame Lifecycle

Top-level frame

src/main.rs clamps Macroquad's frame delta to at most 0.05 seconds, reads wall-clock time, and then performs:

dt = min(get_frame_time(), 0.05)
step_world(world, dt)
draw_simulation(world)
action = weapon_crate(...) if crate is open
if no crate action and Space: try open crate
if action: apply_action
if Q: exit(1)
next_frame().await

step_world is unconditional. Therefore the crate overlay is live UI over a continuing simulation, not a pause screen.

Simulation order

src/systems.rs advances world.now and runs this exact order:

Phase Live work Important deferred consequences
1. Wave Decrement timer; spawn one blob when due and below 8 enemies Spawn is direct, not commanded
2. Enemies Enemy hooks, status hooks, then position integration DoT damage and center despawns apply after the whole pass
3. Tower Aim at nearest live-vector enemy; cool down; create shot Newly dead-but-unswept enemies may still be targeted
4. Projectiles Projectile ticks, movement, one enemy/wall collision, lifetime check Hit damage/status commands apply after all projectiles
5. Death sweep Run death hooks, emit kill events, then retain alive enemies Splits, VFX, and XP are applied through commands/events
6. Animations Increment elapsed, call draw method, remove expired This phase also draws once before render proper

Notably, the defined pool_pass is absent from this sequence, so ground pools never tick.

A behavior pass

Enemy, projectile, and death phases use the same run_system shell:

create CommandBuffer
fork world RNG
run pass:
  collect EnemySnapshot
  rebuild Grid
  create SpatialQuery
  dispatch hooks / mutate current entity
  append commands
apply_commands
deliver_events

Commands generated by one entity do not mutate the vector while it is being iterated. They become visible only after that phase. Each call builds its own snapshot; consequently later phases see commands applied by earlier phases.

Enemy phase details

For each enemy:

dispatch_enemy_tick
dispatch_status_tick
enemy.pos += enemy.vel * dt

SeekCenter sets velocity and emits DespawnEnemy when close enough. Statuses queue Damage commands; they are retained unless on_tick returns true. Because commands apply after iteration, all statuses scheduled for the pass run even if an earlier command will kill the enemy.

Projectile phase details

For each projectile:

  1. Dispatch on_tick.
  2. Integrate position.
  3. Build a hit context.
  4. If an enemy overlaps, dispatch all on_hit_enemy hooks, queue accumulated damage and statuses, and consume unless reflection or pierce says otherwise.
  5. Otherwise, if outside arena bounds, dispatch on_hit_wall.
  6. Independently check lifetime, dispatch on_expire(Lifetime), and mark expired.
  7. Retain projectiles not marked expired.

Only one enemy can be hit per projectile per frame. A consumed projectile can still reach the lifetime branch in that same iteration. The default shot lifetime is 1000 simulation seconds.

Death and event timing

Damage application can immediately push EnemyKilled(Corpse) into an event list. Event delivery then grants XP and spawns death VFX, but the dead Enemy remains in the vector until death_sweep.

During death_sweep, every dead enemy runs on_death, emits another EnemyKilled(Corpse), and is then retained away. Thus the current live path can emit the kill event twice for the same death: once in apply_commands(Command::Damage) and once in death_sweep. Since the kill handler grants XP and VFX, rewards/animations can be duplicated. This is current behavior, not an intended contract.

After sweeping, pending level-ups auto-open a crate unless the player manually closed one and the screen is Playing.

Rendering order

draw_simulation clears, draws arena and spawn markers, tower, enemies, projectiles, animations, then HUD. animation_system also calls animation.animate() earlier in the frame, before the later clear, making that first draw invisible in normal rendering. The crate cards are drawn after the simulation and HUD.

See Commands and Events for cascade semantics and World and Entities for storage.

Home · Architecture · Combat · Extensions · Known gaps · Source index