205 lines
12 KiB
Markdown
205 lines
12 KiB
Markdown
# The Steward — Architecture Overview
|
|
|
|
This project is organized around gameplay ownership, not scene-tree location.
|
|
Serializable simulation records are authoritative; loaded Godot nodes present
|
|
that state and contribute active-world facts such as positions and navigation
|
|
results.
|
|
|
|
## Runtime flow
|
|
|
|
```text
|
|
SimulationClock
|
|
-> SimulationManager orchestrates one deterministic tick
|
|
-> SimulationPopulationView indexes all/living/starving NPCs by stable ID
|
|
-> ActionExecutionSystem advances needs and work
|
|
-> VillageOpportunitySystem re-derives the current capable helper
|
|
-> ActionSelectionSystem chooses an action
|
|
-> ActionTargetResolver resolves a stable target ID
|
|
-> VillageEconomy performs inventory/storage transactions
|
|
-> AnimalCareSystem advances animal needs, routines, and feeding
|
|
-> SimulationEventLog records completed facts
|
|
-> EventKnowledgeSystem records, ranks, transfers, and retains bounded knowledge
|
|
-> RelationshipSystem applies evidence-gated social consequences
|
|
-> VillageOpportunitySystem projects one known unresolved need
|
|
-> WorldViewManager presents travel, NPC state, and world-state cues
|
|
-> ActiveWorldAdapter supplies loaded-world positions/capacity
|
|
-> LoadedResourceSpatialIndex bounds finite-anchor discovery
|
|
-> NpcVisual performs local navigation, animation, and transient reactions
|
|
-> AnimalNode follows saved animal destinations through loaded navigation
|
|
-> PantryStockVisual derives physical stock arrangement from storage state
|
|
```
|
|
|
|
`SimulationManager` is the scene-tree façade for the simulation. It owns the
|
|
tick lifecycle, authoritative NPC/village/resource records, reservations, and
|
|
the signals consumed by presentation. Focused collaborators own rules that
|
|
would otherwise obscure that lifecycle:
|
|
|
|
- `simulation/actions/` owns selection, progress, and target resolution;
|
|
- `simulation/economy/VillageEconomy.gd` owns storage/inventory transactions
|
|
and keeps village resource summaries synchronized;
|
|
- `simulation/animals/animal_care_system.gd` owns animal records, hunger
|
|
advancement, deterministic routine selection, loaded binding, feed
|
|
reservations, exact NPC pantry-to-inventory-to-animal delivery, and the
|
|
bounded direct player pantry-to-animal operation;
|
|
- `simulation/player/player_citizen_system.gd` owns the persisted
|
|
`PlayerStateRecord` (hunger, energy, carried inventory), deterministic
|
|
needs advancement, and the player's carry/deposit/eat transactions through
|
|
the same storage and event contract as NPCs;
|
|
- `simulation/conflict/ConflictSystem.gd` owns combatants (health, weapons,
|
|
factions, hostility), village/tribe faction records, wolf hostility, the
|
|
tribe war motivation decision (desire + victory confidence), raid spawning,
|
|
deterministic battle resolution, and war consequences. It emits combat and
|
|
war narrative facts and spawn/death signals that presentation binds to
|
|
`HostileCombatant` visuals;
|
|
- `simulation/events/SimulationEventLog.gd` owns ordered event identity,
|
|
history queries, and rate calculations;
|
|
- `simulation/knowledge/EventKnowledgeSystem.gd` owns per-NPC references to
|
|
known objective events, immutable acquisition provenance, proximity witnesses
|
|
at record time, one-hop direct fact transfer, and deterministic recent-fact
|
|
retention/importance ranking. It can place the active opportunity trigger
|
|
first without weakening its direct-source or one-hop checks;
|
|
- `simulation/relationships/RelationshipSystem.gd` owns directed relationship
|
|
queries, event-driven trust changes, and deterministic social tie-breaking;
|
|
- `simulation/population/SimulationPopulationView.gd` owns a transient derived
|
|
all/living/starving stable-ID index. The manager rebuilds it once at the tick
|
|
boundary and refreshes it after each interleaved NPC update; relationship and
|
|
action queries may read it, but it never enters save records;
|
|
- `simulation/opportunities/VillageOpportunitySystem.gd` observes immutable
|
|
event references plus current NPC/storage state, then owns the bounded shared
|
|
open/resolved/invalidated lifecycle for the proven pantry-food and blocked-
|
|
work wood consumers. It also derives one ephemeral `OpportunityHelperResult`
|
|
from knowledge, directed relationships, inventory, action definitions, and
|
|
finite-resource state without changing resources or assigning tasks. The
|
|
ordinary action selector consumes that result only for the matching idle NPC.
|
|
When no such helper exists, it can separately derive an ephemeral
|
|
`OpportunityPlayerResponseResult` from the real destination capacity and
|
|
enabled player-usable finite sources;
|
|
- `simulation/persistence/` owns save-slot file safety;
|
|
- `simulation/state/` owns versioned serialized record contracts;
|
|
- `simulation/definitions/` owns stable IDs and immutable action/profession
|
|
definitions.
|
|
|
|
`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
|
|
metadata bounds copied at bind time. It does not own amounts, enabled state,
|
|
reservations, or persistence. `ResourceNode` enter/exit, bind, and transform
|
|
notifications keep it synchronized with the loaded presentation lifecycle.
|
|
Resource anchors may be nested inside authored presentation clusters: runtime
|
|
discovery uses their stable IDs and registration lifecycle, never a parent
|
|
path. Decorative siblings without a `ResourceNode` remain outside the index
|
|
and simulation state.
|
|
|
|
Outside the runtime lifecycle, `simulation/benchmark/` owns reusable,
|
|
schema-valid workload fixtures. CLI tools and headless scenarios consume those
|
|
fixtures; production simulation does not depend on benchmark code.
|
|
|
|
The manager deliberately remains a façade instead of being split into a
|
|
collection of scene-tree manager nodes. A new collaborator is justified when
|
|
one cohesive rule set has several real consumers or makes the tick lifecycle
|
|
hard to read.
|
|
|
|
## Folder ownership
|
|
|
|
| Path | Responsibility |
|
|
| --- | --- |
|
|
| `simulation/` | Headless-capable orchestration and core models |
|
|
| `simulation/actions/` | Action decisions, execution, and target queries |
|
|
| `simulation/animals/` | Animal lifecycle, target claims, and feeding transactions |
|
|
| `simulation/economy/` | Authoritative inventory and storage transactions |
|
|
| `simulation/events/` | Immutable event history and derived event queries |
|
|
| `simulation/knowledge/` | Per-NPC knowledge of objective event IDs |
|
|
| `simulation/relationships/` | Directed social consequences and relationship queries |
|
|
| `simulation/population/` | Transient per-tick population query indexes |
|
|
| `simulation/opportunities/` | Knowledge-gated unresolved-condition projections |
|
|
| `simulation/state/` | Versioned, serializable mutable records |
|
|
| `simulation/definitions/` | Stable IDs and immutable gameplay definitions |
|
|
| `simulation/persistence/` | Validated local save-file storage |
|
|
| `simulation/benchmark/` | Reproducible simulation and loaded-world query workloads |
|
|
| `world/` | Loaded-world interaction geometry and presentation adapters |
|
|
| `world/animals/` | Animal presentation binding, routine sites, and navigation reporting |
|
|
| `world/animals/goat/` | Self-contained goat scene, visual script, habitat, and lookdev scene |
|
|
| `world/resource_nodes/` | Finite resource presentation and disposable loaded-anchor index |
|
|
| `world/storage/` | Storage interaction geometry, never stored quantities |
|
|
| `world/activity/` | Rest/study/patrol interaction sites and capacity facts |
|
|
| `player/` | Player input, camera, and active NPC presentation |
|
|
| `tests/` | Deterministic headless gameplay scenarios |
|
|
|
|
Unrelated established model scripts keep their stable paths because Godot's
|
|
global class cache records `class_name` locations. This slice moves only the
|
|
animal files whose ownership changed, preserves their `.uid` files, and proves
|
|
the new paths through editor import and headless startup rather than expanding
|
|
the cleanup into cosmetic repository-wide churn.
|
|
|
|
Project-owned files use `snake_case` when they are newly added or deliberately
|
|
moved, while scene node names remain `PascalCase`. Reusable scene assets stay
|
|
beside the scene that owns them, so the goat's script, habitat, and lookdev
|
|
scene no longer sit in the broad Jajce level folder. Non-runtime documentation
|
|
is hidden from Godot with `docs/.gdignore`; baseline images therefore remain
|
|
review artifacts without tracked import sidecars. This follows Godot's
|
|
[project organization](https://docs.godotengine.org/en/stable/tutorials/best_practices/project_organization.html)
|
|
and [GDScript naming](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
|
|
guidance without turning established-path cleanup into unrelated churn.
|
|
|
|
## Dependency rules
|
|
|
|
- Simulation code must run without `main.tscn` or loaded world nodes.
|
|
- Persistent references are stable IDs, never `Node`, `NodePath`, or scene
|
|
ownership.
|
|
- Presentation may report facts and submit commands; it does not choose NPC
|
|
actions or own resource, storage, inventory, event, knowledge, relationship,
|
|
opportunity, or reservation state.
|
|
- Inspector history is a read-only façade query over objective events and
|
|
retained knowledge. Opportunity presentation is likewise query-only.
|
|
Relationship cues, interested-villager concern, physical pantry stock, and
|
|
refill feedback consume authoritative state or state-change signals and are
|
|
intentionally absent from saves and checksums. The transient village-whisper
|
|
HUD likewise formats need, player route, transfer, response, and resolution
|
|
signals without owning them. It drops the player route when a capable helper
|
|
emerges. Stable visuals are rebuilt from state after restore; transient
|
|
reactions are not replayed.
|
|
- Resource changes go through `ResourceStateRecord`, NPC inventory, and
|
|
`VillageEconomy`; `village.food` and `village.wood` are synchronized views.
|
|
- Animal identity, position, hunger, last feed tick, reservation, routine site,
|
|
destination, and next due tick live in `AnimalStateRecord`. `AnimalNode`
|
|
binds that state, contributes its loaded interaction point, follows the
|
|
selected navigation path, and reports position/arrival facts. Routine and
|
|
care candidates are currently scanned linearly; they do not enter the
|
|
resource grid.
|
|
- New mutable features define serialization and deterministic continuation at
|
|
the same time as their first gameplay use. Cross-record causes use stable
|
|
event IDs rather than object references or prose.
|
|
- Benchmark fixtures use ordinary simulation records and must round-trip
|
|
through the current state schema. They may control workload setup, but must
|
|
not add benchmark-only fields or branches to production saves and ticks.
|
|
- Derived population indexes are disposable acceleration structures. NPC
|
|
records remain authoritative, and rebuilding the view after initialization,
|
|
restore, or a tick must produce the same decisions and checksum.
|
|
- The loaded-resource grid is likewise disposable. `ResourceStateRecord`
|
|
remains authoritative while its scene node is absent; rebuilding or
|
|
incrementally refreshing the grid must preserve the linear resolver's exact
|
|
score winner and stable tie order.
|
|
- Camera-local grass, ambient butterflies, water animation, smoke, and foliage
|
|
motion are presentation only. Grass consumes bounded real actor transforms;
|
|
it does not write NPC positions or become persistent state.
|
|
- Opportunity records reference stable NPC, storage, resource, trigger-event,
|
|
and resolution-event IDs. Their generator may observe authoritative state
|
|
and history, but it does not mutate the economy or command NPC behavior. The
|
|
manager re-queries capable helpers at idle selection boundaries; any selected
|
|
supply task then follows the ordinary persisted NPC-task and target contracts.
|
|
- Prefer one tested vertical behavior over a generic framework with no proven
|
|
consumers.
|
|
|
|
## Where new code goes
|
|
|
|
Put a rule beside the state it governs. A relationship consequence belongs in
|
|
a focused simulation system plus serialized relationship records; its icon or
|
|
animation belongs in presentation. Add a world node only when the behavior
|
|
needs loaded-world geometry. Add a stable ID or definition when content must be
|
|
referenced across saves, scenes, or unloaded simulation.
|
|
|
|
The architectural decision and detailed contracts live in
|
|
[ADR 0001](decisions/0001-simulation-authority-boundary.md),
|
|
[the action system architecture](ACTION_SYSTEM_ARCHITECTURE.md), and
|
|
[the simulation state schema](SIMULATION_STATE_SCHEMA.md).
|