Export venues as editor-authorable .tres resources
- tools/export_venues.gd (SceneTree script) saves the three factory scenarios to content/venues/<id>.tres via ResourceSaver. The factory stays the source of truth; the exported files are what an editor workflow would edit and ship. - New tests load the files back and check that id, name, event pool, profiles, policy (ID/wand), equipment, door-staff lines, shift phases, and locations all survive. They also fail when the exported files are missing or stale, so the export is re-run after content changes. - GUT is consciously not adopted: the custom headless runner already covers 157 assertions without an editor plugin and keeps CI dependency-free. Recorded in DONE.md. - TODO: the planned backlog is complete; only optional future ideas remain. - README/MEMORY updated. Version 0.2.16.
This commit is contained in:
1 parent
72b9378e69
commit
7ec99a66b4
10 files changed
+2318
-9
No files matched your search
@@ -583,3 +583,15 @@ Completed 2026-10-31 (working tree). 30 Python tooling tests pass; the live judg
|
|||||||
capture + reference design to a vision model, classes verdicts as
|
capture + reference design to a vision model, classes verdicts as
|
||||||
match/review/diff with a confidence threshold, supports `--dry-run`, `--json`,
|
match/review/diff with a confidence threshold, supports `--dry-run`, `--json`,
|
||||||
`--only`, and `--model`, and reads the repo `.env` for the API key.
|
`--only`, and `--model`, and reads the repo `.env` for the API key.
|
||||||
|
|
||||||
|
## Tooling (when an editor workflow exists)
|
||||||
|
|
||||||
|
Completed 2026-10-31 (working tree). Headless **157/157**; the exported `.tres` venues round-trip through the resource loader.
|
||||||
|
|
||||||
|
|
||||||
|
- [x] Author venues as `.tres` resources: `tools/export_venues.gd` writes
|
||||||
|
`content/venues/<id>.tres` from the factory, and a test loads them back to check
|
||||||
|
nothing is lost (events, profiles, policy, phases, door lines, locations).
|
||||||
|
- [x] GUT decision: **not adopted.** The custom headless runner (`src/test/test_runner.gd`)
|
||||||
|
already covers 157 assertions without an editor plugin, runs in CI, and keeps the
|
||||||
|
project dependency-free; adding GUT would duplicate coverage and require the editor.
|
||||||
@@ -21,8 +21,9 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
Windowed control smoke: `godot --path . --script res://src/test/smoke_focus_radial.gd` → exit 0.
|
Windowed control smoke: `godot --path . --script res://src/test/smoke_focus_radial.gd` → exit 0.
|
||||||
- `main` is in sync with origin. **v0.2.2** tags the session-shell + world-follow-up commit;
|
- `main` is in sync with origin. **v0.2.2** tags the session-shell + world-follow-up commit;
|
||||||
CI builds the portable binaries, NSIS installer, and AppImage on the tag push.
|
CI builds the portable binaries, NSIS installer, and AppImage on the tag push.
|
||||||
- Next work: only the optional editor-tooling section remains (GUT plugin, `.tres` venues);
|
- **Backlog complete:** every planned TODO section is done and moved to `DONE.md`; `TODO.md`
|
||||||
everything else in `TODO.md` is complete.
|
now only lists optional future ideas (live 3D replay, NPC LOD, more venues, real audio, web
|
||||||
|
export). GUT was consciously not adopted (custom runner covers it without an editor plugin).
|
||||||
- Do not re-open `override_access`. Do not bind wand to `W`. Do not remove the radio easter egg.
|
- Do not re-open `override_access`. Do not bind wand to `W`. Do not remove the radio easter egg.
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
@@ -95,6 +96,11 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
`_begin_incident()` now starts one per focused NPC (`has_open_incident()` guard), verbs call
|
`_begin_incident()` now starts one per focused NPC (`has_open_incident()` guard), verbs call
|
||||||
`_ensure_incident()`, and `ShiftCoordinator._finish_shift()` closes leftovers as `unresolved`
|
`_ensure_incident()`, and `ShiftCoordinator._finish_shift()` closes leftovers as `unresolved`
|
||||||
before building the review.
|
before building the review.
|
||||||
|
- **Editor-ready venues (2026-10-31):** `tools/export_venues.gd` (a SceneTree script) saves the
|
||||||
|
three factory scenarios to `content/venues/<id>.tres` with `ResourceSaver`; the `.tres` files
|
||||||
|
round-trip through `ResourceLoader` (events, profiles, policy, shift phases, door lines,
|
||||||
|
locations). `test_venue_resources.gd` also fails if the exported files are missing or stale
|
||||||
|
relative to the factory, so re-run the export after changing `ScenarioFactory`.
|
||||||
- **Vision judge (2026-10-31):** `python3 tools/screen_designs.py judge` sends the capture plus
|
- **Vision judge (2026-10-31):** `python3 tools/screen_designs.py judge` sends the capture plus
|
||||||
its concept mockup to a vision model (`DEFAULT_JUDGE_MODEL = google/gemini-2.5-flash-lite`,
|
its concept mockup to a vision model (`DEFAULT_JUDGE_MODEL = google/gemini-2.5-flash-lite`,
|
||||||
override with `--model`; `openai/gpt-4o-mini` also works). Reads the repo `.env` via
|
override with `--model`; `openai/gpt-4o-mini` also works). Reads the repo `.env` via
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ The central design goal is simple:
|
|||||||
> Downloads are public (no sign-in required) since the host's instance-wide
|
> Downloads are public (no sign-in required) since the host's instance-wide
|
||||||
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
||||||
>
|
>
|
||||||
> **Latest: v0.2.15** — vision-model screenshot judge for the QA tooling.
|
> **Latest: v0.2.16** — editor-ready `.tres` venues; the planned backlog is complete.
|
||||||
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
||||||
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
||||||
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
||||||
@@ -195,6 +195,9 @@ shared art-direction suffix so the frames read as one coherent game. Requests us
|
|||||||
a 3:2 landscape source and crop to the 16:9 capture dimensions.
|
a 3:2 landscape source and crop to the 16:9 capture dimensions.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Export the built-in scenarios to editor-authorable .tres resources
|
||||||
|
godot --headless --path . --script res://tools/export_venues.gd
|
||||||
|
|
||||||
# Deterministic wireframes (offline, no API key)
|
# Deterministic wireframes (offline, no API key)
|
||||||
python3 tools/screen_designs.py generate --draft
|
python3 tools/screen_designs.py generate --draft
|
||||||
|
|
||||||
@@ -326,7 +329,7 @@ Implemented so far:
|
|||||||
- Godot 4.3 project scaffold, input map, procedural world builder
|
- Godot 4.3 project scaffold, input map, procedural world builder
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
||||||
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
||||||
- Headless unit tests: **153 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting)
|
- Headless unit tests: **157 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip)
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
||||||
@@ -343,6 +346,7 @@ Implemented so far:
|
|||||||
- NPC theater: seated guests sit on the steps with bent legs, influencers and tourists raise a camera prop while photographing, performers sway their sign, and filming visitors pan slowly — all once they reach their intent's waypoint
|
- NPC theater: seated guests sit on the steps with bent legs, influencers and tourists raise a camera prop while photographing, performers sway their sign, and filming visitors pan slowly — all once they reach their intent's waypoint
|
||||||
- Audio: a looping procedural crowd bed (filtered murmur) whose volume follows `CrowdSystem.mood`, plus radio open/close crackle. All generated at runtime, no audio assets, and safe with a dummy audio server
|
- Audio: a looping procedural crowd bed (filtered murmur) whose volume follows `CrowdSystem.mood`, plus radio open/close crackle. All generated at runtime, no audio assets, and safe with a dummy audio server
|
||||||
- Accessibility: the escalation cue shows a shape meter plus the numeric level (`Escalation: Blocking [###--] L3/5`) so it reads in grayscale, and the HUD font scale from settings is applied at boot by resizing fonts (not Control.scale, which broke the right-aligned layout)
|
- Accessibility: the escalation cue shows a shape meter plus the numeric level (`Escalation: Blocking [###--] L3/5`) so it reads in grayscale, and the HUD font scale from settings is applied at boot by resizing fonts (not Control.scale, which broke the right-aligned layout)
|
||||||
|
- Editor-ready venues: `tools/export_venues.gd` writes `content/venues/<id>.tres` from `ScenarioFactory`, and the `.tres` files load back with events, profiles, policy, phases, door lines, and locations intact (the factory stays the source of truth)
|
||||||
- Localization safety: `Strings.fmt` validates the placeholder count before formatting and fails safe (warns, returns the template) instead of raising an engine error, `Strings.placeholder_count` ignores `%%`, and `Strings.validate()` lints the table for trailing `%`, unsupported conversions, and mixed positional/sequential placeholders
|
- Localization safety: `Strings.fmt` validates the placeholder count before formatting and fails safe (warns, returns the template) instead of raising an engine error, `Strings.placeholder_count` ignores `%%`, and `Strings.validate()` lints the table for trailing `%`, unsupported conversions, and mixed positional/sequential placeholders
|
||||||
- Authored scenario content: Joke Shop reservation-without-ID, borrowed/mismatched ID, a guest who refuses the wand screen, and a late arrival during a performance; Studio filming-boundary repeats and a celebrity arrival that draws a small crowd; Building events that point the player at the body camera and request-reason verbs
|
- Authored scenario content: Joke Shop reservation-without-ID, borrowed/mismatched ID, a guest who refuses the wand screen, and a late arrival during a performance; Studio filming-boundary repeats and a celebrity arrival that draws a small crowd; Building events that point the player at the body camera and request-reason verbs
|
||||||
- Visual polish pass (v0.2.0): phase-based lighting (sun/sky/ambient per shift phase), street lamps with night-mode bulbs and omni lights, glowing marquee signage, crosswalk, planters and low-poly street trees, NPC height/brightness variety, denser routine traffic — day and late-night screenshot sets
|
- Visual polish pass (v0.2.0): phase-based lighting (sun/sky/ambient per shift phase), street lamps with night-mode bulbs and omni lights, glowing marquee signage, crosswalk, planters and low-poly street trees, NPC height/brightness variety, denser routine traffic — day and late-night screenshot sets
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
Godot 4.3 prototype. Latest release **v0.2.15**. Headless suite **153/153** plus 30 Python
|
Godot 4.3 prototype. Latest release **v0.2.16**. Headless suite **157/157** plus 30 Python
|
||||||
tooling tests; the session, PLAN-verb, crowd, emergency, timeline, audio, scenario-content, and
|
tooling tests; the session, PLAN-verb, crowd, emergency, timeline, audio, scenario-content, and
|
||||||
windowed focus/radial smokes pass. Anonymous downloads are enabled on the host.
|
windowed focus/radial smokes pass. Anonymous downloads are enabled on the host.
|
||||||
|
|
||||||
## Tooling (when an editor workflow exists)
|
All planned backlog sections are complete (see `DONE.md`). Optional future ideas live below.
|
||||||
|
|
||||||
- [ ] IDE/editor-based GUT integration. Until then the custom headless runner stays.
|
## Future ideas (optional, not scheduled)
|
||||||
- [ ] Optional later: author venues as `.tres` resources instead of `ScenarioFactory` functions.
|
|
||||||
|
- [ ] Live 3D body-camera replay (the timeline is text-only today).
|
||||||
|
- [ ] LOD/culling for NPCs if `MAX_LIVE_NPCS` grows past the measured 16-avatar budget.
|
||||||
|
- [ ] Additional venues or seasonal events authored from the new `.tres` files.
|
||||||
|
- [ ] Real voice/dialogue audio (the crowd bed and radio crackle are procedural).
|
||||||
|
- [ ] Web export build target.
|
||||||
@@ -0,0 +1,794 @@
|
|||||||
|
[gd_resource type="Resource" script_class="VenueScenario" load_steps=17 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/core/access_policy.gd" id="1_2u28i"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/npc_profile.gd" id="2_acokf"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/venue_scenario.gd" id="3_mqiqi"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_i8fga"]
|
||||||
|
script = ExtResource("1_2u28i")
|
||||||
|
required_artifacts = {
|
||||||
|
1: true
|
||||||
|
}
|
||||||
|
grant_if_any_required = false
|
||||||
|
minimum_age = 0
|
||||||
|
require_id = false
|
||||||
|
wand_screening_required = false
|
||||||
|
policy_summary = "Private property. Entry requires recognition or a valid wristband."
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_c7lhe"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "tourist"
|
||||||
|
display_name = "Tourist"
|
||||||
|
npc_type = 0
|
||||||
|
temperament = 0.1
|
||||||
|
base_compliance = 0.9
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Oh sure, sorry, just taking a picture.", "Is there a better spot to stand?", "Understood, moving."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_0t6ar"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "confused_visitor"
|
||||||
|
display_name = "Confused Visitor"
|
||||||
|
npc_type = 3
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.85
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Wait, this isn't the entrance?", "Sorry about that, I'm a bit lost."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_wyv8p"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "delivery"
|
||||||
|
display_name = "Delivery Worker"
|
||||||
|
npc_type = 10
|
||||||
|
temperament = 0.25
|
||||||
|
base_compliance = 0.8
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Where should I leave this?"])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_v2hc4"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "street_performer"
|
||||||
|
display_name = "Street Performer"
|
||||||
|
npc_type = 1
|
||||||
|
temperament = 0.45
|
||||||
|
base_compliance = 0.55
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 55,
|
||||||
|
2: 15,
|
||||||
|
3: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_lg4td"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "influencer"
|
||||||
|
display_name = "Influencer"
|
||||||
|
npc_type = 2
|
||||||
|
temperament = 0.5
|
||||||
|
base_compliance = 0.5
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 40,
|
||||||
|
2: 10,
|
||||||
|
3: 30,
|
||||||
|
4: 20
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
4: 35,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 10,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 30,
|
||||||
|
8: 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["This is public sidewalk, you can't stop me from filming."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_ijl2s"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "intoxicated"
|
||||||
|
display_name = "Intoxicated Pedestrian"
|
||||||
|
npc_type = 4
|
||||||
|
temperament = 0.6
|
||||||
|
base_compliance = 0.4
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
3: 50
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
3: 15,
|
||||||
|
4: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 1.8
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_eycmk"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "trespasser"
|
||||||
|
display_name = "Trespasser"
|
||||||
|
npc_type = 5
|
||||||
|
temperament = 0.55
|
||||||
|
base_compliance = 0.35
|
||||||
|
return_probability = 0.6
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 25,
|
||||||
|
3: 45
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
6: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 35,
|
||||||
|
2: 25,
|
||||||
|
6: 15,
|
||||||
|
7: 10,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 70,
|
||||||
|
7: 30
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 50,
|
||||||
|
7: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["I'm just here to see a friend, come on.", "I'll be inside for ten seconds."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_ggtpw"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "mental_crisis"
|
||||||
|
display_name = "Person in Crisis"
|
||||||
|
npc_type = 7
|
||||||
|
temperament = 0.7
|
||||||
|
base_compliance = 0.3
|
||||||
|
return_probability = 0.4
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 25,
|
||||||
|
2: 20,
|
||||||
|
3: 55
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 25,
|
||||||
|
2: 35,
|
||||||
|
3: 30,
|
||||||
|
5: 10
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 25,
|
||||||
|
1: 15,
|
||||||
|
2: 30,
|
||||||
|
5: 30
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 40,
|
||||||
|
5: 35,
|
||||||
|
7: 25
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 25,
|
||||||
|
5: 60,
|
||||||
|
7: 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 2.4
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_oa64f"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "violent_threat"
|
||||||
|
display_name = "Violent Threat"
|
||||||
|
npc_type = 6
|
||||||
|
temperament = 0.95
|
||||||
|
base_compliance = 0.05
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
2: 25,
|
||||||
|
3: 45,
|
||||||
|
5: 30
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
2: 30,
|
||||||
|
5: 45,
|
||||||
|
7: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 20,
|
||||||
|
5: 45,
|
||||||
|
7: 35
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 15,
|
||||||
|
5: 30,
|
||||||
|
7: 55
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_7t7dh"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "protester"
|
||||||
|
display_name = "Protester"
|
||||||
|
npc_type = 12
|
||||||
|
temperament = 0.5
|
||||||
|
base_compliance = 0.4
|
||||||
|
return_probability = 0.25
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 25,
|
||||||
|
2: 35,
|
||||||
|
3: 25,
|
||||||
|
4: 15
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 15,
|
||||||
|
2: 40,
|
||||||
|
3: 15,
|
||||||
|
4: 25,
|
||||||
|
8: 5
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 10,
|
||||||
|
1: 25,
|
||||||
|
2: 25,
|
||||||
|
4: 25,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 60,
|
||||||
|
2: 20,
|
||||||
|
7: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["I have every right to be on public sidewalk.", "You can't touch me, this is a lawful protest."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_8tj1k"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "authorized_staff"
|
||||||
|
display_name = "Staff Member"
|
||||||
|
npc_type = 8
|
||||||
|
temperament = 0.15
|
||||||
|
base_compliance = 1.0
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_tnpxm"]
|
||||||
|
script = ExtResource("2_acokf")
|
||||||
|
id = "authorized_guest"
|
||||||
|
display_name = "Authorized Guest"
|
||||||
|
npc_type = 9
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.95
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 92,
|
||||||
|
2: 8
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 88,
|
||||||
|
2: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("3_mqiqi")
|
||||||
|
id = "hollywood_building"
|
||||||
|
display_name = "Hollywood Boulevard Building"
|
||||||
|
description = "Steps and entrance of a private building off Hollywood Boulevard."
|
||||||
|
access_policy = SubResource("Resource_i8fga")
|
||||||
|
equipment = Array[String](["wristband_policy", "flashlight", "radio"])
|
||||||
|
locations = {
|
||||||
|
"entrance": Vector3(0, 0, 1),
|
||||||
|
"guard_post": Vector3(0, 0, -3),
|
||||||
|
"queue_area": Vector3(0, 0, 6),
|
||||||
|
"sidewalk": Vector3(0, 0, 9),
|
||||||
|
"steps_end": Vector3(0, 0, 3)
|
||||||
|
}
|
||||||
|
npc_profiles = Array[ExtResource("2_acokf")]([SubResource("Resource_c7lhe"), SubResource("Resource_0t6ar"), SubResource("Resource_wyv8p"), SubResource("Resource_v2hc4"), SubResource("Resource_lg4td"), SubResource("Resource_ijl2s"), SubResource("Resource_eycmk"), SubResource("Resource_ggtpw"), SubResource("Resource_oa64f"), SubResource("Resource_7t7dh"), SubResource("Resource_8tj1k"), SubResource("Resource_tnpxm")])
|
||||||
|
event_pool = Array[Dictionary]([{
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "tourist_photo",
|
||||||
|
"intent": "photo",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 0,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.42,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "protest_gather",
|
||||||
|
"intent": "protest",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 12,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.18,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "tourist_sit",
|
||||||
|
"intent": "sit",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 0,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.26,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "confused_enter",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 3,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.25,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "delivery_drop",
|
||||||
|
"intent": "deliver",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 10,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.3,
|
||||||
|
"time_range_s": Vector2(0, 600)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "staff_entry",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 8,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.35,
|
||||||
|
"time_range_s": Vector2(0, 600)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "guest_entry",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.25,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "performer_show",
|
||||||
|
"intent": "perform",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 1,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.24,
|
||||||
|
"time_range_s": Vector2(300, 800)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.body_camera_reason",
|
||||||
|
"id": "building_filming_boundary",
|
||||||
|
"intent": "film",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 1,
|
||||||
|
"probability": 0.4,
|
||||||
|
"time_range_s": Vector2(300, 600)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.ask_reason",
|
||||||
|
"id": "building_unclear_intent",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 3,
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.4,
|
||||||
|
"time_range_s": Vector2(600, 780)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "influencer_film",
|
||||||
|
"intent": "film",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.26,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "intoxicated_wander",
|
||||||
|
"intent": "wander",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 4,
|
||||||
|
"phase": 1,
|
||||||
|
"probability": 0.15,
|
||||||
|
"time_range_s": Vector2(600, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "trespass_test",
|
||||||
|
"intent": "trespass",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 5,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.16,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "crisis_event",
|
||||||
|
"intent": "crisis",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 7,
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.5,
|
||||||
|
"time_range_s": Vector2(640, 740)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "violent_emergency_event",
|
||||||
|
"intent": "threat",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 6,
|
||||||
|
"phase": 3,
|
||||||
|
"probability": 0.28,
|
||||||
|
"requires_emergency": true,
|
||||||
|
"time_range_s": Vector2(760, 880)
|
||||||
|
}])
|
||||||
|
shift_phases = {
|
||||||
|
0: {
|
||||||
|
"start_s": 0.0
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
"start_s": 300.0
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
"start_s": 600.0
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
"start_s": 700.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shift_duration_s = 900.0
|
||||||
|
policy_summary = ""
|
||||||
|
door_staff_lines = {}
|
||||||
@@ -0,0 +1,590 @@
|
|||||||
|
[gd_resource type="Resource" script_class="VenueScenario" load_steps=13 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/core/access_policy.gd" id="1_lxsgk"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/npc_profile.gd" id="2_wfc6m"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/venue_scenario.gd" id="3_8xm7a"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_xha77"]
|
||||||
|
script = ExtResource("1_lxsgk")
|
||||||
|
required_artifacts = {
|
||||||
|
2: true,
|
||||||
|
3: true
|
||||||
|
}
|
||||||
|
grant_if_any_required = false
|
||||||
|
minimum_age = 21
|
||||||
|
require_id = true
|
||||||
|
wand_screening_required = true
|
||||||
|
policy_summary = "Government ID and a valid ticket or reservation required. Carry items are screened."
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_8ksyk"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "tourist"
|
||||||
|
display_name = "Tourist"
|
||||||
|
npc_type = 0
|
||||||
|
temperament = 0.1
|
||||||
|
base_compliance = 0.9
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Oh sure, sorry, just taking a picture.", "Is there a better spot to stand?", "Understood, moving."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_wt1op"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "confused_visitor"
|
||||||
|
display_name = "Confused Visitor"
|
||||||
|
npc_type = 3
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.85
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Wait, this isn't the entrance?", "Sorry about that, I'm a bit lost."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_g1jr2"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "influencer"
|
||||||
|
display_name = "Influencer"
|
||||||
|
npc_type = 2
|
||||||
|
temperament = 0.5
|
||||||
|
base_compliance = 0.5
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 40,
|
||||||
|
2: 10,
|
||||||
|
3: 30,
|
||||||
|
4: 20
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
4: 35,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 10,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 30,
|
||||||
|
8: 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["This is public sidewalk, you can't stop me from filming."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_ys0b0"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "intoxicated"
|
||||||
|
display_name = "Intoxicated Pedestrian"
|
||||||
|
npc_type = 4
|
||||||
|
temperament = 0.6
|
||||||
|
base_compliance = 0.4
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
3: 50
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
3: 15,
|
||||||
|
4: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 1.8
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_bv0jw"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "trespasser"
|
||||||
|
display_name = "Trespasser"
|
||||||
|
npc_type = 5
|
||||||
|
temperament = 0.55
|
||||||
|
base_compliance = 0.35
|
||||||
|
return_probability = 0.6
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 25,
|
||||||
|
3: 45
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
6: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 35,
|
||||||
|
2: 25,
|
||||||
|
6: 15,
|
||||||
|
7: 10,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 70,
|
||||||
|
7: 30
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 50,
|
||||||
|
7: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["I'm just here to see a friend, come on.", "I'll be inside for ten seconds."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_7vgx3"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "violent_threat"
|
||||||
|
display_name = "Violent Threat"
|
||||||
|
npc_type = 6
|
||||||
|
temperament = 0.95
|
||||||
|
base_compliance = 0.05
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
2: 25,
|
||||||
|
3: 45,
|
||||||
|
5: 30
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
2: 30,
|
||||||
|
5: 45,
|
||||||
|
7: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 20,
|
||||||
|
5: 45,
|
||||||
|
7: 35
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 15,
|
||||||
|
5: 30,
|
||||||
|
7: 55
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_x43ed"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "authorized_staff"
|
||||||
|
display_name = "Staff Member"
|
||||||
|
npc_type = 8
|
||||||
|
temperament = 0.15
|
||||||
|
base_compliance = 1.0
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_07lsg"]
|
||||||
|
script = ExtResource("2_wfc6m")
|
||||||
|
id = "authorized_guest"
|
||||||
|
display_name = "Authorized Guest"
|
||||||
|
npc_type = 9
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.95
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 92,
|
||||||
|
2: 8
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 88,
|
||||||
|
2: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("3_8xm7a")
|
||||||
|
id = "joke_shop"
|
||||||
|
display_name = "The Joke Shop"
|
||||||
|
description = "Comedy club door: IDs, reservations, and wand screening."
|
||||||
|
access_policy = SubResource("Resource_xha77")
|
||||||
|
equipment = Array[String](["id_check", "guest_list", "wand", "radio"])
|
||||||
|
locations = {
|
||||||
|
"door_area": Vector3(0, 0, 3),
|
||||||
|
"entrance": Vector3(0, 0, 1),
|
||||||
|
"guard_post": Vector3(0, 0, -3),
|
||||||
|
"queue_area": Vector3(0, 0, 9),
|
||||||
|
"sidewalk": Vector3(0, 0, 9),
|
||||||
|
"steps_end": Vector3(0, 0, 3)
|
||||||
|
}
|
||||||
|
npc_profiles = Array[ExtResource("2_wfc6m")]([SubResource("Resource_8ksyk"), SubResource("Resource_wt1op"), SubResource("Resource_g1jr2"), SubResource("Resource_ys0b0"), SubResource("Resource_bv0jw"), SubResource("Resource_7vgx3"), SubResource("Resource_x43ed"), SubResource("Resource_07lsg")])
|
||||||
|
event_pool = Array[Dictionary]([{
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "guest_valid",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": true
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.3,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "guest_no_ticket",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": false
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.18,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "guest_expired_id",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": true,
|
||||||
|
"id_status": 1
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.12,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "guest_no_id",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": false,
|
||||||
|
"has_ticket": true
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.12,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.reservation_no_id",
|
||||||
|
"id": "joke_guest_reservation_no_id",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"guest_list_status": 0,
|
||||||
|
"has_guest_list": true,
|
||||||
|
"has_id": false,
|
||||||
|
"has_ticket": false
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.45,
|
||||||
|
"time_range_s": Vector2(620, 860)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.borrowed_id",
|
||||||
|
"id": "joke_borrowed_id",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": true,
|
||||||
|
"id_status": 3,
|
||||||
|
"ticket_status": 0
|
||||||
|
},
|
||||||
|
"phase": 1,
|
||||||
|
"probability": 0.4,
|
||||||
|
"time_range_s": Vector2(320, 700)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.refuses_wand",
|
||||||
|
"id": "joke_refuses_wand",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": true,
|
||||||
|
"id_status": 0,
|
||||||
|
"ticket_status": 0
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.4,
|
||||||
|
"refuses_screening": true,
|
||||||
|
"time_range_s": Vector2(650, 880)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.late_arrival",
|
||||||
|
"id": "joke_late_arrival",
|
||||||
|
"intent": "arrive",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_id": true,
|
||||||
|
"has_ticket": true,
|
||||||
|
"id_status": 0,
|
||||||
|
"ticket_status": 0
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.35,
|
||||||
|
"time_range_s": Vector2(700, 860)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "joke_staff",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 8,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.25,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "performer_show",
|
||||||
|
"intent": "perform",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 1,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.15,
|
||||||
|
"time_range_s": Vector2(300, 800)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "influencer_film",
|
||||||
|
"intent": "film",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.15,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "joke_disruptive",
|
||||||
|
"intent": "force_entry",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 4,
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.5,
|
||||||
|
"time_range_s": Vector2(650, 830)
|
||||||
|
}])
|
||||||
|
shift_phases = {
|
||||||
|
0: {
|
||||||
|
"start_s": 0.0
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
"start_s": 300.0
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
"start_s": 600.0
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
"start_s": 700.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shift_duration_s = 900.0
|
||||||
|
policy_summary = ""
|
||||||
|
door_staff_lines = {
|
||||||
|
"id_check": ["May I see your ID, please?", "Could you show me some identification?"],
|
||||||
|
"prohibited_item": ["I can't allow that item inside. Please leave it outside.", "That item isn't permitted in the venue."],
|
||||||
|
"refusal": ["I'm sorry, I can't let you in without that.", "Without a valid ID, I can't admit you tonight."],
|
||||||
|
"ticket_check": ["Your ticket or reservation, please.", "Do you have a ticket for tonight's show?"],
|
||||||
|
"wand_check": ["Please step forward for the wand check.", "Arms out, please — just a quick screening."]
|
||||||
|
}
|
||||||
@@ -0,0 +1,810 @@
|
|||||||
|
[gd_resource type="Resource" script_class="VenueScenario" load_steps=17 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://src/core/access_policy.gd" id="1_xxhx3"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/npc_profile.gd" id="2_fiebn"]
|
||||||
|
[ext_resource type="Script" path="res://src/core/venue_scenario.gd" id="3_2dvuq"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_blmt0"]
|
||||||
|
script = ExtResource("1_xxhx3")
|
||||||
|
required_artifacts = {
|
||||||
|
1: true,
|
||||||
|
3: true,
|
||||||
|
4: true,
|
||||||
|
5: true
|
||||||
|
}
|
||||||
|
grant_if_any_required = true
|
||||||
|
minimum_age = 0
|
||||||
|
require_id = false
|
||||||
|
wand_screening_required = false
|
||||||
|
policy_summary = "Entry requires a wristband, credential, ticket, or guest-list confirmation."
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_oddac"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "tourist"
|
||||||
|
display_name = "Tourist"
|
||||||
|
npc_type = 0
|
||||||
|
temperament = 0.1
|
||||||
|
base_compliance = 0.9
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Oh sure, sorry, just taking a picture.", "Is there a better spot to stand?", "Understood, moving."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_byir2"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "confused_visitor"
|
||||||
|
display_name = "Confused Visitor"
|
||||||
|
npc_type = 3
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.85
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Wait, this isn't the entrance?", "Sorry about that, I'm a bit lost."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_3tt0b"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "delivery"
|
||||||
|
display_name = "Delivery Worker"
|
||||||
|
npc_type = 10
|
||||||
|
temperament = 0.25
|
||||||
|
base_compliance = 0.8
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["Where should I leave this?"])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_ihxib"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "street_performer"
|
||||||
|
display_name = "Street Performer"
|
||||||
|
npc_type = 1
|
||||||
|
temperament = 0.45
|
||||||
|
base_compliance = 0.55
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 55,
|
||||||
|
2: 15,
|
||||||
|
3: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_c57iy"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "influencer"
|
||||||
|
display_name = "Influencer"
|
||||||
|
npc_type = 2
|
||||||
|
temperament = 0.5
|
||||||
|
base_compliance = 0.5
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 40,
|
||||||
|
2: 10,
|
||||||
|
3: 30,
|
||||||
|
4: 20
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
4: 35,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 10,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 30,
|
||||||
|
8: 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["This is public sidewalk, you can't stop me from filming."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_bk410"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "intoxicated"
|
||||||
|
display_name = "Intoxicated Pedestrian"
|
||||||
|
npc_type = 4
|
||||||
|
temperament = 0.6
|
||||||
|
base_compliance = 0.4
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 20,
|
||||||
|
3: 50
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
3: 15,
|
||||||
|
4: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 1.8
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_jp52w"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "trespasser"
|
||||||
|
display_name = "Trespasser"
|
||||||
|
npc_type = 5
|
||||||
|
temperament = 0.55
|
||||||
|
base_compliance = 0.35
|
||||||
|
return_probability = 0.6
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 30,
|
||||||
|
2: 25,
|
||||||
|
3: 45
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 40,
|
||||||
|
2: 35,
|
||||||
|
6: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 35,
|
||||||
|
2: 25,
|
||||||
|
6: 15,
|
||||||
|
7: 10,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 70,
|
||||||
|
7: 30
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 50,
|
||||||
|
7: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["I'm just here to see a friend, come on.", "I'll be inside for ten seconds."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_5kplf"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "mental_crisis"
|
||||||
|
display_name = "Person in Crisis"
|
||||||
|
npc_type = 7
|
||||||
|
temperament = 0.7
|
||||||
|
base_compliance = 0.3
|
||||||
|
return_probability = 0.4
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 25,
|
||||||
|
2: 20,
|
||||||
|
3: 55
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 25,
|
||||||
|
2: 35,
|
||||||
|
3: 30,
|
||||||
|
5: 10
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 25,
|
||||||
|
1: 15,
|
||||||
|
2: 30,
|
||||||
|
5: 30
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 40,
|
||||||
|
5: 35,
|
||||||
|
7: 25
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 25,
|
||||||
|
5: 60,
|
||||||
|
7: 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 2.4
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_6bscs"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "violent_threat"
|
||||||
|
display_name = "Violent Threat"
|
||||||
|
npc_type = 6
|
||||||
|
temperament = 0.95
|
||||||
|
base_compliance = 0.05
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
2: 25,
|
||||||
|
3: 45,
|
||||||
|
5: 30
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
2: 30,
|
||||||
|
5: 45,
|
||||||
|
7: 25
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
1: 20,
|
||||||
|
5: 45,
|
||||||
|
7: 35
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 15,
|
||||||
|
5: 30,
|
||||||
|
7: 55
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_n4lxh"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "protester"
|
||||||
|
display_name = "Protester"
|
||||||
|
npc_type = 12
|
||||||
|
temperament = 0.5
|
||||||
|
base_compliance = 0.4
|
||||||
|
return_probability = 0.25
|
||||||
|
reactions_by_level = {
|
||||||
|
1: {
|
||||||
|
0: 25,
|
||||||
|
2: 35,
|
||||||
|
3: 25,
|
||||||
|
4: 15
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 15,
|
||||||
|
2: 40,
|
||||||
|
3: 15,
|
||||||
|
4: 25,
|
||||||
|
8: 5
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 10,
|
||||||
|
1: 25,
|
||||||
|
2: 25,
|
||||||
|
4: 25,
|
||||||
|
8: 15
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 60,
|
||||||
|
2: 20,
|
||||||
|
7: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 80,
|
||||||
|
2: 8,
|
||||||
|
3: 12
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 70,
|
||||||
|
1: 10,
|
||||||
|
2: 12,
|
||||||
|
4: 8
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
0: 55,
|
||||||
|
1: 15,
|
||||||
|
2: 20,
|
||||||
|
4: 10
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
1: 65,
|
||||||
|
2: 15,
|
||||||
|
7: 20
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
1: 30,
|
||||||
|
5: 40,
|
||||||
|
7: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String](["I have every right to be on public sidewalk.", "You can't touch me, this is a lawful protest."])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_6st6l"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "authorized_staff"
|
||||||
|
display_name = "Staff Member"
|
||||||
|
npc_type = 8
|
||||||
|
temperament = 0.15
|
||||||
|
base_compliance = 1.0
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_nf2oq"]
|
||||||
|
script = ExtResource("2_fiebn")
|
||||||
|
id = "authorized_guest"
|
||||||
|
display_name = "Authorized Guest"
|
||||||
|
npc_type = 9
|
||||||
|
temperament = 0.2
|
||||||
|
base_compliance = 0.95
|
||||||
|
return_probability = 0.0
|
||||||
|
reactions_by_level = {}
|
||||||
|
default_reactions = {
|
||||||
|
1: {
|
||||||
|
0: 92,
|
||||||
|
2: 8
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
0: 88,
|
||||||
|
2: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reaction_delay = 0.6
|
||||||
|
dialogue_lines = Array[String]([])
|
||||||
|
carried_items = Array[String]([])
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("3_2dvuq")
|
||||||
|
id = "tv_studio"
|
||||||
|
display_name = "Hollywood Blvd Late Night TV Studio"
|
||||||
|
description = "Crowd, celebrities, and staff around a late-night studio."
|
||||||
|
access_policy = SubResource("Resource_blmt0")
|
||||||
|
equipment = Array[String](["wristband_policy", "guest_list", "radio", "flashlight"])
|
||||||
|
locations = {
|
||||||
|
"entrance": Vector3(0, 0, 1),
|
||||||
|
"guard_post": Vector3(0, 0, -3),
|
||||||
|
"queue_area": Vector3(0, 0, 9),
|
||||||
|
"sidewalk": Vector3(0, 0, 9),
|
||||||
|
"stage_door": Vector3(6, 0, -2),
|
||||||
|
"steps_end": Vector3(0, 0, 3)
|
||||||
|
}
|
||||||
|
npc_profiles = Array[ExtResource("2_fiebn")]([SubResource("Resource_oddac"), SubResource("Resource_byir2"), SubResource("Resource_3tt0b"), SubResource("Resource_ihxib"), SubResource("Resource_c57iy"), SubResource("Resource_bk410"), SubResource("Resource_jp52w"), SubResource("Resource_5kplf"), SubResource("Resource_6bscs"), SubResource("Resource_n4lxh"), SubResource("Resource_6st6l"), SubResource("Resource_nf2oq")])
|
||||||
|
event_pool = Array[Dictionary]([{
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "audience_ticket",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_ticket": true
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.35,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "protest_outside",
|
||||||
|
"intent": "protest",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 12,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.18,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "celebrity_arrival",
|
||||||
|
"intent": "arrive",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"has_guest_list": true
|
||||||
|
},
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.2,
|
||||||
|
"time_range_s": Vector2(300, 700)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "crew_shift",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 8,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.3,
|
||||||
|
"time_range_s": Vector2(0, 700)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "tourist_photo",
|
||||||
|
"intent": "photo",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 0,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.35,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "confused_enter",
|
||||||
|
"intent": "enter",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 3,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.25,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "influencer_film",
|
||||||
|
"intent": "film",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.28,
|
||||||
|
"time_range_s": Vector2(0, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "trespass_blend",
|
||||||
|
"intent": "blend_in",
|
||||||
|
"kind": "routine",
|
||||||
|
"npc_type": 5,
|
||||||
|
"phase": 0,
|
||||||
|
"probability": 0.15,
|
||||||
|
"time_range_s": Vector2(300, 900)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "studio_counterfeit",
|
||||||
|
"intent": "counterfeit_credential",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 5,
|
||||||
|
"override_access": {
|
||||||
|
"credential_status": 2,
|
||||||
|
"wristband_status": 2
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.55,
|
||||||
|
"time_range_s": Vector2(620, 780)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.film_boundary",
|
||||||
|
"id": "studio_film_boundary",
|
||||||
|
"intent": "film_boundary",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 1,
|
||||||
|
"probability": 0.5,
|
||||||
|
"time_range_s": Vector2(330, 560)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"hint": "hint.film_boundary_repeat",
|
||||||
|
"id": "studio_film_boundary_repeat",
|
||||||
|
"intent": "film_boundary",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 2,
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.45,
|
||||||
|
"time_range_s": Vector2(640, 830)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"extra_spawns": [{
|
||||||
|
"intent": "photo",
|
||||||
|
"npc_type": 0
|
||||||
|
}, {
|
||||||
|
"intent": "photo",
|
||||||
|
"npc_type": 0
|
||||||
|
}, {
|
||||||
|
"intent": "film",
|
||||||
|
"npc_type": 2
|
||||||
|
}],
|
||||||
|
"hint": "hint.celebrity_arrival",
|
||||||
|
"id": "studio_celebrity_arrival",
|
||||||
|
"intent": "arrive",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 9,
|
||||||
|
"override_access": {
|
||||||
|
"guest_list_status": 0,
|
||||||
|
"has_guest_list": true
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.5,
|
||||||
|
"time_range_s": Vector2(620, 760)
|
||||||
|
}, {
|
||||||
|
"base_weight": 1.0,
|
||||||
|
"id": "studio_follow_through",
|
||||||
|
"intent": "follow_authorized",
|
||||||
|
"kind": "scripted",
|
||||||
|
"npc_type": 5,
|
||||||
|
"override_access": {
|
||||||
|
"has_credential": false,
|
||||||
|
"has_guest_list": false,
|
||||||
|
"has_ticket": false,
|
||||||
|
"has_wristband": false
|
||||||
|
},
|
||||||
|
"phase": 2,
|
||||||
|
"probability": 0.45,
|
||||||
|
"time_range_s": Vector2(700, 860)
|
||||||
|
}])
|
||||||
|
shift_phases = {
|
||||||
|
0: {
|
||||||
|
"start_s": 0.0
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
"start_s": 300.0
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
"start_s": 600.0
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
"start_s": 700.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shift_duration_s = 900.0
|
||||||
|
policy_summary = ""
|
||||||
|
door_staff_lines = {}
|
||||||
+1
-1
@@ -8,7 +8,7 @@ config_version=5
|
|||||||
|
|
||||||
config/name="Step Line"
|
config/name="Step Line"
|
||||||
config/description="Workplace security simulation about observation, access verification, and proportionate escalation."
|
config/description="Workplace security simulation about observation, access verification, and proportionate escalation."
|
||||||
config/version="0.2.15"
|
config/version="0.2.16"
|
||||||
run/main_scene="res://scenes/main.tscn"
|
run/main_scene="res://scenes/main.tscn"
|
||||||
config/features=PackedStringArray("4.3", "GL Compatibility")
|
config/features=PackedStringArray("4.3", "GL Compatibility")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
extends RefCounted
|
||||||
|
## Venues can be authored as `.tres` resources: the built-in factories export and
|
||||||
|
## round-trip without losing their data (optional editor workflow, TODO tooling).
|
||||||
|
|
||||||
|
const Factory := preload("res://src/content/scenario_factory.gd")
|
||||||
|
const Rules := preload("res://src/core/rules.gd")
|
||||||
|
const VenueScenario := preload("res://src/core/venue_scenario.gd")
|
||||||
|
|
||||||
|
func test_venues_round_trip_through_tres() -> bool:
|
||||||
|
var scenarios := [Factory.hollywood_building(), Factory.joke_shop(), Factory.tv_studio()]
|
||||||
|
for scenario in scenarios:
|
||||||
|
var path := "user://%s_roundtrip.tres" % scenario.id
|
||||||
|
var err: int = ResourceSaver.save(scenario, path)
|
||||||
|
if err != OK:
|
||||||
|
return TestUtils.fail("could not save %s (error %d)" % [scenario.id, err])
|
||||||
|
var loaded = ResourceLoader.load(path)
|
||||||
|
if not (loaded is VenueScenario):
|
||||||
|
return TestUtils.fail("%s did not load back as VenueScenario" % scenario.id)
|
||||||
|
var a := TestUtils.check_eq(loaded.id, scenario.id, "%s id survives" % scenario.id)
|
||||||
|
var b := TestUtils.check_eq(loaded.display_name, scenario.display_name, "%s name survives" % scenario.id)
|
||||||
|
var c := TestUtils.check_eq(loaded.event_pool.size(), scenario.event_pool.size(), "%s event pool survives" % scenario.id)
|
||||||
|
var d := TestUtils.check_eq(loaded.npc_profiles.size(), scenario.npc_profiles.size(), "%s profiles survive" % scenario.id)
|
||||||
|
var e := TestUtils.check_eq(loaded.waypoint_for_intent("sit"), scenario.waypoint_for_intent("sit"), "%s locations survive" % scenario.id)
|
||||||
|
if not (a and b and c and d and e):
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
func test_exported_tres_files_match_the_factory() -> bool:
|
||||||
|
## content/venues/*.tres is generated by tools/export_venues.gd; make sure it is
|
||||||
|
## present and not stale relative to the factory.
|
||||||
|
for scenario in [Factory.hollywood_building(), Factory.joke_shop(), Factory.tv_studio()]:
|
||||||
|
var path := "res://content/venues/%s.tres" % scenario.id
|
||||||
|
if not FileAccess.file_exists(path):
|
||||||
|
return TestUtils.fail("missing exported venue %s — run tools/export_venues.gd" % path)
|
||||||
|
var loaded = ResourceLoader.load(path)
|
||||||
|
if not (loaded is VenueScenario):
|
||||||
|
return TestUtils.fail("%s is not a VenueScenario" % path)
|
||||||
|
if loaded.event_pool.size() != scenario.event_pool.size():
|
||||||
|
return TestUtils.fail("%s is stale (events %d vs %d) — re-run tools/export_venues.gd" % [
|
||||||
|
path, loaded.event_pool.size(), scenario.event_pool.size()])
|
||||||
|
if not TestUtils.check_eq(loaded.shift_duration_s, scenario.shift_duration_s, "%s duration matches" % scenario.id):
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
func test_exported_policy_and_events_intact() -> bool:
|
||||||
|
var loaded = ResourceLoader.load("res://content/venues/joke_shop.tres")
|
||||||
|
var a := TestUtils.check(loaded != null and loaded.access_policy != null, "access policy survives export")
|
||||||
|
var b := TestUtils.check_eq(loaded.access_policy.require_id, true, "joke shop still requires ID")
|
||||||
|
var c := TestUtils.check_eq(loaded.access_policy.wand_screening_required, true, "wand screening still required")
|
||||||
|
var d := TestUtils.check_eq(loaded.equipment.has("wand"), true, "equipment survives export")
|
||||||
|
var ids: Array = []
|
||||||
|
for event in loaded.event_pool:
|
||||||
|
ids.append(str((event as Dictionary).get("id", "")))
|
||||||
|
var e := TestUtils.check(ids.has("joke_refuses_wand"), "authored events survive export")
|
||||||
|
var f := TestUtils.check(loaded.door_staff_lines.has("refusal"), "door-staff lines survive export")
|
||||||
|
return a and b and c and d and e and f
|
||||||
|
|
||||||
|
func test_exported_phases_survive() -> bool:
|
||||||
|
var loaded = ResourceLoader.load("res://content/venues/hollywood_building.tres")
|
||||||
|
var a := TestUtils.check(loaded.shift_phases.has(Rules.Phase.LATE), "late phase survives export")
|
||||||
|
var b := TestUtils.check_eq(int(loaded.shift_phases[Rules.Phase.LATE].get("start_s", -1)), 700, "late start preserved")
|
||||||
|
return a and b
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
extends SceneTree
|
||||||
|
## Export the built-in scenarios to `.tres` resources for editor authoring.
|
||||||
|
##
|
||||||
|
## Usage:
|
||||||
|
## godot --headless --path . --script res://tools/export_venues.gd
|
||||||
|
##
|
||||||
|
## Writes content/venues/<id>.tres. These mirror ScenarioFactory exactly; the
|
||||||
|
## factory stays the source of truth for the prototype, and the exported files are
|
||||||
|
## what an editor-based workflow would edit and ship.
|
||||||
|
|
||||||
|
const Factory := preload("res://src/content/scenario_factory.gd")
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
var out_dir := "res://content/venues"
|
||||||
|
DirAccess.make_dir_recursive_absolute(out_dir)
|
||||||
|
var scenarios := [Factory.hollywood_building(), Factory.joke_shop(), Factory.tv_studio()]
|
||||||
|
var failures := 0
|
||||||
|
for scenario in scenarios:
|
||||||
|
var path := "%s/%s.tres" % [out_dir, scenario.id]
|
||||||
|
var err: int = ResourceSaver.save(scenario, path)
|
||||||
|
if err == OK:
|
||||||
|
print("wrote %s" % path)
|
||||||
|
else:
|
||||||
|
printerr("failed to write %s (error %d)" % [path, err])
|
||||||
|
failures += 1
|
||||||
|
quit(0 if failures == 0 else 1)
|
||||||
Reference in new issue
Block a user