# EditScene Feature - Agent Guide

This guide covers the `src/features/editScene` sub-project: the OGRE3D/Flecs scene
editor and the `--game` playable mode built on top of it.

## Overview

`editScene` is a self-contained executable (`editSceneEditor`) that can run in two
modes:

- **Editor mode** (default): load, edit and save JSON scenes through ImGui panels.
- **Game mode** (`--game`): present a startup menu, load a base scene, and let the
  player control a character with save/load support.

The feature is built from `src/features/editScene/CMakeLists.txt` and links against
OGRE (Bites, Overlay, MeshLodGenerator), Flecs, nlohmann/json, SDL2, Jolt and
OgreProcedural. Recast/Detour is built from the copy in
`src/features/editScene/recastnavigation`.

## Build & Run

The CMake target is `editSceneEditor`. The working directory for the runtime must
be the executable's own directory because resources are copied next to it at build
time.

```bash
# From the project root
cmake -B build-vscode -S . -DCMAKE_PREFIX_PATH=/path/to/ogre-sdk
cmake --build build-vscode --target editSceneEditor -j4

# Run editor
cd build-vscode/src/features/editScene
./editSceneEditor

# Run game mode (loads the configured base scene through the startup menu)
./editSceneEditor --game

# Auto-load a scene in editor mode
./editSceneEditor /path/to/scene.json
```

The main test target is `component_lua_test`:

```bash
cmake --build build-vscode --target component_lua_test -j4
./build-vscode/src/features/editScene/component_lua_test
```

## Entry Point & Modes

- `main.cpp` parses `--game` and `--debug-buoyancy`, sets the mode on
  `EditorApp`, calls `initApp()` and enters the OGRE render loop.
- `EditorApp` owns all systems and implements `frameRenderingQueued()`, which is
the per-frame update function.
- Modes/states are also exposed globally through `GameMode.hpp` so systems can
query `editScene::isGameMode()` / `editScene::isGamePlaying()` without a pointer
to `EditorApp`.

| Mode | Purpose |
|------|---------|
| `EditorApp::GameMode::Editor` | Free-camera scene editing, full UI panels |
| `EditorApp::GameMode::Game` | Playable mode with menu / playing / paused states |

| Game play state | Purpose |
|-----------------|---------|
| `Menu` | Startup menu, game not running yet |
| `Playing` | Player controller and gameplay systems active |
| `Paused` | Pause menu / character sheet open, gameplay systems frozen |

## Frame Update Order

`EditorApp::frameRenderingQueued()` updates systems in this order when not paused:

1. `PlayerControllerSystem` (only in `Playing`)
2. `CharacterSpawnerSystem`
3. `CharacterSlotSystem`
4. `AnimationTreeSystem` / `BehaviorTreeSystem`
5. `PathFollowingSystem`
6. `ProceduralMeshSystem`
7. `RoomLayoutSystem`
8. `CellGridSystem`
9. `NormalDebugSystem`
10. `NavMeshSystem`
11. `SmartObjectSystem`
12. `GoapPlannerSystem`
13. `GoapRunnerSystem`
14. `ActuatorSystem`
15. `EventHandlerSystem`
16. `CharacterSystem`
17. `BuoyancySystem`
18. `PhysicsSystem`
19. `HairPhysicsSystem` pose read-back
20. Rendering support systems (sun, skybox, water, light, LOD, etc.)

Systems that write animation state or velocity should respect systems that run
before them. In particular, `PlayerControllerSystem` runs first and is the only
system that should drive player locomotion animation.

## Important Subsystems

### PlayerControllerSystem

- Reads `GameInputState` and drives TPS/FPS camera and locomotion animation.
- Sets animation states on the `locomotion` state machine:
  `idle` / `walking` / `running` and the swim equivalents.
- In game mode it adds `PlayerControlledComponent` to the target entity so AI
  systems skip it.
- When `targetCharacterName` resolves to a `CharacterSpawnerComponent`, it locks
  the spawner and forces a spawn; the controlled entity is the spawned instance,
  not the spawner.

### CharacterSpawnerSystem

Distance-based spawn/despawn of registry characters. Special API for player
controller integration:

```cpp
void spawn(flecs::entity spawner);
void lockSpawner(flecs::entity spawner);    // keep alive even out of range
void unlockSpawner(flecs::entity spawner);
flecs::entity getSpawnedCharacter(flecs::entity spawner) const;
flecs::entity getSpawnerForCharacter(flecs::entity character) const;
bool isSpawnerLocked(flecs::entity spawner) const;
```

