Files
gamedev-the-steward/docs/ACTION_SYSTEM_ARCHITECTURE.md
T
2026-07-17 00:34:30 +02:00

171 lines
7.8 KiB
Markdown

# The Steward — Action System Architecture
## Current flow
```text
SimulationManager tick
-> ActionExecutionSystem advances needs and working progress
-> SimulationPopulationView refreshes transient living/starving indexes
-> VillageOpportunitySystem re-derives the current capable helper
-> ActionSelectionSystem chooses an action when the NPC is idle
-> SimulationManager stores the selected action
-> npc_target_requested
-> WorldViewManager supplies the active visual's current position
-> ActionTargetResolver consumes simulation state + ActiveWorldAdapter facts
-> SimulationManager stores/reserves the target
-> npc_travel_requested
-> WorldViewManager commands NpcVisual travel
-> arrival/navigation failure returns to SimulationManager
```
## Responsibilities
### ActionSelectionSystem
- reads NPC and village state;
- queries `RelationshipSystem` after personal survival/schedule overrides, so a
trusted starving acquaintance can redirect ordinary work toward food
gathering while the pantry is low;
- consumes the manager's current `SimulationPopulationView` for that social
query instead of rebuilding an all-NPC lookup for each idle decision;
- accepts the current ephemeral `OpportunityHelperResult` and, only for its
matching NPC, selects the reported ordinary gather/deposit action after
urgent self-care and sleep/meal schedule branches;
- evaluates current utility scores;
- consumes the NPC's deterministic decision RNG;
- returns an action ID, optional urgent-duration override, branch reason,
compared utility scores, and definition-backed rejection reasons;
- does not mutate targets, navigation, or visuals.
### ActionExecutionSystem
- advances hunger, energy, starvation, death, and working progress;
- marks an action complete when its duration elapses;
- does not select the next action or resolve a target.
### ActionTargetResolver
- reads target metadata from ActionDefinition;
- consumes active-world facts through ActiveWorldAdapter;
- expands resource queries through nearby grid ranges and prunes farther cells
only when authoritative risk/comfort/priority bounds prove they cannot win;
- preserves the former registration-order tie behavior and keeps a linear
compatibility path for isolated adapters;
- checks simulation-owned ResourceStateRecord availability;
- reserves and returns stable resource target IDs;
- skips full-capacity activity sites based on authoritative NPC target claims;
- returns typed activity-site, storage, or wander positions without owning
presentation.
### ActiveWorldAdapter
- owns the disposable `LoadedResourceSpatialIndex` of loaded stable-ID resource
interaction positions;
- refreshes the index as ResourceNodes enter, leave, rebind authoritative
state, or move;
- exposes loaded storage and activity-site interaction positions;
- exposes activity-site capacity as an active-world fact;
- contains active-world query facts, not persistent mutable authority;
- does not choose actions or reserve resources.
### WorldViewManager
- spawns and tracks NpcVisual instances;
- supplies a visual's current position when resolution is requested;
- applies travel commands;
- reports arrival, navigation failure, and visual death state;
- does not map actions, select targets, write target IDs, or mutate resources.
### SimulationManager
SimulationManager remains the orchestrator and event boundary. It owns
simulation records, invokes the focused systems, stores selected actions and
targets, and translates presentation callbacks into simulation transitions.
It also owns one disposable `SimulationPopulationView`, rebuilt at tick start
and refreshed after each NPC advances so interleaved decision semantics remain
unchanged.
The manager publishes the latest `ActionSelectionResult` for presentation; the
UI does not recompute decisions. Each idle selection re-derives the capable
helper instead of consulting persisted assignment state. At completion it
atomically pays any definition-backed stored-resource cost before applying the
action effect. A late shortfall suppresses the effect and records a
`task_blocked` fact. The manager also captures actor/nearby knowledge before
forwarding newly recorded events into `RelationshipSystem`. When an NPC arrives
beside a worker at the same non-storage activity site, the manager may transfer
one direct known fact and applies its consequence only to that newly informed
listener. An open opportunity's exact trigger receives bounded priority in
that conversation before normal lasting/recent ranking. It exposes
knowledge, provenance, relationship, and cause queries without moving social
authority into UI. After consequences are known, it protects current causal
facts, enforces the recent-memory cap, and runs age review from authoritative
simulation ticks.
### VillageEconomy
- owns storage and NPC-inventory transfer operations;
- keeps `village.food` and `village.wood` synchronized as aggregate views;
- validates and pays definition-backed completion costs;
- emits completed transaction facts without owning their history.
### SimulationEventLog
- owns ordered economic and narrative event identity;
- answers recent-history, actor-history, and consumption-rate queries;
- restores persisted history without performing or replaying transactions.
### EventKnowledgeSystem
- stores stable `(knower_id, event_id)` references separately from objective
history;
- initially observes successful food-deposit events for the actor and living
NPCs within a fixed proximity radius;
- preserves first acquisition as performed, witnessed, communicated, or
legacy, with a stable source NPC ID for direct communication;
- permits one performed/witnessed fact to cross one co-located social hop, but
does not relay communicated or ambiguous legacy facts;
- tries the active opportunity trigger first when the speaker directly knows
it, then orders remaining communication by lasting relationship relevance
and acquisition recency;
- retains current relationship causes and at most three other recent facts per
NPC, expiring day-old recent facts at deterministic review boundaries;
- captures evidence only when the event happens, never from current positions
during restore;
- answers deterministic known-event and knower queries without formatting
prose or choosing actions.
### RelationshipSystem
- owns directed familiarity/trust records and stable relationship queries;
- applies a bounded trust gain when a hungry familiar NPC knows about a
contributor's successful food-deposit event;
- stores the exact event ID as the trust cause and ignores replayed or older
events;
- selects trusted starving subjects deterministically for action selection;
- uses a supplied per-tick population view when available, with a compatibility
fallback for isolated callers;
- does not choose actions, perform transactions, or format presentation text.
These collaborators are `RefCounted` rule services, not additional scene-tree
managers. Further decomposition should follow measured pressure and a proven
gameplay consumer.
## Active-position contract
NpcVisual emits active position changes after successful movement.
WorldViewManager forwards those facts through
`SimulationManager.synchronize_npc_position()`. Arrival, navigation failure,
and visual unload also perform a final synchronization.
SimNPC stores both authoritative position and the resolved travel destination.
The destination is versioned simulation state, so reloading a visual resumes
the same route request without rerolling wander, resolving another resource, or
changing a reservation.
`tests/npc_visual_lifecycle_test.gd` proves that visual unload/reload preserves
action, target ID, travel destination, resource reservation, position, and
state checksum.
Unloaded NPCs do not yet resolve abstract travel time; they remain in their
current task state until an active visual or a future simulation-LOD travel
system advances them.