Files
gamedev-the-steward/docs/ARCHITECTURE_OVERVIEW.md
T

13 KiB

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

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
        -> 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/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/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/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/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/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 and GDScript naming 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, the action system architecture, and the simulation state schema.