Files
gamedev-the-steward/docs/FEATURE_COMMANDS_TARGETS_AND_ECONOMY.md
2026-08-13 00:26:55 +02:00

150 lines
6.7 KiB
Markdown

# Feature slice: commands, targets, and economy
This slice makes player and NPC interactions converge on one authority path.
The main pieces are `ActionCommand`, `ActionOffer`, `ActionResult`,
`ActionCommandService`, `CatalogInteractionService`,
`ActivityActionCommandService`, `WorldTargetRegistry`, `ActiveWorldAdapter`,
`StorageRoutingPolicy`, and `VillageEconomy`.
## Offer versus command
An offer is a read-only, copied description of what an actor could attempt.
It may contain stable action/target IDs, capability data, copy text, a reason
trace, expected state revision, target generation, context identity, and
registry-instance identity. It must not contain a `Node`, provider reference,
callable, or authoritative result.
The actor submits an `ActionCommand` with stable actor/action/target IDs,
optional item and amount, and an expected state revision. The service rechecks
everything at execution time. A valid offer is not permission to execute after
the world changes.
```text
query -> copied ActionOffer
|
v
player/NPC command
|
v
revalidate revision + registry instance + generation + provider
|
v
revalidate actor life + capability + range + target + reservation + cost
|
v
atomic mutation -> exact world fact -> copied ActionResult
```
## Context-scoped targets
`ActiveWorldAdapter` has explicit `world_id`, `location_id`, and `context_id`
identity. It owns one `WorldTargetRegistry` for that context. Resource,
storage, activity, and animal providers register a `WorldTargetDescriptor`
containing a canonical target ID, target kind, capability descriptors, and a
transient provider binding.
The registry provides:
- O(1) canonical ID lookup;
- cross-kind duplicate rejection within a context;
- deterministic sorted IDs and capability filtering;
- generation-checked transient `WorldTargetHandle` values;
- provider registration/unregistration and stale-provider pruning.
The target ID and contextual scope are the authority. A handle is only a
short-lived geometry/provider reference. Rebuilding a context restarts local
generations, so player results also carry a registry-instance token to prevent
ABA execution against a replacement registry.
`LoadedResourceSpatialIndex` accelerates finite resource queries by action and
horizontal cell. It is disposable presentation state; amounts, reservations,
yield, risk, and discovery priority remain in simulation records.
## The current shared handler
`ActivityActionCommandService` is the first production shared command handler.
Patrol and study are authored `activity_metric_delta` actions. The handler:
1. validates the actor and expected revision;
2. validates the action's typed handler/effect contract;
3. validates activity target kind, live provider, registry scope/instance,
capability, range, capacity, and task authorization;
4. preflights and withdraws any authored completion cost through
`VillageEconomy` and `StorageRoutingPolicy`;
5. applies the bounded safety or knowledge metric delta;
6. appends one scoped `activity_completed` fact;
7. returns exact event IDs, final revision, and reason trace.
If the cost is unavailable, no effect is applied and one structured
`task_blocked` fact records the action, source storage, item, and required
amount. A capped metric does not consume a cost or emit a false delta. The
primary event keeps the command revision; synchronous situation/commitment
consequences may append a causal suffix, and the result reports the final
revision.
NPCs must use their actual current action, target, task state, completion flag,
alive state, and context scope. An idle, dead, downed, wrong-target, or
wrong-world direct command fails without mutation. Players use the dedicated
player boundary, which requires all stale-offer tokens rather than accepting
sentinel defaults. Both paths share definition validation, economy, event, and
state checks.
## Typed effects and transactional planning
`ActionEffectPlanner` turns authored `ActionEffect` resources into an explicit
`ActionEffectPlan` with stable operation IDs, authored order, parameters, and a
reason trace. It supports the bounded typed effects currently registered:
metric/need changes, exact inventory/storage/cargo transfers, damage/healing,
relationship changes, scheduling ordinary work, and site-condition changes.
`TransactionalActionEffectCommitter` is the extension seam for a mechanic that
needs more than the built-in activity handler. A committer must apply one whole
plan atomically, return unique event IDs on success, or return a rollback-safe
failure with no event IDs. Plans reject unknown kinds, duplicate/reordered
operations, non-primitive values, non-finite numbers, same-role transfers, and
empty effect sets.
## Economy and routing
`VillageEconomy` owns exact storage/NPC/player inventory mutations. The
pantry, woodpile, apothecary, resource yields, animal feeding, player carry
loop, and action costs use the same transaction boundary. `village.food` and
`village.wood` are synchronized aggregate views, not second mutation paths.
`StorageRoutingPolicy` selects compatible storage by accepted item ID/tag,
priority, capacity, settlement scope, and stable target ID. The herb →
apothecary route proves that routing is no longer a food/wood switch. The
production Jajce economy still has one village settlement; regional stockpiles
and markets are future work.
## Adding a new interaction
1. Add or reuse an `ActionDefinition` with target/capability/handler/effect
fields.
2. Add target capability data to the provider definition or scene provider.
3. Register the provider through `ActiveWorldAdapter`.
4. Add an offer query and a command service/handler only if the existing typed
handler cannot express the mechanic.
5. Revalidate all authority at command time.
6. Mutate records, append the exact event, and test both player and NPC paths.
7. Add loaded/abstract and stale-offer regressions where applicable.
Do not put persistent amounts on `StorageNode`, use abstract Vector3 fallback
targets, or let the HUD call `SimulationManager` mutation methods directly.
## Reference tests and contracts
- `tests/world_target_registry_test.gd`
- `tests/context_scoped_world_targets_test.gd`
- `tests/unit/test_catalog_interaction_service.gd`
- `tests/unit/test_activity_action_command_service.gd`
- `tests/unit/test_activity_content_catalog_contract.gd`
- `tests/unit/test_action_effect_planning.gd`
- `tests/resource_node_player_parity_test.gd`
- `tests/food_storage_loop_test.gd`
- `tests/animal_food_delivery_test.gd`
For the complete action lifecycle and active-position rules, read
[Action System Architecture](ACTION_SYSTEM_ARCHITECTURE.md). For definition
fields, read [Simulation Definitions](SIMULATION_DEFINITIONS.md).