# 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<T>` 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<FollowPlayerIntent>,
pub stop_following_intents: Storage<StopFollowingIntent>,
pub camera_transition_intents: Storage<CameraTransitionIntent>,
```

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<T>` 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
