Home · Architecture · Combat · Extensions · Known gaps · Source index
Architecture Overview
Shape of the program
The current program is a single-threaded Macroquad game. World owns all mutable simulation state. Systems run in a fixed sequence, behaviors receive a read-only spatial view plus a write-only command buffer, and registries build trait-object content from typed IDs.
main loop
|
+-- systems -------- direct local entity updates
| |
| +-- behavior dispatch
| | read: EnemySnapshot + Grid
| | write: CommandBuffer
| v
+------ World::apply_commands
|
+-- mutate world
+-- produce GameEvent
|
+-- bounded delivery waves
Despite the hecs dependency, this is not currently an ECS. Entities are plain structs stored in Vecs, except the singleton tower and player. See src/world.rs.
Module responsibilities
| Area | Principal source | Current role |
|---|---|---|
| Entrypoint | src/main.rs | Window configuration, frame clock, screen input |
| Orchestration | src/systems.rs | Phase order, simulation passes, rendering, crate UI |
| State and mutation | src/world.rs | World ownership, spawning, IDs, command application, events |
| Deferred writes | src/command.rs | Command and CommandBuffer |
| Hook capabilities | src/context.rs | Shared and event-specific contexts, damage accumulator |
| Extensible hooks | src/behavior/ | Trait families, cloning, dispatch |
| Content construction | src/registry.rs | Generic registry, typed IDs, built-in definitions |
| Domain entities | src/enemy.rs, src/tower.rs, src/pool.rs | Runtime data and local methods |
| Concrete content | src/weapons/, src/enemies/, src/pools/ | Weapon, enemy, movement, split, pool implementations |
| Query support | src/spatial.rs | Enemy snapshots, uniform grid, nearest/radius/hit queries |
| Utilities | src/rng.rs, src/geometry.rs | Deterministic streams and vector helpers |
Ownership and borrowing strategy
Each behavior pass follows run_system:
- Fork a deterministic child RNG.
- Snapshot all enemies.
- Rebuild the world's uniform grid from that snapshot.
- Iterate a mutable entity vector while behavior hooks query the immutable snapshot.
- Append requested cross-entity changes to a command buffer.
- End the borrow, then apply commands and deliver events.
This “read by snapshot, write by ID” boundary avoids re-entrant world mutation and Rust borrow conflicts. Snapshot data may be stale within a pass, but command application resolves IDs against live world state. src/spatial.rs documents and implements this split.
Identity and content
Runtime entities use monotonically allocated u64 EntityIds. Content uses a separate generic Id<K> whose marker type prevents mixing weapon, enemy, damage, and status IDs. A registry interns stable string names and stores factories, display names, and priorities. Factories produce fresh enemies or boxed behavior objects; no serialization or dynamic loading is live.
Architectural status
Live
- Vector-owned enemies, projectiles, animations, tower, and player.
- Snapshot/grid query layer.
- Deferred commands and bounded event waves.
- Projectile, enemy, and status behavior dispatch.
- Typed weapon/enemy/damage/status registries.
Partial
- Screen state exists, but simulation continues under the crate.
- Damage types are IDs in accumulators and resistance maps, but their registered strategy objects are never consulted.
- Ground pools can be constructed and commanded into the world, but have no complete lifecycle.
- Event handling is centralized but only three built-in variants exist;
Customis ignored.
Unwired
pool_passis never called bystep_world.ProjectileBehavior::on_fire, allTowerBehaviorhooks, andTower.mods.FireContext, non-lifetime expiration reasons, Lua/Serde/Strum/HECS integration.
For exact temporal ordering, see Frame Lifecycle. For extension contracts and their actual reachability, see Behavior System.
Home · Architecture · Combat · Extensions · Known gaps · Source index