237 lines
13 KiB
Markdown
237 lines
13 KiB
Markdown
# 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
|
|
npc_slept
|
|
npc_died
|
|
task_started
|
|
resource_depleted
|
|
animal_fed
|
|
villager_weak
|
|
home_damaged
|
|
combatant_hurt
|
|
combatant_killed
|
|
raid_started
|
|
war_resolved
|
|
war_aborted
|
|
wolf_hunt
|
|
activity_completed
|
|
task_blocked
|
|
commitment_accepted
|
|
commitment_fulfilled
|
|
commitment_broken
|
|
commitment_released
|
|
commitment_superseded
|
|
```
|
|
|
|
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.
|
|
|
|
Conflict facts are narrative events emitted by `ConflictSystem`: `combatant_hurt`
|
|
and `combatant_killed` carry the combatant ID as the source and the actor's NPC
|
|
ID (or a hostile sentinel) as the actor; `raid_started`, `war_resolved`, and
|
|
`war_aborted` narrate the tribe conflict lifecycle; `wolf_hunt` marks a wolf
|
|
attack. These are objective history but intentionally not knowable facts, so
|
|
they never gate NPC memory or relationships directly.
|
|
|
|
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 v16 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, situation, journal, commitment, and conversation records that
|
|
reference exact trigger/resolution/outcome events. Historical world schemas
|
|
migrate explicitly through nested record versions; current v16 adds non-empty
|
|
world/location scope and rejects a save whose scope differs from the active
|
|
adapter before mutating the simulation. Parsing rejects duplicate event IDs,
|
|
invalid or duplicate knowledge/situation/commitment references, impossible
|
|
communicator sources, relationship causes the observer does not know, forged
|
|
activity facts, 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.
|