Files
gamedev-the-steward/docs/ARCHITECTURE_OVERVIEW.md
2026-08-16 16:53:30 +02:00

312 lines
20 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.
For onboarding and feature-specific implementation guides, use the
[developer index](DEVELOPER_INDEX.md). This file remains the concise ownership
and dependency reference; it intentionally does not repeat every field and
test case from the focused guides.
## Emergent content and narrative boundary
Definitions, authored placements, mutable records, systems, and presentation
are separate layers. New content is registered through deterministic typed
content packs and stable definition IDs. World targets expose capabilities in an
active-context registry; player and NPC actors submit the same authoritative
action commands. Presentation catalogs resolve stable cue IDs without entering
headless simulation or saved state.
World events describe completed authoritative mutations. Knowledge,
relationships, unresolved situations, commitments, dialogue topics, and quest
journal entries reference those exact facts instead of creating parallel quest
state. Dialogue planning owns semantic intents and options; rendered prose and
the Dialogue Manager balloon are replaceable presentation. Every generator
returns its definition, causal references, and rejection/selection reason so a
developer can trace why the world produced an outcome.
Regional expansion uses one simulation authority. Locations, settlements,
routes, mobile groups, people, cohorts, and polities are data records; loaded
scenes are optional presentation contexts. Scheduled simulation-time work owns
distant travel and completion, while visuals interpolate nearby state. This
direction is governed by
[ADR 0002](decisions/0002-emergent-world-content-and-narrative.md).
## 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 and indexes completed facts
-> WorldEventStore exposes typed participants, locations, and causal queries
-> EventKnowledgeSystem records, ranks, transfers, and retains bounded knowledge
-> RelationshipSystem applies seven-dimensional evidence-backed consequences
-> SituationSystem projects real unresolved conditions from facts and events
-> QuestJournalSystem records only player discovery/tracking preference
-> SocialCommitmentSystem resolves promises from ordinary outcome events
-> ConversationService plans deterministic semantic turns from current context
-> VillageOpportunitySystem preserves the legacy bounded compatibility slice
-> ConflictSystem advances indexed combatants and authoritative combat outcomes
-> PlayerQuestSystem turns open needs into named player quests and
grants Standing as the player resolves them through real supply/feed
-> PlayerNeedsSystem advances the player's hunger, energy, and carry
-> 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 real pantry-backed war consequences.
Player commands are revalidated against authoritative life, faction, range,
and cooldown state. Player attack readiness advances in unscaled presentation
time; NPC battle cooldowns remain deterministic simulation ticks. Transient
NPC and sorted-combatant indexes bound stable-ID callbacks without entering
saves. 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,
O(1) ID lookup, participant/type indexes, history queries, and rate
calculations. `WorldEventStore` supplies the richer typed entity/location
boundary without making presentation assets part of headless facts;
- `simulation/knowledge/EventKnowledgeSystem.gd` owns player/NPC references to
known objective events, immutable acquisition provenance, proximity witnesses
at record time, one-hop direct fact transfer, salience/confidence/pinning,
and deterministic recent-fact retention/importance ranking;
- `simulation/relationships/RelationshipSystem.gd` owns directed relationship
queries across familiarity, trust, affection, respect, fear, obligation, and
hostility, exact event-backed changes, and deterministic social tie-breaking;
- `simulation/situations/` owns typed predicates, bounded/deduplicated world
situations, live progress projection, the player journal, and social
commitments. None of these systems creates quest-only stock, enemies, or
actions;
- `simulation/dialogue/` owns semantic intent definitions, deterministic
conversation planning, safe template substitution, and the narrow Dialogue
Manager presentation adapter. Only semantic acts/topics enter persistence;
- `simulation/commands/` owns copy-only commands/offers/results and reusable
typed action effects. Concrete command services must revalidate authority at
execution time for both player and NPC callers;
- `simulation/regional/` owns deterministic scheduled jobs, keyed random
decisions, and analytical elapsed-tick helpers. Presentation frame budgets
may defer due work but cannot reorder it;
- `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/quests/PlayerQuestSystem.gd` turns real open needs into named
player quests (`PlayerQuestRecord`) and owns the player's `PlayerStandingRecord`
reputation. A quest is generated only when the player can genuinely act
through the ordinary economy (an unassisted pantry/wood shortage, or a hungry
player-feedable animal), and it is resolved only by a real player supply or
feed event. Completing a quest grants Standing and raises the requester's
gratitude; a requester who personally trusts the player can ask them directly
even when another capable helper exists. Quests, Standing, and gratitude are
persisted and never become quest-only simulation state;
- `simulation/quests/PlayerNeedsSystem.gd` owns the player's embodied hunger,
energy, starvation, death, and carried inventory, advancing on the
deterministic tick and mutating finite resources and storage only through the
ordinary economy and recorder;
- `simulation/quests/PlayerNegotiationSystem.gd` owns the read-only villager
talk surface plus accept/decline of personal requests, delegating quest
lifecycle to `PlayerQuestSystem`;
- `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/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. Failed paths keep their target and use bounded exponential retry
instead of querying navigation every physics frame. `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
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.
Player resource interaction uses the same radius-bounded loaded-anchor grid.
HUD proximity probes, the time dial, player status, and day/night environment
are presentation-only and update at human-readable rates rather than every
rendered frame. `JajceWorld` exposes reversible High, Balanced, and Low
presentation profiles with instance-local mutable render resources and owned
viewport scaling. The default Balanced tier reduces 3D scale, grass, shadow
coverage, and volumetric work, while Low can stop grass/effects without changing
simulation state or UI resolution. Forward+-only volumetric fog stays disabled
under Mobile and Compatibility feature selection, even when High is requested.
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/commands/` | Shared player/NPC command, offer, result, and effect contracts |
| `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/situations/` | World situation, journal, and commitment lifecycles |
| `simulation/dialogue/` | Semantic conversation planning and presenter boundary |
| `simulation/regional/` | Regional records, due-job scheduling, keyed randomness, analytical updates |
| `simulation/quests/` | Named player quests and Standing reputation from real need resolution |
| `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/targets/` | Context-scoped target capabilities and transient presentation handles |
| `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.
- A situation or journal entry is a projection of ordinary facts. Accepting it
creates a social commitment only; fulfillment, supersession, failure, and
consequences always cite exact ordinary outcome events.
- Dialogue authority is the selected semantic intent/action at an expected
revision. Dialogue Manager renders one already-planned turn and never
evaluates gameplay conditions or mutates simulation state.
- Loaded navigation and visuals never decide whether distant work or travel
completes. Regional completion belongs to serialized due jobs; presentation
may interpolate and report local feasibility only.
- 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).