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

213 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# The Steward — Economic Event Stream
## Current contract
Every successful food or wood movement creates an `EconomicEventRecord` using
the applicable event type:
```text
resource_extracted
storage_deposited
storage_withdrawn
item_consumed
animal_fed
villager_weak
home_damaged
```
Each record contains a monotonically increasing event ID, event type,
simulation tick, actor ID, source ID, destination ID, item ID, and transferred
amount, plus the authoritative world position captured when the fact is
recorded. NPC events preserve their authoritative interaction target (or NPC
position when no target applies); player extraction/depletion events preserve
the ResourceNode interaction position. NPC animal care records a
`storage_withdrawn` move from `village_pantry` to the matching caretaker
inventory at pickup, then an `animal_fed` move from that inventory to the
stable animal ID at delivery. Player `animal_fed` remains a direct
pantry-to-animal fact; actor `-1` identifies that player-triggered feeding or
extraction until persistent player identity is introduced.
The player now lives as a citizen with a persisted `PlayerStateRecord`. Player
gathering records a `resource_extracted` move from the finite source to the
`player_inventory` holder; depositing at the pantry or woodpile records a
`storage_deposited` move from that holder into the typed storage; eating
records `item_consumed` from carried food or the pantry. All player events use
the stable `PLAYER_ACTOR_ID`, so nearby living villagers witness a pantry
restock as a knowable fact and a hungry familiar witness can gain directed
trust toward the player. The old direct harvest-into-pantry `resource_extracted`
facts remain legacy-compatible for v12 saves.
Two bounded narrative facts feed the care opportunity family: `villager_weak`
marks a starving, low-energy villager who cannot feed themselves from an empty
pantry, and `home_damaged` marks a villager who slept through critically low
safety. Both are knowable by their actor and nearby witnesses.
Events are immutable facts about completed transfers. They do not perform the
transaction and are not replayed to reconstruct current state. Resource,
inventory, and storage records remain authoritative.
`SimulationEventLog` owns ordered event identity, append/restore behavior, and
history/rate queries, including exact lookup through `get_by_id()`.
`VillageEconomy` performs inventory and storage transactions and requests event
records only after state changes succeed. `AnimalCareSystem` composes the
exact carried-inventory removal with animal hunger relief and requests
`animal_fed` only after both succeed. `SimulationManager` remains the
scene-tree signal boundary used by presentation.
An action whose definition-backed completion cost becomes unavailable records
a zero-amount `task_blocked` narrative fact with stable action, source storage,
resource, and required-amount fields plus the readable shortfall reason. This
makes late contention queryable and inspectable without pretending that a
transfer occurred.
## Persistence and determinism
`SimulationStateRecord` schema v11 stores the ordered event stream,
`next_event_id`, directed relationships that may reference an exact event, and
per-NPC known-event references with first-acquisition provenance, plus
opportunity records that reference exact trigger/resolution events. Schema v1
and v2 saves migrate to an empty stream beginning at ID zero; world schemas
v1v7 migrate to an empty opportunity list, while world schemas v8v10
preserve and normalize opportunity history. World v9 adds an empty animal list
during migration; world v10 preserves and upgrades its animal records. Parsing
rejects duplicate event IDs, invalid or duplicate knowledge/opportunity
references, impossible communicator sources, relationship causes the observer
does not know, and next IDs that could collide with restored history.
Animal-feed facts receive an additional cross-record check: actor is either an
existing NPC or the player sentinel, destination is an existing animal, item
and definition cost are exactly one food, event tick is not in the future, and
the animal's `last_fed_tick` agrees with its latest feed fact. New NPC facts
emitted at runtime name that actor's matching inventory; player facts name the
pantry. Pantry-sourced NPC facts remain accepted as legacy-compatible v11
history rather than being silently rewritten into an invented carried
transfer.
Positive food deposits, positive NPC food withdrawals from `village_pantry`
into that actor's matching inventory, and definition-backed patrol/study
`task_blocked` facts caused by missing wood at `village_woodpile` are currently
knowable. The actor and living NPCs within the bounded witness radius receive a
`KnownEventStateRecord` at record time. Witness distance uses the event's
captured position, never the actor's later location. A successful food deposit
can raise a hungry familiar witness's directed trust; the relationship stores
that same deposit event ID rather than copied prose, so the inspector can
resolve and display the real completed fact. This is evidence-gated use of the
event stream, not general event sourcing.
Knowledge now records whether an NPC performed, witnessed, heard, or inherited
a fact from a legacy save. When an NPC arrives beside an already-working NPC at
the same non-storage activity site, the worker may communicate one newest
direct fact within 2.5 metres. The listener references the same immutable event
and retains the speaker ID; no transaction or event is replayed. Only
performed/witnessed facts can cross this one social hop, so this is not yet a
rumour cascade. Relationship appraisal uses the listener's state when the fact
is acquired, not a reconstruction of their needs when the old event occurred.
Known facts are now bounded without deleting objective history. A fact is
lasting while it remains that NPC's current trust cause; other facts are recent.
An NPC keeps at most three recent facts, and a deterministic review once per
simulated-day-sized interval removes recent facts at least one day old. Current
causes remain so `Relationship` and `Because` never point at forgotten
evidence. Conversations prefer lasting direct facts, then the most recently
acquired direct fact. Communicated provenance snapshots how the speaker knew
the event, so a listener's memory stays valid after the speaker forgets.
`VillageOpportunitySystem` is another read-only consumer of immutable facts.
A known positive NPC pantry withdrawal, combined with the pantry currently
being empty and a living critically hungry knower, can open one
`restock_empty_pantry` record. A known missing-wood `task_blocked` fact can open
one `supply_missing_wood` record for its living performer. At most one need is
open globally. The system does not perform a transaction, assign an NPC, or
append a quest-only event. A need resolves only after its target storage
actually reaches the one-unit target through either:
- a later matching positive `storage_deposited` event from a real NPC
inventory; or
- a later player `resource_extracted` event from an existing player-usable
matching ResourceNode directly into the target storage.
The exact trigger and resolution IDs remain in opportunity history. The active
trigger memory is protected until closure. A wood need is invalidated if its
interested performer dies or if it is still unresolved one simulated day after
opening; invalidation emits no event and stores the close tick and stable
reason. Resolution or invalidation returns the trigger to normal bounded
retention.
When a performer or direct witness meets another villager at an eligible shared
activity, the active opportunity's trigger is attempted before normal
lasting/recent communication ranking. `EventKnowledgeSystem` still requires
performed/witnessed source provenance and records only the existing event ID,
speaker ID, source method, listener, and acquisition tick. A newer unrelated
fact cannot displace the active report, while communicated listeners still
cannot relay it onward.
For an open need, the same system can derive one capable helper without
assigning work. A candidate must know the exact trigger and have a familiar
directed relationship with at least 0.6 trust toward the interested villager.
Enough carried inventory produces the matching deposit route; otherwise a
matching enabled, NPC-usable, sufficiently stocked and available finite
ResourceNode state produces the gather route. Ranking is deterministic and
prefers ready inventory, trust, profession fit, familiarity, then stable NPC
ID. The returned opportunity/trigger/helper/action/resource, inventory source
or available finite-source count, and reason are an ephemeral query result. It
does not select an exact gather target; that remains with `ActiveWorldAdapter`
and `ActionTargetResolver`. No event, reservation, RNG draw, task mutation, or
serialized assignment is created.
At an idle NPC decision boundary, `SimulationManager` performs that query again
and gives the ephemeral result to `ActionSelectionSystem`. Personal survival,
low-energy rest, mourning, and sleep/meal schedule branches retain precedence;
otherwise only the named helper selects the reported ordinary gather/deposit
action. From there, existing target resolution chooses and reserves an exact
finite resource or storage target. The current NPC task may serialize normally
while in progress, but no opportunity-to-helper assignment or acceptance state
exists in the save.
The food-loop regression verifies this chain:
```text
bush -> NPC inventory -> village pantry -> NPC inventory -> consumed
```
It checks event order, stable IDs, exact quantities, conservation, and
save/restore continuity.
## Presentation
`npc_inventory_changed` is a transient presentation signal. The active
`NpcVisual` shows a small food sack whenever its authoritative inventory
contains food. A newly loaded visual derives the same state directly from the
NPC record, so unloading presentation does not lose the fact.
The selected-NPC inspector shows up to four lasting-first retained facts with
acquisition provenance, separately from three objective events the NPC
personally performed. A real trust-changing consequence emits one transient
amber blossom above its observer. The cue is presentation-only, remains visible
in cinematic mode, and is neither saved nor replayed after visual rebuild.
The village summary also shows one compact active need with its real target
progress and interested villager; only that villager's inspector retains its
open/closed detail and names the exact supplier for a resolution. The empty-bowl
world cue remains specific to the pantry shortage rather than representing a
wood need as hunger. The active summary additionally names the currently
derived helper, supply action, trust fact, and inventory or finite-source route,
or states that no informed trusted villager is currently able to supply it.
One separate cardless player HUD gives transient warm-amber feedback for the
authoritative need opening, exact named report, newly informed helper decision,
and real resolution. It stays available when development overlays are hidden,
but clears on restore and never becomes objective or save state.
## Deliberate limits
The objective stream is currently kept in full for the small simulation
garden; only per-NPC known references are bounded. Before large populations or
long-running worlds, add measured archival/summary rules and query indexes.
Proximity is the only current direct witness rule, and communication is
intentionally one-hop and tied to shared activity arrival. Line of sight,
hearing, personalized reinforcement/decay, multi-hop rumours, secrecy, false
beliefs, and multi-event causal graphs belong in later event/history slices.
They should extend this record family without making prose authoritative or
recomputing old evidence from current positions.
The current opportunity family is likewise a bounded two-type projection with
one read-only helper query and one ordinary action-selection consumer, not a
generic quest, reward, acceptance, dialogue, helper assignment, or capable-
helper framework.