feat: replace activity markers with typed sites
This commit is contained in:
@@ -39,12 +39,13 @@ SimulationManager tick
|
|||||||
- consumes active-world facts through ActiveWorldAdapter;
|
- consumes active-world facts through ActiveWorldAdapter;
|
||||||
- checks simulation-owned ResourceStateRecord availability;
|
- checks simulation-owned ResourceStateRecord availability;
|
||||||
- reserves and returns stable resource target IDs;
|
- reserves and returns stable resource target IDs;
|
||||||
- returns activity or wander positions without owning presentation.
|
- returns typed activity-site, storage, or wander positions without owning
|
||||||
|
presentation.
|
||||||
|
|
||||||
### ActiveWorldAdapter
|
### ActiveWorldAdapter
|
||||||
|
|
||||||
- exposes loaded resource interaction positions;
|
- exposes loaded resource interaction positions;
|
||||||
- maps current temporary activity markers to action IDs;
|
- exposes loaded storage and activity-site interaction positions;
|
||||||
- contains active-world query facts, not persistent mutable authority;
|
- contains active-world query facts, not persistent mutable authority;
|
||||||
- does not choose actions or reserve resources.
|
- does not choose actions or reserve resources.
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ Do not redo these tasks:
|
|||||||
- hunger, energy, starvation, death, task scoring, and task duration;
|
- hunger, energy, starvation, death, task scoring, and task duration;
|
||||||
- task-change and village-change signals;
|
- task-change and village-change signals;
|
||||||
- visual NPC navigation and arrival reporting;
|
- visual NPC navigation and arrival reporting;
|
||||||
- finite berry/tree ResourceNodes plus food, guard, study, and rest activity
|
- finite berry/tree ResourceNodes plus typed food, guard, study, and rest
|
||||||
markers;
|
world sites;
|
||||||
- aggregate village UI;
|
- aggregate village UI;
|
||||||
- initial tree assets;
|
- initial tree assets;
|
||||||
- a baked navigation region for the current flat test area.
|
- a baked navigation region for the current flat test area.
|
||||||
@@ -56,21 +56,20 @@ plugin content. They are references, not the game.
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
Main
|
Main
|
||||||
├── current flat World and NavigationRegion3D
|
├── JajceWorld
|
||||||
├── Player
|
├── Player
|
||||||
├── CameraRig
|
├── CameraRig
|
||||||
├── SimulationManager
|
├── SimulationManager
|
||||||
├── WorldViewManager
|
├── WorldViewManager
|
||||||
├── ActiveNPCs
|
├── ActiveNPCs
|
||||||
├── ActivityMarkers
|
├── typed world sites inside JajceWorld/WorldObjects
|
||||||
├── WorldEnvironment
|
├── WorldEnvironment
|
||||||
└── UI
|
└── UI
|
||||||
```
|
```
|
||||||
|
|
||||||
The player and `WorldViewManager` still reference non-resource activity sites
|
The player still references interactable world sites through exported `NodePath`
|
||||||
through exported `NodePath` values. `NpcVisual` relies on the navigation map to
|
values. `NpcVisual` relies on the navigation map to reach typed `ActivitySite`,
|
||||||
reach legacy guard/study/rest markers, `ResourceNode` interaction points, and
|
`StorageNode`, and `ResourceNode` interaction points.
|
||||||
the typed village pantry `StorageNode`.
|
|
||||||
|
|
||||||
Replacing the ground without respecting those references would break behavior
|
Replacing the ground without respecting those references would break behavior
|
||||||
that already works.
|
that already works.
|
||||||
@@ -95,12 +94,12 @@ Main
|
|||||||
│ │ ├── ResourceNodes
|
│ │ ├── ResourceNodes
|
||||||
│ │ ├── BerryBush instances
|
│ │ ├── BerryBush instances
|
||||||
│ │ └── Tree instances
|
│ │ └── Tree instances
|
||||||
│ │ └── StorageSites
|
│ │ ├── StorageSites
|
||||||
│ │ └── VillagePantry
|
│ │ │ └── VillagePantry
|
||||||
│ ├── LegacyActivityMarkers temporary non-resource activities
|
│ │ └── ActivitySites
|
||||||
│ │ ├── GuardZone
|
│ │ ├── GuardPost
|
||||||
│ │ ├── StudyZone
|
│ │ ├── StudyDesk
|
||||||
│ │ └── RestZone
|
│ │ └── RestBench
|
||||||
│ ├── DirectionalLight3D
|
│ ├── DirectionalLight3D
|
||||||
│ └── WorldEnvironment
|
│ └── WorldEnvironment
|
||||||
├── Player
|
├── Player
|
||||||
@@ -111,11 +110,10 @@ Main
|
|||||||
└── UI
|
└── UI
|
||||||
```
|
```
|
||||||
|
|
||||||
`JajceWorld` should contain geography, world-object presentations, and temporary
|
`JajceWorld` should contain geography and world-object presentations. The former
|
||||||
legacy interaction locations. The pantry has since moved out of
|
legacy interaction locations now live as typed storage and activity sites under
|
||||||
`LegacyActivityMarkers` into `WorldObjects/StorageSites/VillagePantry`; guard,
|
`WorldObjects`; `JajceWorld` should not own `SimulationManager`, simulated NPC
|
||||||
study, and rest remain temporary markers. `JajceWorld` should not own
|
data, player state, or UI.
|
||||||
`SimulationManager`, simulated NPC data, player state, or UI.
|
|
||||||
|
|
||||||
Create a small look-development wrapper that instances the same world:
|
Create a small look-development wrapper that instances the same world:
|
||||||
|
|
||||||
@@ -295,7 +293,7 @@ Create `world/jajce/JajceWorld.tscn` with:
|
|||||||
- placeholder `VillageRoot`;
|
- placeholder `VillageRoot`;
|
||||||
- placeholder `FoliageRoot`;
|
- placeholder `FoliageRoot`;
|
||||||
- a navigation region;
|
- a navigation region;
|
||||||
- resource-node containers plus temporary activity markers;
|
- resource-node, storage-site, and activity-site containers;
|
||||||
- lighting and environment.
|
- lighting and environment.
|
||||||
|
|
||||||
Create `JajceLookdev.tscn` that instances `JajceWorld` and adds a temporary
|
Create `JajceLookdev.tscn` that instances `JajceWorld` and adds a temporary
|
||||||
@@ -422,8 +420,8 @@ Once the world composition is stable:
|
|||||||
2. Disable or remove the old flat world only after the new instance is present.
|
2. Disable or remove the old flat world only after the new instance is present.
|
||||||
3. Place or move bushes and trees under
|
3. Place or move bushes and trees under
|
||||||
`JajceWorld/WorldObjects/ResourceNodes` while preserving stable IDs.
|
`JajceWorld/WorldObjects/ResourceNodes` while preserving stable IDs.
|
||||||
4. Reconnect only remaining non-resource activity markers under
|
4. Reconnect remaining non-resource activities as typed sites under
|
||||||
`JajceWorld/LegacyActivityMarkers`.
|
`JajceWorld/WorldObjects`.
|
||||||
5. Bake navigation for the new walkable area.
|
5. Bake navigation for the new walkable area.
|
||||||
6. Test each existing task from multiple spawn positions.
|
6. Test each existing task from multiple spawn positions.
|
||||||
7. Verify task arrival still transitions from traveling to working.
|
7. Verify task arrival still transitions from traveling to working.
|
||||||
@@ -436,8 +434,8 @@ Once the world composition is stable:
|
|||||||
Do not move simulation ownership into `JajceWorld` to make wiring convenient.
|
Do not move simulation ownership into `JajceWorld` to make wiring convenient.
|
||||||
|
|
||||||
**Status: runtime integration complete.** `main.tscn` now instances the reusable
|
**Status: runtime integration complete.** `main.tscn` now instances the reusable
|
||||||
world and points player/simulation adapters at its stable-ID resources and
|
world and points player/simulation adapters at its stable-ID resources, storage
|
||||||
remaining activity markers. The duplicate flat world objects were removed.
|
site, and activity sites. The duplicate flat world objects were removed.
|
||||||
The runtime regression checks Terrain3D presence, 24 navigation routes, core
|
The runtime regression checks Terrain3D presence, 24 navigation routes, core
|
||||||
task execution, starvation/death, player extraction parity, reservations, and
|
task execution, starvation/death, player extraction parity, reservations, and
|
||||||
NPC visual lifecycle. Terrain sculpting and presentation polish remain
|
NPC visual lifecycle. Terrain sculpting and presentation polish remain
|
||||||
@@ -445,7 +443,7 @@ separate work, not blockers for this wiring milestone.
|
|||||||
|
|
||||||
### Exit condition
|
### Exit condition
|
||||||
|
|
||||||
The current three NPCs and player can perform all existing prototype behaviors
|
The current six NPCs and player can perform all existing prototype behaviors
|
||||||
inside the new terrain without regression.
|
inside the new terrain without regression.
|
||||||
|
|
||||||
### Phase 5 — Make the simulation readable on video
|
### Phase 5 — Make the simulation readable on video
|
||||||
|
|||||||
@@ -652,9 +652,11 @@ Completed after the architecture gate:
|
|||||||
|
|
||||||
The practical next sequence is:
|
The practical next sequence is:
|
||||||
|
|
||||||
1. Replace the remaining generic activity markers with typed world sites,
|
1. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
|
||||||
beginning with the pantry/storage presentation.
|
2. Expand resource discovery with many finite `ResourceNode` instances placed
|
||||||
2. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
|
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
|
||||||
|
Score them by reachability, distance, safety, profession, and NPC comfort
|
||||||
|
range rather than reintroducing abstract resource zones.
|
||||||
3. Expand persistence only when schedules, player state, or a real save menu
|
3. Expand persistence only when schedules, player state, or a real save menu
|
||||||
creates a concrete requirement.
|
creates a concrete requirement.
|
||||||
|
|
||||||
|
|||||||
+30
-34
@@ -180,9 +180,9 @@ The current prototype implements a very early version of observe, autonomous
|
|||||||
task choice, physical travel, work completion, resource change, and direct
|
task choice, physical travel, work completion, resource change, and direct
|
||||||
player assistance. NPC `gather_food` and `gather_wood` tasks now use finite
|
player assistance. NPC `gather_food` and `gather_wood` tasks now use finite
|
||||||
world `ResourceNode` instances (berry bushes and trees) rather than abstract
|
world `ResourceNode` instances (berry bushes and trees) rather than abstract
|
||||||
zone markers. The obsolete farm and forest markers have been removed. Separate
|
zone markers. The obsolete farm, forest, and activity markers have been
|
||||||
temporary activity markers remain for rest, study, and patrol; eating and food
|
removed. Eating and food transfer target the typed village pantry, while rest,
|
||||||
transfer now target the typed village pantry. The migration is documented in
|
study, and patrol target typed activity sites. The migration is documented in
|
||||||
[the ResourceNode plan](RESOURCE_NODE_MIGRATION.md).
|
[the ResourceNode plan](RESOURCE_NODE_MIGRATION.md).
|
||||||
|
|
||||||
## Current technology
|
## Current technology
|
||||||
@@ -207,7 +207,7 @@ plugin content, not game architecture.
|
|||||||
|
|
||||||
### World and player
|
### World and player
|
||||||
|
|
||||||
- `main.tscn` contains a flat approximately 30×30 prototype ground.
|
- `main.tscn` instances the reusable Jajce Terrain3D world.
|
||||||
- The player is a `CharacterBody3D` represented by placeholder primitive
|
- The player is a `CharacterBody3D` represented by placeholder primitive
|
||||||
geometry.
|
geometry.
|
||||||
- WASD movement is camera-relative.
|
- WASD movement is camera-relative.
|
||||||
@@ -215,17 +215,19 @@ plugin content, not game architecture.
|
|||||||
follow/focus behavior.
|
follow/focus behavior.
|
||||||
- Pressing `E` near a berry bush or tree extracts its configured yield into the
|
- Pressing `E` near a berry bush or tree extracts its configured yield into the
|
||||||
village through the same `ResourceNode` contract used by NPCs.
|
village through the same `ResourceNode` contract used by NPCs.
|
||||||
- Guard, study, rest, and food interactions still use temporary activity
|
- Guard, study, rest, and food interactions now use typed world sites.
|
||||||
markers.
|
|
||||||
- `Escape` releases captured mouse input.
|
- `Escape` releases captured mouse input.
|
||||||
|
|
||||||
### Village simulation
|
### Village simulation
|
||||||
|
|
||||||
The simulation currently creates three NPCs:
|
The simulation currently creates six NPCs:
|
||||||
|
|
||||||
- Amina
|
- Amina
|
||||||
- Tarik
|
- Tarik
|
||||||
- Jasmin
|
- Jasmin
|
||||||
|
- Elma
|
||||||
|
- Mirza
|
||||||
|
- Lejla
|
||||||
|
|
||||||
Each receives a random profession from:
|
Each receives a random profession from:
|
||||||
|
|
||||||
@@ -283,39 +285,34 @@ Needs override ordinary work. Otherwise a utility-like score combines:
|
|||||||
- profession preference;
|
- profession preference;
|
||||||
- a small random contribution.
|
- a small random contribution.
|
||||||
|
|
||||||
The visual NPC walks toward the task's marker. Arrival tells the simulation to
|
The visual NPC walks toward the task's typed target position. Arrival tells the
|
||||||
start work. Work progresses on simulation ticks. The village receives the
|
simulation to start work. Work progresses on simulation ticks. The village
|
||||||
result only when the task duration completes.
|
receives the result only when the task duration completes.
|
||||||
|
|
||||||
Starvation can reduce productivity and eventually kill an NPC. Dead visuals
|
Starvation can reduce productivity and eventually kill an NPC. Dead visuals
|
||||||
stop moving, change appearance, and no longer collide.
|
stop moving, change appearance, and no longer collide.
|
||||||
|
|
||||||
### Current activity markers
|
### Current world targets
|
||||||
|
|
||||||
The main scene contains placeholder activity markers for guard duty, study,
|
The former farm, forest, food, guard, study, and rest markers have been deleted.
|
||||||
and rest. The former farm and forest zones and their decorative geometry have
|
The runtime now uses semantic world targets:
|
||||||
been deleted, and food storage now uses a typed pantry site.
|
|
||||||
|
|
||||||
**Migration status:** NPC `gather_food` and `gather_wood` target `ResourceNode`
|
**Migration status:** NPC `gather_food` and `gather_wood` target `ResourceNode`
|
||||||
instances (berry bushes and trees), with no marker fallback. The remaining
|
instances (berry bushes and trees), with no marker fallback. Non-resource
|
||||||
markers are direct transitional targets for non-resource NPC tasks, while
|
actions route through typed sites:
|
||||||
storage actions now route to a typed pantry site:
|
|
||||||
|
|
||||||
- patrol → guard marker;
|
- patrol → `ActivitySite` (`guard_post`);
|
||||||
- study → study marker;
|
- study → `ActivitySite` (`study_desk`);
|
||||||
- rest → rest marker;
|
- rest → `ActivitySite` (`rest_bench`);
|
||||||
- eat/deposit/withdraw → `StorageNode` (`village_pantry`).
|
- eat/deposit/withdraw → `StorageNode` (`village_pantry`).
|
||||||
|
|
||||||
The player also harvests food and wood directly from nearby `ResourceNode`
|
The player also harvests food and wood directly from nearby `ResourceNode`
|
||||||
instances. Extraction returns the actual amount removed, updates the village by
|
instances. Extraction returns the actual amount removed, updates the village by
|
||||||
that amount, and releases an NPC reservation if the source is depleted.
|
that amount, and releases an NPC reservation if the source is depleted.
|
||||||
The food bullet above is the historical transition point: active eating,
|
Future resource expansion should add many finite `ResourceNode` instances rather
|
||||||
deposit, and withdrawal now resolve to `WorldObjects/StorageSites/VillagePantry`
|
than broad resource zones: trees in foliage clusters, berry patches, animal
|
||||||
instead of a pantry marker.
|
camps, and village stockpiles can be ranked by reachability, distance, safety,
|
||||||
|
profession, and NPC comfort range.
|
||||||
Rest, study, and guard behavior should later migrate to target types that match
|
|
||||||
their actual semantics rather than treating every usable object as a generic
|
|
||||||
marker.
|
|
||||||
|
|
||||||
### Jajce scaffold
|
### Jajce scaffold
|
||||||
|
|
||||||
@@ -327,8 +324,7 @@ marker.
|
|||||||
- fortress, houses, mill, bridge, shader river/waterfall, mist, and smoke;
|
- fortress, houses, mill, bridge, shader river/waterfall, mist, and smoke;
|
||||||
- warm sky, fog, shadows, and wind-reactive foliage;
|
- warm sky, fog, shadows, and wind-reactive foliage;
|
||||||
- four ResourceNodes preserving the flat-map stable IDs;
|
- four ResourceNodes preserving the flat-map stable IDs;
|
||||||
- typed village pantry storage plus remaining guard, study, and rest activity
|
- typed village pantry storage and typed guard, study, and rest activity sites;
|
||||||
markers;
|
|
||||||
- a temporary baked navigation loop with 24 tested routes;
|
- a temporary baked navigation loop with 24 tested routes;
|
||||||
- a look-development scene and camera.
|
- a look-development scene and camera.
|
||||||
|
|
||||||
@@ -506,11 +502,11 @@ NpcVisual navigates through the active world
|
|||||||
|
|
||||||
These are expected prototype constraints, not necessarily isolated bugs:
|
These are expected prototype constraints, not necessarily isolated bugs:
|
||||||
|
|
||||||
- Action and profession IDs are stable and definition-backed, but activity
|
- Action and profession IDs are stable and definition-backed, but several
|
||||||
marker mapping and several action effects remain hard-coded.
|
action effects remain hard-coded.
|
||||||
- Temporary activity markers remain for rest, study, and patrol; NPC and player
|
- Temporary activity markers have been removed; NPC and player food/wood
|
||||||
food/wood gathering use `ResourceNode` instances with no fallback, while food
|
gathering use `ResourceNode` instances with no fallback, food transfer uses
|
||||||
transfer uses the typed pantry `StorageNode`.
|
the typed pantry `StorageNode`, and patrol/study/rest use `ActivitySite`.
|
||||||
- Current NPC, village, resource, storage, event, clock, and RNG state serialize
|
- Current NPC, village, resource, storage, event, clock, and RNG state serialize
|
||||||
through world schema v3. F5/F9 provide one validated local quicksave; a save
|
through world schema v3. F5/F9 provide one validated local quicksave; a save
|
||||||
menu, metadata, and player-transform persistence remain deferred.
|
menu, metadata, and player-transform persistence remain deferred.
|
||||||
|
|||||||
+2
-2
@@ -13,8 +13,8 @@ sources of truth.
|
|||||||
visual slice. It must respect the architecture gates in the learning
|
visual slice. It must respect the architecture gates in the learning
|
||||||
roadmap.
|
roadmap.
|
||||||
4. [`RESOURCE_NODE_MIGRATION.md`](RESOURCE_NODE_MIGRATION.md) is a focused
|
4. [`RESOURCE_NODE_MIGRATION.md`](RESOURCE_NODE_MIGRATION.md) is a focused
|
||||||
migration plan. Phases 1–5 are complete; Phase 6 tracks replacement of the
|
migration plan. Phases 1–6 are complete; follow-up work should expand
|
||||||
remaining temporary activity markers.
|
resource discovery without reintroducing abstract resource zones.
|
||||||
5. [`ACTION_SYSTEM_ARCHITECTURE.md`](ACTION_SYSTEM_ARCHITECTURE.md),
|
5. [`ACTION_SYSTEM_ARCHITECTURE.md`](ACTION_SYSTEM_ARCHITECTURE.md),
|
||||||
[`ECONOMIC_EVENTS.md`](ECONOMIC_EVENTS.md),
|
[`ECONOMIC_EVENTS.md`](ECONOMIC_EVENTS.md),
|
||||||
[`FOOD_STORAGE_ARCHITECTURE.md`](FOOD_STORAGE_ARCHITECTURE.md),
|
[`FOOD_STORAGE_ARCHITECTURE.md`](FOOD_STORAGE_ARCHITECTURE.md),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
| **4a** | Wood target selection (NPC) | ✅ Complete |
|
| **4a** | Wood target selection (NPC) | ✅ Complete |
|
||||||
| **4b** | Player parity (extraction contract) | ✅ Complete |
|
| **4b** | Player parity (extraction contract) | ✅ Complete |
|
||||||
| **5** | Jajce world placement and runtime integration | ✅ Complete |
|
| **5** | Jajce world placement and runtime integration | ✅ Complete |
|
||||||
| **6** | Replace remaining activity markers | 🔶 Resource zones removed; activity targets pending |
|
| **6** | Replace remaining activity markers | ✅ Storage and activity sites authored |
|
||||||
|
|
||||||
### Key divergences from the original plan
|
### Key divergences from the original plan
|
||||||
|
|
||||||
@@ -61,9 +61,9 @@ environment is integrated. It is easier to debug target selection, reservation,
|
|||||||
depletion, and task completion without terrain and navigation changes happening
|
depletion, and task completion without terrain and navigation changes happening
|
||||||
at the same time.
|
at the same time.
|
||||||
|
|
||||||
The old resource zones have been removed. Temporary activity markers remain
|
The old resource zones and temporary activity markers have been removed. Food
|
||||||
only for actions whose real target type does not exist yet. They are not
|
transfer targets `StorageNode`; rest, study, and patrol target `ActivitySite`.
|
||||||
fallbacks and are not part of the target architecture.
|
These are not resource fallbacks and are not resource-search areas.
|
||||||
|
|
||||||
## Why this is the next systems step
|
## Why this is the next systems step
|
||||||
|
|
||||||
@@ -522,10 +522,10 @@ Remove each old zone only when its replacement has:
|
|||||||
- debug visibility;
|
- debug visibility;
|
||||||
- save representation.
|
- save representation.
|
||||||
|
|
||||||
**Progress:** the `TaskZone` container and obsolete `FarmZone`/`ForestZone`
|
**Progress:** the `TaskZone` container, obsolete `FarmZone`/`ForestZone` nodes,
|
||||||
nodes have been deleted from `main.tscn`. The surviving nodes live under
|
and final `LegacyActivityMarkers` container have been deleted from `main.tscn`
|
||||||
`LegacyActivityMarkers` to make their transitional role explicit. Task-to-marker
|
and `JajceWorld`. Task-to-marker mapping has been replaced by typed site
|
||||||
mapping still exists for rest, study, and patrol, so this phase is not complete.
|
queries.
|
||||||
|
|
||||||
The former `FoodZone`/`PantryMarker` is now an authored `StorageNode` at
|
The former `FoodZone`/`PantryMarker` is now an authored `StorageNode` at
|
||||||
`JajceWorld/WorldObjects/StorageSites/VillagePantry`, backed by
|
`JajceWorld/WorldObjects/StorageSites/VillagePantry`, backed by
|
||||||
@@ -533,9 +533,21 @@ simulation-owned `StorageStateRecord` state and explicit deposit/withdraw/eat
|
|||||||
transactions. Successful food transfers emit serializable economic events, and
|
transactions. Successful food transfers emit serializable economic events, and
|
||||||
an active NPC visibly carries food between source, pantry, and consumption.
|
an active NPC visibly carries food between source, pantry, and consumption.
|
||||||
|
|
||||||
|
Guard, study, and rest are now authored `ActivitySite` instances at
|
||||||
|
`JajceWorld/WorldObjects/ActivitySites`. The adapter chooses the nearest loaded
|
||||||
|
site that supports the requested action. Capacity is currently presentation
|
||||||
|
metadata; reservation/enforced occupancy can be added when multiple NPCs
|
||||||
|
compete for the same social/workstation site.
|
||||||
|
|
||||||
**Exit:** temporary activity markers and task-to-marker mapping are deleted,
|
**Exit:** temporary activity markers and task-to-marker mapping are deleted,
|
||||||
not merely unused.
|
not merely unused.
|
||||||
|
|
||||||
|
**Status: complete for the marker-removal migration.** Future work should
|
||||||
|
extend target scoring rather than reintroducing zones: resources can be spread
|
||||||
|
as many finite `ResourceNode` instances across foliage, animal camps, berry
|
||||||
|
patches, and village stockpiles, then ranked by distance, reachability, safety,
|
||||||
|
profession, and NPC comfort range.
|
||||||
|
|
||||||
## Test scenarios
|
## Test scenarios
|
||||||
|
|
||||||
At minimum, exercise:
|
At minimum, exercise:
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ same contract now runs against the integrated `JajceWorld` runtime.
|
|||||||
|
|
||||||
The automated baseline verifies:
|
The automated baseline verifies:
|
||||||
|
|
||||||
- the scene creates three simulated NPCs;
|
- the scene creates six simulated NPCs;
|
||||||
- four stable-ID ResourceNodes register;
|
- four stable-ID ResourceNodes register;
|
||||||
- three fixed origins can reach the four remaining activity markers and four
|
- three fixed origins can reach the typed pantry, three typed activity sites,
|
||||||
ResourceNode interaction points (24 route checks);
|
and four ResourceNode interaction points (24 route checks);
|
||||||
- a two-tick working task completes;
|
- a two-tick working task completes;
|
||||||
- starvation still reaches death at its configured threshold.
|
- starvation still reaches death at its configured threshold.
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ The companion player-parity scenario verifies:
|
|||||||
- player food extraction conserves the actual remaining amount;
|
- player food extraction conserves the actual remaining amount;
|
||||||
- player wood extraction uses the configured yield;
|
- player wood extraction uses the configured yield;
|
||||||
- player depletion releases an NPC reservation.
|
- player depletion releases an NPC reservation.
|
||||||
|
- player pantry and guard interactions use typed world sites.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ height = 1.7
|
|||||||
|
|
||||||
[node name="JajceWorld" parent="." instance=ExtResource("11_jajce")]
|
[node name="JajceWorld" parent="." instance=ExtResource("11_jajce")]
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "pantry_storage", "guard_zone", "study_zone")]
|
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "pantry_storage", "guard_site", "study_site")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
||||||
script = ExtResource("1_h2yge")
|
script = ExtResource("1_h2yge")
|
||||||
camera_rig = NodePath("../CameraRig")
|
camera_rig = NodePath("../CameraRig")
|
||||||
simulation_manager = NodePath("../SimulationManager")
|
simulation_manager = NodePath("../SimulationManager")
|
||||||
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
||||||
guard_zone = NodePath("../JajceWorld/LegacyActivityMarkers/GuardZone")
|
guard_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/GuardPost")
|
||||||
study_zone = NodePath("../JajceWorld/LegacyActivityMarkers/StudyZone")
|
study_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/StudyDesk")
|
||||||
stomach_capacity_for_food = 10
|
stomach_capacity_for_food = 10
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player" unique_id=1249497228]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player" unique_id=1249497228]
|
||||||
@@ -57,11 +57,8 @@ deadzone_forward = 2.0
|
|||||||
current = true
|
current = true
|
||||||
fov = 65.0
|
fov = 65.0
|
||||||
|
|
||||||
[node name="ActiveWorldAdapter" type="Node" parent="." node_paths=PackedStringArray("guard_marker", "study_marker", "rest_marker", "pantry_storage")]
|
[node name="ActiveWorldAdapter" type="Node" parent="." node_paths=PackedStringArray("pantry_storage")]
|
||||||
script = ExtResource("9_adapter")
|
script = ExtResource("9_adapter")
|
||||||
guard_marker = NodePath("../JajceWorld/LegacyActivityMarkers/GuardZone")
|
|
||||||
study_marker = NodePath("../JajceWorld/LegacyActivityMarkers/StudyZone")
|
|
||||||
rest_marker = NodePath("../JajceWorld/LegacyActivityMarkers/RestZone")
|
|
||||||
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
||||||
|
|
||||||
[node name="SimulationManager" type="Node" parent="." unique_id=1099676814 node_paths=PackedStringArray("active_world_adapter")]
|
[node name="SimulationManager" type="Node" parent="." unique_id=1099676814 node_paths=PackedStringArray("active_world_adapter")]
|
||||||
|
|||||||
+11
-4
@@ -8,8 +8,8 @@ extends CharacterBody3D
|
|||||||
@export var simulation_manager: Node
|
@export var simulation_manager: Node
|
||||||
|
|
||||||
@export var pantry_storage: StorageNode
|
@export var pantry_storage: StorageNode
|
||||||
@export var guard_zone: Marker3D
|
@export var guard_site: ActivitySite
|
||||||
@export var study_zone: Marker3D
|
@export var study_site: ActivitySite
|
||||||
|
|
||||||
@export var stomach_capacity_for_food := 5
|
@export var stomach_capacity_for_food := 5
|
||||||
@export var interaction_range := 3.0
|
@export var interaction_range := 3.0
|
||||||
@@ -59,10 +59,10 @@ func try_interact() -> void:
|
|||||||
if try_harvest_resource_node():
|
if try_harvest_resource_node():
|
||||||
return
|
return
|
||||||
|
|
||||||
if is_near(guard_zone):
|
if is_near_activity_site(guard_site):
|
||||||
simulation_manager.add_safety(3.0)
|
simulation_manager.add_safety(3.0)
|
||||||
print("Player helped guard the village.")
|
print("Player helped guard the village.")
|
||||||
elif is_near(study_zone):
|
elif is_near_activity_site(study_site):
|
||||||
simulation_manager.add_knowledge(2.0)
|
simulation_manager.add_knowledge(2.0)
|
||||||
print("Player shared knowledge.")
|
print("Player shared knowledge.")
|
||||||
elif is_near_storage(pantry_storage):
|
elif is_near_storage(pantry_storage):
|
||||||
@@ -112,3 +112,10 @@ func is_near_storage(storage_node: StorageNode) -> bool:
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
return global_position.distance_to(storage_node.get_interaction_position()) <= interaction_range
|
return global_position.distance_to(storage_node.get_interaction_position()) <= interaction_range
|
||||||
|
|
||||||
|
|
||||||
|
func is_near_activity_site(activity_site: ActivitySite) -> bool:
|
||||||
|
if activity_site == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return global_position.distance_to(activity_site.get_interaction_position()) <= interaction_range
|
||||||
|
|||||||
@@ -432,7 +432,8 @@ func get_pantry() -> StorageStateRecord:
|
|||||||
|
|
||||||
|
|
||||||
func register_loaded_storage_nodes() -> void:
|
func register_loaded_storage_nodes() -> void:
|
||||||
for node in StorageNode.get_all():
|
for candidate in StorageNode.get_all():
|
||||||
|
var node := candidate as StorageNode
|
||||||
register_storage_node(node)
|
register_storage_node(node)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func resolve(
|
|||||||
npc, origin, definition, simulation_manager, active_world_adapter
|
npc, origin, definition, simulation_manager, active_world_adapter
|
||||||
)
|
)
|
||||||
SimulationIds.TARGET_ACTIVITY:
|
SimulationIds.TARGET_ACTIVITY:
|
||||||
return active_world_adapter.get_activity_target(npc.current_task)
|
return active_world_adapter.get_activity_target(npc.current_task, origin)
|
||||||
SimulationIds.TARGET_FREE:
|
SimulationIds.TARGET_FREE:
|
||||||
return {
|
return {
|
||||||
"target_id": "", "position": origin + simulation_manager.get_wander_offset(npc.id)
|
"target_id": "", "position": origin + simulation_manager.get_wander_offset(npc.id)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ func _run() -> void:
|
|||||||
not main_scene.has_node("World")
|
not main_scene.has_node("World")
|
||||||
and not main_scene.has_node("ResourceNodes")
|
and not main_scene.has_node("ResourceNodes")
|
||||||
and not main_scene.has_node("ActivityMarkers")
|
and not main_scene.has_node("ActivityMarkers")
|
||||||
|
and not main_scene.has_node("JajceWorld/LegacyActivityMarkers")
|
||||||
),
|
),
|
||||||
"Runtime should not retain duplicate flat-world objects"
|
"Runtime should not retain duplicate flat-world objects"
|
||||||
)
|
)
|
||||||
@@ -57,12 +58,10 @@ func _run() -> void:
|
|||||||
)
|
)
|
||||||
var pantry := main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry") as StorageNode
|
var pantry := main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry") as StorageNode
|
||||||
destinations.append(pantry.get_interaction_position())
|
destinations.append(pantry.get_interaction_position())
|
||||||
for marker_path in [
|
var activity_root := main_scene.get_node("JajceWorld/WorldObjects/ActivitySites")
|
||||||
"JajceWorld/LegacyActivityMarkers/GuardZone",
|
_check(activity_root.get_child_count() == 3, "Runtime should expose three typed activity sites")
|
||||||
"JajceWorld/LegacyActivityMarkers/StudyZone",
|
for site in activity_root.get_children():
|
||||||
"JajceWorld/LegacyActivityMarkers/RestZone"
|
destinations.append((site as ActivitySite).get_interaction_position())
|
||||||
]:
|
|
||||||
destinations.append(main_scene.get_node(marker_path).global_position)
|
|
||||||
|
|
||||||
for resource in resource_root.get_children():
|
for resource in resource_root.get_children():
|
||||||
destinations.append((resource as ResourceNode).interaction_point.global_position)
|
destinations.append((resource as ResourceNode).interaction_point.global_position)
|
||||||
@@ -96,9 +95,14 @@ func _run() -> void:
|
|||||||
storage_target.get("target_id", "") == String(SimulationIds.STORAGE_VILLAGE_PANTRY),
|
storage_target.get("target_id", "") == String(SimulationIds.STORAGE_VILLAGE_PANTRY),
|
||||||
"Runtime adapter should route storage actions to village_pantry"
|
"Runtime adapter should route storage actions to village_pantry"
|
||||||
)
|
)
|
||||||
|
var study_target := adapter.get_activity_target(SimulationIds.ACTION_STUDY)
|
||||||
|
_check(
|
||||||
|
study_target.get("target_id", "") == "study_desk",
|
||||||
|
"Runtime adapter should route study to the typed study desk"
|
||||||
|
)
|
||||||
|
|
||||||
if failures.is_empty():
|
if failures.is_empty():
|
||||||
print("[TEST] Jajce runtime passed: 6 NPCs, 4 resources, typed pantry route")
|
print("[TEST] Jajce runtime passed: 6 NPCs, typed storage and activity sites")
|
||||||
quit(0)
|
quit(0)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,19 @@ func _run() -> void:
|
|||||||
not world.has_node("LegacyActivityMarkers/PantryMarker"),
|
not world.has_node("LegacyActivityMarkers/PantryMarker"),
|
||||||
"Pantry should no longer be a legacy activity marker"
|
"Pantry should no longer be a legacy activity marker"
|
||||||
)
|
)
|
||||||
|
_check(
|
||||||
|
not world.has_node("LegacyActivityMarkers"),
|
||||||
|
"JajceWorld should no longer expose legacy activity markers"
|
||||||
|
)
|
||||||
|
var activity_root := world.get_node("WorldObjects/ActivitySites")
|
||||||
|
var activity_ids: Array[String] = []
|
||||||
|
for site in activity_root.get_children():
|
||||||
|
activity_ids.append(String((site as ActivitySite).site_id))
|
||||||
|
activity_ids.sort()
|
||||||
|
_check(
|
||||||
|
activity_ids == ["guard_post", "rest_bench", "study_desk"],
|
||||||
|
"Jajce activity sites should preserve stable semantic IDs"
|
||||||
|
)
|
||||||
|
|
||||||
var navigation_region := world.get_node("NavigationRegion3D") as NavigationRegion3D
|
var navigation_region := world.get_node("NavigationRegion3D") as NavigationRegion3D
|
||||||
var navigation_map: RID = NavigationServer3D.region_get_map(navigation_region.get_rid())
|
var navigation_map: RID = NavigationServer3D.region_get_map(navigation_region.get_rid())
|
||||||
@@ -143,8 +156,8 @@ func _run() -> void:
|
|||||||
for resource in resources:
|
for resource in resources:
|
||||||
destinations.append((resource as ResourceNode).interaction_point.global_position)
|
destinations.append((resource as ResourceNode).interaction_point.global_position)
|
||||||
destinations.append(pantry.get_interaction_position())
|
destinations.append(pantry.get_interaction_position())
|
||||||
for marker in world.get_node("LegacyActivityMarkers").get_children():
|
for site in activity_root.get_children():
|
||||||
destinations.append((marker as Marker3D).global_position)
|
destinations.append((site as ActivitySite).get_interaction_position())
|
||||||
|
|
||||||
for origin in origins:
|
for origin in origins:
|
||||||
for destination in destinations:
|
for destination in destinations:
|
||||||
@@ -187,10 +200,15 @@ func _run() -> void:
|
|||||||
storage_target_position.distance_to(pantry.get_interaction_position()) < 0.01,
|
storage_target_position.distance_to(pantry.get_interaction_position()) < 0.01,
|
||||||
"Storage actions should use the pantry StorageNode interaction point"
|
"Storage actions should use the pantry StorageNode interaction point"
|
||||||
)
|
)
|
||||||
|
var rest_target := adapter.get_activity_target(SimulationIds.ACTION_REST)
|
||||||
|
_check(
|
||||||
|
rest_target.get("target_id", "") == "rest_bench",
|
||||||
|
"Rest should target the typed RestBench ActivitySite"
|
||||||
|
)
|
||||||
|
|
||||||
if failures.is_empty():
|
if failures.is_empty():
|
||||||
print(
|
print(
|
||||||
"[TEST] Jajce scaffold passed: Terrain3D, storage site, shader water, building blockouts"
|
"[TEST] Jajce scaffold passed: Terrain3D, storage/activity sites, shader water"
|
||||||
)
|
)
|
||||||
quit(0)
|
quit(0)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ func _run() -> void:
|
|||||||
pantry_state.get_amount(SimulationIds.RESOURCE_FOOD) < pantry_food_before,
|
pantry_state.get_amount(SimulationIds.RESOURCE_FOOD) < pantry_food_before,
|
||||||
"Player should eat from the typed pantry StorageNode"
|
"Player should eat from the typed pantry StorageNode"
|
||||||
)
|
)
|
||||||
|
var guard_site := main_scene.get_node("JajceWorld/WorldObjects/ActivitySites/GuardPost") as ActivitySite
|
||||||
|
player.global_position = guard_site.get_interaction_position()
|
||||||
|
var safety_before: float = simulation_manager.village.safety
|
||||||
|
player.try_interact()
|
||||||
|
_check(
|
||||||
|
simulation_manager.village.safety > safety_before,
|
||||||
|
"Player should help guard from the typed GuardPost ActivitySite"
|
||||||
|
)
|
||||||
|
|
||||||
if failures.is_empty():
|
if failures.is_empty():
|
||||||
print("[TEST] ResourceNode player parity passed")
|
print("[TEST] ResourceNode player parity passed")
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
class_name ActiveWorldAdapter
|
class_name ActiveWorldAdapter
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
@export var guard_marker: Marker3D
|
|
||||||
@export var study_marker: Marker3D
|
|
||||||
@export var rest_marker: Marker3D
|
|
||||||
@export var pantry_storage: StorageNode
|
@export var pantry_storage: StorageNode
|
||||||
|
|
||||||
|
|
||||||
@@ -18,22 +15,40 @@ func get_resource_candidates(action_id: StringName) -> Array[Dictionary]:
|
|||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
func get_activity_target(action_id: StringName) -> Dictionary:
|
func get_activity_target(action_id: StringName, origin: Vector3 = Vector3.ZERO) -> Dictionary:
|
||||||
var marker: Marker3D
|
|
||||||
match action_id:
|
match action_id:
|
||||||
SimulationIds.ACTION_PATROL:
|
SimulationIds.ACTION_PATROL:
|
||||||
marker = guard_marker
|
return _get_activity_site_target(action_id, origin)
|
||||||
SimulationIds.ACTION_STUDY:
|
SimulationIds.ACTION_STUDY:
|
||||||
marker = study_marker
|
return _get_activity_site_target(action_id, origin)
|
||||||
SimulationIds.ACTION_REST:
|
SimulationIds.ACTION_REST:
|
||||||
marker = rest_marker
|
return _get_activity_site_target(action_id, origin)
|
||||||
SimulationIds.ACTION_EAT:
|
SimulationIds.ACTION_EAT:
|
||||||
return _get_storage_target(pantry_storage)
|
return _get_storage_target(pantry_storage)
|
||||||
SimulationIds.ACTION_DEPOSIT_FOOD, SimulationIds.ACTION_WITHDRAW_FOOD:
|
SimulationIds.ACTION_DEPOSIT_FOOD, SimulationIds.ACTION_WITHDRAW_FOOD:
|
||||||
return _get_storage_target(pantry_storage)
|
return _get_storage_target(pantry_storage)
|
||||||
if marker == null:
|
|
||||||
return {}
|
return {}
|
||||||
return {"target_id": "", "position": marker.global_position}
|
|
||||||
|
|
||||||
|
func _get_activity_site_target(action_id: StringName, origin: Vector3) -> Dictionary:
|
||||||
|
var best_site: ActivitySite
|
||||||
|
var best_distance := INF
|
||||||
|
for candidate in ActivitySite.get_all():
|
||||||
|
var site := candidate as ActivitySite
|
||||||
|
if site == null:
|
||||||
|
continue
|
||||||
|
if not site.supports_action(action_id):
|
||||||
|
continue
|
||||||
|
var distance := origin.distance_squared_to(site.get_interaction_position())
|
||||||
|
if distance < best_distance:
|
||||||
|
best_distance = distance
|
||||||
|
best_site = site
|
||||||
|
if best_site == null:
|
||||||
|
return {}
|
||||||
|
return {
|
||||||
|
"target_id": String(best_site.site_id),
|
||||||
|
"position": best_site.get_interaction_position()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func _get_storage_target(storage_node: StorageNode) -> Dictionary:
|
func _get_storage_target(storage_node: StorageNode) -> Dictionary:
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
class_name ActivitySite
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
static var _all: Array = []
|
||||||
|
|
||||||
|
@export var site_id: StringName
|
||||||
|
@export var action_id: StringName = SimulationIds.ACTION_REST
|
||||||
|
@export var display_name := "Activity Site"
|
||||||
|
@export_range(1, 12, 1) var capacity := 1
|
||||||
|
@export var debug_label_enabled := true
|
||||||
|
|
||||||
|
@onready var interaction_point: Marker3D = $InteractionPoint
|
||||||
|
|
||||||
|
var _last_label_text := ""
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if site_id.is_empty():
|
||||||
|
push_error("ActivitySite at %s has empty site_id" % get_path())
|
||||||
|
_update_presentation()
|
||||||
|
return
|
||||||
|
var existing := get_by_id(site_id)
|
||||||
|
if existing != null:
|
||||||
|
push_error(
|
||||||
|
(
|
||||||
|
"Duplicate ActivitySite site_id '%s' at %s; first registered at %s"
|
||||||
|
% [site_id, get_path(), existing.get_path()]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_update_presentation()
|
||||||
|
return
|
||||||
|
_all.append(self)
|
||||||
|
add_to_group("activity_sites")
|
||||||
|
_update_presentation()
|
||||||
|
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if debug_label_enabled:
|
||||||
|
_update_presentation()
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
_all.erase(self)
|
||||||
|
|
||||||
|
|
||||||
|
func get_interaction_position() -> Vector3:
|
||||||
|
return interaction_point.global_position if interaction_point != null else global_position
|
||||||
|
|
||||||
|
|
||||||
|
func supports_action(candidate_action_id: StringName) -> bool:
|
||||||
|
return action_id == candidate_action_id
|
||||||
|
|
||||||
|
|
||||||
|
func _update_presentation() -> void:
|
||||||
|
if not has_node("DebugLabel"):
|
||||||
|
return
|
||||||
|
var label := $DebugLabel as Label3D
|
||||||
|
label.visible = debug_label_enabled
|
||||||
|
if not debug_label_enabled:
|
||||||
|
return
|
||||||
|
var text := "%s\n%s\ncap:%d" % [display_name, action_id, capacity]
|
||||||
|
if text == _last_label_text:
|
||||||
|
return
|
||||||
|
_last_label_text = text
|
||||||
|
label.text = text
|
||||||
|
|
||||||
|
|
||||||
|
static func get_by_id(search_id: StringName):
|
||||||
|
for node in _all:
|
||||||
|
if node.site_id == search_id:
|
||||||
|
return node
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
static func get_all() -> Array:
|
||||||
|
return _all.duplicate()
|
||||||
+58
-10
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=25 format=3]
|
[gd_scene load_steps=26 format=3]
|
||||||
|
|
||||||
[ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"]
|
[ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"]
|
||||||
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
|
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
[ext_resource type="PackedScene" path="res://world/jajce/ChimneySmoke.tscn" id="12_smoke"]
|
[ext_resource type="PackedScene" path="res://world/jajce/ChimneySmoke.tscn" id="12_smoke"]
|
||||||
[ext_resource type="Script" path="res://world/jajce/day_night_cycle.gd" id="13_daynight"]
|
[ext_resource type="Script" path="res://world/jajce/day_night_cycle.gd" id="13_daynight"]
|
||||||
[ext_resource type="PackedScene" path="res://world/storage/StorageNode.tscn" id="14_storage"]
|
[ext_resource type="PackedScene" path="res://world/storage/StorageNode.tscn" id="14_storage"]
|
||||||
|
[ext_resource type="Script" path="res://world/activity/ActivitySite.gd" id="15_activity"]
|
||||||
|
|
||||||
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_jajce"]
|
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_jajce"]
|
||||||
_shader_parameters = {
|
_shader_parameters = {
|
||||||
@@ -251,29 +252,76 @@ yield_per_action = 3.0
|
|||||||
[node name="VillagePantry" parent="WorldObjects/StorageSites" instance=ExtResource("14_storage")]
|
[node name="VillagePantry" parent="WorldObjects/StorageSites" instance=ExtResource("14_storage")]
|
||||||
position = Vector3(0, 0, -8)
|
position = Vector3(0, 0, -8)
|
||||||
|
|
||||||
[node name="LegacyActivityMarkers" type="Node3D" parent="."]
|
[node name="ActivitySites" type="Node3D" parent="WorldObjects"]
|
||||||
|
|
||||||
[node name="GuardZone" type="Marker3D" parent="LegacyActivityMarkers"]
|
[node name="GuardPost" type="Node3D" parent="WorldObjects/ActivitySites"]
|
||||||
position = Vector3(0, 0, 8)
|
position = Vector3(0, 0, 8)
|
||||||
|
script = ExtResource("15_activity")
|
||||||
|
site_id = &"guard_post"
|
||||||
|
action_id = &"patrol"
|
||||||
|
display_name = "Guard Post"
|
||||||
|
capacity = 2
|
||||||
|
|
||||||
[node name="GuardTowerVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/GuardZone"]
|
[node name="GuardTowerVisual" type="MeshInstance3D" parent="WorldObjects/ActivitySites/GuardPost"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)
|
||||||
mesh = SubResource("Mesh_guard_post")
|
mesh = SubResource("Mesh_guard_post")
|
||||||
|
|
||||||
[node name="StudyZone" type="Marker3D" parent="LegacyActivityMarkers"]
|
[node name="InteractionPoint" type="Marker3D" parent="WorldObjects/ActivitySites/GuardPost"]
|
||||||
position = Vector3(-6, 0, 6)
|
|
||||||
|
|
||||||
[node name="StudyDeskVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/StudyZone"]
|
[node name="DebugLabel" type="Label3D" parent="WorldObjects/ActivitySites/GuardPost"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.8, 0)
|
||||||
|
billboard = 1
|
||||||
|
no_depth_test = true
|
||||||
|
font_size = 18
|
||||||
|
outline_modulate = Color(0, 0, 0, 1)
|
||||||
|
outline_size = 4
|
||||||
|
text = "Guard Post"
|
||||||
|
|
||||||
|
[node name="StudyDesk" type="Node3D" parent="WorldObjects/ActivitySites"]
|
||||||
|
position = Vector3(-6, 0, 6)
|
||||||
|
script = ExtResource("15_activity")
|
||||||
|
site_id = &"study_desk"
|
||||||
|
action_id = &"study"
|
||||||
|
display_name = "Study Desk"
|
||||||
|
|
||||||
|
[node name="StudyDeskVisual" type="MeshInstance3D" parent="WorldObjects/ActivitySites/StudyDesk"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.125, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.125, 0)
|
||||||
mesh = SubResource("Mesh_study_desk")
|
mesh = SubResource("Mesh_study_desk")
|
||||||
|
|
||||||
[node name="RestZone" type="Marker3D" parent="LegacyActivityMarkers"]
|
[node name="InteractionPoint" type="Marker3D" parent="WorldObjects/ActivitySites/StudyDesk"]
|
||||||
position = Vector3(4, 0, 5)
|
|
||||||
|
|
||||||
[node name="RestBenchVisual" type="MeshInstance3D" parent="LegacyActivityMarkers/RestZone"]
|
[node name="DebugLabel" type="Label3D" parent="WorldObjects/ActivitySites/StudyDesk"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1, 0)
|
||||||
|
billboard = 1
|
||||||
|
no_depth_test = true
|
||||||
|
font_size = 18
|
||||||
|
outline_modulate = Color(0, 0, 0, 1)
|
||||||
|
outline_size = 4
|
||||||
|
text = "Study Desk"
|
||||||
|
|
||||||
|
[node name="RestBench" type="Node3D" parent="WorldObjects/ActivitySites"]
|
||||||
|
position = Vector3(4, 0, 5)
|
||||||
|
script = ExtResource("15_activity")
|
||||||
|
site_id = &"rest_bench"
|
||||||
|
action_id = &"rest"
|
||||||
|
display_name = "Rest Bench"
|
||||||
|
capacity = 2
|
||||||
|
|
||||||
|
[node name="RestBenchVisual" type="MeshInstance3D" parent="WorldObjects/ActivitySites/RestBench"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
|
||||||
mesh = SubResource("Mesh_rest_bench")
|
mesh = SubResource("Mesh_rest_bench")
|
||||||
|
|
||||||
|
[node name="InteractionPoint" type="Marker3D" parent="WorldObjects/ActivitySites/RestBench"]
|
||||||
|
|
||||||
|
[node name="DebugLabel" type="Label3D" parent="WorldObjects/ActivitySites/RestBench"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1, 0)
|
||||||
|
billboard = 1
|
||||||
|
no_depth_test = true
|
||||||
|
font_size = 18
|
||||||
|
outline_modulate = Color(0, 0, 0, 1)
|
||||||
|
outline_size = 4
|
||||||
|
text = "Rest Bench"
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
rotation_degrees = Vector3(-55, -35, 0)
|
rotation_degrees = Vector3(-55, -35, 0)
|
||||||
light_color = Color(1, 0.87, 0.72, 1)
|
light_color = Color(1, 0.87, 0.72, 1)
|
||||||
|
|||||||
@@ -91,15 +91,12 @@ func _update_presentation() -> void:
|
|||||||
label.text = text
|
label.text = text
|
||||||
|
|
||||||
|
|
||||||
static func get_by_id(search_id: StringName) -> StorageNode:
|
static func get_by_id(search_id: StringName):
|
||||||
for node in _all:
|
for node in _all:
|
||||||
if node.storage_id == search_id:
|
if node.storage_id == search_id:
|
||||||
return node
|
return node
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
static func get_all() -> Array[StorageNode]:
|
static func get_all() -> Array:
|
||||||
var nodes: Array[StorageNode] = []
|
return _all.duplicate()
|
||||||
for node in _all:
|
|
||||||
nodes.append(node as StorageNode)
|
|
||||||
return nodes
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cgxnb8qsttpqy
|
||||||
Reference in New Issue
Block a user