479 lines
27 KiB
Markdown
479 lines
27 KiB
Markdown
# The Steward — Simulation State Schema
|
||
|
||
## Current contract
|
||
|
||
`SimulationStateRecord` is the versioned JSON boundary for the current
|
||
simulation. The current world schema is v16 and captures:
|
||
|
||
- simulation seed, stable regional world/location scope, tick interval, tick
|
||
count, clock remainder, and elapsed clock ticks;
|
||
- every NPC's identity, needs, attributes, task lifecycle, target, position,
|
||
starvation state, and decision RNG stream;
|
||
- village resource counters;
|
||
- controlled per-NPC wander RNG streams;
|
||
- scene-independent resource state plus the definition facts needed while its
|
||
ResourceNode is unloaded;
|
||
- scene-independent animal identity, position, hunger, feeding history,
|
||
reservation, availability, current routine site, active travel destination,
|
||
and next routine tick;
|
||
- pantry contents, carried NPC inventory, the ordered economic event stream,
|
||
and its next stable event ID;
|
||
- sparse directed relationship records for familiarity, trust, affection,
|
||
respect, fear, obligation, and hostility, with the exact causal event for
|
||
every non-neutral change;
|
||
- player and NPC known-event records that reference objective event history
|
||
without copying it, including acquisition source, hop count, confidence,
|
||
salience, pin state, acquisition tick, and historical communicator
|
||
provenance;
|
||
- opportunity records with stable type/status, interested NPC, trigger event,
|
||
target storage/resource/amount, exact later resolution event identity, or a
|
||
deterministic invalidation reason and close tick;
|
||
- world-backed situations, player journal discovery/tracking state, social
|
||
commitments, and bounded semantic conversation acts. These records retain
|
||
definition IDs and exact causal event references, never rendered dialogue
|
||
prose or copied quest progress;
|
||
- a player record holding the player-citizen's hunger, energy, health,
|
||
downed state, and carried inventory. The player's world position remains
|
||
presentation-owned and is intentionally not serialized;
|
||
- combatant records (health, weapon, faction, position, hostility, and enemy
|
||
definition ID) for NPC defenders and standalone hostile archetypes;
|
||
- faction records (food, warriors, aggression, morale, stance, war plan) for
|
||
the village and the hill tribe.
|
||
|
||
The top-level identity is:
|
||
|
||
```json
|
||
{
|
||
"schema": "the_steward.simulation",
|
||
"schema_version": 16
|
||
}
|
||
```
|
||
|
||
Unknown schema versions and structurally incomplete records are rejected. Add
|
||
an explicit migration function before accepting a future version; never
|
||
silently reinterpret old data as the newest layout.
|
||
|
||
NPC action and profession references are serialized as stable strings and
|
||
restored as `StringName`. Records referencing unknown executable actions or
|
||
professions are rejected against the validated definition registry. See
|
||
[the simulation definitions](SIMULATION_DEFINITIONS.md).
|
||
|
||
## Deterministic continuation
|
||
|
||
RNG seeds and internal states are encoded as decimal strings. They are signed
|
||
64-bit values and cannot safely pass through every JSON number implementation
|
||
without precision loss. Converting them to ordinary JSON numbers caused a
|
||
restored simulation to retain the same visible state while silently changing
|
||
its future random sequence.
|
||
|
||
`tests/simulation_state_serialization_test.gd` protects this contract by:
|
||
|
||
1. running a fixed-seed scenario for 48 ticks without interruption;
|
||
2. running the same scenario for 24 ticks;
|
||
3. serializing and restoring into a fresh manager;
|
||
4. running the remaining 24 ticks;
|
||
5. requiring the complete final-state checksums to match.
|
||
|
||
It also verifies clock remainder, resource amount/reservation/enabled
|
||
round-tripping, presentation unload/rebind, directed relationship/cause
|
||
round-tripping, divergent known-event state, communicated provenance, and
|
||
deterministic retention boundaries, and rejection of unsupported schemas.
|
||
Active, resolved, and invalidated opportunity round-tripping, contradictory
|
||
target state, invalid resource sources, and cross-record event references are
|
||
covered too. The emergent slice additionally verifies that situation evidence,
|
||
journal links, commitment participants/outcomes, and semantic conversation
|
||
history survive a save between accepting and completing real work.
|
||
|
||
NPCStateRecord v2 adds the resolved travel destination and whether it is
|
||
active. Nested v1 NPC records migrate explicitly with no invented active
|
||
destination. This allows a reloaded visual to resume travel without changing
|
||
target selection or deterministic RNG state.
|
||
|
||
NPCStateRecord v3 adds carried inventory. SimulationStateRecord v2 adds
|
||
StorageStateRecord entries; world-schema v1 migrates legacy village food and
|
||
wood into the stable `village_pantry` and `village_woodpile` records. NPC
|
||
inventory IDs and amounts are canonicalized to strings and floats; empty or
|
||
colliding normalized IDs, non-numeric values, and negative or non-finite
|
||
amounts are rejected.
|
||
Parsed storage values are likewise canonicalized so save/restore continuation
|
||
retains byte-stable checksums. Current storage parsing rejects empty item IDs,
|
||
negative or non-finite amounts, negative or non-finite capacity, and contents
|
||
above capacity. Runtime deposit and withdrawal requests also reject empty item
|
||
IDs and non-finite amounts without mutating authoritative stock. When the
|
||
authored pantry or woodpile is present, current world parsing also requires
|
||
its authoritative food or wood amount to match the duplicated village summary;
|
||
contradictory saves are rejected instead of silently changing checksum during
|
||
restore. World-v1 migration expands a storage's inferred capacity when needed
|
||
so valid legacy stock above the current default is preserved rather than
|
||
discarded.
|
||
|
||
The compound NPC `feed_animal` route needs no additional saved phase. An
|
||
in-progress record already contains the exact animal `target_id`, matching
|
||
animal reservation, carried inventory, task state, and active travel
|
||
destination. Insufficient carried food plus a pantry destination is the pickup
|
||
leg; the definition cost in inventory plus the animal destination is the
|
||
delivery leg. Restore emits only the saved travel request and never repeats
|
||
pickup. A compatible pre-delivery v11 feed record may instead have empty
|
||
inventory and the claimed animal as its destination; arrival redirects that
|
||
same claim through the current pantry waypoint before any withdrawal. If that
|
||
older record is already working, its first resumed tick performs the same
|
||
reroute and carries its saved work progress back to the goat. Successful
|
||
pickup changes the saved destination to the exact animal before publishing
|
||
synchronous inventory, event, or village signals, so signal-time saves cannot
|
||
encode carried food with a stale pantry destination.
|
||
|
||
SimulationStateRecord v3 adds ordered `EconomicEventRecord` entries and
|
||
`next_event_id`. World schemas v1 and v2 migrate explicitly to an empty event
|
||
stream. Event records use stable source/destination/item IDs, reject invalid or
|
||
duplicate IDs, and preserve deterministic ordering across save/restore. See
|
||
[the economic event stream](ECONOMIC_EVENTS.md).
|
||
|
||
EconomicEventRecord v2 adds the authoritative world position captured when the
|
||
fact is recorded. Nested v1 events migrate with a zero-vector fallback; old
|
||
saves already encode their stable location IDs, and migrated knowledge is not
|
||
recomputed spatially.
|
||
|
||
SimulationStateRecord v4 moves social authority into top-level directed
|
||
`RelationshipStateRecord` entries. Each record contains `observer_id`,
|
||
`subject_id`, familiarity, trust, and `last_trust_cause_event_id` (`-1` when no
|
||
event has changed trust). Parsing requires unique ordered pairs, existing NPC
|
||
IDs, bounded relationship values, and a real event for every non-empty cause
|
||
ID.
|
||
The relationship system serializes records in stable observer/subject order so
|
||
they participate in deterministic checksums.
|
||
|
||
World schema v3 migrates its NPC-local familiarity pairs into directed
|
||
relationships with neutral trust. Nested NPC schema v4 removes that obsolete
|
||
duplicate field; v3 NPC records remain accepted only through the explicit
|
||
migration. World schemas v1–v2 also derive the graph from any legacy
|
||
familiarity data (or initialize it empty) after their storage/event migrations.
|
||
|
||
SimulationStateRecord v5 adds top-level `KnownEventStateRecord` entries keyed
|
||
by `knower_id` and `event_id`. The objective `EconomicEventRecord` remains the
|
||
single completed fact; knowledge records only state which NPC knows it. Parsing
|
||
requires unique NPC/event pairs and valid references to both an existing NPC
|
||
and an event in the same record. A relationship's causal event must also be
|
||
known by that relationship's observer and, for the current trust contract, be
|
||
a food deposit performed by the relationship subject.
|
||
|
||
World schema v4 migrates relationship causes into known-event records. The old
|
||
schema used village-wide awareness, so this preserves the implied known fact
|
||
without falsely inventing spatial witness provenance.
|
||
|
||
SimulationStateRecord v6 / KnownEventStateRecord v2 adds immutable
|
||
first-acquisition provenance: `performed`, `witnessed`, `communicated`, or
|
||
`legacy`. Communicated records also retain the stable NPC ID of the direct
|
||
speaker. New direct facts are created only at event-record time from the actor
|
||
and nearby NPC positions; historical events are never re-evaluated against
|
||
current positions. A co-located shared-activity encounter can add one
|
||
communicated reference to the original fact without appending or replaying the
|
||
event.
|
||
|
||
World schema v5 migrates actor-owned knowledge to `performed`; non-actor
|
||
knowledge becomes `legacy` because v5 may contain either a proximity witness or
|
||
an older implied relationship fact. Parsing never invents that missing
|
||
provenance. At runtime, communication requires a distinct source who currently
|
||
knows the event through `performed` or `witnessed` acquisition. Persisted
|
||
records validate that direct-method snapshot even after the source forgets.
|
||
This enforces the current one-hop boundary and makes validation independent of
|
||
array ordering.
|
||
|
||
SimulationStateRecord v7 / KnownEventStateRecord v3 adds `acquired_tick` and a
|
||
snapshot of the direct speaker's acquisition method. Direct facts use the
|
||
objective event tick; newly communicated facts use the conversation tick. The
|
||
speaker snapshot keeps historical provenance valid even if the speaker later
|
||
forgets their own copy, while runtime communication still requires a currently
|
||
known performed/witnessed fact and remains one-hop.
|
||
|
||
World schema v6 migrates direct acquisition to the objective event tick.
|
||
Communicated and ambiguous legacy facts receive a fresh retention lease at the
|
||
saved simulation tick because their exact acquisition time was not previously
|
||
stored. The v6 source record supplies the persisted direct-method snapshot.
|
||
|
||
Importance combines persisted salience/pinning with current causal use. A fact
|
||
is protected while it explains any relationship dimension, an open situation,
|
||
journal discovery, or commitment. Unpinned ordinary memories remain bounded and
|
||
age through deterministic reviews. The objective event stream is never pruned
|
||
by this rule.
|
||
|
||
SimulationStateRecord v8 adds the top-level `opportunities` array and
|
||
`simulation.next_opportunity_id`. The first nested `OpportunityStateRecord`
|
||
schema stores `opportunity_id`, `opportunity_type`, `status`, `created_tick`,
|
||
`trigger_event_id`, `interested_npc_id`, `target_id`, `resource_id`,
|
||
`target_amount`, `resolution_event_id`, and `resolved_tick`. World schemas
|
||
v1–v7 migrate explicitly to an empty opportunity list with next ID zero.
|
||
|
||
SimulationStateRecord v9 preserves world-v8 opportunity history and upgrades
|
||
the nested record to `OpportunityStateRecord` v2. The nested schema adds
|
||
`closed_tick` and `invalidation_reason`, accepts `supply_missing_wood`, and adds
|
||
the stable `invalidated` status. Nested opportunity v1 records normalize their
|
||
resolved tick into the close tick and remain valid pantry history. Economic
|
||
event schema v3 adds normalized `action_id` and `required_amount` fields;
|
||
nested v1/v2 events receive empty/zero defaults, while a new `task_blocked`
|
||
event requires its stable action, source storage, resource, and positive
|
||
requirement contract.
|
||
|
||
The v3 event envelope also carries validated extension fields for authored
|
||
activity facts without adding a parallel quest record. An
|
||
`activity_completed` fact stores the target/action IDs, world and location
|
||
scope, metric ID, actual bounded delta, and exact item cost. Loading derives
|
||
the expected handler, effect, event type, metric, maximum delta, and cost from
|
||
the current action definition and rejects forged actor, discriminator, scope,
|
||
or amount combinations even when no knowledge record references the event.
|
||
Player and NPC performed knowledge points to that same immutable fact.
|
||
|
||
SimulationStateRecord v10 adds the top-level `animals` array and the first
|
||
`AnimalStateRecord` schema. Each animal record stores its stable animal and
|
||
species IDs, display name, authoritative world position, hunger,
|
||
`last_fed_tick`, reservation owner, and enabled/player/NPC feeding flags.
|
||
World schema v9 preserves opportunity history while migrating to an empty
|
||
animal list.
|
||
|
||
SimulationStateRecord v11 preserves world-v10 animals and upgrades nested
|
||
`AnimalStateRecord` v1 to v2. The nested schema adds `routine_site_id`,
|
||
`travel_target_site_id`, exact `travel_target_position`,
|
||
`has_travel_target`, and `next_routine_tick`. Migrated animals receive an
|
||
idle, immediately due routine without inventing a destination; loaded-world
|
||
selection establishes the first real target later.
|
||
|
||
SimulationStateRecord v12 lets `RelationshipStateRecord` reference the player
|
||
sentinel (`PLAYER_ACTOR_ID`) as a subject, so a villager's directed trust can
|
||
point at the player. World v11 migration adds nothing but accepts the relaxed
|
||
validation; existing NPC-only relationship history is preserved. Player-triggered
|
||
food supply is recorded as a pantry `storage_deposited` from the player inventory
|
||
(or, for legacy v11 saves, as a direct harvest into the pantry).
|
||
|
||
SimulationStateRecord v13 adds the top-level `player` record
|
||
(`PlayerStateRecord` v1) with the player-citizen's hunger, energy, and carried
|
||
inventory. World schemas v12 and earlier migrate to a fresh default player
|
||
record (hunger 40, energy 100, empty inventory). Player needs advance on the
|
||
deterministic tick with exact binary fractions so continuation checksums stay
|
||
stable. Resource records also migrate to v4, adding `max_amount` and
|
||
`regrow_rate`; legacy v3 resources keep their current amount as the cap and
|
||
gain no regrowth.
|
||
|
||
SimulationStateRecord v14 adds the top-level `combatants` and `factions`
|
||
arrays. `CombatantStateRecord` v1 tracks health, equipped weapon, faction,
|
||
hostile flag, and position for NPC defenders, tribe raiders, and wolves.
|
||
`FactionStateRecord` v1 tracks food, warriors, aggression, morale, stance, and
|
||
the tribe's war plan with its confidence. World schemas v13 and earlier migrate
|
||
to empty combatant lists plus default village/tribe factions; the manager
|
||
registers living NPC combatants on restore. Combatant positions are quantized
|
||
to a binary-exact grid and faction food to a binary-exact step so that
|
||
arbitrary world floats round-trip through JSON without breaking deterministic
|
||
continuation checksums.
|
||
|
||
`PlayerStateRecord` v2 adds `health`, `max_health`, `downed`, and
|
||
`downed_ticks` so wolves and raiders can wound and down the player, with a
|
||
brief forced recovery that restores a fighting baseline. Nested v1 player
|
||
records migrate to full health and standing. The player combatant used by the
|
||
conflict system is derived state and is never serialized; hostile attacks
|
||
reduce the authoritative player health through the ordered event stream.
|
||
|
||
SimulationStateRecord v15 adds top-level `situations`, `quest_journal`,
|
||
`commitments`, and `conversation_history` arrays plus monotonic next-ID counters
|
||
for each family. World schemas v1-v14 migrate to empty emergent collections and
|
||
zero counters. On manager restore, a compatible legacy pantry opportunity can
|
||
be imported once into the new situation system when no situation history is
|
||
present; the two records are not independently generated substitutes for world
|
||
state.
|
||
|
||
`SituationStateRecord` stores the definition and dedupe IDs, trigger event,
|
||
stable primitive context, selected objective alternative, lifecycle, and exact
|
||
resolution event. Live progress remains derived from the definition, ordinary
|
||
event store, and current authoritative facts. `QuestJournalEntryStateRecord`
|
||
stores discovery, tracking preference, selected alternative, and archive/close
|
||
state only; it neither copies objectives nor owns progress. The player therefore
|
||
cannot discover a journal entry until knowledge reaches them through an actual
|
||
conversation or other ordinary knowledge path.
|
||
|
||
`CommitmentStateRecord` links one situation to typed debtor and creditor entity
|
||
references, semantic terms, a deadline, lifecycle, and exact outcome event.
|
||
Acceptance does not reserve stock or prevent other actors from resolving the
|
||
situation. A matching outcome performed by the debtor fulfills it; another
|
||
actor's outcome supersedes it; a still-achievable missed deadline breaks it at
|
||
the due tick. Release remains an explicit social action. Consequence application
|
||
is keyed by commitment/outcome identity so replay or restore cannot award it
|
||
twice.
|
||
|
||
`ConversationActStateRecord` stores only a bounded semantic history: stable act
|
||
ID, tick, typed speaker/listener references, supported intent ID, and at most
|
||
eight topic IDs. Conversation IDs, current turns, option revisions, rendered
|
||
templates, balloon state, and prose are transient. Reopening dialogue plans from
|
||
current facts plus the retained semantic acts, so wording can evolve without
|
||
changing authoritative history.
|
||
|
||
SimulationStateRecord v16 adds non-empty `world_id` and `location_id` fields to
|
||
the top-level simulation record. Activity completion and blocked-action facts
|
||
retain that same scope in their immutable payload, so event-store indexes,
|
||
knowledge validation, and save restoration cannot reinterpret a local position
|
||
as belonging to another loaded context. World v15 migrates explicitly to the
|
||
existing Bosnia/Jajce IDs. An event log that already contains facts refuses a
|
||
different configured scope instead of silently reindexing history. Restore
|
||
preflights this scope against the active adapter before changing clock,
|
||
economy, NPC, or event state, so both empty- and populated-history mismatches
|
||
fail atomically. Older unstructured `task_blocked` facts are retained with
|
||
explicit legacy provenance for save continuity, but they are not promoted into
|
||
knowable evidence or new situation triggers.
|
||
|
||
Nested `KnownEventStateRecord` v4 adds the player as a valid knower/source and
|
||
records hop count, confidence, salience, and explicit pinning. Communicated
|
||
facts remain exactly one hop from a performed or witnessed source. Nested
|
||
`RelationshipStateRecord` v2 adds affection, respect, fear, obligation, and
|
||
hostility, makes either player or NPC a valid endpoint, and stores a causal
|
||
event ID for all seven dimensions. Nested `AnimalStateRecord` v3 persists an
|
||
`animal_definition_id`; older records migrate to the goat definition. Nested
|
||
`CombatantStateRecord` v2 persists `enemy_definition_id`; legacy hostile kinds
|
||
map to their current raider or wolf definitions, while NPC defenders retain an
|
||
empty enemy definition.
|
||
|
||
Current-schema parsing requires unique animal IDs that do not collide with
|
||
resource or storage IDs. An animal reservation must belong to an existing NPC
|
||
whose active feed task targets that exact animal. Every `animal_fed` event
|
||
must name an existing animal as its destination, food as its item, the exact
|
||
feed action cost, and a valid NPC actor or the player sentinel. New NPC events
|
||
name the matching `npc_<id>_inventory`; player events name the pantry. Direct
|
||
pantry-sourced NPC facts remain accepted as legacy-compatible v11 history,
|
||
while runtime emits matching-inventory sources for new NPC deliveries. The
|
||
latest matching feed event tick must agree with the animal's `last_fed_tick`.
|
||
|
||
The bounded opportunity family accepts at most one open record globally.
|
||
Opportunity, trigger-event, and resolution-event IDs are unique and the next
|
||
ID must remain above restored history. Referenced NPCs, storage, resources, and
|
||
events must exist. A `restock_empty_pantry` trigger is a positive NPC food
|
||
withdrawal from `village_pantry` to that actor's matching inventory; an open
|
||
record additionally requires the pantry to remain below its one-food target
|
||
and its interested NPC to have acquired that fact no later than creation. A
|
||
`supply_missing_wood` trigger is the performer's known patrol or study
|
||
`task_blocked` fact against `village_woodpile` with a one-wood requirement; an
|
||
open record additionally requires a living interested performer and the
|
||
woodpile below target.
|
||
|
||
Resolution references an exact later matching NPC inventory-to-storage deposit
|
||
or player `resource_extracted` event from an existing player-usable matching
|
||
resource into the target storage, with matching resolved and closed ticks.
|
||
Resolved history does not require the storage still to contain the resource
|
||
because later use is valid. Invalidation has no resolution event and requires a
|
||
stable close tick and is currently valid only for `supply_missing_wood`.
|
||
`interested_died` must agree with the restored NPC's death state; the runtime
|
||
creates `evidence_stale` at the one-day unresolved boundary.
|
||
|
||
While a need is open, its interested villager's trigger evidence is treated as
|
||
lasting during deterministic memory maintenance. Resolution or invalidation
|
||
releases that fact back to the normal bounded-retention rules; neither deletes
|
||
objective event or opportunity history.
|
||
|
||
`OpportunityHelperResult` and `OpportunityPlayerResponseResult` are deliberately
|
||
absent from this schema. The helper result is re-derived from the open
|
||
opportunity plus current NPC knowledge, directed relationship, inventory,
|
||
action-definition, storage-capacity, and finite-resource records. The player
|
||
result additionally requires no capable helper and derives its ordinary gather
|
||
action, real destination, and source count from current enabled player-usable
|
||
finite resources. Save/restore regressions require the same applicable route
|
||
result and an unchanged checksum before and after querying.
|
||
|
||
When an idle derived helper chooses the reported supply action, only the
|
||
existing NPC task, task-state, target, travel, and reservation fields serialize.
|
||
The helper result remains absent: restore retains any in-progress ordinary task
|
||
and independently re-derives current capability from restored facts.
|
||
|
||
Active-need communication adds no schema fields. It persists through the
|
||
existing `KnownEventStateRecord` event, listener, source NPC, direct source
|
||
method, and acquisition-tick fields. Communication and player-response HUD
|
||
animations are transient and are cleared rather than replayed after restore.
|
||
|
||
## Resource authority
|
||
|
||
`SimulationManager` owns `ResourceStateRecord` instances independently of the
|
||
scene tree. Amount, reservation, enabled state, action/resource identity,
|
||
yield, usage permissions, and discovery metadata remain available and
|
||
serializable while no ResourceNode is loaded.
|
||
|
||
ResourceNode binds to a record by stable ID and provides presentation,
|
||
interaction transforms, and active-world discoverability. Unloading a node does
|
||
not delete its record. Loading another node with the same stable ID rebinds to
|
||
the existing record and cannot overwrite its amount or reservation with scene
|
||
defaults.
|
||
|
||
ResourceStateRecord v3 adds `safety_risk`, `comfort_distance`, and
|
||
`discovery_priority` so unloaded resources keep the same target-selection
|
||
meaning after save/restore. Nested v1 and v2 resource records are migrated
|
||
explicitly. Current-schema parsing also canonicalizes numeric, boolean, and ID
|
||
variants so a freshly registered finite source retains a byte-stable checksum
|
||
after a JSON round trip.
|
||
|
||
## Animal authority
|
||
|
||
`AnimalCareSystem` owns `AnimalStateRecord` instances independently of the
|
||
scene tree and advances hunger and due routine selection in stable-ID order.
|
||
`AnimalNode` binds a loaded presentation and interaction point to that
|
||
authority; the simulation position and selected destination remain canonical
|
||
while the node is loaded or absent.
|
||
|
||
Animals are not `ResourceNode` instances and are not inserted into the
|
||
resource discovery grid. The first bounded slice uses a deterministic linear
|
||
loaded-animal query. NPC and player feeding resolve through the same exact
|
||
animal mutation but pay from their real current source. Player care withdraws
|
||
directly from `village_pantry`. NPC care reserves the animal, performs an exact
|
||
pantry-to-inventory pickup at the authored storage waypoint, carries that unit
|
||
to the same claim, and consumes inventory only at delivery. Failed or partial
|
||
pickup changes neither inventory nor animal history.
|
||
|
||
The first routine consumers alternate Dunja and Zora between stable-ID private
|
||
shelters and one shared pasture without drawing RNG. `AnimalRoutineSite`
|
||
declares optional loaded-world resident ownership: an empty resident ID is
|
||
shared species context, while a named resident makes a shelter private.
|
||
`ActiveWorldAdapter` filters those loaded markers for each stable animal; site
|
||
ownership is not persistent state and does not require a schema bump. The
|
||
selected site ID and exact position do enter state, and each `AnimalNode`
|
||
follows a real `NavigationServer3D` path while reporting factual position
|
||
changes. A mid-route save restores the same position and target; unloading a
|
||
scene node pauses presentation only, and reloading it resumes from those facts.
|
||
Hunger cancels travel before care targeting.
|
||
|
||
Multiple animal records remain independently authoritative. A schema-v11 save
|
||
may contain concurrent feed reservations only when each reserving NPC targets
|
||
that exact animal in a valid travel or work state. Restoring Dunja and Zora
|
||
preserves both claim pairings and the checksum. The same existing NPC fields
|
||
preserve either pantry or goat travel plus carried food, so a post-pickup
|
||
restore continues without another withdrawal or RNG draw. Completing one feed
|
||
releases and mutates only its destination record.
|
||
|
||
The persistent hungry/content presentation derives from authoritative hunger.
|
||
The short feed response is transient and is reset rather than serialized or
|
||
replayed after restore.
|
||
|
||
## Local quicksave boundary
|
||
|
||
`SaveSlotStore` writes the existing versioned JSON record to
|
||
`user://saves/quicksave.json`. It validates the serialized record before
|
||
replacement, limits file size, restricts slot names, preserves the previous
|
||
file during replacement, and can recover that backup if replacement is
|
||
interrupted. Loading parses and validates the complete record before mutating
|
||
the simulation.
|
||
|
||
In `main.tscn`, F5 saves and F9 loads this slot. Restoration rebuilds active NPC
|
||
visuals from authoritative state.
|
||
|
||
## Deliberately out of scope
|
||
|
||
This phase does not yet provide:
|
||
|
||
- a save-slot menu, metadata, thumbnails, autosaves, or multiple profiles;
|
||
- migrations from any historical world schema other than the explicitly
|
||
supported v1-v14 layouts;
|
||
- line-of-sight/hearing evidence, personalized confidence decay,
|
||
reinforcement, false beliefs, or multi-hop rumours;
|
||
- the full regional situation predicate library, regional quest bounds,
|
||
contracts beyond the first social commitment consumer, or generated
|
||
free-form prose;
|
||
- persistence of a currently open dialogue UI/turn. Semantic conversation
|
||
history and commitment authority persist, while a balloon is regenerated
|
||
after restore;
|
||
- chunked regional saves, lazy location loading, or aggregate population
|
||
persistence;
|
||
- persistence for the player transform or presentation-only scene state.
|
||
|
||
Those features should build on this boundary rather than inventing parallel
|
||
serialization paths.
|