feat: add physical animal care delivery

This commit is contained in:
Rijad Zuzo
2026-07-31 00:19:41 +02:00
parent 7d410d8193
commit ead75fca68
25 changed files with 1469 additions and 191 deletions
+39 -6
View File
@@ -53,6 +53,8 @@ SimulationManager tick
compatibility path for isolated adapters;
- checks simulation-owned ResourceStateRecord availability;
- reserves and returns stable resource target IDs;
- reserves the exact loaded animal before returning the pantry pickup waypoint,
then resolves only that same claim for the delivery leg;
- skips full-capacity activity sites based on authoritative NPC target claims;
- returns typed activity-site, storage, or wander positions without owning
presentation.
@@ -88,13 +90,16 @@ 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
action effect. `feed_animal` is the one bounded compound exception: pantry
arrival withdraws the exact cost into inventory and requests the already
claimed animal as the second destination; goat arrival then enters normal
working completion. 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
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
@@ -103,6 +108,8 @@ simulation ticks.
### VillageEconomy
- owns storage and NPC-inventory transfer operations;
- supports exact all-or-nothing pickup and carried-inventory removal for the
animal-care cost;
- 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.
@@ -164,6 +171,32 @@ State restore clears active feedback and re-derives the prompt from current
simulation and loaded-world facts. The HUD is separate from development
overlays so the embodied action remains legible in cinematic mode.
## Compound animal-care delivery contract
NPC `feed_animal` remains one task and one exact animal claim across two travel
legs. `ActionTargetResolver` first chooses and reserves the nearest eligible
loaded animal but returns `ActiveWorldAdapter`'s pantry interaction position
when the caretaker lacks the definition cost. Pantry arrival atomically moves
the missing amount into `SimNPC.inventory`; the existing inventory signal makes
the carried-food prop visible. The resolver then looks up only the claimed
animal and requests its current loaded interaction position.
The second leg is derived from carried inventory rather than a separate saved
phase. Pickup occurs only when the completed saved waypoint still matches the
current pantry waypoint. A compatible older route with empty inventory and a
goat destination is first redirected to the pantry instead of withdrawing
remotely. An older already-working feed state with no carried food recovers the
same route on its first resumed tick and preserves its work progress. Before a
successful pickup publishes inventory, event, or village signals, the saved
destination already names the exact goat; synchronous observers therefore
cannot capture carried food with a stale pantry waypoint. At the goat,
completion removes the exact cost from the matching NPC inventory, mutates only
that animal, releases its claim, and records the inventory-to-animal fact. If
pantry contention loses, no partial transfer is allowed, one `task_blocked`
fact names the pantry shortfall, and only that caretaker's goat claim is
released. Player feeding remains the bounded direct pantry-to-animal path
because player inventory is still deferred.
## Active-position contract
NpcVisual emits active position changes after successful movement.
+2 -2
View File
@@ -39,8 +39,8 @@ would otherwise obscure that lifecycle:
and keeps village resource summaries synchronized;
- `simulation/animals/animal_care_system.gd` owns animal records, hunger
advancement, deterministic routine selection, loaded binding, feed
reservations, and the exact conserved pantry-to-animal operation used by
NPCs and the player;
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
+15 -8
View File
@@ -801,9 +801,9 @@ Completed:
40. `Jajce Goat 07`: Dunja is the first identity-backed animal, with a stable
goat ID, simulation-owned position and hunger, and a loaded cozy
presentation with an honest hungry cue. NPC and player feeding share one
exact transaction that withdraws pantry food, changes only Dunja's state,
and emits one economic fact. World-schema v10 save/restore preserves the
result without replaying the transient feed response.
exact target mutation backed by real food that changes only Dunja's state
and emits one exact `animal_fed` fact. World-schema v10 save/restore
preserves the result without replaying the transient feed response.
41. `Jajce Dunja Habitat 08`: a self-contained timber shelter and clover
pasture now expose two stable routine sites in an open south-village
clearing. Dunja alternates between them without RNG, follows the real
@@ -823,14 +823,21 @@ Completed:
availability, reports success or blockage, remains visible in cinematic
mode, and also covers existing resource, pantry, guard, and study actions.
Restore discards transient feedback and re-derives context from live facts.
44. `Jajce Care Delivery 11`: NPC animal care now keeps one exact named-goat
claim while walking first to the real pantry and then to that goat. Pantry
pickup creates authoritative carried inventory and reveals the existing
food sack; delivery consumes that unit and records the inventory-to-animal
fact. One-unit contention, both route legs, and post-pickup continuation
prove conservation without adding a saved phase field.
Next:
1. Make NPC feeding visibly collect one real pantry food into the caretaker's
existing inventory/carried-food presentation before delivery to the exact
reserved goat.
2. Prove conservation, claim continuity, and save/restore during both travel
legs; keep herd, product, indexing, and LOD systems deferred.
1. Give the player one restrained nearby-villager inspection context backed by
the authoritative task, exact target, carried item, and recorded decision
reason when it currently exists.
2. Prove that the field note follows live task changes and, after restore,
re-derives the persistent facts while honestly awaiting the next decision
reason. It must not become dialogue, quest, or saved presentation state.
Do not start with GIS data, a full city, a large asset pack, or more NPC
mechanics. The next proof is a beautiful stage for the systems that already
+16 -11
View File
@@ -18,10 +18,12 @@ 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. `animal_fed` records one food moving
from `village_pantry` to a stable animal ID at the animal's authoritative
position. Actor `-1` identifies a player-triggered extraction or feeding until
persistent player identity is introduced.
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.
Events are immutable facts about completed transfers. They do not perform the
transaction and are not replayed to reconstruct current state. Resource,
@@ -31,9 +33,9 @@ inventory, and storage records remain authoritative.
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
pantry withdrawal with animal hunger relief and requests `animal_fed` only
after both succeed. `SimulationManager` remains the scene-tree signal boundary
used by presentation.
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,
@@ -56,10 +58,13 @@ 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, source is the real pantry, 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.
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
+24 -7
View File
@@ -16,12 +16,22 @@ ResourceStateRecord (berry bush)
Food is conserved across source, carried inventory, and pantry. It leaves the
world only when consumed.
Animal care extends the same ownership chain without a parallel counter:
```text
village_pantry
-> feed_animal pickup moves one food into exact caretaker inventory
-> existing carried-food prop derives from that inventory
-> delivery consumes caretaker inventory into exact named animal
```
## Authority
- `StorageStateRecord` owns pantry and woodpile contents and capacity.
- `SimNPC.inventory` owns carried item amounts.
- `VillageEconomy` performs deposit, withdrawal, consumption, and
definition-backed completion-cost transactions.
definition-backed completion-cost transactions, including exact
all-or-nothing animal-care pickup and inventory removal.
- `SimulationManager` coordinates action lifecycle and exposes the transaction
results to presentation.
- `village.food` and `village.wood` are synchronized aggregate views used by
@@ -40,6 +50,8 @@ Successful extraction, deposit, withdrawal, and consumption also append
structured economic facts. See [the economic event stream](ECONOMIC_EVENTS.md).
Active NPCs display a small food sack while their authoritative inventory
contains food; this is presentation derived from state, not a second inventory.
NPC animal care therefore uses the same visible prop between pantry pickup and
the claimed goat.
## Failure behavior
@@ -50,23 +62,28 @@ Transactions apply the amount actually available:
- player extraction is limited to storage capacity until player inventory
exists, so overflow remains at the source;
- withdrawal cannot exceed pantry contents;
- animal-care pickup succeeds only when the complete missing cost is available;
- eating succeeds only when the NPC carries one food.
If another NPC empties the pantry first, withdrawal returns zero and the NPC
replans on its next idle tick.
If another caretaker empties the pantry first, animal-care pickup records the
definition-backed shortfall, transfers nothing, releases only the losing
caretaker's exact animal claim, and replans. A caretaker that already carries
the unit never consults pantry stock again before delivery.
## Deliberate limits
This slice contains one authored pantry and one carried item type. It does not
yet add:
The current bounded economy has one authored pantry and one woodpile with
simple food/wood stacks. It does not yet add:
- player inventory;
- storage reservations or access capacity;
- multiple households or ownership;
- spoilage, item quality, weight, or stack definitions.
Economic transfer events exist, but event retention, denial/depletion facts,
witnesses, and social interpretation are deliberately deferred.
Objective event history, depletion and blocked-task facts, bounded per-NPC
knowledge, proximity witnesses, and the first social consequences now exist.
Long-run event archival, generalized denial facts, wider witness models, and
broader social interpretation remain deferred.
Those should extend the transaction boundary rather than mutate counters
directly.
+25 -9
View File
@@ -926,9 +926,10 @@ code proven by those two consumers.
The first identity-backed livestock slice is complete. Dunja has a stable
animal ID, simulation-owned position and hunger/feed state, and a loaded cozy
presentation whose persistent cue derives from that state. One exact operation
serves both NPC and player feeding, withdraws real pantry food, mutates only the
target animal, and appends one economic fact. World-schema v10 save/restore
preserves the result without replaying the transient response.
serves both NPC and player feeding, consumes real food from the actor's current
source, mutates only the target animal, and appends one economic fact.
World-schema v10 save/restore preserves the result without replaying the
transient response.
The first loaded-animal routine slice is complete. Dunja alternates between
stable shelter and pasture IDs without RNG; her exact target and current
@@ -955,15 +956,30 @@ shows the action and real availability, remains visible in cinematic mode, and
briefly reports success or blockage. Restore clears transient feedback and
re-derives the current prompt without adding saved state or drawing RNG.
The next bounded slice should make NPC animal care physically honest:
`village_pantry -> NPC inventory -> exact named goat`. A caretaker should keep
the same animal claim, collect one real food into the existing carried-food
presentation, and deliver it without duplication across contention or
mid-route restore. Keep breeding, products, herd scheduling, animal indexing,
and active/abstract animal LOD deferred.
NPC animal care is now physically honest:
`village_pantry -> NPC inventory -> exact named goat`. A caretaker reserves the
goat before visiting the pantry, keeps that stable claim through both travel
legs, visibly carries the withdrawn unit, and consumes only carried inventory
at delivery. One-unit contention records the late shortfall and releases only
the losing claim. A post-pickup restore preserves the inventory, exact goat,
destination, event count, and final checksum without a new saved phase field
or RNG draw.
The next bounded slice should advance Milestone 9 with player-facing villager
inspection. One nearby-person context should show the authoritative name,
current action, exact target, carried item, and current recorded decision reason
when available. After restore it should honestly say that it is awaiting the
next decision while the other facts re-derive. Do not add dialogue, trade,
reputation, quest state, breeding, products, herd scheduling, animal indexing,
or active/abstract animal LOD in that slice.
Recently completed:
- `Jajce Care Delivery 11`: NPC `feed_animal` is now one compound, two-leg
action from the real pantry through caretaker inventory to the exact reserved
goat. Carried presentation, ordered transfer facts, late contention,
conservation, and post-pickup deterministic continuation are proven without
a schema bump or generic job framework.
- `Jajce Context Action 10`: one ephemeral result now keeps the displayed `E`
prompt and executed stable target aligned across existing player
interactions. Named goat care shows exact pantry availability and transient
+19 -7
View File
@@ -642,6 +642,10 @@ These are expected prototype constraints, not necessarily isolated bugs:
- Food and wood now move through finite sources, NPC inventory, and typed
village storage. Other village metrics remain aggregate values rather than
located items.
- NPC animal care keeps one exact named-goat claim through a real pantry pickup
and carried-inventory delivery. The existing persisted task, target,
destination, inventory, and animal reservation encode both legs without a
separate saved phase.
- NPCs have home positions, schedule periods, carried food/wood, and directed
familiarity/trust. They retain direct or one-hop communicated knowledge of a
food deposit or NPC pantry withdrawal, plus direct missing-wood blocked-work
@@ -895,10 +899,12 @@ The location-based food loop is complete and traceable: gathering creates
carried food, depositing fills the pantry, withdrawal retrieves one unit, and
eating consumes it. Each successful transfer creates a persisted structured
event, and active NPCs visibly carry food through presentation derived from
their inventory. The bounded save-slot slice is also complete, including
validation, atomic replacement recovery, and active-visual rebuilding.
Terrain3D runtime integration, the bounded Jajce beauty pass, and the first
simulation-garden runtime capture are complete.
their inventory. Animal care now uses that same ownership path: the caretaker
withdraws at the pantry, carries the unit to the exact claimed goat, and pays
from inventory at delivery. The bounded save-slot slice is also complete,
including validation, atomic replacement recovery, and active-visual
rebuilding. Terrain3D runtime integration, the bounded Jajce beauty pass, and
the first simulation-garden runtime capture are complete.
The first relationship and knowledge consequences are also complete:
food-deposit events become separate known facts for their actor and nearby
@@ -1001,7 +1007,7 @@ decorative density from simulation authority, support discovery beyond the
first 24 m range, and reconstruct sparse/depleted visuals from authoritative
amount. Dunja now proves the first identity-backed animal contract: stable
animal identity, simulation-owned position and hunger, a loaded visual binding,
and the same exact pantry-conserving feed operation for NPC and player.
and the same exact food-conserving target mutation for NPC and player.
World-schema v10 restore reconstructs her persistent state without replaying
the transient response. Dunja now also alternates between a stable shelter and
pasture through real loaded navigation. World-schema v11 preserves exact
@@ -1014,8 +1020,14 @@ preserves both claim pairings without serializing shelter ownership. Do not
generalize animal discovery or introduce herd scheduling, products, breeding,
or active/abstract animal LOD. The named-pair gate instead produced one
Milestone 9 context-action result shared by prompt and execution, with exact
pantry feedback and no persistent UI state. The next animal-facing proof should
make NPC care visibly carry one real pantry food to its exact reserved goat.
pantry feedback and no persistent UI state. NPC care now completes that
physical proof with one compound pantry-to-inventory-to-goat action, exact
claim continuity, visible carrying, late-contention safety, and post-pickup
deterministic continuation. The next bounded proof should let the player
inspect one nearby villager's real action, target, carried item, and recorded
decision reason when available. After restore it should re-derive persistent
facts and honestly await the next decision reason without introducing dialogue
or saved UI state.
The remaining simulation-garden target still aims for:
+4 -2
View File
@@ -35,8 +35,10 @@ both require one stored wood without duplicating that rule.
`feed_animal` is the first animal-targeted action. Its definition prefers the
farmer profession and declares an exact one-food completion cost. Selection,
animal target reservation, and completion remain separate; NPC and player
completion both use `AnimalCareSystem`'s same pantry-to-animal transaction.
animal target reservation, supply pickup, and completion remain separate. NPC
care pays that definition cost from carried inventory after a real pantry
pickup; player care pays it directly from the pantry while player inventory is
deferred. Both paths use `AnimalCareSystem` and mutate only the exact target.
`SimulationIds` names Dunja and Zora, their private shelter anchors, and the
shared pasture anchor. These IDs are saved routine vocabulary; their loaded
positions and private/shared ownership come from `AnimalRoutineSite` rather
+42 -18
View File
@@ -76,19 +76,37 @@ 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. Parsed
storage values are canonicalized so save/restore continuation retains
byte-stable checksums. Current 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;
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
@@ -199,10 +217,12 @@ selection establishes the first real target later.
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 the pantry as its source, an existing animal as its destination,
food as its item, the exact feed action cost, and a valid NPC actor or the
player sentinel. The latest matching feed event tick must agree with the
animal's `last_fed_tick`.
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
@@ -279,10 +299,12 @@ 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 atomic
care operation: reserve or select the exact animal, withdraw one real food
from `village_pantry`, reduce that animal's hunger, and append one exact
`animal_fed` fact. Failed withdrawal changes neither animal nor history.
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`
@@ -299,8 +321,10 @@ 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; completing one feed releases
and mutates only its destination record.
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