diff --git a/src/features/editScene/AGENTS.md b/src/features/editScene/AGENTS.md new file mode 100644 index 0000000..43f73bd --- /dev/null +++ b/src/features/editScene/AGENTS.md @@ -0,0 +1,222 @@ +# 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(...)`. +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()` 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