docs: codify entity systems pattern and record combat extensions

This commit is contained in:
Rijad Zuzo
2026-08-12 00:19:48 +02:00
parent 661d53f31a
commit cd29da95e0
6 changed files with 69 additions and 15 deletions
+19
View File
@@ -86,6 +86,25 @@ Current resource gathering should use finite `ResourceNode` instances:
- food: berries, animal camps, village stock, future farms/crops; - food: berries, animal camps, village stock, future farms/crops;
- wood: trees, wood piles, future forestry contexts. - 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: Resource additions should preserve:
- stable unique IDs; - stable unique IDs;
+8
View File
@@ -79,6 +79,14 @@ would otherwise obscure that lifecycle:
- `simulation/definitions/` owns stable IDs and immutable action/profession - `simulation/definitions/` owns stable IDs and immutable action/profession
definitions. 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 `world/resource_nodes/LoadedResourceSpatialIndex.gd` is a focused disposable
acceleration structure owned by `ActiveWorldAdapter`. It indexes loaded acceleration structure owned by `ActiveWorldAdapter`. It indexes loaded
interaction positions by action and horizontal cell, plus authoritative interaction positions by action and horizontal cell, plus authoritative
+9 -7
View File
@@ -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 -> attacks, the tribe war-motivation decision (tribe hunger + village surplus ->
desire; tribe strength vs NPC-derived village defence -> victory confidence; desire; tribe strength vs NPC-derived village defence -> victory confidence;
watch one interval, then raid, plan, or abort), raid spawning, battle watch one interval, then raid, plan, or abort), raid spawning, battle
resolution, and war consequences. Strong villagers automatically defend during resolution, and war consequences. Strong villagers take a real `defend` duty
a raid. The player fights with an equipped sword and dash through a action during raids. The player fights with an equipped sword and dash, has
`PlayerCombatController`, and `CombatPresentation` binds hostile raider/wolf persisted health, and can be downed by wolves or raiders before recovering.
visuals to the authoritative combatants. Headless tests cover combat Hostile raiders and wolves share a `CreatureVisual` movement root and are
determinism, wolf attacks, war motivation (raid only when confident, abort when described by data-driven `EnemyDefinition`s, so the next bear, bandit, or boar
the village is strong), raid-to-resolution chains, player kills, and is a definition plus a visual hook rather than a new movement or combat system.
save/restore. 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: Recently completed:
+7 -3
View File
@@ -245,11 +245,15 @@ plugin content, not game architecture.
- Combat is playable: the player swings an equipped sword (left click) and - Combat is playable: the player swings an equipped sword (left click) and
dashes (Shift) with cooldowns; hits land against hostile raiders and wolves dashes (Shift) with cooldowns; hits land against hostile raiders and wolves
through a deterministic simulation `ConflictSystem`. Wolves hunt hungry 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 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, strength (strong NPCs who can swap to defence) and judges victory unlikely,
in which case it plans or aborts. Strong villagers automatically defend in which case it plans or aborts. During a raid, strong villagers and guards
during a raid. 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. - `Escape` releases captured mouse input.
### Village simulation ### Village simulation
+16 -2
View File
@@ -24,8 +24,13 @@ Each executable action definition contains:
- optional completion-cost resource ID and amount. - optional completion-cost resource ID and amount.
The current actions are gather food, gather wood, feed animal, deposit food, The current actions are gather food, gather wood, feed animal, deposit food,
deposit wood, withdraw food, patrol, study, eat, rest, sleep, and wander. Idle deposit wood, withdraw food, patrol, study, eat, rest, sleep, wander, and
and dead are stable state sentinels, not executable action definitions. 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 SimNPC reads default duration and preferred-profession metadata from these
definitions. WorldViewManager reads target type and resource-action metadata 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`, `combatant_hurt`, `combatant_killed`, `raid_started`, `war_resolved`,
`war_aborted`, and `wolf_hunt`. `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 ## Validation
The registry rejects: The registry rejects:
+10 -3
View File
@@ -26,9 +26,9 @@ simulation. The current world schema is v14 and captures:
- opportunity records with stable type/status, interested NPC, trigger event, - opportunity records with stable type/status, interested NPC, trigger event,
target storage/resource/amount, exact later resolution event identity, or a target storage/resource/amount, exact later resolution event identity, or a
deterministic invalidation reason and close tick; deterministic invalidation reason and close tick;
- a player record holding the player-citizen's hunger, energy, and carried - a player record holding the player-citizen's hunger, energy, health,
inventory. The player's world position remains presentation-owned and is downed state, and carried inventory. The player's world position remains
intentionally not serialized; presentation-owned and is intentionally not serialized;
- combatant records (health, weapon, faction, position, hostility) for NPC - combatant records (health, weapon, faction, position, hostility) for NPC
defenders and standalone raiders/wolves; defenders and standalone raiders/wolves;
- faction records (food, warriors, aggression, morale, stance, war plan) for - 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 arbitrary world floats round-trip through JSON without breaking deterministic
continuation checksums. 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 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 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 whose active feed task targets that exact animal. Every `animal_fed` event