# Intent-Based Architecture Systems communicate through **intents** — one-frame typed data in shared queues — not through direct function calls. This decouples producers from consumers: the system that wants something to happen does not know or care which system processes it. ## The Pattern ``` Producer World (shared state) Consumer ──────── ──────────────────── ──────── detects dialog change ↓ insert(camera, CameraTransitionIntent) ↓ camera_transition_intents camera_intent_system reads it sets up CameraTransition component removes intent ``` An intent is ephemeral (one frame). The state change it triggers (a component mutation) persists. ## Intent Types Intents are plain structs stored in `Storage` on `World`, keyed by target entity: ```rust // components/intent.rs pub struct FollowPlayerIntent; pub struct StopFollowingIntent; pub struct CameraTransitionIntent { pub duration: f32 } ``` ```rust // world.rs pub follow_player_intents: Storage, pub stop_following_intents: Storage, pub camera_transition_intents: Storage, ``` Adding a new intent = one struct + one storage field. Nothing else changes. ## Producing Intents Any code with access to the storage can submit. The entity is the target: ```rust // Event handler wants camera to follow player — doesn't call camera functions world.follow_player_intents.insert(camera_entity, FollowPlayerIntent); // Dialog system detects state change — doesn't know how transitions work world.camera_transition_intents.insert(camera_entity, CameraTransitionIntent { duration: 0.8 }); ``` Producers don't import consumer modules. They only know about the intent type and the storage. ## Consuming Intents A consumer system reads, acts, and removes: ```rust pub fn camera_intent_system(world: &mut World) { // 1. Read let follow_entities = world.follow_player_intents.all(); for entity in follow_entities { // 2. Act — all the follow setup logic lives here start_camera_following(world, entity); // 3. Remove (consume) world.follow_player_intents.remove(entity); } // ... same for stop_following_intents, camera_transition_intents } ``` The consumer owns the implementation. `start_camera_following` is a private function inside the camera module — no other module can call it directly. ## Why This Matters **Decoupling.** The dialog system doesn't import camera functions. It inserts a `CameraTransitionIntent`. If the camera system changes how transitions work, the dialog system is unaffected. **Multiple producers, same path.** Editor toggle, dialog state changes, and future cutscene systems all produce the same `CameraTransitionIntent`. They all go through the same processing. No special cases. **Testability.** To test camera transitions: insert a `CameraTransitionIntent`, call `camera_intent_system`, check the result. No need to simulate dialog state or editor toggles. **Additive behavior.** To add a new reaction to `StopFollowingIntent` (e.g., play a sound), write a new system that reads the intent before the camera system consumes it. No existing code changes. ## Execution Order The main loop is a flat pipeline. Order encodes causality: ``` Input + intent generation (camera_input, player_input, dialog_transition_detect) ↓ Intent processing (camera_intent_system) ↓ Camera behavior (noclip, dialog_camera, follow, transition, ground_clamp) ↓ Editor overlay (UI only — not a game system) ↓ Fixed-step physics (state_machine, physics, triggers, dialog) ↓ Per-frame systems (state_machine, rotate, trees, spotlights, snow) ↓ Render ``` Moving a system changes the data flow. The order is the contract. ## Systems Also Self-Gate Because intents express what should happen, systems naturally have nothing to do when their data is absent: - `camera_follow_system` — no `FollowComponent` = no work - `dialog_camera_system` — no active bubbles = no work - `player_input_system` — camera not following = no player input - `camera_noclip_system` — camera has `FollowComponent` = skip The main loop doesn't branch on mode flags. Systems check their own data. ## When to Use Intents vs. Direct Mutation | Situation | Approach | |---|---| | One system wants another to do something | Intent | | A system updating its own components | Direct mutation | | Per-frame continuous computation | Components + tick system | | Persistent state (is the camera following?) | Component (`FollowComponent`) | | One-shot request (start following) | Intent (`FollowPlayerIntent`) | ## Adding New Intents 1. Define the struct in `components/intent.rs` 2. Add a `Storage` field to `World` (+ `new()` + `despawn()`) 3. Producers insert into the storage 4. A consumer system reads, acts, and removes 5. Done — no other code changes