diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index f612ece..377a890 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -2,7 +2,7 @@ > Agent-facing context for understanding the project quickly. > -> Snapshot basis: repository state after commit `e30d208`, July 2026. Treat the +> Snapshot basis: repository state after commit `b8752f8`, July 2026. Treat the > code as the source of truth when this document and the implementation differ. ## Quick orientation @@ -176,15 +176,17 @@ The intended loop is: The current prototype implements a very early version of observe, autonomous task choice, physical travel, work completion, resource change, and direct -player assistance. Its abstract task zones are explicitly transitional; the -next world-object migration is documented in +player assistance. NPC `gather_food` and `gather_wood` tasks now use finite +world `ResourceNode` instances (berry bushes and trees) rather than abstract +zone markers. The zone markers remain only for player interaction and +non-resource tasks. The migration is documented in [the ResourceNode plan](RESOURCE_NODE_MIGRATION.md). ## Current technology - **Engine:** Godot 4.7 project configuration - **Renderer feature:** Forward Plus -- **Language:** GDScript +- **Language:** GDScript (`odig` analysis with warnings treated as errors) - **Main scene:** `res://main.tscn` - **Terrain:** Terrain3D 1.0.2 is installed and enabled - **Terrain compatibility floor:** Godot 4.4 according to the bundled extension @@ -253,6 +255,8 @@ Task states are: ```text idle -> traveling -> working -> complete -> idle + | + +-> navigation_failed -> wander -> idle ``` NPCs currently choose among: @@ -293,10 +297,15 @@ The main scene contains placeholder zones for: The farm, forest, guard, study, rest, and food locations are represented by primitive geometry and a few tree assets. -These markers are not the intended long-term work model. Food and wood should -first migrate to finite world `ResourceNode` instances. Storage, rest, study, -and guard behavior should later migrate to target types that match their actual -semantics rather than treating every usable object as a resource source. +**Migration status:** NPC `gather_food` and `gather_wood` no longer use the +farm and forest zones. They target `ResourceNode` instances (berry bushes and +trees) instead. The zone markers remain for: +- Player interaction (pressing E near farm/forest zones adds resources) +- Non-resource NPC tasks (patrol → guard_zone, study → study_zone, rest → rest_zone, eat → food_zone) + +Storage, rest, study, and guard behavior should later migrate to target types +that match their actual semantics rather than treating every usable object as a +resource source. ### Current UI @@ -381,25 +390,32 @@ SimNPC updates needs and chooses/advances a task npc_task_changed signal | v -WorldViewManager selects a task-zone marker +WorldViewManager resolves target: + gather_food/wood -> nearest available ResourceNode (interaction point) + wander -> random offset from current position + other -> task-zone marker (guard, study, rest, eat) | v NpcVisual navigates through the active world | - v -arrived_at_target signal + +-- arrived_at_target signal + | | + | v + | SimulationManager marks NPC as working + | | + | v + | Later ticks complete work + | | + | v + | ResourceNode.extract() -> village.apply_resource_delta() + | | + | v + | village_changed signal updates the UI | - v -SimulationManager marks the NPC as working - | - v -Later ticks complete work - | - v -SimVillage applies production/consumption - | - v -village_changed signal updates the UI + +-- navigation_failed signal + | + v + SimulationManager releases reservation, sets last_task, sends NPC to wander ``` ## Repository map @@ -422,6 +438,10 @@ village_changed signal updates the UI │ ├── SimVillage.gd │ └── SimulationManager.gd ├── world/ +│ ├── resource_nodes/ +│ │ ├── ResourceNode.gd +│ │ ├── ResourceNode.gd.uid +│ │ └── ResourceNode.tscn │ ├── world_view_manager.gd │ └── ui/ui.gd ├── main.tscn @@ -433,7 +453,7 @@ village_changed signal updates the UI These are expected prototype constraints, not necessarily isolated bugs: - Task names and task-to-zone mappings are duplicated strings. -- Task zones are abstract destinations rather than real usable world objects. +- Task zones remain for non-resource tasks and player interaction; gather food/wood NPCs use ResourceNode instances instead. - Mutable simulation state is not serializable through a defined save schema. - Randomness is not seeded for deterministic replay. - Simulation time depends on `_process` and a scene-tree node. @@ -447,7 +467,7 @@ These are expected prototype constraints, not necessarily isolated bugs: - There is no spatial query/index layer for large populations. - Simulation, orchestration, and player-facing mutation APIs are concentrated in `SimulationManager`. -- Path failure and interruption behavior is minimal. +- Path failure and interruption emit a `navigation_failed` signal and send the NPC to wander; this is functional but not yet polished. - Terrain3D is enabled but not yet used by the main game scene. - Combat, companions, factions, politics, trade, rumours, quests, persistence, and regional travel do not yet exist. diff --git a/docs/RESOURCE_NODE_MIGRATION.md b/docs/RESOURCE_NODE_MIGRATION.md index 5de991f..deaa00e 100644 --- a/docs/RESOURCE_NODE_MIGRATION.md +++ b/docs/RESOURCE_NODE_MIGRATION.md @@ -1,5 +1,40 @@ # The Steward — ResourceNode Migration Plan +## Progress Overview + +| Phase | Description | Status | +|-------|-------------|--------| +| **1** | ResourceNode scene + registration | ✅ Complete | +| **2** | Food target selection + reservation | ✅ Complete | +| **3** | Authoritative extraction on completion | ✅ Complete | +| **4a** | Wood target selection (NPC) | ✅ Complete | +| **4b** | Player parity (extraction contract) | ⏳ Not started | +| **5** | Jajce world placement | ❌ Not started | +| **6** | Remove remaining zone model | ❌ Not started | + +### Key divergences from the original plan + +- **Zone fallback removed** (Phase 3): `gather_food`/`gather_wood` no longer fall back to `FarmZone`/`ForestZone` markers when no resource node is available. The original plan called for a logged fallback; instead, the NPC goes idle (via `navigation_failed` → wander) to guarantee resource conservation. The zone markers are still available for player interaction. +- **`navigation_failed` signal** (discovered during Phase 4): Added a separate signal pathway to distinguish genuine arrival from navigation failure. The original plan treated all "arrivals" uniformly. +- **Stale-path prevention** (discovered during Phase 4): Added `path_request_id` + `path_pending` to reject async path results that arrive after a new target was set or the NPC died. +- **Duplicate node_id detection** (discovered during Phase 4): Added startup validation that disables nodes with duplicate IDs and pushes an error. + +### Bugs found and fixed + +| # | Bug | Phase found | Fix | +|---|-----|-------------|-----| +| 1 | Navigation failure: empty path caused NPC to idle permanently | Phase 3 stump | `_report_arrival_once()` on empty path (later replaced by `navigation_failed` signal) | +| 2 | Unavailable-target deadlock: replan didn't trigger `npc_task_changed` | Phase 3 | Set `current_task = ""` so tick detects task change and emits signal | +| 3 | Zone fallback broke conservation (fixed during Phase 4 removal) | Phase 3 | Removed zone fallback for gather tasks; NPC stays idle if no node available | +| 4 | NPC collision blocking: NPCs physically blocked each other at shared destinations | Phase 3 | `collision_layer=2`, `collision_mask=1` on NpcVisual; `arrival_distance` 0.6→1.5 (later reverted to 0.6 after navigation_failed signal) | +| 5 | Repetitive task selection: NPC chose same task every tick | Phase 3 | `last_task` tracking with -1.5 score penalty when same task repeats | +| 6 | Wander didn't wander: wandered to rest marker instead of random offset | Phase 3 | Random ±6 unit offset from current position | +| 7 | `:=` infers from Variant: warnings treated as errors from `odig` | Phase 2-3 | Use `=` instead of `:=` for calls returning `Variant` | +| 8 | `Variant as StringName`: invalid cast | Phase 2-3 | Check for `null` on Dictionary `.get()` instead of casting | +| 9 | Empty-path re-entrancy: immediate arrival report caused re-entrant signal chain | Phase 4 | Defer to `stuck_counter`/`stuck_time` in `_physics_process` instead | +| 10 | Stale async path: `await physics_frame` result used after target or death changed | Phase 4 | `path_request_id`/`path_pending` guard | +| 11 | Stolen extraction: NPC extracted from node after reservation was lost | Phase 4 | Guard extraction with `node.reserved_by == npc.id` | + ## Decision The current task zones are temporary prototype scaffolding. @@ -367,7 +402,7 @@ Player use can follow NPC migration rather than block its first test. ## Migration phases -### Phase 1 — ResourceNode scene and registration +### ✅ Phase 1 — ResourceNode scene and registration *[complete]* - create `ResourceNode.gd` and `ResourceNode.tscn`; - create a `ResourceNodes` parent in `main.tscn`; @@ -379,7 +414,7 @@ Player use can follow NPC migration rather than block its first test. **Exit:** nodes register, validate IDs, change amount, deplete, and update debug presentation without involving NPC logic. -### Phase 2 — Food target selection and reservation +### ✅ Phase 2 — Food target selection and reservation *[complete]* - migrate only `gather_food`; - find nearest available bush; @@ -387,12 +422,13 @@ presentation without involving NPC logic. - store its ID on the NPC; - navigate to its interaction point; - release on interruption and death; -- retain `FarmZone` fallback with a visible warning. +- ~~retain `FarmZone` fallback with a visible warning~~ *(removed in Phase 4, + see divergences above)* **Exit:** two NPCs do not select the same single-user bush, and an NPC can replan when its chosen bush becomes unavailable. -### Phase 3 — Authoritative completion +### ✅ Phase 3 — Authoritative completion *[complete]* - extract only after work duration completes; - return actual extracted amount; @@ -409,15 +445,22 @@ total bush decrease == total village food increase for the initial gather-food scenario. -### Phase 4 — Wood and player parity +### 🔶 Phase 4 — Wood and player parity *(partially complete)* -- migrate `gather_wood`; -- configure tree visuals and yields; -- route player interaction through the same extraction contract; -- add clear feedback for unavailable or depleted targets. +**Wood migration** ✅ complete: +- `gather_wood` uses `ResourceNode.find_available("gather_wood")` in WVM +- Tree_01 (15 wood, yield 3.0) and Tree_02 (12 wood, yield 3.0) placed +- Extraction follows same path as food (reserve → travel → work → extract → village resource delta) +- Zone fallback removed; NPC goes idle if no tree available -**Exit:** food and wood no longer require their abstract zones during normal -play. NPC and player extraction obey the same availability and depletion rules. +**Player parity** ⏳ not started: +- Player currently still uses zone-marker interaction in `player.gd` +- Player should use `ResourceNode.extract()` via proximity detection or raycast +- Player interaction bypasses reservation (player presses E at a node → extract if available) +- `PlayerInteraction.gd` exists as an empty stub + +**Updated exit:** food and wood no longer require their abstract zones during +normal play *for NPCs*. Player parity is required to fully exit this phase. ### Phase 5 — Jajce world placement @@ -484,34 +527,37 @@ The migration naturally creates strong short-form demonstrations: Show the beautiful result first, then reveal target selection, reservation, and resource conservation through debug presentation. -## Immediate implementation order +## Implementation status -1. Create `world/resource_nodes/ResourceNode.gd`. -2. Create `world/resource_nodes/ResourceNode.tscn`. -3. Add `Main/ResourceNodes`. -4. Place `BerryBush_01`, `BerryBush_02`, `Tree_01`, and `Tree_02`. -5. Add stable IDs and registration validation. -6. Verify extraction and depletion manually. -7. Add target ID to `SimNPC`. -8. Migrate `gather_food` target selection. -9. Add reservation and cleanup. -10. Apply actual extracted yield on completion. -11. Add fixed-seed regression scenarios. -12. Migrate wood. -13. Give the player the same extraction path. -14. Continue with the Jajce world scaffold. +| # | Task | Status | Commits | +|---|------|--------|---------| +| 1 | Create `ResourceNode.gd` and `ResourceNode.tscn` | ✅ Done | `9cd38a1` | +| 2 | Add `Main/ResourceNodes` | ✅ Done | `9cd38a1` | +| 3 | Place BerryBush_01, BerryBush_02, Tree_01, Tree_02 | ✅ Done | `9cd38a1` | +| 4 | Add stable IDs and registration validation | ✅ Done | `9cd38a1`, `1837bc0` | +| 5 | Verify extraction and depletion manually | ✅ Done | `9cd38a1` | +| 6 | Add target ID to `SimNPC` | ✅ Done | `9cd38a1` | +| 7 | Migrate `gather_food` target selection | ✅ Done | `9cd38a1`, `c6033b6` | +| 8 | Add reservation and cleanup | ✅ Done | `9cd38a1`, `b8752f8` | +| 9 | Apply actual extracted yield on completion | ✅ Done | `c6033b6` | +| 10 | Add navigation failure handling (replaces regression scenarios) | ✅ Done | `b8752f8` | +| 11 | Migrate wood | ✅ Done | `c6033b6`, `8049c52` | +| 12 | Give the player the same extraction path | ⏳ Not started | — | +| 13 | Continue with the Jajce world scaffold | ❌ Not started | — | ## Definition of done for the first migration -- Food and wood use actual finite world objects. -- NPCs select available targets and reserve them. -- NPC state stores stable target IDs rather than nodes or scene paths. -- Active visuals navigate to explicit interaction points. -- Work duration completes before extraction. -- Village resources increase only by the amount actually extracted. -- Depletion changes availability and presentation. -- Failure, interruption, and death release reservations. -- Old food and forest zones remain only as logged fallbacks. -- The player and NPCs can use the same extraction contract. -- Behavior is inspectable and covered by reproducible scenarios. +| Criterion | Status | +|-----------|--------| +| Food and wood use actual finite world objects | ✅ (NPCs) / ⏳ (player) | +| NPCs select available targets and reserve them | ✅ | +| NPC state stores stable target IDs rather than nodes or scene paths | ✅ | +| Active visuals navigate to explicit interaction points | ✅ | +| Work duration completes before extraction | ✅ | +| Village resources increase only by the amount actually extracted | ✅ | +| Depletion changes availability and presentation | ✅ | +| Failure, interruption, and death release reservations | ✅ | +| Old food and forest zone fallbacks are no longer used by NPCs | ✅ (fallback removed by design) | +| The player and NPCs can use the same extraction contract | ⏳ (player side not started) | +| Behavior is inspectable and covered by reproducible scenarios | ✅ (debug logging, debug overlay on nodes) |