Spawned characters have `EditorMarkerComponent` removed and are removed from
`EditorUISystem` caches so they don't appear in editor lists.

### Player Character Resolution

Never resolve the player by matching `PlayerControllerComponent::targetCharacterName`
against `EntityNameComponent` – when the controller targets a spawner that returns
the spawner entity, not the character. Use the central helper:

```cpp
flecs::entity player = editorApp->getPlayerCharacterEntity();
```

This returns the live target (spawning through the spawner if necessary). It is
used by inventory/character-sheet UI, save/load and `ActuatorSystem`.

### Game Mode Input

Movement keys are polled each frame with `SDL_PumpEvents()` + `SDL_GetKeyboardState()`
to avoid stuck keys caused by missed `keyReleased` events. One-shot action flags
(`ePressed`, `fPressed`, `iPressed`) are still set from event handlers.

### AnimationTreeSystem & Root Motion

- Selects a root bone (`Root`, `mixamorig:Hips`, then `Spineroot`), freezes it,
  and excludes it from animation via a blend mask.
- Computes `CharacterComponent::linearVelocity` from root-bone displacement when
  `AnimationTreeComponent::useRootMotion` is true.
- `CharacterSystem` then applies that velocity to the Jolt physics character.

### ActuatorSystem

Handles player interaction prompts and executes smart-object/item actions. It
uses `EditorApp::getPlayerCharacterEntity()` to know which character performs the
action. Actions run through `BehaviorTreeSystem` and lock player input while
running (`PlayerControllerComponent::inputLocked`).

### Save/Load

Game-mode saves are JSON files in the OS user-data directory (see
`docs/SaveLoadSystem.md`). Key fields:

- `baseScene` – the scene file loaded as the foundation
- `playerCharacterId` – registry ID of the player character
- `characterRegistry` – full character registry state
- `runtimeEntities` – runtime-spawned entities (dropped items, etc.)
- `characterRuntimeData` – per-character component overrides
- `luaData` – data from Lua save callbacks

On load, if the saved character is owned by a spawner, the controller target is
restored to the spawner name so the character respawns correctly.

## Adding a New Component

1. Define the component struct in `components/MyComponent.hpp`.
2. Register it for UI editing in `components/MyComponentModule.cpp` using
   `REGISTER_COMPONENT_GROUP(...)` and `registry.registerComponent<T>(...)`.
3. Implement an editor in `ui/MyComponentEditor.hpp` / `.cpp` deriving from
   `ComponentEditor`.
4. If the component should be saved/loaded, add serialization support in
   `systems/SceneSerializer.cpp`.
5. Add a Lua binding in `lua/LuaComponentApi.cpp` if needed.
6. Add a component-module test case in `tests/component_lua_test.cpp`.

## Adding a New System

1. Create `systems/MySystem.hpp` and `systems/MySystem.cpp`.
2. Add the source files to `CMakeLists.txt`.
3. Instantiate the system in `EditorApp::setup()` and wire any dependencies.
4. Call `update()` from `EditorApp::frameRenderingQueued()` in the appropriate
   order.
5. Skip `PlayerControlledComponent` entities if the system would override player
   animation or movement.

## Critical Constraints & Rules
- The source code is the only source of truth. Please check code as documentation might be out of sync.
- Please ask questions if anything is unclear.
- Please update tests, documentation and examples every time API or modules and systems change.
Make sure documentation, tests and examples are always in sync.

## Conventions & Caveats

- Use `editScene::isGameMode()` / `editScene::isGamePlaying()` for mode checks
  when you don't have an `EditorApp` pointer.
- Systems that set animation states must not affect player-controlled entities;
  check `e.has<PlayerControlledComponent>()` early.
- Spawner-targeted characters are transient instances; persistent player data
  (inventory, identity) must live on the spawned instance, not the spawner.
- The editor uses `EditorMarkerComponent` to know which entities belong to the
  scene file. Spawned/runtime characters usually don't have this tag.
- Keep changes in `EditorApp::frameRenderingQueued()` order-aware; systems later
  in the list should not stomp state produced by earlier systems.

## Additional Docs

- `docs/SaveLoadSystem.md` – save file format and API details
- Root `AGENTS.md` – project-wide build, style and architecture notes
