diff --git a/AGENTS.md b/AGENTS.md index 86177bf..1fad24a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,6 +86,25 @@ Current resource gathering should use finite `ResourceNode` instances: - food: berries, animal camps, village stock, future farms/crops; - wood: trees, wood piles, future forestry contexts. +## Entity systems pattern + +Build every resource, enemy, and animal as a reusable system with shared root +behaviour, not as a one-off object. Add a new type through data/definitions +plus a small behaviour or visual hook, reusing the root for movement, +presentation, serialization, and lifecycle: + +- resources: `ResourceNode` + `ResourceStateRecord` (amount, yield, regrowth); +- creatures: `CreatureVisual` shared movement root (navigate to the + authoritative simulation position, report position, death), with per-type + visuals built from definitions; +- enemies: `EnemyDefinition` + `SimulationEnemies` registry driving combatant + spawns and hostile visuals; +- animals: `AnimalNode` + `AnimalStateRecord` following the same + follow-the-authoritative-position contract. + +A new berry, tree, bear, bandit, boar, or goat should be mostly a definition +plus a bounded hook — never a new movement/combat/save system. + Resource additions should preserve: - stable unique IDs; diff --git a/docs/ARCHITECTURE_OVERVIEW.md b/docs/ARCHITECTURE_OVERVIEW.md index 69b1f5b..31c7cac 100644 --- a/docs/ARCHITECTURE_OVERVIEW.md +++ b/docs/ARCHITECTURE_OVERVIEW.md @@ -79,6 +79,14 @@ would otherwise obscure that lifecycle: - `simulation/definitions/` owns stable IDs and immutable action/profession definitions. +`world/creatures/creature_visual.gd` is the shared root for creature +presentation: any `CreatureVisual` follows a simulation-owned position through +the navigation mesh, reports position changes, and plays a shared death +collapse. `HostileCombatant` extends it and builds its body from an +`EnemyDefinition`; `NpcVisual` and `AnimalNode` already follow the same +follow-the-authoritative-position contract, so future creatures (bears, boars, +archers) add a definition and a visual hook instead of a new movement system. + `world/resource_nodes/LoadedResourceSpatialIndex.gd` is a focused disposable acceleration structure owned by `ActiveWorldAdapter`. It indexes loaded interaction positions by action and horizontal cell, plus authoritative diff --git a/docs/LEARNING_ROADMAP.md b/docs/LEARNING_ROADMAP.md index a25f0f7..570d989 100644 --- a/docs/LEARNING_ROADMAP.md +++ b/docs/LEARNING_ROADMAP.md @@ -1005,13 +1005,15 @@ factions, hostility), the village and hill-tribe faction records, wolf hunger attacks, the tribe war-motivation decision (tribe hunger + village surplus -> desire; tribe strength vs NPC-derived village defence -> victory confidence; watch one interval, then raid, plan, or abort), raid spawning, battle -resolution, and war consequences. Strong villagers automatically defend during -a raid. The player fights with an equipped sword and dash through a -`PlayerCombatController`, and `CombatPresentation` binds hostile raider/wolf -visuals to the authoritative combatants. Headless tests cover combat -determinism, wolf attacks, war motivation (raid only when confident, abort when -the village is strong), raid-to-resolution chains, player kills, and -save/restore. +resolution, and war consequences. Strong villagers take a real `defend` duty +action during raids. The player fights with an equipped sword and dash, has +persisted health, and can be downed by wolves or raiders before recovering. +Hostile raiders and wolves share a `CreatureVisual` movement root and are +described by data-driven `EnemyDefinition`s, so the next bear, bandit, or boar +is a definition plus a visual hook rather than a new movement or combat system. +Headless tests cover combat determinism, wolf attacks, enemy definitions, +player downing/recovery, defend duty, war motivation, raid-to-resolution +chains, player kills, and save/restore. Recently completed: diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index 778be22..0e0dacd 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -245,11 +245,15 @@ plugin content, not game architecture. - Combat is playable: the player swings an equipped sword (left click) and dashes (Shift) with cooldowns; hits land against hostile raiders and wolves through a deterministic simulation `ConflictSystem`. Wolves hunt hungry - nearby villagers, and the hill tribe raids the village when it is short of + nearby villagers and the player, and can wound and even down the player + (brief forced recovery). The hill tribe raids the village when it is short of food while the village holds plenty — unless the tribe reads the village's strength (strong NPCs who can swap to defence) and judges victory unlikely, - in which case it plans or aborts. Strong villagers automatically defend - during a raid. + in which case it plans or aborts. During a raid, strong villagers and guards + take a real `defend` duty action and rally at the guard post. Hostiles follow + the navigation mesh through a shared `CreatureVisual` root, and enemies are + data-driven `EnemyDefinition`s so new types add with a definition plus a + visual hook. - `Escape` releases captured mouse input. ### Village simulation diff --git a/docs/SIMULATION_DEFINITIONS.md b/docs/SIMULATION_DEFINITIONS.md index 55161a8..ad0d3ad 100644 --- a/docs/SIMULATION_DEFINITIONS.md +++ b/docs/SIMULATION_DEFINITIONS.md @@ -24,8 +24,13 @@ Each executable action definition contains: - optional completion-cost resource ID and amount. The current actions are gather food, gather wood, feed animal, deposit food, -deposit wood, withdraw food, patrol, study, eat, rest, sleep, and wander. Idle -and dead are stable state sentinels, not executable action definitions. +deposit wood, withdraw food, patrol, study, eat, rest, sleep, wander, and +defend. Idle and dead are stable state sentinels, not executable action +definitions. + +`defend` is the raid-response action: eligible strong villagers and guards +prefer it during an active raid, travel to the guard post, and raise village +safety while they work. Patrol activity sites accept it as a rally point. SimNPC reads default duration and preferred-profession metadata from these definitions. WorldViewManager reads target type and resource-action metadata @@ -104,6 +109,15 @@ Combat, raid, and war narrative facts use the stable event types `combatant_hurt`, `combatant_killed`, `raid_started`, `war_resolved`, `war_aborted`, and `wolf_hunt`. +## Enemy vocabulary + +`EnemyDefinition` is the data contract for hostile creatures and +`SimulationEnemies` is the current registry. Each enemy carries a `kind` +(raider/wolf), equipped weapon, health, move speed, body/accent colors, and a +visual scale, so a new bear, bandit, or boar is mostly one definition plus a +small `_build_*` visual hook on the shared `CreatureVisual` root. The current +enemies are `enemy_raider` (sword) and `enemy_wolf` (claw). + ## Validation The registry rejects: diff --git a/docs/SIMULATION_STATE_SCHEMA.md b/docs/SIMULATION_STATE_SCHEMA.md index 3b99293..b1e8710 100644 --- a/docs/SIMULATION_STATE_SCHEMA.md +++ b/docs/SIMULATION_STATE_SCHEMA.md @@ -26,9 +26,9 @@ simulation. The current world schema is v14 and captures: - opportunity records with stable type/status, interested NPC, trigger event, target storage/resource/amount, exact later resolution event identity, or a deterministic invalidation reason and close tick; -- a player record holding the player-citizen's hunger, energy, and carried - inventory. The player's world position remains presentation-owned and is - intentionally not serialized; +- a player record holding the player-citizen's hunger, energy, health, + downed state, and carried inventory. The player's world position remains + presentation-owned and is intentionally not serialized; - combatant records (health, weapon, faction, position, hostility) for NPC defenders and standalone raiders/wolves; - faction records (food, warriors, aggression, morale, stance, war plan) for @@ -248,6 +248,13 @@ to a binary-exact grid and faction food to a binary-exact step so that arbitrary world floats round-trip through JSON without breaking deterministic continuation checksums. +`PlayerStateRecord` v2 adds `health`, `max_health`, `downed`, and +`downed_ticks` so wolves and raiders can wound and down the player, with a +brief forced recovery that restores a fighting baseline. Nested v1 player +records migrate to full health and standing. The player combatant used by the +conflict system is derived state and is never serialized; hostile attacks +reduce the authoritative player health through the ordered event stream. + Current-schema parsing requires unique animal IDs that do not collide with resource or storage IDs. An animal reservation must belong to an existing NPC whose active feed task targets that exact animal. Every `animal_fed` event