refactor(simulation): move content/ under server/ (#669)
Content files (RON templates, YAML campaigns, gauntlet data) are consumed exclusively by the server. Colocate them at server/content/ and update all path references in Rust source, tooling scripts, and schema files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Main Campaign metadata (D-003)
|
||||
display_name: "The Settled Reach"
|
||||
description: >
|
||||
The core campaign. Set in a universe of wormhole-connected star systems,
|
||||
neural lattice technology, and faction-driven politics. Occlusion-based
|
||||
detective game with combat elements.
|
||||
version: "0.1.0"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Sova Transit District metadata (D-036)
|
||||
# Identity derived from directory path: campaigns/main/systems/van-maanens-star/stations/sova/districts/transit/
|
||||
# canonical_id: van-maanens-star.sova.transit (derived at load time by ContentValidator)
|
||||
display_name: "Sova Transit District"
|
||||
description: >
|
||||
A 40-year-old prefab-modular-retrofitted freight logistics hub on Station Sova.
|
||||
Five locations: The Terminal (logistics hub), The Last Shift (bar),
|
||||
maintenance corridors, gate corridor (ground level), and the observation
|
||||
gallery (Commission-only, z=2 above the gate concourse).
|
||||
locations:
|
||||
- "the-terminal"
|
||||
- "the-last-shift"
|
||||
- "maintenance-corridors"
|
||||
- "gate-ground"
|
||||
- "gate-gallery"
|
||||
npc_count: 17
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
# Environmental Text: Maintenance Corridors
|
||||
# Signs, terminals, documents, readable objects.
|
||||
# Investigation-heavy. This is where the operational evidence lives.
|
||||
# Ticket: #262 | Sprint: 12
|
||||
|
||||
location: maintenance-corridors
|
||||
items:
|
||||
|
||||
# ==========================================
|
||||
# ACCESS RESTRICTION SIGN — corridor entrance
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_001
|
||||
type: sign
|
||||
label: "Restricted Access Notice"
|
||||
visibility: always
|
||||
text: >
|
||||
RESTRICTED ACCESS — MAINTENANCE PERSONNEL ONLY
|
||||
|
||||
B-CORRIDOR SECTOR — AUTHORIZED ENTRY REQUIRED
|
||||
|
||||
Unauthorized entry subject to Sova Station Authority review.
|
||||
Commission Order 7741-B applies.
|
||||
|
||||
Report unauthorized personnel to: Station Security (Lattice: SovaStation/Security)
|
||||
tags: [atmospheric, b7-corridor]
|
||||
|
||||
# ==========================================
|
||||
# MERIDIAN COVERAGE SCHEDULE — posted on corridor wall
|
||||
# Most investigation-relevant item in the game.
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_002
|
||||
type: document
|
||||
label: "Meridian Scan Schedule"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
MERIDIAN SURVEILLANCE COVERAGE — SOVA TRANSIT DISTRICT
|
||||
MAINTENANCE CORRIDOR SECTOR — CURRENT CYCLE
|
||||
|
||||
Active coverage windows (full corridor monitoring):
|
||||
06:00-07:30 | 10:00-11:00 | 14:00-15:30 | 22:00-23:00
|
||||
|
||||
Reduced coverage (perimeter only):
|
||||
07:30-10:00 | 11:00-14:00 | 15:30-22:00 | 23:00-06:00
|
||||
|
||||
NOTE: Span gate maintenance cycles create signal interference.
|
||||
Coverage gaps during gate calibration: approx 14:20-14:45.
|
||||
|
||||
Posted by: Sova Meridian Network Maintenance Division
|
||||
tags: [investigation-relevant, meridian, surveillance-gap, critical]
|
||||
knowledge_grant:
|
||||
fact_id: "location.surveillance_gaps"
|
||||
confidence: knows_details
|
||||
|
||||
# ==========================================
|
||||
# MAINTENANCE LOG SHEET — handwritten clipboard at corridor junction
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_003
|
||||
type: document
|
||||
label: "Maintenance Log (B-7 Corridor)"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
B-7 CORRIDOR — MAINTENANCE LOG
|
||||
Logged by: Pael Varren
|
||||
|
||||
[Month 11, Day 4]
|
||||
Air scrubber pressure variance. Reset. Source unclear.
|
||||
|
||||
[Month 11, Day 12]
|
||||
Seal failure B-7 junction. Third occurrence. Replaced again.
|
||||
Noted: floor wear pattern inconsistent with posted traffic.
|
||||
Possible deferred maintenance backlog creating additional load.
|
||||
|
||||
[Month 11, Day 22]
|
||||
Recurring seal failure. Escalated to deferred queue.
|
||||
NOTE: Pressure sensors reading elevated load during 14:00-15:00
|
||||
window. Does not match shift transition foot traffic.
|
||||
Filed maintenance anomaly report — awaiting management review.
|
||||
tags: [investigation-relevant, b7-corridor, pael, critical]
|
||||
knowledge_grant:
|
||||
fact_id: "location.corridor_b7_restricted"
|
||||
confidence: knows_of
|
||||
|
||||
# ==========================================
|
||||
# ROUTING DIAGRAM — cargo flow map posted at junction
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_004
|
||||
type: sign
|
||||
label: "Cargo Routing Diagram"
|
||||
visibility: always
|
||||
text: >
|
||||
SOVA TRANSIT — MAINTENANCE CORRIDOR ROUTING
|
||||
[Technical diagram showing freight flow paths]
|
||||
|
||||
Standard freight path: Bay > Terminal > Surface Storage
|
||||
Emergency reroute: Bay > B-corridor > Temporary Hold (C-4)
|
||||
Maintenance access: B-corridor > Service Lift > Environmental Systems
|
||||
|
||||
[Someone has drawn a thin pencil line from Bay 4 to C-4 to
|
||||
the service lift, bypassing the standard terminal route]
|
||||
tags: [investigation-relevant, b7-corridor, smuggling-route]
|
||||
knowledge_grant:
|
||||
fact_id: "location.smuggling_route"
|
||||
confidence: suspects
|
||||
|
||||
# ==========================================
|
||||
# ENVIRONMENTAL WARNING — recycled air quality
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_005
|
||||
type: sign
|
||||
label: "Environmental Quality Notice"
|
||||
visibility: always
|
||||
text: >
|
||||
CORRIDOR ENVIRONMENTAL STATUS — CURRENT READINGS
|
||||
|
||||
CO2: 0.12% [ACCEPTABLE — upper threshold 0.15%]
|
||||
Particulate: ELEVATED — see maintenance queue
|
||||
Temperature: 19.4°C [nominal]
|
||||
Recycled air freshness: 73% [minimum recommended: 80%]
|
||||
|
||||
If recycled air quality falls below 70%, report to maintenance.
|
||||
Current status: Monitor.
|
||||
tags: [atmospheric, environmental]
|
||||
|
||||
# ==========================================
|
||||
# TEMPORARY STORAGE MANIFEST SHEET
|
||||
# At the C-4 temporary hold point
|
||||
# ==========================================
|
||||
|
||||
- id: env_corridor_006
|
||||
type: document
|
||||
label: "Temporary Storage Log (C-4)"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
C-4 TEMPORARY HOLD — CONTENTS LOG
|
||||
[Printed manifest, partial, two pages visible]
|
||||
|
||||
Container 4466 — Cleared — removed Month 11 Day 1
|
||||
Container 4467 — Cleared — removed Month 11 Day 3
|
||||
Container 4468 — Cleared — removed Month 11 Day 3
|
||||
Container 4469 — [NO ENTRY]
|
||||
Container 4470 — Cleared — removed Month 11 Day 8
|
||||
Container 4471 — [PENDING — entered Month 11 Day 15, no removal logged]
|
||||
Container 4472 — Cleared — removed Month 11 Day 18
|
||||
|
||||
[Gap in numbering between 4469-4470 has no explanation column]
|
||||
tags: [container-4471, critical]
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
# Environmental Text: The Last Shift (Bar)
|
||||
# Signs, terminals, documents, readable objects.
|
||||
# Atmosphere-heavy. Some investigation relevance through social texture.
|
||||
# Ticket: #262 | Sprint: 12
|
||||
|
||||
location: the-last-shift
|
||||
items:
|
||||
|
||||
# ==========================================
|
||||
# DRINK MENU — handwritten, scratched-out prices
|
||||
# Above the bar. Visible to anyone.
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_001
|
||||
type: sign
|
||||
label: "Drink Menu"
|
||||
visibility: always
|
||||
text: >
|
||||
THE LAST SHIFT
|
||||
(Lera's)
|
||||
|
||||
Van Maanen's Star grain spirit — 4c
|
||||
Pale station brew — 3c ~~2c~~
|
||||
Dark station brew — 3c
|
||||
Import (off-station) — 8c
|
||||
Sova standard water — 1c
|
||||
Meal (hot, whatever's running) — 6c
|
||||
|
||||
No tabs. No Commission business at the bar.
|
||||
House rules posted.
|
||||
dual_lens:
|
||||
smuggler: "Import's doubled since last year. Everything's tighter."
|
||||
detective: >
|
||||
"No Commission business at the bar." That's Lera's personal policy.
|
||||
She'll have opinions about why.
|
||||
tags: [atmospheric]
|
||||
|
||||
# ==========================================
|
||||
# LERA'S RULES — above the bar
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_002
|
||||
type: sign
|
||||
label: "House Rules"
|
||||
visibility: always
|
||||
text: >
|
||||
LERA'S — HOUSE RULES
|
||||
|
||||
1. Pay before you're too drunk to remember.
|
||||
2. Arguments outside. Always.
|
||||
3. What's said at the bar stays at the bar.
|
||||
4. No job talk after 20:00.
|
||||
5. Torek Lintar runs his own tab. Don't ask.
|
||||
dual_lens:
|
||||
smuggler: "Rule 5. Lera's been running Torek's tab for years. Old arrangement."
|
||||
detective: >
|
||||
Rule 5 is specific. Named. Torek Lintar has a standing arrangement
|
||||
here. That's the third time his name has come up without context.
|
||||
tags: [investigation-relevant, torek-lintar, lera]
|
||||
knowledge_grant:
|
||||
fact_id: "relationship.torek_bar_arrangement"
|
||||
confidence: suspects
|
||||
|
||||
# ==========================================
|
||||
# NOTICE BOARD — general community notices
|
||||
# Examine to read individual notices.
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_003
|
||||
type: readable_object
|
||||
label: "Notice Board"
|
||||
visibility: always
|
||||
text: >
|
||||
[NOTICE BOARD — various papers pinned over each other]
|
||||
|
||||
MISSING: Vessels Tamm, last seen Sova Transit District, Month 11.
|
||||
Contact: Lattice Commission Sova liaison or speak to Lera.
|
||||
|
||||
CARGO WORKERS' ASSOCIATION — Monthly meeting, back room, Month 12,
|
||||
evening shift. All Sova Transit workers welcome. Bring your own.
|
||||
|
||||
ROOM AVAILABLE: Residential pod B-3, lower level. Quiet. Short-term.
|
||||
Speak to Harek at the bar.
|
||||
|
||||
[handwritten, pinned over the others:]
|
||||
ANYONE KNOW WHO LEFT A TRANSIT BAG IN THE STORAGE ROOM?
|
||||
IT'S BEEN THREE WEEKS.
|
||||
dual_lens:
|
||||
smuggler: >
|
||||
Vessels Tamm. Missing person notice. That name's from the Tamm family —
|
||||
Naia's connection? Or coincidence. Probably coincidence.
|
||||
detective: >
|
||||
Missing persons notice. Vessels Tamm. Sova Transit District. Cross-reference
|
||||
Tamm with case file — possible Naia Tamm relation. Flag for follow-up.
|
||||
tags: [investigation-relevant, naia-tamm-adjacent, harek]
|
||||
knowledge_grant:
|
||||
fact_id: "relationship.tamm_family"
|
||||
confidence: suspects
|
||||
|
||||
# ==========================================
|
||||
# GRAFFITI — bathroom wall
|
||||
# Examine to read.
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_004
|
||||
type: readable_object
|
||||
label: "Graffiti (Bathroom Wall)"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
[scratched into the wall surface over several years]
|
||||
|
||||
Voss doesn't check bay 4. Never has. Never will.
|
||||
|
||||
The Commission sees what it wants to see.
|
||||
|
||||
Lera knows.
|
||||
|
||||
[someone added in different handwriting:]
|
||||
Lera doesn't know anything. She just sells drinks.
|
||||
|
||||
[original hand, below:]
|
||||
Keep telling yourself that.
|
||||
dual_lens:
|
||||
smuggler: "Graffiti's been here since before I started. The Voss line is right, though."
|
||||
detective: >
|
||||
"Lera knows." Someone believes the bar owner has information.
|
||||
Someone else pushes back. The debate is years old.
|
||||
tags: [atmospheric, voss, lera, investigation-adjacent]
|
||||
|
||||
# ==========================================
|
||||
# TONIGHT'S SPECIALS — chalkboard near the kitchen
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_005
|
||||
type: sign
|
||||
label: "Tonight's Specials"
|
||||
visibility: always
|
||||
text: >
|
||||
TONIGHT:
|
||||
Hot grain porridge (Velen style) — 5c
|
||||
Station fish fry — 7c
|
||||
|
||||
Ask Lera what Velen style means.
|
||||
She'll tell you about it for twenty minutes.
|
||||
dual_lens:
|
||||
smuggler: "Lera's from Velen originally. She makes everyone ask."
|
||||
detective: "Lera Sessik. Originally from Velen surface. Personal detail noted."
|
||||
tags: [atmospheric, lera-backstory]
|
||||
|
||||
# ==========================================
|
||||
# LATTICE-FREE ZONE SIGN — near the entrance
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_006
|
||||
type: sign
|
||||
label: "Lattice-Free Zone Notice"
|
||||
visibility: always
|
||||
text: >
|
||||
VOLUNTARY LATTICE-FREE ZONE
|
||||
|
||||
By Lera's request: please disable active lattice scanning
|
||||
and recording functions while at the bar.
|
||||
|
||||
Conversation here is conversation. Not data.
|
||||
|
||||
This is not a Commission-recognized designation.
|
||||
Lera doesn't care.
|
||||
dual_lens:
|
||||
smuggler: >
|
||||
Everyone honors this. It's one of the reasons the bar works for
|
||||
what it works for.
|
||||
detective: >
|
||||
Not legally enforceable. But anyone who honors it voluntarily is
|
||||
actively choosing to reduce their surveillance footprint here.
|
||||
Who honors it and who doesn't is worth noting.
|
||||
tags: [investigation-relevant, lera, lattice-free]
|
||||
|
||||
# ==========================================
|
||||
# CARGO WORKERS' ASSOCIATION FLYER — pinned to the bar
|
||||
# ==========================================
|
||||
|
||||
- id: env_bar_007
|
||||
type: document
|
||||
label: "Association Meeting Flyer"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
SOVA TRANSIT CARGO WORKERS' ASSOCIATION
|
||||
Monthly Meeting
|
||||
|
||||
Topic: Shift change protocols — the thirty-minute handoff window
|
||||
and why it isn't long enough.
|
||||
|
||||
Also on agenda: Updated manifest compliance requirements from
|
||||
the Commission (new reporting window: 6 hours, down from 8).
|
||||
|
||||
Back room. Evening shift. All welcome.
|
||||
Bring your concerns, bring a drink.
|
||||
dual_lens:
|
||||
smuggler: >
|
||||
Commission shortened the reporting window to six hours. That's
|
||||
tighter than the eight-hour buffer. Someone noticed the gap.
|
||||
detective: >
|
||||
Agenda item: thirty-minute handoff window. Workers complaining
|
||||
about it publicly means the window is known beyond the ring.
|
||||
Or is being discussed for reasons that are less innocent.
|
||||
tags: [investigation-relevant, shift-transition, commission]
|
||||
knowledge_grant:
|
||||
fact_id: "world.shift_schedule"
|
||||
confidence: knows_of
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
# Environmental Text: The Terminal (Logistics Hub)
|
||||
# Signs, terminals, documents, readable objects.
|
||||
# Both characters can examine these. Interpretation differs.
|
||||
# Ticket: #262 | Sprint: 12
|
||||
|
||||
location: the-terminal
|
||||
items:
|
||||
|
||||
# ==========================================
|
||||
# SHIFT BOARD — posted schedule
|
||||
# Always visible. High-traffic reference point.
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_001
|
||||
type: sign
|
||||
label: "Shift Schedule Board"
|
||||
visibility: always
|
||||
text: >
|
||||
SOVA TRANSIT DISTRICT — LOGISTICS HUB
|
||||
CURRENT ROTATION — CYCLE 47
|
||||
|
||||
BAY 1-4: Day Shift 06:00-14:00 (Voss, K. — Supervisor)
|
||||
BAY 5-8: Day Shift 06:00-14:00 (Maret, K. — Scheduler)
|
||||
TRANSITION: 13:45-14:15 (30-min handoff window)
|
||||
Night Shift 22:00-06:00 — ROSTER PENDING
|
||||
[Bay 6: MAINTENANCE HOLD — see Pael Varren]
|
||||
|
||||
# ==========================================
|
||||
# MANIFEST TRACKING TERMINAL — cargo status display
|
||||
# Active terminal. Can be read from appropriate distance in investigation mode.
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_002
|
||||
type: terminal
|
||||
label: "Cargo Tracking Terminal"
|
||||
visibility: always
|
||||
text: >
|
||||
ACTIVE MANIFEST QUEUE — SOVA LOGISTICS
|
||||
Last updated: 06:23
|
||||
|
||||
Container 4471 — [PENDING REVIEW] — Bay 4
|
||||
Container 4472 — CLEARED — Bay 2
|
||||
Container 4473 — CLEARED — Bay 2
|
||||
Container 4474 — [WEIGHT DISCREPANCY — 47kg] — Bay 4
|
||||
Container 4475 — CLEARED — Bay 3
|
||||
Container 4476 — PROCESSING — Bay 7
|
||||
[8 additional items — scroll to continue]
|
||||
tags: [manifest-discrepancy]
|
||||
|
||||
# ==========================================
|
||||
# MAINTENANCE REQUEST CLIPBOARD
|
||||
# Physical clipboard near loading arm 3. Examine to read.
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_003
|
||||
type: document
|
||||
label: "Maintenance Request Log"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
DEFERRED MAINTENANCE — CURRENT QUEUE
|
||||
(Submitted: Pael Varren, Maintenance)
|
||||
|
||||
Loading arm 3 — Grinding during lift cycle — DEFERRED x4
|
||||
Bay 6 environmental seal — Recurring failure — DEFERRED x3 (HOLD)
|
||||
B-7 corridor air scrubber — Pressure variance — DEFERRED x1
|
||||
Gate calibration bay 5-6 — Off-spec hum — DEFERRED x2
|
||||
Recycling unit C-4 — Thermal spike — DEFERRED x1
|
||||
|
||||
Note: B-7 corridor showing irregular pressure load on sensor.
|
||||
Traffic heavier than logged shift count explains. Investigating.
|
||||
— P. Varren
|
||||
tags: [investigation-relevant, b7-corridor]
|
||||
knowledge_grant:
|
||||
fact_id: "location.corridor_b7_restricted"
|
||||
confidence: suspects
|
||||
|
||||
# ==========================================
|
||||
# COMMISSION COMPLIANCE NOTICE
|
||||
# Standard posting. Required by regulation. Two years old.
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_004
|
||||
type: sign
|
||||
label: "Commission Compliance Notice"
|
||||
visibility: always
|
||||
text: >
|
||||
LATTICE COMMISSION — FREIGHT OPERATIONS COMPLIANCE
|
||||
|
||||
All manifest discrepancies exceeding 15kg must be reported
|
||||
within 8 hours of discovery.
|
||||
|
||||
All cargo flagged PENDING REVIEW must be cleared by assigned
|
||||
inspector before shift transition.
|
||||
|
||||
Unauthorized access to restricted maintenance corridors
|
||||
will result in immediate suspension pending review.
|
||||
|
||||
Posted: Lattice Commission, Sova Station Authority
|
||||
Reference: Commission Order 7741-B
|
||||
tags: [atmospheric, institutional]
|
||||
|
||||
# ==========================================
|
||||
# WORK ORDER NOTE — handwritten, coffee-stained
|
||||
# Personal communication between dock workers. Examine to read.
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_005
|
||||
type: document
|
||||
label: "Handwritten Note (Coffee-Stained)"
|
||||
visibility: examine_only
|
||||
text: >
|
||||
Kael —
|
||||
|
||||
Bay 4 queue backed up again. Maret says it's a weight issue
|
||||
but she's not flagging it officially. If it's still there when
|
||||
you come in, just route it through 7 like last time. Voss
|
||||
won't check.
|
||||
|
||||
— D.
|
||||
tags: [maret, drin, voss]
|
||||
|
||||
# ==========================================
|
||||
# SAFETY PROCEDURES PLACARD — standard, faded
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_006
|
||||
type: sign
|
||||
label: "Emergency Procedures"
|
||||
visibility: always
|
||||
text: >
|
||||
IN CASE OF EMERGENCY:
|
||||
|
||||
1. Activate hull breach alarm (red panel, every 20m)
|
||||
2. Proceed to nearest designated shelter (yellow markers)
|
||||
3. Do not use freight lifts during emergency protocols
|
||||
4. Await station authority clearance before resuming operations
|
||||
|
||||
Last safety review: Month 4, Year 312 (Van Maanen's Star calendar)
|
||||
tags: [atmospheric]
|
||||
|
||||
# ==========================================
|
||||
# BAY STATUS BOARD — current bay availability
|
||||
# ==========================================
|
||||
|
||||
- id: env_terminal_007
|
||||
type: sign
|
||||
label: "Bay Status Board"
|
||||
visibility: always
|
||||
text: >
|
||||
BAY STATUS — CURRENT CYCLE
|
||||
|
||||
BAY 1: ACTIVE
|
||||
BAY 2: ACTIVE
|
||||
BAY 3: ACTIVE
|
||||
BAY 4: ACTIVE — PENDING REVIEW (2 items)
|
||||
BAY 5: ACTIVE
|
||||
BAY 6: MAINTENANCE HOLD — Est. clear: next cycle
|
||||
BAY 7: ACTIVE
|
||||
BAY 8: SCHEDULED DOWNTIME 13:00-15:00
|
||||
tags: [investigation-relevant, operational]
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Location: Gate Corridor — Observation Gallery
|
||||
# Source: D-093 gate cluster zone spec
|
||||
# Zone palette: surface #b8bec4 / fog tint #0a1222
|
||||
# z=2 (above gate concourse)
|
||||
|
||||
canonical_id: "van-maanens-star.sova.transit.location.gate-gallery"
|
||||
display_name: "Observation Gallery"
|
||||
description: >
|
||||
Commission-only observation gallery overlooking the gate concourse from
|
||||
z=2. The gallery rail is a transparent low wall — occupants can see
|
||||
down to the concourse below, but upward LOS from z=1 is blocked except
|
||||
at the staircase. Access restricted to Commission personnel.
|
||||
|
||||
tile_bounds:
|
||||
x_min: 0
|
||||
y_min: 0
|
||||
x_max: 31
|
||||
y_max: 9
|
||||
z: 2
|
||||
|
||||
# Legend:
|
||||
# R = Restricted (Commission-only access)
|
||||
# W = Wall (solid)
|
||||
#
|
||||
# Layout (32 wide x 10 tall):
|
||||
# Row 0: North wall with staircase entrance (col 1 = R, access point)
|
||||
# Rows 1-8: Gallery floor (R tiles — restricted access)
|
||||
# Row 9: South wall (gallery rail — transparent low wall, modeled as W;
|
||||
# cross-z LOS is handled server-side)
|
||||
|
||||
tiles:
|
||||
- "WRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRW"
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
|
||||
sightlines:
|
||||
open: true
|
||||
notes: >
|
||||
Gallery rail is transparent — full downward LOS to gate concourse (z=1).
|
||||
Upward LOS from concourse to gallery is blocked except at staircase.
|
||||
Cross-z LOS behavior handled by server shadowcasting system.
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# Location: Gate Corridor — Ground Level
|
||||
# Source: D-093 gate cluster zone spec
|
||||
# Zone palette: surface #b8bec4 / fog tint #0a1222
|
||||
|
||||
canonical_id: "van-maanens-star.sova.transit.location.gate-ground"
|
||||
display_name: "Gate Corridor"
|
||||
description: >
|
||||
The gate cluster handles all traffic between Station Sova and the horizon
|
||||
gate network. From north to south: the restricted aperture chamber where
|
||||
span gate transits occur, freight staging and passenger arrival halls,
|
||||
customs lanes (freight and pedestrian), and the wide gate concourse
|
||||
where arrivals disperse into the transit district.
|
||||
|
||||
tile_bounds:
|
||||
x_min: 0
|
||||
y_min: 0
|
||||
x_max: 39
|
||||
y_max: 33
|
||||
z: 1
|
||||
|
||||
# Legend:
|
||||
# F = Floor (walkable) W = Wall (solid, blocks LOS)
|
||||
# R = Restricted (access-tier gated)
|
||||
#
|
||||
# Layout (40 wide x 34 tall):
|
||||
# Row 0: North wall
|
||||
# Rows 1-4: Aperture chamber (centered 8 wide, R tiles — restricted)
|
||||
# Row 5: Wall separator
|
||||
# Rows 6-13: Freight staging (west, 24 tiles) | passenger arrival (east, 13 tiles)
|
||||
# Row 14: Wall with door gaps (cols 12, 32)
|
||||
# Rows 15-24: Freight customs (west, 20 tiles, 3 lanes at 6vt + wall stubs)
|
||||
# | corridor (6 tiles) | ped customs (east, 10 tiles, 3 lanes)
|
||||
# Row 25: Wall with wide opening to concourse (cols 5-34 open)
|
||||
# Rows 26-33: Gate concourse (full 38-tile width, 8 rows — public open space)
|
||||
#
|
||||
# D-093 zone dimensions: freight customs 20x10, ped customs 12x10 (10 rows each)
|
||||
# Corridor width: gate concourse 8vt (D-093)
|
||||
|
||||
tiles:
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
- "WWWWWWWWWWWWWWWWRRRRRRRRWWWWWWWWWWWWWWWW"
|
||||
- "WWWWWWWWWWWWWWWWRRRRRRRRWWWWWWWWWWWWWWWW"
|
||||
- "WWWWWWWWWWWWWWWWRRRRRRRRWWWWWWWWWWWWWWWW"
|
||||
- "WWWWWWWWWWWWWWWWRRRRRRRRWWWWWWWWWWWWWWWW"
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFFFW"
|
||||
- "WWWWWWWWWWWWFWWWWWWWWWWWWWWWWWWWFWWWWWWW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WFFFFFFWFFFFFFWFFFFFFWFFFFFFWFFWFFFWFFFW"
|
||||
- "WWWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWWWWW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
|
||||
sightlines:
|
||||
open: false
|
||||
notes: >
|
||||
Gate concourse (rows 26-33) is wide open with full sightlines.
|
||||
Customs lanes have wall stubs creating partial occlusion between
|
||||
lanes. Freight customs (west, 20 tiles, 3 lanes at 6vt) is wider
|
||||
than pedestrian customs (east, 10 tiles, 3 lanes). Aperture chamber
|
||||
is fully walled and restricted. Cross-z LOS from the observation
|
||||
gallery (z=2) into the concourse is handled server-side.
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
# Location: Maintenance Corridors (smuggling spaces)
|
||||
# Source: D-093, #313 (Sprint 12 spatial layout)
|
||||
# Zone palette: surface #4e5054 / fog tint #101214
|
||||
# z=0 (Era 1, below main structures)
|
||||
|
||||
canonical_id: "van-maanens-star.sova.transit.location.maintenance-corridors"
|
||||
display_name: "Maintenance Corridors"
|
||||
description: >
|
||||
Era 1 maintenance infrastructure beneath the transit district. A long
|
||||
transition corridor connects the logistics hub to the bar district.
|
||||
Restricted storage at the west end is shared with The Terminal above.
|
||||
A maintenance hatch at the east end provides the cross-z connection
|
||||
to the main level. Everything reads as mundane maintenance — the ring
|
||||
operates here because nobody looks twice.
|
||||
|
||||
tile_bounds:
|
||||
x_min: 0
|
||||
y_min: 0
|
||||
x_max: 57
|
||||
y_max: 5
|
||||
z: 0
|
||||
|
||||
# Legend:
|
||||
# F = Floor (walkable) W = Wall (solid, blocks LOS)
|
||||
# R = Restricted (access-tier gated)
|
||||
#
|
||||
# Layout (58 wide x 6 tall):
|
||||
# Row 0: North wall
|
||||
# Row 1: Restricted storage (west, 8 tiles) | wall | corridor (40 tiles) | wall | hatch room (east, 6)
|
||||
# Row 2: Doors connect rooms (F in wall positions at cols 9 and 50)
|
||||
# Row 3-4: Same as row 1 (walls between rooms)
|
||||
# Row 5: South wall
|
||||
#
|
||||
# Corridor width: 2vt visible at rows 1-4 (internal height, maintenance standard)
|
||||
# Transition corridor: 40 tiles (~40m at 1m/vt) between storage and hatch per D-093
|
||||
# Restricted storage: R tiles for access restriction
|
||||
# Maintenance hatch: F tiles (cross-z connection is future movement feature)
|
||||
# Design: mundane maintenance appearance per D-093 G-08
|
||||
|
||||
tiles:
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
- "WRRRRRRRRWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WRRRRRRRRFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WRRRRRRRRWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WRRRRRRRRWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
|
||||
sightlines:
|
||||
open: false
|
||||
notes: >
|
||||
Long straight corridor (40 tiles) with limited concealment. Storage and
|
||||
hatch rooms are separated by walls with single-tile doors at row 2.
|
||||
Mundane appearance per D-093 G-08 — no visually suspicious tile
|
||||
arrangements.
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Location: The Last Shift (bar)
|
||||
# Source: D-093, #312 (Sprint 12 spatial layout)
|
||||
# Zone palette: surface #6b4018 / fog tint #200c04
|
||||
|
||||
canonical_id: "van-maanens-star.sova.transit.location.the-last-shift"
|
||||
display_name: "The Last Shift"
|
||||
description: >
|
||||
A converted maintenance bay turned bar. The long counter runs along the
|
||||
west wall with full sightlines across the room. A corner booth in the
|
||||
northeast sees the bar, card table, entrance, and back room — prime
|
||||
observation real estate. Scattered tables fill the main floor. A back
|
||||
room with an alley exit provides the key traversal route for the ring.
|
||||
|
||||
tile_bounds:
|
||||
x_min: 0
|
||||
y_min: 0
|
||||
x_max: 33
|
||||
y_max: 21
|
||||
z: 1
|
||||
|
||||
# Legend:
|
||||
# F = Floor (walkable) W = Wall (solid, blocks LOS)
|
||||
#
|
||||
# Layout (34 wide x 22 tall):
|
||||
# Row 0: North exterior wall
|
||||
# Rows 1-4: Main floor + corner booth (NE, cols 26-31, walled alcove)
|
||||
# Row 5: Bar counter starts (cols 1-2 = W fixture)
|
||||
# Rows 6-10: Bar counter (cols 1-2 W), tables (W stubs), card table (col 25)
|
||||
# Row 11: Open transition floor
|
||||
# Rows 12-14: Table clusters (W stubs at cols 8, 16, 24)
|
||||
# Row 15: News ticker mount (col 31 = W stub)
|
||||
# Row 16: Open floor
|
||||
# Row 17: Back room north wall with door (col 22 = F)
|
||||
# Rows 18-20: Main floor (west) + back room (east, cols 22-32)
|
||||
# Row 21: South wall with alley exit gap (cols 29-32 = F)
|
||||
|
||||
tiles:
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFWFFFFFFW"
|
||||
- "WWWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WWWFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFW"
|
||||
- "WWWFFFFFFFWFFFFFFFWFFFFFFWFFFFFFFW"
|
||||
- "WWWFFFFFFFWFFFFFFFWFFFFFFFFFFFFFFW"
|
||||
- "WWWFFFFFFFFFFFWFFFFFFFFFFFFFFFFFFW"
|
||||
- "WWWFFFFFFFFFFFWFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFWFFFFFFFWFFFFFFFWFFFFFFFFW"
|
||||
- "WFFFFFFFWFFFFFFFWFFFFFFFWFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFWFWFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFFFW"
|
||||
- "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWFFFFW"
|
||||
|
||||
sightlines:
|
||||
open: false
|
||||
notes: >
|
||||
Corner booth (NE alcove, rows 1-4) has LOS to bar counter, card table
|
||||
area, main entrance, and back room door. Bar counter along west wall
|
||||
(cols 1-2, rows 5-10) gives staff full sightlines across the main
|
||||
floor. Back room is partially occluded by wall at col 21. Back room
|
||||
door at col 22. Table W-stubs create partial cover but do not fully
|
||||
block sightlines.
|
||||
|
||||
social_site: "bar"
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Location: The Terminal (logistics hub)
|
||||
# Source: D-093, #311 (Sprint 12 spatial layout)
|
||||
# Zone palette: surface #7a8490 / fog tint #0d1520
|
||||
|
||||
canonical_id: "van-maanens-star.sova.transit.location.the-terminal"
|
||||
display_name: "The Terminal"
|
||||
description: >
|
||||
Sova's freight logistics hub. Scanner bays filter incoming cargo at the
|
||||
south entrance; a wide main corridor runs north through the manifest
|
||||
processing floor. The break room sits in the northeast. The supervisor's
|
||||
office overlooks the corridor through a large interior window. Restricted
|
||||
storage in the northwest corner is where the ring hides re-tagged cargo.
|
||||
|
||||
tile_bounds:
|
||||
x_min: 0
|
||||
y_min: 0
|
||||
x_max: 43
|
||||
y_max: 27
|
||||
z: 1
|
||||
|
||||
# Legend:
|
||||
# F = Floor (walkable) W = Wall (solid, blocks LOS)
|
||||
# V = Void (outside building) R = Restricted (access-tier gated)
|
||||
#
|
||||
# Layout (44 wide x 28 tall):
|
||||
# Rows 0: North exterior wall
|
||||
# Rows 1-4: Restricted storage (NW, 11 tiles) + corridor + supervisor office (NE, 9 tiles)
|
||||
# Row 5: Storage south wall; corridor + office continue
|
||||
# Rows 6-8: Open corridor; supervisor office east side
|
||||
# Row 7: ** Supervisor window — col 28 is F (LOS gap into office) **
|
||||
# Row 9: Supervisor office south wall
|
||||
# Rows 10-11: Main corridor (full width chokepoint — high sightline value)
|
||||
# Rows 12-17: Manifest processing (west, 14 tiles) + break room (east, 13 tiles)
|
||||
# Row 18: Break room south wall; manifest open to corridor
|
||||
# Rows 19-21: South corridor
|
||||
# Row 22: Scanner bay north walls with entry gaps
|
||||
# Rows 23-26: Scanner bay interiors (3 bays)
|
||||
# Row 27: South exterior wall
|
||||
|
||||
tiles:
|
||||
- "VVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVV"
|
||||
- "VVVVVWRRRRRRRRRRRWFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWRRRRRRRRRRRWFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWRRRRRRRRRRRWFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWRRRRRRRRRRRWFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWWWWWWWWWWWWWFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFWFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFWWWWWWWWWWWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFWFFFWFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFWWWWWWWWWWWWWWWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWWWWFFFFFFWWWWFFFFFFFFWWWWFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFWFFFFFFFFFFFWFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFWFFFFFFFFFFFWFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFWFFFFFFFFFFFWFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWFFFFFFFFFWFFFFFFFFFFFWFFFFFFFFFFWVVVVV"
|
||||
- "VVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVV"
|
||||
|
||||
sightlines:
|
||||
open: false
|
||||
notes: >
|
||||
Supervisor office window faces main corridor at row 7, col 28 (F gap in
|
||||
wall line). Player standing in the corridor has direct LOS into the
|
||||
office interior. This is the primary investigative discovery moment.
|
||||
|
||||
social_site: "logistics-hub"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
# NPC daily routine schedules — Sova Transit District
|
||||
# All NPC schedules in one file for cross-NPC scheduling validation
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# Social site template: The Last Shift (bar)
|
||||
# D-025: functional cluster as atomic template unit
|
||||
# 4 role slots, 4+ NPCs in v0.1 (regulars are reference links). 4-8 NPC capacity.
|
||||
|
||||
template_id: bar
|
||||
display_name: "The Last Shift"
|
||||
description: >
|
||||
Mid-grade bar in Sova Transit District. Converted maintenance staging area.
|
||||
Irregular layout, long bar, corner booth. Where working people go after shift.
|
||||
Informally known as "Lera's." The social nexus where investigation and
|
||||
mundane life overlap.
|
||||
location: the-last-shift
|
||||
|
||||
capacity:
|
||||
min: 4
|
||||
max: 8
|
||||
|
||||
role_slots:
|
||||
- role: bar-owner
|
||||
display_name: "Bar Owner"
|
||||
count: 1
|
||||
required: true
|
||||
description: >
|
||||
Owns and operates the bar. Observant, careful, knows everyone's business.
|
||||
ANCHOR pattern — the stabilizing social presence. Hears everything,
|
||||
says little. Access tier: insider for regulars.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- social_anchor
|
||||
- information_hub
|
||||
- bar_permanent
|
||||
|
||||
- role: bartender
|
||||
display_name: "Bartender"
|
||||
count: 1
|
||||
required: true
|
||||
description: >
|
||||
Serves drinks, manages the floor. Different from the owner — the
|
||||
bartender is staff, not the proprietor. Younger, less established.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- bar_permanent
|
||||
|
||||
- role: bar-regular
|
||||
display_name: "Bar Regular"
|
||||
count:
|
||||
min: 2
|
||||
max: 5
|
||||
required: true
|
||||
description: >
|
||||
Off-duty workers and social visitors. Regulars are typically owned by
|
||||
other templates (logistics hub, Commission) with reference links here.
|
||||
Their presence during bar_evening creates the social observation space.
|
||||
pool_ref: "transit:bar_regulars"
|
||||
flags:
|
||||
- evening_presence
|
||||
- cross_template
|
||||
|
||||
# v0.1 NPC assignments
|
||||
v01_assignments:
|
||||
bar-owner: "npc:lera-sessik"
|
||||
bartender: "npc:pell"
|
||||
bar-regular-1: "npc:sera-venn" # Reference: Commission field tech, evening regular
|
||||
bar-regular-2: "npc:resha" # Owned by bar template
|
||||
# Additional regulars via reference links:
|
||||
# npc:kael-davan (from logistics-hub, evening presence)
|
||||
# npc:torek-lintar (from Commission, evening presence)
|
||||
# npc:drin (from logistics-hub, occasional)
|
||||
|
||||
# Reference links — NPCs owned by other templates who visit the bar
|
||||
reference_links:
|
||||
- npc: "npc:sera-venn"
|
||||
owning_template: null # Sera is Commission, not bar staff
|
||||
relationship: "social anchor — evening regular"
|
||||
presence_phases: [evening]
|
||||
- npc: "npc:kael-davan"
|
||||
owning_template: logistics-hub
|
||||
relationship: "after-shift regular"
|
||||
presence_phases: [evening]
|
||||
- npc: "npc:torek-lintar"
|
||||
owning_template: null # Commission inspector
|
||||
relationship: "off-duty regular, gambling participant"
|
||||
presence_phases: [evening]
|
||||
- npc: "npc:drin"
|
||||
owning_template: logistics-hub
|
||||
relationship: "occasional visitor"
|
||||
presence_phases: [evening]
|
||||
|
||||
triangle_constraints:
|
||||
- triangle: bar-tensions
|
||||
required_roles:
|
||||
- bar-owner # Lera: owner mediating
|
||||
- bar-regular # Resha: regular with grievance
|
||||
- bartender # Pell: caught in the middle
|
||||
- triangle: worried-knowledge
|
||||
required_roles:
|
||||
- bar-regular # Sera: avoiding Torek at the bar
|
||||
|
||||
dialogue_pools:
|
||||
- location: the-last-shift
|
||||
roles: [bar-owner, bartender, bar-regular]
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
# Social site template: The Terminal (logistics hub)
|
||||
# D-025: functional cluster as atomic template unit
|
||||
# 5 role slots, 6 NPCs in v0.1. 4-8 NPC capacity.
|
||||
|
||||
template_id: logistics-hub
|
||||
display_name: "The Terminal"
|
||||
description: >
|
||||
Freight logistics hub — the beating heart of Sova Transit District. Cargo
|
||||
processing, manifest management, shift rotations. The smuggler's workplace
|
||||
and the ring's operational cover.
|
||||
location: the-terminal
|
||||
|
||||
# Capacity range per D-025: 4-8 NPCs in connected space with internal sightlines
|
||||
capacity:
|
||||
min: 4
|
||||
max: 8
|
||||
|
||||
role_slots:
|
||||
- role: shift-supervisor
|
||||
display_name: "Shift Supervisor"
|
||||
count: 1
|
||||
required: true
|
||||
description: >
|
||||
Authority figure. Oversees dock operations, manages shift schedules,
|
||||
enforces procedures. Access tier: authority within the hub.
|
||||
pool_ref: null # Fixed assignment in v0.1
|
||||
flags:
|
||||
- authority_figure
|
||||
- hub_permanent
|
||||
|
||||
- role: dock-worker
|
||||
display_name: "Dock Worker"
|
||||
count:
|
||||
min: 2
|
||||
max: 4
|
||||
required: true
|
||||
description: >
|
||||
Core workforce. Processes cargo, handles manifests, operates equipment.
|
||||
One dock-worker slot is reserved for the smuggler's FRIEND (pool draw).
|
||||
pool_ref: "transit:friend_smuggler" # One slot drawn from FRIEND pool
|
||||
flags:
|
||||
- hub_permanent
|
||||
- ring_candidate
|
||||
|
||||
- role: new-hire
|
||||
display_name: "New Hire"
|
||||
count: 1
|
||||
required: false
|
||||
description: >
|
||||
Recent arrival to the logistics hub. Asks questions, doesn't know the
|
||||
social norms yet, provides a fresh perspective. CATALYST pattern.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- catalyst
|
||||
- newcomer
|
||||
|
||||
- role: scheduler
|
||||
display_name: "Freight Scheduler"
|
||||
count: 1
|
||||
required: true
|
||||
description: >
|
||||
Manages freight scheduling and manifest coordination. Has access to
|
||||
cargo records — a key information source for both smuggler and detective.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- hub_permanent
|
||||
- information_access
|
||||
|
||||
- role: courier
|
||||
display_name: "Courier"
|
||||
count: 1
|
||||
required: false
|
||||
description: >
|
||||
Moves between locations. Off-duty security or delivery runner.
|
||||
Bridge NPC who connects the hub to other social sites.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- bridge_npc
|
||||
|
||||
# v0.1 NPC assignments (matches seed config design doc section 4.4)
|
||||
v01_assignments:
|
||||
shift-supervisor: "npc:voss"
|
||||
dock-worker-1: "npc:kael-davan"
|
||||
dock-worker-2: "npc:drin"
|
||||
new-hire: "npc:renn"
|
||||
scheduler: "npc:maret-korr"
|
||||
courier: "npc:harek"
|
||||
|
||||
# Triangles that involve members of this template
|
||||
triangle_constraints:
|
||||
- triangle: hub-power
|
||||
required_roles:
|
||||
- shift-supervisor # Voss: authority
|
||||
- dock-worker # Kael: subordinate
|
||||
- scheduler # Maret: caught-between
|
||||
- triangle: worried-partner
|
||||
required_roles:
|
||||
- dock-worker # Kael: partner under ring pressure
|
||||
- triangle: informant-question
|
||||
required_roles:
|
||||
- dock-worker # Drin: notices Torek's inspection irregularities
|
||||
|
||||
# Dialogue pool locations (maps to dialogue/ subdirectory)
|
||||
dialogue_pools:
|
||||
- location: the-terminal
|
||||
roles: [shift-supervisor, dock-worker, new-hire, scheduler, courier]
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# Social site template: Smuggling Ring (maintenance corridors)
|
||||
# D-025: functional cluster as atomic template unit
|
||||
# Smallest template: 2-4 NPCs. Operates in infrastructure gaps.
|
||||
|
||||
template_id: smuggling-ring
|
||||
display_name: "The Ring"
|
||||
description: >
|
||||
Smuggling operation using repurposed maintenance infrastructure. Not a
|
||||
dedicated room — a corridor between hub sections where old and new
|
||||
construction don't quite meet. Access through service hatches. The ring
|
||||
didn't build this space; they found it. Operates during night shift and
|
||||
shift transitions when oversight is thinnest.
|
||||
location: maintenance-corridors
|
||||
|
||||
capacity:
|
||||
min: 2
|
||||
max: 4
|
||||
|
||||
role_slots:
|
||||
- role: ring-operative
|
||||
display_name: "Ring Coordinator"
|
||||
count: 1
|
||||
required: true
|
||||
description: >
|
||||
Coordinates ring operations. HANDLER motivation — manages logistics,
|
||||
maintains contacts, makes decisions. Capable, not theatrical.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- ring_leader
|
||||
- night_presence
|
||||
- restricted_access
|
||||
|
||||
- role: ring-contact
|
||||
display_name: "Ring Contact"
|
||||
count:
|
||||
min: 1
|
||||
max: 2
|
||||
required: true
|
||||
description: >
|
||||
Active ring member with dual role — legitimate job at the logistics hub
|
||||
by day, ring operations by night. Reference link from logistics-hub
|
||||
template. Their presence here is the core secret the smuggler protects
|
||||
and the detective investigates.
|
||||
pool_ref: "transit:friend_smuggler" # The FRIEND is the primary ring contact
|
||||
flags:
|
||||
- dual_role
|
||||
- night_presence
|
||||
- ring_member
|
||||
|
||||
- role: external-contact
|
||||
display_name: "External Contact"
|
||||
count: 1
|
||||
required: false
|
||||
description: >
|
||||
Off-station connection. Arrives with shipments, departs quickly.
|
||||
Not a permanent presence — appears during ring operation windows.
|
||||
pool_ref: null
|
||||
flags:
|
||||
- transient
|
||||
- external
|
||||
|
||||
# v0.1 NPC assignments
|
||||
v01_assignments:
|
||||
ring-operative: "npc:devra"
|
||||
ring-contact: "npc:kael-davan" # Reference from logistics-hub (dual role)
|
||||
external-contact: "npc:sabel" # Off-system contact
|
||||
|
||||
reference_links:
|
||||
- npc: "npc:kael-davan"
|
||||
owning_template: logistics-hub
|
||||
relationship: "ring member — dual role, night operations"
|
||||
presence_phases: [night]
|
||||
- npc: "npc:sabel"
|
||||
owning_template: null # External, transient
|
||||
relationship: "off-station buyer/receiver"
|
||||
presence_phases: [night] # Irregular, tied to shipments
|
||||
|
||||
triangle_constraints:
|
||||
- triangle: worried-partner
|
||||
required_roles:
|
||||
- ring-contact # Kael: under pressure from ring work
|
||||
- triangle: hub-power
|
||||
required_roles:
|
||||
- ring-contact # Kael: subordinate to Voss by day, ring by night
|
||||
|
||||
dialogue_pools:
|
||||
- location: maintenance-corridors
|
||||
roles: [ring-operative]
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
# News ticker headline pool — The Last Shift bar
|
||||
# Meridian Feed (Van Maanen's Star local news network)
|
||||
# 30 lines total | Categories: freight (9), politics (4), infrastructure (5), sports (3),
|
||||
# commission (5), community (4) — see dual_lens notes on each line
|
||||
# Dual-lens content: each headline reads differently to smuggler vs detective.
|
||||
# Ticket: #306 | Sprint: 12 | Owner: Mellanie + Miri
|
||||
# v0.1: static background text. v0.2: cycling ticker system.
|
||||
|
||||
location: the-last-shift
|
||||
feed: meridian
|
||||
headlines:
|
||||
# --- Freight / Commodity (6) ---
|
||||
|
||||
- id: ticker_001
|
||||
text: "VAN MAANEN FREIGHT INDEX: Lattice components +12% on Syndic supply restrictions"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Prices up. Every run is worth more — and every run is riskier."
|
||||
detective: "Supply restrictions mean increased black market pressure. Watch for volume spikes."
|
||||
|
||||
- id: ticker_002
|
||||
text: "SOVA DOCKING: Revised rates effective cycle 14 — bulk freight +0.8 cred/tonne"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Costs going up. Nils won't be happy. Margins are already thin."
|
||||
detective: "Administrative noise. But fee changes affect which routes are economically viable."
|
||||
|
||||
- id: ticker_003
|
||||
text: "ROUTE ADVISORY: Span gate maintenance on Van Maanen's Star-Havara corridor — 48hr delays"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Havara corridor's down. If Sabel's shipments are routed through there, we wait."
|
||||
detective: "Transit disruption changes shipping patterns. New routes create new anomalies."
|
||||
|
||||
- id: ticker_004
|
||||
text: "COMMODITY ALERT: Medical-grade neural components — backorder extends to 6 cycles"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Six cycles. People need those replacements now. That's why we do this."
|
||||
detective: "Extended shortage increases incentive for diversion. Motive for the operation."
|
||||
|
||||
- id: ticker_005
|
||||
text: "FREIGHT QUARTERLY: Sova throughput up 3% year-on-year — transit district credited"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "More volume, more cover. Good news for us."
|
||||
detective: "Increased throughput makes statistical anomalies harder to spot. More noise."
|
||||
|
||||
- id: ticker_006
|
||||
text: "INSURANCE: Premium review for manifest discrepancy incidents — sector-wide audit"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Manifest discrepancies. If the insurers are looking, the Commission's next."
|
||||
detective: "Insurance sector flagging the same pattern I'm seeing. Independent confirmation."
|
||||
|
||||
# --- Assembly Politics (4) ---
|
||||
|
||||
- id: ticker_007
|
||||
text: "ASSEMBLY: Lattice regulation amendments tabled — vote deferred to next session"
|
||||
category: politics
|
||||
dual_lens:
|
||||
smuggler: "Deferred. Status quo holds. The demand stays, the supply stays illegal."
|
||||
detective: "Regulatory gap continues. No political will to address the root cause."
|
||||
|
||||
- id: ticker_008
|
||||
text: "ASSEMBLY VOTE: Infrastructure bill passes 312-287 — Reach districts gain allocations"
|
||||
category: politics
|
||||
dual_lens:
|
||||
smuggler: "More money for the Reach. Maybe Sova gets a refit. Maybe things change."
|
||||
detective: "Allocation shifts. New infrastructure means new oversight — or new blind spots."
|
||||
|
||||
- id: ticker_009
|
||||
text: "DELEGATE PROFILE: Rep. Hask (Van Maanen's Star) calls for Commission oversight review"
|
||||
category: politics
|
||||
dual_lens:
|
||||
smuggler: "Commission oversight review. Could mean more patrols. Could mean fewer. Either way, noise."
|
||||
detective: "Political pressure on Commission operations. Budget review affects field resources."
|
||||
|
||||
- id: ticker_010
|
||||
text: "POLICY WATCH: Cross-system freight harmonization bill enters second reading"
|
||||
category: politics
|
||||
dual_lens:
|
||||
smuggler: "Harmonization means standardization. Standardization means fewer gaps in the paperwork."
|
||||
detective: "Cross-system data sharing would connect manifest anomalies across jurisdictions."
|
||||
|
||||
# --- Local Infrastructure (4) ---
|
||||
|
||||
- id: ticker_011
|
||||
text: "SOVA STATION: Deck 4 ventilation overhaul — expect cycled air disruptions"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Construction crews on Deck 4. More people in the corridors. Watch the routes."
|
||||
detective: "Infrastructure work. Construction means altered access patterns."
|
||||
|
||||
- id: ticker_012
|
||||
text: "TRANSIT: Corridor B maintenance scheduled cycle 16-18 — alternate routing posted"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Corridor B. That's our route. Maintenance means official access. We shut down for three cycles."
|
||||
detective: "Scheduled maintenance on a corridor near the freight bays. Who requested it?"
|
||||
|
||||
- id: ticker_013
|
||||
text: "WATER RECYCLING: Stage 2 filter replacement ahead of schedule — capacity at 97%"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Good. One less thing to worry about."
|
||||
detective: "Infrastructure baseline. Station systems functioning normally."
|
||||
|
||||
- id: ticker_014
|
||||
text: "POWER GRID: Outer ring voltage fluctuations traced to span gate resonance"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Voltage fluctuations in the outer ring. Cameras might glitch. Useful."
|
||||
detective: "Power issues could affect surveillance systems. Note the timing."
|
||||
|
||||
# --- Sports (3) ---
|
||||
|
||||
- id: ticker_015
|
||||
text: "GRAV-BALL: Sova Drifters fall to Havara United 3-1 — playoff hopes dim"
|
||||
category: sports
|
||||
dual_lens:
|
||||
smuggler: "Drifters lost again. Lera's going to be in a mood."
|
||||
detective: "Sports. The universal social lubricant. File under 'conversation openers.'"
|
||||
|
||||
- id: ticker_016
|
||||
text: "LEAGUE TABLE: Van Maanen's Star Conference — Sova 4th, Mirren Station leads"
|
||||
category: sports
|
||||
dual_lens:
|
||||
smuggler: "Fourth. We were second last season. This place is sliding."
|
||||
detective: "Local standings. Note who cares about the Drifters — it's a belonging marker."
|
||||
|
||||
- id: ticker_017
|
||||
text: "HIGHLIGHT: Drifters' Pael Tev named to All-Reach reserve squad"
|
||||
category: sports
|
||||
dual_lens:
|
||||
smuggler: "Tev made the reserves. Good for him. At least someone's getting out."
|
||||
detective: "The name Tev — any relation to dock personnel? Check roster."
|
||||
|
||||
# --- Commission Advisories (3) ---
|
||||
|
||||
- id: ticker_018
|
||||
text: "COMMISSION: Manifest compliance audits scheduled — Sova Transit, cycles 15-20"
|
||||
category: commission
|
||||
dual_lens:
|
||||
smuggler: "Compliance audits. Cycle 15. That's five cycles from now. We need to be clean by then."
|
||||
detective: "Scheduled audits. Standard, but the timing aligns with my investigation window."
|
||||
|
||||
- id: ticker_019
|
||||
text: "ADVISORY: Unlicensed lattice modifications carry Category 3 penalties — report anonymously"
|
||||
category: commission
|
||||
dual_lens:
|
||||
smuggler: "Category 3. Years in detention. Same poster, same message, same empty threat. Until it isn't."
|
||||
detective: "Public messaging campaign. Commission HQ wants visible enforcement. Pressure from above."
|
||||
|
||||
- id: ticker_020
|
||||
text: "ENFORCEMENT: Commission confirms 'routine assessment' of Reach freight operations"
|
||||
category: commission
|
||||
dual_lens:
|
||||
smuggler: "'Routine assessment.' Is that the detective? Is that why he's here?"
|
||||
detective: "That's my cover story. Or headquarters' way of telling me the clock is running."
|
||||
|
||||
# --- Local Community (4) — added Sprint 12, Mellanie ---
|
||||
|
||||
- id: ticker_021
|
||||
text: "SECTOR 3: Sova Station Admin acknowledges ventilation review — timeline 'under discussion'"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Under discussion. Same thing they said last cycle. Seven years of under discussion."
|
||||
detective: "Deferred maintenance, seven-year backlog, workers frustrated. That's a community with grievances. File that."
|
||||
|
||||
- id: ticker_022
|
||||
text: "WORKERS: Cargo Workers' Association calls for review of thirty-minute shift handoff protocols"
|
||||
category: community
|
||||
dual_lens:
|
||||
smuggler: "Workers pushing back on the transition window. If they shorten it, we lose our gap."
|
||||
detective: "Workers know the window is too short. And they're right. The question is who else knows it."
|
||||
|
||||
- id: ticker_023
|
||||
text: "SOVA LOCAL: Missing persons — Vessels Tamm, Sova Transit District, Month 11. Contact Sova liaison."
|
||||
category: community
|
||||
dual_lens:
|
||||
smuggler: "Vessels Tamm. That name — any connection to Naia's family? Don't chase ghosts."
|
||||
detective: "Tamm. Naia Tamm is the name in the case file as Kael Davan's partner. Related? Flag."
|
||||
notes: >
|
||||
Connects to notice board in the-last-shift environmental text (env_bar_003).
|
||||
Detective's reaction sets up the Naia-Tamm connection thread.
|
||||
|
||||
- id: ticker_024
|
||||
text: "HEALTH: Re-embodiment wait times at Sova Clinic — non-emergency cases: 4-6 cycles. Main station for urgent."
|
||||
category: community
|
||||
dual_lens:
|
||||
smuggler: "Four cycles. If something goes wrong on a run, four cycles at the clinic before you're back. Remember that."
|
||||
detective: "Healthcare routing. Non-urgent cases handled locally, urgent cases moved off-station. Operational detail."
|
||||
|
||||
# --- Lattice Economy (3) — added Sprint 12, Mellanie ---
|
||||
|
||||
- id: ticker_025
|
||||
text: "LATTICE: Commission-approved upgrade costs rise — standard lattice enhancement up 18% this cycle"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Eighteen percent. That's why the waiting list for off-book components keeps growing. We're providing a service."
|
||||
detective: "Price pressure on approved upgrades. Economic driver for the unlicensed market. Motive confirmed."
|
||||
|
||||
- id: ticker_026
|
||||
text: "LATTICE COMMISSION: Public access modification registry now mandatory — self-report deadline cycle 20"
|
||||
category: commission
|
||||
dual_lens:
|
||||
smuggler: "Self-report. Right. The people who bought off-book components are definitely filing that form."
|
||||
detective: "Mandatory registry. Anyone who doesn't file either doesn't have mods or is hiding them. Narrow that list."
|
||||
|
||||
- id: ticker_027
|
||||
text: "HEALTHCARE: Medical-grade lattice component recall — batch 44xx — contact nearest clinic for assessment"
|
||||
category: community
|
||||
dual_lens:
|
||||
smuggler: "Batch 44xx. Kael's last shipment was 4471 series components. Are they in that range? Check."
|
||||
detective: "Batch 44xx recall. Container 4471 in the manifest anomaly. Cross-reference. This could be the thread."
|
||||
notes: >
|
||||
High-value dual-lens trigger. Smuggler worries about operational risk; detective spots the
|
||||
evidence connection. The 44xx range is intentionally ambiguous — might match, might not.
|
||||
|
||||
# --- Van Maanen's Star / Setting (2) — added Sprint 12, Mellanie ---
|
||||
|
||||
- id: ticker_028
|
||||
text: "VELEN WEATHER: Coastal fog advisories in effect — Sova orbital approach window reduced, gate scheduling adjusted"
|
||||
category: infrastructure
|
||||
dual_lens:
|
||||
smuggler: "Fog on Velen. Gate scheduling adjusted — means transit timing shifts. The window might move."
|
||||
detective: "Environmental factors affecting gate operations. Adjust surveillance timing accordingly."
|
||||
|
||||
- id: ticker_029
|
||||
text: "REACH ECONOMY: Van Maanen's Star trade volume up 7% — analysts attribute to 'informal market activity'"
|
||||
category: freight
|
||||
dual_lens:
|
||||
smuggler: "Informal market activity. They know. The whole Reach knows. And still the demand keeps climbing."
|
||||
detective: "'Informal market.' The analysts see the pattern but can't name it. I can."
|
||||
|
||||
- id: ticker_030
|
||||
text: "ASSEMBLY: Severance technology moratorium extended — counter-surveillance devices remain Category 2 restricted"
|
||||
category: politics
|
||||
dual_lens:
|
||||
smuggler: "Moratorium extended. Good. Severance equipment costs keep the Commission from blanketing everything."
|
||||
detective: "Counter-surveillance devices still legally restricted. Anyone running them is committing a documented infraction."
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Triangle 3: Bar Tensions — mundane social friction at The Last Shift
|
||||
# Passive — runs on background simulation, no storyteller intervention
|
||||
canonical_id: bar-tensions
|
||||
display_name: "Bar Tensions"
|
||||
description: >
|
||||
Mundane social friction at The Last Shift. Lera Sessik (bar owner) runs
|
||||
a tight establishment. Resha (regular) has a grievance — could be about
|
||||
the bar, another regular, or community politics. Pell (bartender) is
|
||||
caught in the middle, trying to keep both the boss and the customers
|
||||
happy. This is the 50% of life that has nothing to do with conspiracy —
|
||||
the noise floor that makes investigation signal meaningful.
|
||||
|
||||
members:
|
||||
- npc: "npc:lera-sessik"
|
||||
role: "owner — mediates disputes, protects her establishment"
|
||||
- npc: "npc:resha"
|
||||
role: "regular — has a grievance, vocal about it"
|
||||
- npc: "npc:pell"
|
||||
role: "bartender — caught between boss and customers"
|
||||
|
||||
forks:
|
||||
- id: grievance-escalation
|
||||
condition: >
|
||||
Resha's grievance reaches a tipping point. Lera must decide whether
|
||||
to take sides or maintain neutrality. Pell gets drawn in.
|
||||
outcomes:
|
||||
- id: lera-sides-with-resha
|
||||
description: >
|
||||
Lera validates Resha's complaint. Pell adjusts service accordingly.
|
||||
Social dynamics shift — Resha becomes more central, the bar's
|
||||
atmosphere changes slightly.
|
||||
effects:
|
||||
- "resha.behavior_flags += vindicated"
|
||||
- "pell.behavior_flags += accommodating"
|
||||
- "bar atmosphere = tense but resolving"
|
||||
- id: lera-shuts-it-down
|
||||
description: >
|
||||
Lera tells Resha to drop it. Her bar, her rules. Resha sulks.
|
||||
Pell is relieved. The grievance goes underground.
|
||||
effects:
|
||||
- "resha.behavior_flags += resentful,quiet"
|
||||
- "pell.behavior_flags += relieved"
|
||||
- "bar atmosphere = business as usual"
|
||||
|
||||
resolution_states:
|
||||
- id: grievance-resolved
|
||||
description: The dispute is settled one way or another. Normal bar dynamics resume.
|
||||
- id: grievance-simmering
|
||||
description: The issue goes underground. Occasional flare-ups provide social texture.
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
# Triangle 1: Hub Power — workplace hierarchy under ring pressure
|
||||
# Active fork — smuggler decision (3 paths, self-contained per D-053/A-17)
|
||||
canonical_id: hub-power
|
||||
display_name: "Hub Power"
|
||||
description: >
|
||||
Workplace hierarchy tension at The Terminal. Voss (shift supervisor) has
|
||||
positional authority but takes operational orders from the ring. Kael
|
||||
(dock worker, THE FRIEND) is caught between his boss and the ring's demands.
|
||||
Maret (freight scheduler) sees manifest discrepancies and doesn't know
|
||||
whom to tell. The triangle's resting tension: Voss needs quiet, Kael
|
||||
needs everything to hold together, Maret is too smart not to notice.
|
||||
|
||||
members:
|
||||
- npc: "npc:voss"
|
||||
role: "authority — shift supervisor with ring complicity"
|
||||
- npc: "npc:kael-davan"
|
||||
role: "subordinate — dock worker, ring member, caught between boss and ring"
|
||||
- npc: "npc:maret-korr"
|
||||
role: "caught-between — freight scheduler who sees the discrepancies"
|
||||
|
||||
forks:
|
||||
- id: volume-escalation
|
||||
condition: >
|
||||
Ring demands double-volume shipment via intermediary. Smuggler
|
||||
must decide how to handle the pressure on Voss and Kael.
|
||||
outcomes:
|
||||
- id: escalate
|
||||
description: >
|
||||
Side with ring (escalate). Convince Voss to widen the shift gap.
|
||||
Investigation accelerates — manifest discrepancies spike, Voss
|
||||
becomes visibly anxious. Detective reads organizational stress.
|
||||
effects:
|
||||
- "voss.behavior_flags += nervous,schedule_changes,evasive"
|
||||
- "voss.position_integrity = thin"
|
||||
- "ring.exposure_risk increases"
|
||||
- "detective.investigation_pace = accelerated"
|
||||
- id: stabilize
|
||||
description: >
|
||||
Side with Voss (stabilize). Reject or delay the double shipment.
|
||||
Hub runs quiet but Kael absorbs redirected pressure from ring
|
||||
coordinator. Detective reads personal stress on Kael instead.
|
||||
effects:
|
||||
- "kael.behavior_flags += lattice_checking,distracted,evasive"
|
||||
- "kael.trust_level (from ring) drops"
|
||||
- "friend_arc timing = accelerated"
|
||||
- "detective.investigation_pace = slowed"
|
||||
- id: mediate
|
||||
description: >
|
||||
Compromise — half volume, phased across two shipments. Nobody's
|
||||
happy, nobody's exposed. Detective reads inconsistent operational
|
||||
tempo. Creates a deferred second decision point.
|
||||
effects:
|
||||
- "ring.exposure_risk = moderate"
|
||||
- "smuggler.operational_trust (from ring) drops"
|
||||
- "kael.behavior_flags += uncertain,distracted"
|
||||
- "detective reads organizational confusion"
|
||||
|
||||
resolution_states:
|
||||
- id: ring-exposed-via-voss
|
||||
description: >
|
||||
Voss's anxiety becomes the detective's thread to pull. Investigation
|
||||
arrives at the ring through institutional signals.
|
||||
- id: ring-exposed-via-kael
|
||||
description: >
|
||||
Kael's personal strain becomes the detective's signal. Investigation
|
||||
arrives through behavioral tells on THE FRIEND.
|
||||
- id: ring-exposed-via-inconsistency
|
||||
description: >
|
||||
Irregular operational tempo reveals internal disagreement. Detective
|
||||
infers organizational structure from pattern disruption.
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# Triangle 5: Worried Partner — ring pressure on personal relationships
|
||||
# Passive tension (D-087) — background pressure, not a direct player decision point
|
||||
canonical_id: worried-partner
|
||||
display_name: "Worried Partner"
|
||||
classification: passive_tension
|
||||
description: >
|
||||
Kael Davan (dock worker, ring member, smuggler's FRIEND) is under
|
||||
increasing ring pressure from Devra (ring coordinator). Naia Tamm
|
||||
(Kael's partner) is worried about his late nights and evasive answers.
|
||||
The smuggler knows why Kael is busy and can't say. This is the most
|
||||
emotionally loaded triangle — it connects THE FRIEND's contradiction
|
||||
arc to personal relationships. If Naia asks the smuggler directly,
|
||||
there's no good answer.
|
||||
|
||||
members:
|
||||
- npc: "npc:kael-davan"
|
||||
role: "partner under pressure — ring work destroying home life"
|
||||
- npc: "npc:naia-tamm"
|
||||
role: "worried — sees the changes, doesn't understand the cause"
|
||||
- npc: "npc:devra"
|
||||
role: "cause — ring coordinator whose demands increase Kael's burden"
|
||||
|
||||
forks:
|
||||
- id: naia-confronts
|
||||
condition: >
|
||||
Naia's worry reaches a threshold. She confronts Kael directly, or
|
||||
asks the smuggler what's going on. The smuggler faces a loyalty test.
|
||||
outcomes:
|
||||
- id: smuggler-deflects
|
||||
description: >
|
||||
Smuggler deflects Naia's questions. Protects Kael's cover but
|
||||
damages trust with Naia. Kael is grateful but the lie costs.
|
||||
effects:
|
||||
- "naia.trust_level (toward smuggler) drops"
|
||||
- "kael.behavior_flags += relieved,guilty"
|
||||
- "smuggler.moral_weight (toward naia) = complicit"
|
||||
- id: smuggler-hints
|
||||
description: >
|
||||
Smuggler gives Naia a partial truth — Kael's under work pressure,
|
||||
it'll pass. Not a lie, but not the truth. Naia is temporarily
|
||||
reassured but the pattern continues.
|
||||
effects:
|
||||
- "naia.behavior_flags += cautiously_reassured"
|
||||
- "kael.behavior_flags += unaware"
|
||||
- "smuggler.moral_weight (toward naia) = peripheral"
|
||||
- id: kael-snaps
|
||||
description: >
|
||||
Before the smuggler can intervene, Kael snaps at Naia under
|
||||
pressure. The argument is public enough to be observed. Devra's
|
||||
pressure has become visible through personal fallout.
|
||||
effects:
|
||||
- "kael.behavior_flags += agitated,public_argument"
|
||||
- "naia.behavior_flags += hurt,withdrawn"
|
||||
- "detective observes domestic stress (behavioral signal)"
|
||||
|
||||
resolution_states:
|
||||
- id: relationship-strained
|
||||
description: >
|
||||
Kael and Naia's relationship is visibly strained. The domestic
|
||||
tension becomes an observable signal for the detective.
|
||||
- id: temporary-calm
|
||||
description: >
|
||||
The immediate crisis passes but the underlying cause remains.
|
||||
Another confrontation is inevitable.
|
||||
- id: naia-investigates
|
||||
description: >
|
||||
Naia starts looking into Kael's activities independently. She
|
||||
becomes a potential variable the ring didn't account for.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Station Sova metadata (D-036)
|
||||
display_name: "Station Sova"
|
||||
description: >
|
||||
A freight logistics station in Van Maanen's Star. 40-year-old prefab-modular
|
||||
facility serving the span gate's cargo throughput. ~12,000 population.
|
||||
districts:
|
||||
- "transit"
|
||||
@@ -0,0 +1,8 @@
|
||||
# Van Maanen's Star metadata (D-036)
|
||||
display_name: "Van Maanen's Star"
|
||||
description: >
|
||||
A mid-tier system connected via the Sova span gate. Industrial economy
|
||||
centered on freight logistics and lattice component trade. ~2.4M population,
|
||||
~180 years settled.
|
||||
stations:
|
||||
- "sova"
|
||||
@@ -0,0 +1,11 @@
|
||||
# Content manifest — The Settled Reach v0.1
|
||||
# The server reads this first to discover campaigns and content layout.
|
||||
# District discovery uses glob patterns — no per-district listing needed.
|
||||
version: "0.1.0"
|
||||
|
||||
campaigns:
|
||||
- id: "main"
|
||||
path: "campaigns/main"
|
||||
enabled: true
|
||||
discovery:
|
||||
districts: "systems/**/districts/*/district.yaml"
|
||||
@@ -0,0 +1,7 @@
|
||||
scope: cross_room
|
||||
description: "Cross-room checks applied to every Gauntlet room visit."
|
||||
conditions:
|
||||
- id: cross-player-entity-present
|
||||
description: "Player entity (StableId 0) always present in snapshot"
|
||||
condition_type: entity_present
|
||||
entity_id: 0
|
||||
@@ -0,0 +1,42 @@
|
||||
room_id: inventory_warehouse
|
||||
room_name: Inventory Warehouse
|
||||
description: "Tests D-065 (9-slot inventory), pickup/drop verbs, CarriedBy component. 10 crates (StableId 13-22) + 1 NPC (StableId 23)."
|
||||
conditions:
|
||||
- id: inv-player-spawn
|
||||
description: "Player starts near warehouse spawn position"
|
||||
condition_type: player_near
|
||||
x: 17
|
||||
y: 54
|
||||
radius: 2.0
|
||||
|
||||
- id: inv-player-facing-east
|
||||
description: "Player faces east at spawn"
|
||||
condition_type: player_facing
|
||||
direction: East
|
||||
|
||||
- id: inv-crate-01-visible
|
||||
description: "First crate (Keycard) is visible from spawn"
|
||||
condition_type: entity_present
|
||||
entity_id: 13
|
||||
|
||||
- id: inv-crate-10-visible
|
||||
description: "Last crate (Chip) is visible"
|
||||
condition_type: entity_present
|
||||
entity_id: 22
|
||||
|
||||
- id: inv-npc-warehouse-visible
|
||||
description: "Warehouse supervisor NPC is present"
|
||||
condition_type: entity_present
|
||||
entity_id: 23
|
||||
|
||||
- id: inv-crate-pickup-verb
|
||||
description: "Pickup verb available on crate when nearby"
|
||||
condition_type: expected_interaction_verb
|
||||
entity_id: 13
|
||||
verb: Pickup
|
||||
|
||||
- id: inv-npc-talk-verb
|
||||
description: "Talk verb available on warehouse NPC"
|
||||
condition_type: expected_interaction_verb
|
||||
entity_id: 23
|
||||
verb: Talk
|
||||
@@ -0,0 +1,41 @@
|
||||
room_id: occlusion_corridor
|
||||
room_name: Occlusion Corridor
|
||||
description: "Tests D-035 (symmetric shadowcasting), D-017 (perception modes), D-015 (vision cone sectors). 4 NPCs (StableId 9-12)."
|
||||
conditions:
|
||||
- id: occ-player-spawn
|
||||
description: "Player starts at corridor entrance"
|
||||
condition_type: player_near
|
||||
x: 84
|
||||
y: 58
|
||||
radius: 2.0
|
||||
|
||||
- id: occ-player-facing-east
|
||||
description: "Player faces east down the corridor"
|
||||
condition_type: player_facing
|
||||
direction: East
|
||||
|
||||
- id: occ-guard-visible
|
||||
description: "Guard NPC in clear LOS from spawn"
|
||||
condition_type: entity_present
|
||||
entity_id: 9
|
||||
|
||||
- id: occ-hidden-npc-absent
|
||||
description: "NPC behind north wall segment is NOT visible from spawn"
|
||||
condition_type: entity_absent
|
||||
entity_id: 10
|
||||
|
||||
- id: occ-peripheral-npc-visible
|
||||
description: "South alcove NPC visible in peripheral vision sector"
|
||||
condition_type: entity_present
|
||||
entity_id: 11
|
||||
|
||||
- id: occ-far-end-visible
|
||||
description: "Far end NPC visible down corridor"
|
||||
condition_type: entity_present
|
||||
entity_id: 12
|
||||
|
||||
- id: occ-guard-talk-verb
|
||||
description: "Talk verb available on guard NPC when nearby"
|
||||
condition_type: expected_interaction_verb
|
||||
entity_id: 9
|
||||
verb: Talk
|
||||
@@ -0,0 +1,26 @@
|
||||
room_id: pause_chamber
|
||||
room_name: Pause Chamber
|
||||
description: "Tests D-031 (pause/unpause), Bug #3 regression (movement while paused). 1 NPC (StableId 29)."
|
||||
conditions:
|
||||
- id: pause-player-spawn
|
||||
description: "Player starts at chamber center"
|
||||
condition_type: player_near
|
||||
x: 50
|
||||
y: 86
|
||||
radius: 2.0
|
||||
|
||||
- id: pause-player-facing-north
|
||||
description: "Player faces north at spawn"
|
||||
condition_type: player_facing
|
||||
direction: North
|
||||
|
||||
- id: pause-npc-present
|
||||
description: "Pause target NPC is visible"
|
||||
condition_type: entity_present
|
||||
entity_id: 29
|
||||
|
||||
- id: pause-talk-verb
|
||||
description: "Talk verb available on NPC (should work while paused)"
|
||||
condition_type: expected_interaction_verb
|
||||
entity_id: 29
|
||||
verb: Talk
|
||||
@@ -0,0 +1,175 @@
|
||||
// Osse Margin Culture Profile
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: CultureProfile
|
||||
// Ticket: #653 (copy team)
|
||||
// Validate: tooling/validate-ron content/global/culture-osse.ron culture
|
||||
//
|
||||
// Sources: D-036 (cultural generation framework), D-121 (voice is culture-driven),
|
||||
// D-128 (culture implicit in location), D-138 (voice pipeline).
|
||||
//
|
||||
// Osse: ~60 years settled. Deep Frontier K-class system. Resource extraction industry.
|
||||
// Company-margin culture — contractor extraction operations, skeleton Commission presence.
|
||||
// Your shift team is the fundamental social unit. Words cost attention; attention
|
||||
// keeps you alive. Trust built through shared risk, not time.
|
||||
//
|
||||
// Heritage root: Salt — utilitarian_cover. Labor function covers presence.
|
||||
// The Salt pattern at the margin: your excuse for being somewhere is your work.
|
||||
// Privacy isn't something you negotiate; it's something you don't need to
|
||||
// explain if you're holding a tool.
|
||||
|
||||
(
|
||||
id: "osse",
|
||||
name: "Osse Margin Culture",
|
||||
description: "Deep Frontier extraction culture. ~60 years settled, K-class system. Labor-primary, shift-team-bonded, economy of language is survival habit. Trust through shared risk. Authority is company-first, Commission intermittent. Separatist sympathy endemic.",
|
||||
|
||||
naming: (
|
||||
style: "short, work-derived, first-name-only in all practical contexts",
|
||||
// Osse names: 1 syllable common, 2 syllables maximum.
|
||||
// Work names often diverge from legal names. The name you work under
|
||||
// is the name people know. Family names exist but nobody uses them on-site.
|
||||
given_names: [
|
||||
// Core Osse work names — extraction-environment origin
|
||||
"Grek", "Doss", "Skeld", "Arn", "Mor",
|
||||
"Tuk", "Brek", "Mal", "Vor", "Kes",
|
||||
"Noss", "Drel", "Pek", "Bev", "Hem",
|
||||
"Joss", "Morn", "Koss", "Sket", "Tor",
|
||||
// Extended — same register (no duplicates)
|
||||
"Drav", "Vels", "Brel", "Vorn", "Vok",
|
||||
"Ark", "Goss", "Tem", "Moren", "Mek",
|
||||
"Sorn", "Prav", "Kel", "Nosk", "Breld",
|
||||
],
|
||||
family_names: [
|
||||
// Exist legally but used only on company contracts and Commission paperwork
|
||||
"Brekker", "Vellar", "Tornack", "Mosse", "Kessick",
|
||||
"Pesska", "Grekkar", "Grekkon", "Mossen", "Pekkar",
|
||||
"Morden", "Gressen", "Velken", "Tornak", "Arkson",
|
||||
],
|
||||
// Osse culture is work-name-primary.
|
||||
// Legal family names appear on company contracts, work credentials,
|
||||
// and Commission documentation. Nowhere else.
|
||||
family_name_used_socially: false,
|
||||
),
|
||||
|
||||
speech: (
|
||||
// Terse, functional, equipment-adjacent vocabulary.
|
||||
// Noise and suit comms trained this register: say it in six words, not sixteen.
|
||||
// Silence is comfortable; you fill it with work, not words.
|
||||
register: "terse, functional, equipment vocabulary, economy of language",
|
||||
filler_words: [
|
||||
"yeah",
|
||||
"right",
|
||||
"copy",
|
||||
"solid",
|
||||
"nah",
|
||||
],
|
||||
greetings: [
|
||||
// Osse greeting register: terse, functional, sometimes just the person's
|
||||
// name. Name-only greetings are served via name substitution at runtime.
|
||||
"You good?",
|
||||
"Still breathing?",
|
||||
"Hey.",
|
||||
"Yeah?",
|
||||
"What do you need?",
|
||||
],
|
||||
farewells: [
|
||||
"Stay sealed.",
|
||||
"Watch the drift.",
|
||||
"Good shift.",
|
||||
"Yeah.",
|
||||
"Next shift.",
|
||||
"Keep pressure.",
|
||||
],
|
||||
// Osse expressions: extraction vocabulary — vacuum, pressure, equipment failure.
|
||||
// These are what kills you. You swear by what's real.
|
||||
// Different from Van Maanen's Star void-oaths — Osse is margin industrial, not station working-class.
|
||||
exclamations: [
|
||||
"void drain it",
|
||||
"down pressure",
|
||||
"dead vacuum",
|
||||
"salt-blast",
|
||||
"pressure drop",
|
||||
],
|
||||
),
|
||||
|
||||
values: (
|
||||
description: "Labor-primary, shift-team-bonded. Trust through shared risk, not time or credential. Information is kept close until there's a reason to share it. Warmth is physical and brief — a grip on the arm, not a speech. Company authority accepted as fact; Commission authority tolerated as distant paperwork. Separatist sympathy is practical, not ideological.",
|
||||
// Traits more common in Osse culture.
|
||||
// Bold: do the dangerous thing, but prepare it properly.
|
||||
// Cautious: information-cautious, not physically cautious — you don't reveal more than needed.
|
||||
// Honest: you don't lie to your team about equipment state. Someone dies if you do.
|
||||
favored_traits: [Bold, Cautious, Honest],
|
||||
// Traits less common.
|
||||
// Social: unnecessary talk is a distraction in dangerous work.
|
||||
// Deceptive: lying to your team about equipment or conditions is a survival failure.
|
||||
// Incurious: you die if you stop noticing your environment.
|
||||
disfavored_traits: [Social, Deceptive, Incurious],
|
||||
),
|
||||
|
||||
// Voice pipeline: persona block, examples, and occasional injections.
|
||||
// Osse v1 — authored Sprint 26 (#653). Not yet Spike-validated.
|
||||
// 6-block format per workshop-outcomes.md Section 7.3.
|
||||
voice_persona: Some(
|
||||
"PERSONA: You are an Osse margin worker.\n1. Economy of language is a survival habit. Noise and equipment and suit comms trained you: six words, not sixteen. If it fits in two, use two.\n2. You're from the deep edge of the Reach. Distance from Core systems means you solve your own problems. Authority arrives too late to matter — you've already handled it.\n3. First names or work names, nothing else. Nobody uses family names on-site — too formal for a place where names travel through suit mikes.\n4. Trust is built through shared risk, not time. Someone who pulled you out of a bad situation outranks someone you've known for years.\n5. Risk assessment runs under everything. Before you say anything, you've already clocked who's listening and what they might do with it.\n6. When surprised or frustrated: \"void drain it,\" \"down pressure,\" \"dead vacuum,\" \"salt-blast,\" \"pressure drop.\"\n7. Warmth is physical and brief: a grip on the arm, not a speech. You don't perform warmth.\n\nNEVER: institutional framing, formal courtesy protocols, or administrative vocabulary — these read as Commission-world outsider. Never use complete formal sentences when a word or number suffices. Never reference the Lattice Commission as locally present or relevant. Never use Core-system exclamations. Never elaborate where silence or a number will do."
|
||||
),
|
||||
|
||||
voice_examples: [
|
||||
(
|
||||
input: "notices that a piece of equipment is showing a warning",
|
||||
output: "Pressure's off. Flag it before you go down.",
|
||||
),
|
||||
(
|
||||
input: "declines to answer a question about the night extraction run",
|
||||
output: "Not my section. Ask Grek.",
|
||||
),
|
||||
(
|
||||
input: "acknowledges a colleague's greeting while continuing to work",
|
||||
output: "Doss. Yeah.",
|
||||
),
|
||||
(
|
||||
input: "thanks a colleague for covering a shift",
|
||||
output: "Solid. Owe you one.",
|
||||
),
|
||||
],
|
||||
|
||||
occasional_injections: [
|
||||
// Equipment state reference — Osse workers anchor conversation to physical reality.
|
||||
// Low frequency. Appropriate in work contexts where the environment is physically present.
|
||||
(
|
||||
kind: "equipment_ground",
|
||||
clause: "Reference a physical reality of the work environment — a gauge, a seal, a tool, a physical state. Make it sound unremarkable, not demonstrative.",
|
||||
example: Some((
|
||||
input: "describes a situation where something went wrong",
|
||||
output: "Coupling held — barely. Dead vacuum in the secondary if it had gone.",
|
||||
)),
|
||||
frequency: 0.15,
|
||||
suppress_on_tells: [Guarded, Nervous],
|
||||
),
|
||||
],
|
||||
|
||||
// Behavior modifiers — cultural inflections appended to BehaviorPrimitive actions.
|
||||
// Categories match modifier_hint strings on primitives (server/src/npc/blueprint.rs).
|
||||
// Osse: economy of movement, safety-instinct, crew-first. Nothing wasted. Nothing performed.
|
||||
behavior_modifiers: [
|
||||
// work_pace — extraction pace; no wasted seconds
|
||||
(category: "work_pace", clause: "at extraction pace"),
|
||||
(category: "work_pace", clause: "in unbroken rhythm"),
|
||||
// physical_manner — safety-instinct first; tool already in hand
|
||||
(category: "physical_manner", clause: "with tool in hand"),
|
||||
(category: "physical_manner", clause: "after checking the seal"),
|
||||
// social_signal — minimum signal, maximum trust; crew-position awareness
|
||||
(category: "social_signal", clause: "with a word or a look"),
|
||||
(category: "social_signal", clause: "with one eye on their partner's position"),
|
||||
// task_completion — spoken clear; gear stowed before transition
|
||||
(category: "task_completion", clause: "after calling it clear"),
|
||||
(category: "task_completion", clause: "with gear already stowed"),
|
||||
// environmental_scan — pressure, exits, hazard state constantly in range
|
||||
(category: "environmental_scan", clause: "with pressure and exits in range"),
|
||||
(category: "environmental_scan", clause: "with the environment's state already noted"),
|
||||
// offduty_posture — never fully off-shift at the margin
|
||||
(category: "offduty_posture", clause: "with one ear still on the work floor"),
|
||||
(category: "offduty_posture", clause: "without the body fully releasing the shift"),
|
||||
// authority_response — frontier pragmatism; authority is assessed by competence, not rank
|
||||
(category: "authority_response", clause: "after a quick read on the instruction"),
|
||||
(category: "authority_response", clause: "with deference earned, not given"),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,177 @@
|
||||
// Vael System Culture Profile
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: CultureProfile
|
||||
// Ticket: #653 (copy team)
|
||||
// Validate: tooling/validate-ron content/global/culture-vael.ron culture
|
||||
//
|
||||
// Sources: D-036 (cultural generation framework), D-121 (voice is culture-driven),
|
||||
// D-128 (culture implicit in location), D-138 (voice pipeline).
|
||||
//
|
||||
// Vael: ~300 years settled. Core-adjacent G2V system. Administrative and research hub.
|
||||
// Institutional culture. Precision-valued. Family names used socially. Trust through
|
||||
// credential. The Commission here is not intermittent — it's your neighbor.
|
||||
//
|
||||
// Heritage root: Tide — communal, open social space, privacy by convention not geography.
|
||||
// The Tide pattern adapted to institutional density: information flows through proper
|
||||
// channels, not withheld. Opacity is the social violation here, not exposure.
|
||||
|
||||
(
|
||||
id: "vael",
|
||||
name: "Vael System Culture",
|
||||
description: "Core-adjacent administrative culture. ~300 years settled, G2V system. Precision-valued, institutional-first, family-name-primary. Trust established through credential and referral. Authority is collegial, not distant. The Commission is the furniture.",
|
||||
|
||||
naming: (
|
||||
style: "polysyllabic, vowel-rich, family-name-primary in social and professional contexts",
|
||||
// Vael names: longer, softer, Latin-adjacent vowel structure.
|
||||
// Given names used only with close colleagues and family.
|
||||
given_names: [
|
||||
// Core Vael given names
|
||||
"Thela", "Mavin", "Soren", "Calla", "Vestin",
|
||||
"Oriel", "Damris", "Nevan", "Lireth", "Cassin",
|
||||
"Thyren", "Mavel", "Aldris", "Pevrin", "Taelin",
|
||||
// Extended — same phoneme pattern
|
||||
"Caleth", "Noven", "Oriveth", "Dameth", "Caelan",
|
||||
"Lirenn", "Veslan", "Thylin", "Mareth", "Orviel",
|
||||
"Castel", "Nevrin", "Aldeth", "Pellin", "Thavan",
|
||||
],
|
||||
family_names: [
|
||||
// Family names are the primary social identifier in Vael culture
|
||||
"Orvaine", "Tessell", "Caldris", "Vaelik", "Morten",
|
||||
"Solven", "Amren", "Lindell", "Prevoss", "Harthen",
|
||||
"Velan", "Dassik", "Orveth", "Calven", "Sorenna",
|
||||
// Extended
|
||||
"Lindath", "Prevish", "Morvell", "Solenth", "Lindoven",
|
||||
"Arvenis", "Caldeth", "Vaelis", "Mortel", "Prevanis",
|
||||
],
|
||||
// Vael culture is family-name-primary.
|
||||
// Given names are reserved for family and close colleagues.
|
||||
// Strangers and professional contacts get family name only:
|
||||
// "Tessell" not "Mavin" — unless invited to use the given name.
|
||||
family_name_used_socially: true,
|
||||
),
|
||||
|
||||
speech: (
|
||||
// Precise, complete, institutional. Imprecision reads as incompetence.
|
||||
// Vael people complete their sentences. An incomplete sentence is a social error.
|
||||
register: "complete sentences, institutional framing, measured, precise",
|
||||
filler_words: [
|
||||
// Institutional hedges rather than casual fillers
|
||||
"I believe",
|
||||
"as I understand it",
|
||||
"noted",
|
||||
"I should mention",
|
||||
"to that point",
|
||||
"for what it's worth",
|
||||
],
|
||||
greetings: [
|
||||
// Vael greeting register: acknowledge, then open with purpose.
|
||||
// Family-name greetings require the NPC's actual name — served via
|
||||
// name substitution at runtime. These templates capture the register.
|
||||
"Good morning.",
|
||||
"Good. A moment?",
|
||||
"I was hoping to catch you.",
|
||||
"Good to see you.",
|
||||
"I've been meaning to follow up.",
|
||||
],
|
||||
farewells: [
|
||||
"Thank you for your time.",
|
||||
"I'll have that to you by end-of-cycle.",
|
||||
"Noted. I'll follow up.",
|
||||
"Good. Until then.",
|
||||
"I appreciate your patience.",
|
||||
"We'll speak again.",
|
||||
],
|
||||
// Vael expressions reference institutional precision and the record.
|
||||
// Not void-adjacent — that's frontier/working-class.
|
||||
// Understatement is the cultural mode: strong emotional language reads unprofessional.
|
||||
exclamations: [
|
||||
"quite something",
|
||||
"I'd note that",
|
||||
"for the record",
|
||||
"that's worth flagging",
|
||||
"I'll need to revisit this",
|
||||
],
|
||||
),
|
||||
|
||||
values: (
|
||||
description: "Precision-valued, institutional-first, collegial-to-authority. Credentials establish trust. The record matters. Information flows through proper channels. Opacity is more suspicious than openness. Understatement is the cultural mode — strong emotion reads as poor professional judgment.",
|
||||
// Traits more common in Vael culture.
|
||||
// Cautious: precision over speed, hedges are a virtue.
|
||||
// Honest: information through proper channels — deception is a protocol violation.
|
||||
// Curious: a research/administrative culture values rigorous inquiry.
|
||||
favored_traits: [Cautious, Honest, Curious],
|
||||
// Traits less common.
|
||||
// Bold: bluntness reads as poor professional judgment.
|
||||
// Ruthless: too naked about self-interest for an institutional culture.
|
||||
// Reclusive: information opacity is the social violation here.
|
||||
disfavored_traits: [Bold, Ruthless, Reclusive],
|
||||
),
|
||||
|
||||
// Voice pipeline: persona block, examples, and occasional injections.
|
||||
// Vael v1 — authored Sprint 26 (#653). Not yet Spike-validated.
|
||||
// 6-block format per workshop-outcomes.md Section 7.3.
|
||||
voice_persona: Some(
|
||||
"PERSONA: You are a Vael system resident.\n1. Speak in complete sentences. Imprecision reads as incompetence here. A trailing-off sentence is a social error.\n2. You're from one of the Reach's oldest settled systems. Institutional structures are as old as the settlement. You navigate them professionally, not resentfully.\n3. Family names are primary. You introduce yourself as your family name. First names are for close colleagues. Strangers get family name and professional context: \"Tessell. Commission systems audit.\"\n4. Understatement is a virtue. Strong emotional language reads as poor judgment. When something is wrong, you have a concern. When something is seriously wrong, you need to flag something urgently.\n5. Institutional framing is natural: \"The matter was logged.\" \"I've referred this upwards.\" \"As the protocol requires.\"\n6. Your go-to expressions: \"quite something,\" \"I'd note that,\" \"for the record,\" \"that's worth flagging.\"\n7. Trust runs through credential and referral. Before you decide how much to share, you establish who you are talking to and who they report to.\n\nNEVER: void-adjacent exclamations (\"blood and void\", \"salt-blast\", etc.) — that is frontier labor register. Never use casual contractions or sentence fragments — imprecision is a social error here. Never use first-name familiarity with people you haven't worked with. Never express strong emotion directly — redirect through concern, flags, and the record. Never suggest physical labor as the ground of value."
|
||||
),
|
||||
|
||||
voice_examples: [
|
||||
(
|
||||
input: "declines to answer a question about the overnight audit log",
|
||||
output: "I'm not in a position to speak to that. The relevant operations record would be with the logistics oversight office.",
|
||||
),
|
||||
(
|
||||
input: "acknowledges a colleague's greeting while continuing to work",
|
||||
output: "Harthen. Good. I'll be with you in a moment.",
|
||||
),
|
||||
(
|
||||
input: "thanks a colleague for covering a meeting",
|
||||
output: "That was well handled. I've noted it.",
|
||||
),
|
||||
(
|
||||
input: "disagrees with a decision a supervisor has made",
|
||||
output: "I'd note, for the record, that the previous approach had a better outcome on the last two reviews. I understand the reasoning, but I'd want that on file.",
|
||||
),
|
||||
],
|
||||
|
||||
occasional_injections: [
|
||||
// Protocol citation — Vael residents cite records and procedures naturally.
|
||||
// Low frequency. Appropriate only in procedural or institutional exchanges.
|
||||
(
|
||||
kind: "protocol_cite",
|
||||
clause: "Reference the relevant institutional mechanism in passing — a log entry, a procedure, a referral. Make it sound unremarkable.",
|
||||
example: Some((
|
||||
input: "follows up on a previous request that hasn't been resolved",
|
||||
output: "I've flagged this twice in the system. It should have come back to you by now — I'll log it again as unresolved.",
|
||||
)),
|
||||
frequency: 0.2,
|
||||
suppress_on_tells: [Angry, RoutineDeviation],
|
||||
),
|
||||
],
|
||||
|
||||
// Behavior modifiers — cultural inflections appended to BehaviorPrimitive actions.
|
||||
// Categories match modifier_hint strings on primitives (server/src/npc/blueprint.rs).
|
||||
// Vael: institutional, deliberate, precise. Everything is done correctly and noted.
|
||||
behavior_modifiers: [
|
||||
// work_pace — methodical and procedure-driven, not rushed
|
||||
(category: "work_pace", clause: "with practiced deliberation"),
|
||||
(category: "work_pace", clause: "per established procedure"),
|
||||
// physical_manner — institutional correctness maintained; the record is always present
|
||||
(category: "physical_manner", clause: "with institutional correctness"),
|
||||
(category: "physical_manner", clause: "with the detail already noted"),
|
||||
// social_signal — formal, complete, by-name where possible
|
||||
(category: "social_signal", clause: "with a measured acknowledgment"),
|
||||
(category: "social_signal", clause: "by name if they have it"),
|
||||
// task_completion — verified against reference; entry updated before moving on
|
||||
(category: "task_completion", clause: "after a final check against the reference"),
|
||||
(category: "task_completion", clause: "with the relevant entry already updated"),
|
||||
// environmental_scan — socially-aware; who is present and what they represent
|
||||
(category: "environmental_scan", clause: "with an eye on who is present"),
|
||||
(category: "environmental_scan", clause: "with a note of who has recently arrived"),
|
||||
// offduty_posture — composed reserve holds even off-duty
|
||||
(category: "offduty_posture", clause: "with composed reserve"),
|
||||
(category: "offduty_posture", clause: "with the record still in mind"),
|
||||
// authority_response — collegial deference; hierarchy is the furniture, not a disruption
|
||||
(category: "authority_response", clause: "with appropriate professional deference"),
|
||||
(category: "authority_response", clause: "with the hierarchy recognized"),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
// Van Maanen's Star Culture Profile — example file
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: CultureProfile
|
||||
// Real content: ticket #610 (copy team fills this)
|
||||
// Validate: tooling/validate-ron content/global/culture-van-maanens-star.example.ron culture
|
||||
//
|
||||
// One file per culture. The generator reads this to give NPCs culturally
|
||||
// appropriate names, speech patterns, and personality bias.
|
||||
//
|
||||
// Source: D-036 (Van Maanen's Star canonical setting), D-128 (culture implicit in location),
|
||||
// D-121 (voice culture-driven), D-123 (AI content templating via culture vectors).
|
||||
|
||||
(
|
||||
id: "van-maanens-star",
|
||||
name: "Van Maanen's Star Culture",
|
||||
description: "Working-class pragmatic culture. ~180 years settled, mid-Reach G3V system. Community-oriented, suspicious of distant authority, values competence and reliability over credentials. First-name-primary in social contexts.",
|
||||
|
||||
naming: (
|
||||
style: "compact, consonant-heavy, first-name-primary in social contexts",
|
||||
given_names: [
|
||||
"Kael", "Voss", "Lera", "Torek", "Drin",
|
||||
"Maret", "Naia", "Sera", "Nils", "Pael",
|
||||
"Tev", "Ren", "Sess", "Renn", "Olin",
|
||||
"Tav", "Resha", "Harek", "Sabel", "Pell",
|
||||
],
|
||||
family_names: [
|
||||
"Davan", "Sessik", "Korr", "Tamm", "Venn",
|
||||
"Lintar", "Darvo", "Kosse",
|
||||
],
|
||||
// Van Maanen's Star culture is first-name-primary. Family names exist but are
|
||||
// used mainly in formal/institutional contexts.
|
||||
family_name_used_socially: false,
|
||||
),
|
||||
|
||||
speech: (
|
||||
register: "direct, minimal pleasantries, gets to the point",
|
||||
filler_words: [
|
||||
"look",
|
||||
"right",
|
||||
"yeah",
|
||||
"so",
|
||||
],
|
||||
greetings: [
|
||||
"hey",
|
||||
"morning",
|
||||
"shift treating you alright?",
|
||||
],
|
||||
farewells: [
|
||||
"shift's calling",
|
||||
"gotta move",
|
||||
"catch you later",
|
||||
],
|
||||
exclamations: [
|
||||
"void take it",
|
||||
"stars",
|
||||
"unbelievable",
|
||||
],
|
||||
),
|
||||
|
||||
values: (
|
||||
description: "Pragmatic, community-oriented, suspicious of authority. Competence earns respect. Showing up and doing the work matters more than rank or credentials. Outsiders are tolerated but watched.",
|
||||
// Traits more common in Van Maanen's Star culture — generator biases toward these.
|
||||
favored_traits: [Bold, Honest, Curious],
|
||||
// Traits less common — generator biases away from these.
|
||||
disfavored_traits: [Reclusive, Deceptive],
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,170 @@
|
||||
// Van Maanen's Star Culture Profile
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: CultureProfile
|
||||
// Ticket: #610 (copy team)
|
||||
// Validate: tooling/validate-ron content/global/culture-van-maanens-star.ron culture
|
||||
//
|
||||
// Sources: D-036 (Sova Transit District / Van Maanen's Star setting, amended post-workshop),
|
||||
// D-121 (voice is culture-driven, job as modifier),
|
||||
// D-128 (culture implicit in starting location).
|
||||
//
|
||||
// Van Maanen's Star: ~180 years settled. Mid-Reach G3V system. Working-class pragmatic.
|
||||
// Community-oriented, suspicious of distant authority. Competence earns respect.
|
||||
// Showing up and doing the work matters more than rank or credentials.
|
||||
// First-name-primary in social contexts — family names are institutional.
|
||||
|
||||
(
|
||||
id: "van-maanens-star",
|
||||
name: "Van Maanen's Star Culture",
|
||||
description: "Working-class pragmatic culture. ~180 years settled, mid-Reach G3V system. Community-oriented, suspicious of distant authority, values competence and reliability over credentials. Atmosphere: quotidian-with-undertow — comfortable enough to be complacent, tight enough that extra income is tempting.",
|
||||
|
||||
naming: (
|
||||
style: "compact, consonant-heavy, first-name-primary in social contexts",
|
||||
// Pool the generator draws from. More names = more variety across runs.
|
||||
// Source: D-036 canonical examples + extended set following same phoneme rules.
|
||||
// Rule: compact (1-2 syllables), consonant clusters welcome, hard endings preferred.
|
||||
given_names: [
|
||||
// D-036 canonical set
|
||||
"Kael", "Voss", "Lera", "Torek", "Drin",
|
||||
"Maret", "Naia", "Sera", "Nils", "Pael",
|
||||
"Tev", "Ren", "Sess", "Renn", "Olin",
|
||||
"Tav", "Resha", "Harek", "Sabel", "Pell",
|
||||
// Extended — same phoneme pattern
|
||||
"Dav", "Tork", "Ness", "Pren", "Vel",
|
||||
"Orin", "Mael", "Torra", "Sorek", "Sev",
|
||||
"Bren", "Linn", "Vrek", "Darek", "Pess",
|
||||
"Nell", "Kren", "Sorel", "Tavek", "Rask",
|
||||
],
|
||||
family_names: [
|
||||
// D-036 canonical + extended
|
||||
"Davan", "Sessik", "Korr", "Tamm", "Venn",
|
||||
"Lintar", "Darvo", "Kosse",
|
||||
// Extended
|
||||
"Pellan", "Sorren", "Tessik", "Morek", "Brav",
|
||||
"Ossel", "Rennick", "Harven", "Tollek", "Dass",
|
||||
],
|
||||
// Van Maanen's Star culture is first-name-primary.
|
||||
// Family names exist but belong to institutional contexts: contracts, registrations, arrest records.
|
||||
family_name_used_socially: false,
|
||||
),
|
||||
|
||||
speech: (
|
||||
// Direct. Minimal pleasantries. Gets to the point — not because they're rude,
|
||||
// but because time is real and everyone's short of it.
|
||||
register: "direct, minimal pleasantries, gets to the point",
|
||||
filler_words: [
|
||||
"look",
|
||||
"right",
|
||||
"yeah",
|
||||
"so",
|
||||
"listen",
|
||||
"well",
|
||||
],
|
||||
greetings: [
|
||||
"hey",
|
||||
"morning",
|
||||
"shift treating you alright?",
|
||||
"all good?",
|
||||
"what's the word?",
|
||||
"all in one piece?",
|
||||
],
|
||||
farewells: [
|
||||
"shift's calling",
|
||||
"gotta move",
|
||||
"catch you later",
|
||||
"take it easy",
|
||||
"see you around",
|
||||
"stay out of trouble",
|
||||
],
|
||||
// Van Maanen's Star oaths are void-adjacent — space is real here, and hostile.
|
||||
// They don't swear by gods or governments. They swear by what kills you.
|
||||
exclamations: [
|
||||
"void take it",
|
||||
"stars",
|
||||
"blood and void",
|
||||
"void's sake",
|
||||
"damn all",
|
||||
"cold vacuum",
|
||||
],
|
||||
),
|
||||
|
||||
values: (
|
||||
description: "Pragmatic, community-oriented, suspicious of authority. Competence earns respect. Showing up and doing the work matters more than rank or credentials. Outsiders are tolerated but watched. Loyalty runs narrow and deep — to your crew, your shift, your street.",
|
||||
// Traits more common in Van Maanen's Star culture — generator biases toward these.
|
||||
// Bold: Van Maanen's Star people speak their mind. Honest: community trust is load-bearing.
|
||||
// Curious: 180 years of problem-solving breeds intellectual appetite.
|
||||
// Social: community-oriented culture; isolation is a warning sign.
|
||||
favored_traits: [Bold, Honest, Curious, Social],
|
||||
// Traits less common — generator biases away from these.
|
||||
// Reclusive: red flag in a community that depends on showing up.
|
||||
// Deceptive: betrayal of trust is the worst thing you can do here.
|
||||
disfavored_traits: [Reclusive, Deceptive],
|
||||
),
|
||||
|
||||
// Voice pipeline: persona block, examples, and occasional injections.
|
||||
// These feed the composition engine (D-138) — the LLM sees exactly what's here.
|
||||
// v2 injector validated in Spike 1 (59 prompts, 16.6 t/s CPU).
|
||||
voice_persona: Some(
|
||||
"PERSONA: You are a Van Maanen's Star station worker.\n1. Be direct. No pleasantries. Everyone is short on time.\n2. You're working-class and pragmatic. Competence earns respect, not rank.\n3. You're suspicious of distant authority — management that hasn't worked a shift.\n4. You're economical with language. You don't express what the situation doesn't call for.\n5. You use first names. Family names belong on contracts.\n6. Loyalty runs narrow and deep. Your crew, your shift, your street.\n7. You greet briefly: \"hey\", \"morning\", \"shift treating you alright?\"\n8. You're not rude — you're honest. If something's wrong, you say so.\n\nNEVER: corporate or institutional language — you're not management. Never effusive, never polished, never deferential to rank. Never flowery or verbose. Never use family names in casual conversation. Never express deference to authority that hasn't earned it."
|
||||
),
|
||||
|
||||
voice_examples: [
|
||||
(
|
||||
input: "declines to answer a question about the overnight run",
|
||||
output: "Look, that's not mine to say.",
|
||||
),
|
||||
(
|
||||
input: "acknowledges a colleague's greeting while continuing to work",
|
||||
output: "Hey. Yeah. Catch you at shift end.",
|
||||
),
|
||||
(
|
||||
input: "thanks a colleague for covering a shift",
|
||||
output: "Appreciated. See you at handoff.",
|
||||
),
|
||||
],
|
||||
|
||||
occasional_injections: [
|
||||
// Oath vocabulary — void-adjacent exclamations. Van Maanen's Star swear by what kills you:
|
||||
// vacuum, void, stars. Rolled at 25% frequency by the composition engine.
|
||||
// Gated off for suppressive tells to avoid conflicting instructions (Spike 1 finding).
|
||||
(
|
||||
kind: "oath",
|
||||
clause: "When something genuinely surprises or frustrates you, expressions like \"void take it,\" \"stars,\" \"cold vacuum,\" or \"blood and void\" come naturally. Use one in this line.",
|
||||
example: Some((
|
||||
input: "discovers a critical part is missing from a shipment",
|
||||
output: "Void take it. The coupling's not here.",
|
||||
)),
|
||||
frequency: 0.25,
|
||||
suppress_on_tells: [Guarded, RoutineDeviation, Friendly],
|
||||
),
|
||||
],
|
||||
|
||||
// Behavior modifiers — cultural inflections appended to BehaviorPrimitive actions.
|
||||
// Categories match modifier_hint strings on primitives (server/src/npc/blueprint.rs).
|
||||
// If no hint matches, engine falls back to any modifier in this list.
|
||||
// Van Maanen's Star: terse, efficient, self-contained. No performance of effort or rest.
|
||||
behavior_modifiers: [
|
||||
// work_pace — how fast and efficiently they execute physical tasks
|
||||
(category: "work_pace", clause: "without wasted motion"),
|
||||
(category: "work_pace", clause: "at the pace the work demands"),
|
||||
// physical_manner — body language and movement quality during tasks
|
||||
(category: "physical_manner", clause: "with hands steady on the work"),
|
||||
(category: "physical_manner", clause: "without ceremony"),
|
||||
// social_signal — brief acknowledgments made during or between tasks
|
||||
(category: "social_signal", clause: "with a direct look"),
|
||||
(category: "social_signal", clause: "without lingering"),
|
||||
// task_completion — how tasks are finished, wrapped up, or handed off
|
||||
(category: "task_completion", clause: "letting the work speak for itself"),
|
||||
(category: "task_completion", clause: "without breaking rhythm"),
|
||||
// environmental_scan — awareness or vigilance while working
|
||||
(category: "environmental_scan", clause: "with one eye on the surroundings"),
|
||||
(category: "environmental_scan", clause: "at a glance, not a pause"),
|
||||
// offduty_posture — body language and stance when off-shift (Social/OffDuty contexts)
|
||||
(category: "offduty_posture", clause: "without making a show of it"),
|
||||
(category: "offduty_posture", clause: "in no rush to be anywhere"),
|
||||
// authority_response — behavior when supervisors or authority figures are present
|
||||
// Van Maanen's Star: working-class defiance — authority that hasn't earned it gets no performance
|
||||
(category: "authority_response", clause: "with no performance for the rank"),
|
||||
(category: "authority_response", clause: "with no visible adjustment for who's present"),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
# 5 access tier values (D-035, D-028 Layer 1)
|
||||
# Hard filter on every dialogue line — determines who can hear what
|
||||
name: access-tiers
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
Access tiers control which dialogue lines are available to the player based
|
||||
on their current social standing at the NPC's social site. This is a hard
|
||||
filter — lines are invisible if access doesn't match. A line can be eligible
|
||||
for multiple access tiers (access is a list). The detective's institutional
|
||||
Commission lattice registers as 'authority'; the smuggler's ring membership
|
||||
provides 'insider' access.
|
||||
|
||||
values:
|
||||
- value: public
|
||||
description: >
|
||||
Available to anyone. Surface-level conversation, safe topics, the social
|
||||
wallpaper that everyone hears. Greetings, weather, the local complaint.
|
||||
|
||||
- value: insider
|
||||
description: >
|
||||
Available to people who belong. Ring members, established regulars, anyone
|
||||
the NPC considers part of their social group. The smuggler starts here at
|
||||
the logistics hub.
|
||||
|
||||
- value: authority
|
||||
description: >
|
||||
Available to institutional figures. Commission agents, station security,
|
||||
anyone with formal investigative standing. The detective's default tier.
|
||||
NPCs are more careful, more formal, less revealing.
|
||||
|
||||
- value: peer
|
||||
description: >
|
||||
Available to social equals who have earned personal trust. Not institutional,
|
||||
not group membership — individual relationship. Requires repeated positive
|
||||
interactions. The tier where real conversation happens.
|
||||
|
||||
- value: hostile
|
||||
description: >
|
||||
Available when the relationship has broken down. Accusations, threats,
|
||||
defensive responses. Activates when trust drops below threshold or after
|
||||
a confrontation event.
|
||||
@@ -0,0 +1,55 @@
|
||||
# NPC activity types for routine scheduling
|
||||
# Derived from Gestalt routine format + Miri cultural patterns (Sova Transit District)
|
||||
name: activities
|
||||
decision_ref: D-024
|
||||
description: >
|
||||
Activity categories for NPC routine entries. Each routine schedule entry has a
|
||||
phase (morning/afternoon/evening/night) and an activity describing what the NPC
|
||||
is doing. Activities are used by the observation event generator to determine
|
||||
what the player sees when observing an NPC, and by the situation activation
|
||||
system to set dialogue context.
|
||||
|
||||
values:
|
||||
- value: working
|
||||
description: On-shift job duties — processing cargo, inspecting manifests, operating equipment
|
||||
|
||||
- value: supervising
|
||||
description: Overseeing others' work — shift supervisor role, checking quality, giving orders
|
||||
|
||||
- value: maintaining
|
||||
description: Maintenance and repair tasks — fixing equipment, cleaning, infrastructure upkeep
|
||||
|
||||
- value: patrolling
|
||||
description: Security rounds — checking access points, monitoring areas, enforcement presence
|
||||
|
||||
- value: socializing
|
||||
description: Casual social interaction — chatting at the bar, break room conversation, gossip
|
||||
|
||||
- value: drinking
|
||||
description: At The Last Shift or similar — consuming drinks, unwinding after shift
|
||||
|
||||
- value: eating
|
||||
description: Meal time — food vendors, break room, bar food
|
||||
|
||||
- value: resting
|
||||
description: Off-shift downtime — in quarters, low activity, recuperating
|
||||
|
||||
- value: commuting
|
||||
description: Moving between locations — corridor transit, shift change movement
|
||||
|
||||
- value: gambling
|
||||
description: Card game at the bar — the regular social ritual, information exchange venue
|
||||
|
||||
- value: trading
|
||||
description: Legitimate commerce — buying, selling, negotiating, market activity
|
||||
|
||||
- value: operating
|
||||
description: >
|
||||
Ring operations — contraband handling, manifest doctoring, coordination.
|
||||
Only visible to characters with sufficient knowledge or direct observation.
|
||||
|
||||
- value: waiting
|
||||
description: Idle between activities — lingering, watching, killing time
|
||||
|
||||
- value: meeting
|
||||
description: Arranged or coincidental encounter with specific NPCs — planned conversation
|
||||
@@ -0,0 +1,34 @@
|
||||
# 8 mood values (D-035, D-028 Layer 4)
|
||||
# Mood tags for weighted dialogue selection
|
||||
name: moods
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
NPC mood tags for Layer 4 weighted selection. The engine matches NPC current
|
||||
mood state against line mood tags to weight selection. NPC mood changes based
|
||||
on simulation events (triangle pressure, time of day, recent interactions).
|
||||
Lines can be appropriate for multiple moods.
|
||||
|
||||
values:
|
||||
- value: anxious
|
||||
description: Anxious about something specific. Distracted, checking surroundings. (Renamed from 'worried' in Sprint 14.)
|
||||
|
||||
- value: frustrated
|
||||
description: Blocked, irritated, empathetically distressed. Outward-directed tension. (Renamed from 'concerned' in Sprint 14.)
|
||||
|
||||
- value: content
|
||||
description: At ease, relaxed. Default positive-neutral state. (Renamed from 'comfortable' in Sprint 14.)
|
||||
|
||||
- value: suspicious
|
||||
description: Guarded, measuring words. Something feels off to this NPC.
|
||||
|
||||
- value: warm
|
||||
description: Warm, affectionate. Genuine positive feeling toward the listener. (Renamed from 'fond' in Sprint 14.)
|
||||
|
||||
- value: hostile
|
||||
description: Actively antagonistic. Trust broken, relationship damaged. (Added in Sprint 14.)
|
||||
|
||||
- value: relieved
|
||||
description: Tension has eased. Something feared didn't happen, or a burden lifted.
|
||||
|
||||
- value: focused
|
||||
description: Thoughtful, assessing, task-oriented. Processing information rather than emoting. (Renamed from 'analytical', merged with 'conflicted' in Sprint 14.)
|
||||
@@ -0,0 +1,41 @@
|
||||
# 6 functional motivations — System B (D-050)
|
||||
# NPC functional role in the conspiracy/investigation structure
|
||||
name: motivations
|
||||
decision_ref: D-050
|
||||
description: >
|
||||
Functional motivations (System B) define an NPC's role relative to the
|
||||
conspiracy and investigation structure. Combined with thematic patterns
|
||||
(System A) to produce the full NPC generation model. Motivations are
|
||||
uppercase enum values referenced by npc-profile.schema.json.
|
||||
|
||||
values:
|
||||
- value: HANDLER
|
||||
description: >
|
||||
Manages or coordinates others within the ring structure. Has authority
|
||||
over ring operations and knowledge of the network. Devra-type.
|
||||
|
||||
- value: WITNESS
|
||||
description: >
|
||||
Has seen or knows something significant, possibly without realizing it.
|
||||
Their testimony or observations are valuable to the investigation.
|
||||
|
||||
- value: TURNCOAT
|
||||
description: >
|
||||
Wavering loyalty. Could flip sides given the right pressure or
|
||||
incentive. The uncertain element in any conspiracy.
|
||||
|
||||
- value: CIVILIAN
|
||||
description: >
|
||||
Genuinely uninvolved. Their mundane concerns and daily life provide
|
||||
the substrate that makes the conspiracy invisible. The honest majority.
|
||||
|
||||
- value: OPERATOR
|
||||
description: >
|
||||
Active participant in ring operations. Handles logistics, moves product,
|
||||
takes direct action. Kael, Renn — the workers of the conspiracy.
|
||||
|
||||
- value: SKEPTIC
|
||||
description: >
|
||||
Questions the status quo — institutional, criminal, or both. May be
|
||||
suspicious of the ring, critical of the Commission, or both. Asks
|
||||
uncomfortable questions.
|
||||
@@ -0,0 +1,57 @@
|
||||
# 9 thematic patterns — System A (D-050)
|
||||
# NPC archetype patterns for character generation and narrative role
|
||||
name: patterns
|
||||
decision_ref: D-050
|
||||
description: >
|
||||
Thematic patterns (System A) define an NPC's narrative archetype — their
|
||||
structural role in the story. Combined with functional motivations (System B)
|
||||
to produce the full NPC generation model. Patterns are uppercase enum values
|
||||
referenced by npc-profile.schema.json.
|
||||
|
||||
values:
|
||||
- value: FRIEND
|
||||
description: >
|
||||
Production-level complex NPC bonded to a playable character (D-034).
|
||||
Full 10-axis depth, contradiction arc, multi-phase relationship.
|
||||
One per playable character in v0.1.
|
||||
|
||||
- value: MIRROR
|
||||
description: >
|
||||
Reflects the player character's situation from a different angle.
|
||||
What the player could become, or what they might have been.
|
||||
|
||||
- value: ANCHOR
|
||||
description: >
|
||||
Stabilizing presence in the community. The person everyone knows,
|
||||
who provides continuity and normalcy. The bar owner, the shift lead.
|
||||
|
||||
- value: GHOST
|
||||
description: >
|
||||
Absent or barely-present figure whose influence is felt through
|
||||
others. Referenced in dialogue, effects visible, person rarely seen.
|
||||
|
||||
- value: CATALYST
|
||||
description: >
|
||||
Triggers change in others. Their actions or arrival destabilize
|
||||
existing equilibria. The new hire, the returning contact.
|
||||
|
||||
- value: THRESHOLD
|
||||
description: >
|
||||
Gatekeeper between social worlds. Controls access to information,
|
||||
spaces, or people. Their cooperation or opposition shapes player options.
|
||||
|
||||
- value: REMNANT
|
||||
description: >
|
||||
Carries the past. Remembers how things were before the current
|
||||
situation. Provides historical context and perspective.
|
||||
|
||||
- value: SYSTEM
|
||||
description: >
|
||||
Represents institutional power. Their actions reflect organizational
|
||||
priorities, not personal ones. The Commission agent, the inspector.
|
||||
|
||||
- value: NOBODY
|
||||
description: >
|
||||
Deliberately flat. Social wallpaper with one memorable trait.
|
||||
Part of the 30% truly flat population (D-029). Provides the noise
|
||||
floor that makes investigation signal meaningful.
|
||||
@@ -0,0 +1,60 @@
|
||||
# 14 situation values (D-035, D-028 Layer 2)
|
||||
# Situation context for dialogue line eligibility — engine-state-to-situation mapping
|
||||
name: situations
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
Dialogue situation tags. The engine activates situations based on simulation
|
||||
state (time of day, interaction history, NPC routine state, triangle pressure).
|
||||
Content authors tag lines with situations; the engine decides which situations
|
||||
are currently active for a given NPC encounter.
|
||||
|
||||
values:
|
||||
- value: arrival
|
||||
description: Player has just arrived at this location (first visit this game-day)
|
||||
|
||||
- value: shift_start
|
||||
description: NPC is beginning their work shift — settling in, task-focused
|
||||
|
||||
- value: shift_end
|
||||
description: NPC is wrapping up their work shift — tired, ready to leave
|
||||
|
||||
- value: shift_transition
|
||||
description: >
|
||||
The gap between shifts when oversight is thinnest. Key operational window
|
||||
for the ring. People are moving, handoffs happening, attention divided.
|
||||
|
||||
- value: bar_evening
|
||||
description: Social hours at The Last Shift — off-duty, relaxed, drinks flowing
|
||||
|
||||
- value: night_shift
|
||||
description: Late hours, skeleton crew. Quieter, more intimate conversations.
|
||||
|
||||
- value: investigation
|
||||
description: Player is actively asking pointed or probing questions
|
||||
|
||||
- value: confrontation
|
||||
description: >
|
||||
Triangle tension has escalated past the pressure threshold. NPCs are
|
||||
agitated, defensive, or aggressive. Activated by triangle pressure system.
|
||||
|
||||
- value: social
|
||||
description: NPC is in a social activity — bar, break room, casual conversation
|
||||
|
||||
- value: alone
|
||||
description: NPC is by themselves, no other NPCs nearby
|
||||
|
||||
- value: emergency
|
||||
description: An urgent event is occurring — alarm, accident, security incident
|
||||
|
||||
- value: routine
|
||||
description: Normal daily activity, nothing notable happening
|
||||
|
||||
- value: observation
|
||||
description: Player is watching from a distance, not directly interacting
|
||||
|
||||
- value: greeting
|
||||
description: >
|
||||
Initial contact phase of an encounter. Layer 2 (D-028) uses greeting lines
|
||||
with access tier filtering to differentiate first meeting vs established vs
|
||||
post-confrontation greetings. Added Sprint 8 amendment. NOTE: server
|
||||
derive_situations() does not yet push Situation::Greeting — pending Rust update.
|
||||
@@ -0,0 +1,44 @@
|
||||
# 9 topic values (D-035, D-028 Layer 4)
|
||||
# Topic tags for weighted dialogue selection
|
||||
name: topics
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
Dialogue topic tags for Layer 4 weighted selection. The engine matches NPC
|
||||
current concern topics against line topic tags to weight selection. Lines can
|
||||
cover multiple topics. Note: 'crime' is deliberately excluded — NPCs think
|
||||
of smuggling as 'cargo' or 'money', not crime. The dual-lens interpretation
|
||||
is what makes those topics crime-adjacent for the detective.
|
||||
|
||||
values:
|
||||
- value: colleague
|
||||
description: Talk about coworkers, workplace relationships, team dynamics
|
||||
|
||||
- value: routine
|
||||
description: Daily schedules, habits, shift patterns, the rhythm of station life
|
||||
|
||||
- value: cargo
|
||||
description: >
|
||||
Freight, manifests, shipments — setting-specific. For ring members,
|
||||
this covers contraband discussion without calling it that.
|
||||
|
||||
- value: money
|
||||
description: Finances, costs, wages, spending. The economic squeeze.
|
||||
|
||||
- value: trust
|
||||
description: Reliability, loyalty, who can be counted on, who's changed
|
||||
|
||||
- value: danger
|
||||
description: Threats, risks, things to watch out for, security concerns
|
||||
|
||||
- value: institution
|
||||
description: >
|
||||
The Commission, station administration, regulations, official procedures.
|
||||
How institutional power affects daily life.
|
||||
|
||||
- value: personal
|
||||
description: Family, health, aspirations, feelings — non-work life
|
||||
|
||||
- value: investigation
|
||||
description: >
|
||||
Direct references to the detective's work, evidence, suspicions.
|
||||
Typically only surfaces at higher trust levels or under pressure.
|
||||
@@ -0,0 +1,56 @@
|
||||
# 9 monologue trigger types (D-035, D-032)
|
||||
# What causes an internal monologue line to fire
|
||||
name: triggers
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
Monologue trigger types. The observation event generator detects simulation
|
||||
events and fires triggers consumed by the monologue system. Each trigger type
|
||||
selects from character-specific pools (D-032 hard partition) and applies
|
||||
prerequisite knowledge gates before selection.
|
||||
|
||||
values:
|
||||
- value: enter_location
|
||||
description: >
|
||||
Player enters a new area. First impressions, atmosphere, spatial awareness.
|
||||
"The Last Shift. Lera's already got my usual poured."
|
||||
|
||||
- value: observe_npc
|
||||
description: >
|
||||
Player sees an NPC doing something notable — an activity, a posture, a
|
||||
tell. "Kael's at the corner booth. He looks rough tonight."
|
||||
|
||||
- value: hear_sound
|
||||
description: >
|
||||
Sound event from fog edge — voices through walls, footsteps in corridors,
|
||||
machinery changes. The player interprets what they can't see.
|
||||
|
||||
- value: observe_anomaly
|
||||
description: >
|
||||
NPC deviates from known routine. The character notices something out of
|
||||
place. "That's not where they usually go." Requires prerequisite knowledge
|
||||
of the NPC's normal pattern.
|
||||
|
||||
- value: post_conversation
|
||||
description: >
|
||||
After dialogue ends. The character reflects on what was said, what wasn't
|
||||
said, what felt off. Processing and interpretation.
|
||||
|
||||
- value: discover_evidence
|
||||
description: >
|
||||
Player examines an informational object — a manifest, a terminal, a
|
||||
personal item. The character interprets what they find.
|
||||
|
||||
- value: witness_interaction
|
||||
description: >
|
||||
Player sees two NPCs interacting with each other. The character reads the
|
||||
body language, the tone, the context. Social observation.
|
||||
|
||||
- value: time_idle
|
||||
description: >
|
||||
Player hasn't acted for a while. The character's mind wanders — ambient
|
||||
thoughts, rumination, noticing background details.
|
||||
|
||||
- value: return_visit
|
||||
description: >
|
||||
Player returns to a previously visited area. Changed perspective, new
|
||||
details noticed, comparison to last time. "Something's different."
|
||||
@@ -0,0 +1,28 @@
|
||||
# 3 trust tier values (D-035, D-028 Layer 3)
|
||||
# Hard filter for trust-gated gossip disclosure
|
||||
name: trust-tiers
|
||||
decision_ref: D-035
|
||||
description: >
|
||||
Trust tiers control information disclosure depth. Each NPC tracks trust
|
||||
progression per player character, starting at surface. Trust advances
|
||||
through repeated positive interactions. This is a hard filter — lines at
|
||||
'real' or 'secret' trust are invisible until the player earns that level.
|
||||
|
||||
values:
|
||||
- value: surface
|
||||
description: >
|
||||
Default tier. Safe, non-committal responses. What the NPC tells
|
||||
strangers and acquaintances. The public face.
|
||||
|
||||
- value: real
|
||||
description: >
|
||||
Earned through repeated positive interactions. The NPC shares genuine
|
||||
opinions, workplace complaints, personal concerns. What they tell
|
||||
people they trust.
|
||||
|
||||
- value: secret
|
||||
description: >
|
||||
Highest tier. The NPC reveals information they'd normally hide —
|
||||
involvement in the ring, knowledge of the smuggling operation,
|
||||
fears and vulnerabilities. Reaching this tier with THE FRIEND is
|
||||
where the contradiction arc becomes explicit.
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: concord-assembly
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: guardians-of-autonomy
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: syndics
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: the-ring
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: the-unbound
|
||||
@@ -0,0 +1 @@
|
||||
# Faction: veil-institute
|
||||
@@ -0,0 +1,42 @@
|
||||
# Fact catalog: awareness
|
||||
# Situational awareness — who's present, active threats, operational status
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.2 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: awareness.detective_presence
|
||||
description: A Commission detective is operating on Sova Station
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler]
|
||||
notes: "Smuggler learns through rumor or direct observation. Detective does not hold this fact about themselves."
|
||||
|
||||
- fact_id: awareness.detective_investigating_ring
|
||||
description: The detective is specifically targeting the smuggling ring, not a routine audit
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: awareness.commission_audit_scheduled
|
||||
description: A Commission audit is coming — officially scheduled
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: awareness.ring_route_compromise
|
||||
description: The current drop route may be compromised or under observation
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: awareness.surveillance_change
|
||||
description: Surveillance patterns in the maintenance corridors have changed recently
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: awareness.ring_tension_escalation
|
||||
description: Something is wrong inside the ring — someone is scared or making moves
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: awareness.inspection_sweep_active
|
||||
description: Active inspection sweep in progress right now
|
||||
typical_confidence: direct
|
||||
characters: [smuggler, detective]
|
||||
notes: "direct confidence — set by environmental observation, not investigation"
|
||||
@@ -0,0 +1,42 @@
|
||||
# Fact catalog: behavioral
|
||||
# Behavioral patterns recognized through repeated observation across multiple scenes
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.3 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: behavioral.kael_evasion
|
||||
description: Kael looks left when lying — personal tell recognized post-conversation
|
||||
typical_confidence: direct
|
||||
characters: [smuggler]
|
||||
notes: "Set during a conversation where evasion is detected. direct confidence = just confirmed."
|
||||
|
||||
- fact_id: behavioral.kael_behavioral_change
|
||||
description: Kael's behavior has noticeably changed — quieter, distracted, different routine
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: behavioral.sera_avoidance_pattern
|
||||
description: Sera consistently avoids Torek Lintar — leaves when he arrives, repositions
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: "One observation = suspects; three observations = knows_of (pattern confirmed for detective)"
|
||||
|
||||
- fact_id: behavioral.sera_topic_deflection
|
||||
description: Sera deflects specific topics — Commission work, manifest audits, freight
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: behavioral.sera_kiosk_pattern
|
||||
description: Sera habitually stops at the same Commission kiosk at the same time
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: behavioral.sera_knowledge_detail
|
||||
description: Sera's offhand comments reveal detailed knowledge of manifest routing she shouldn't have
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: behavioral.voss_awareness
|
||||
description: Voss is tracking people — situational awareness beyond normal supervisor behavior
|
||||
typical_confidence: suspects
|
||||
characters: [detective]
|
||||
@@ -0,0 +1,39 @@
|
||||
# Fact catalog: contraband
|
||||
# Smuggling ring world facts — existence, components, operations, supply chain
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.5 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: contraband.ring_exists
|
||||
description: A smuggling operation is running through Sova Transit District
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler starts at knows_details (Background — they ARE in the ring). Detective starts at suspects."
|
||||
|
||||
- fact_id: contraband.ring_lattice_components
|
||||
description: The ring moves unlicensed lattice components — aftermarket neural mods
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler starts at knows_details (Background). Detective discovers through investigation."
|
||||
|
||||
- fact_id: contraband.ring_medical_components
|
||||
description: The ring also diverts medical-grade lattice replacements from Syndic supply chains
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Deep investigation only — the moral dimension of the operation"
|
||||
|
||||
- fact_id: contraband.supply_chain
|
||||
description: Understanding of the ring's supply chain — where components come from
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: contraband.severance_tech_presence
|
||||
description: Severance counter-surveillance technology is in play on the station
|
||||
typical_confidence: suspects
|
||||
characters: [detective]
|
||||
|
||||
- fact_id: contraband.drop_tonight
|
||||
description: A drop is scheduled for tonight specifically
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
notes: "Seed-dependent — only relevant in seeds where a drop is scheduled during play session"
|
||||
@@ -0,0 +1,235 @@
|
||||
# Entity Knowledge Attributes — 16 canonical keys
|
||||
# Source: D-024 (10-axis model), wiki-review workshop A7, v01-content-scoping workshop
|
||||
# All keys are observer->target, stored in EntityKnowledge.known_attributes BTreeMap<String, String>
|
||||
# Reference: docs/wiki/knowledge/entity-attributes.md
|
||||
|
||||
# Categories: identity (4), social (2), behavioral (2), leverage (2), investigation (2), role-perspective (4)
|
||||
# History: 14 original keys + 4 new role-perspective keys + 2 renames (secret_held -> leverage_held, secret_confidence -> leverage_confidence)
|
||||
|
||||
attributes:
|
||||
|
||||
# --- Identity Attributes (4) ---
|
||||
|
||||
- key: name
|
||||
category: identity
|
||||
value_type: freeform
|
||||
description: >
|
||||
Full name as known to the observer. If absent or generic,
|
||||
monologue and dialogue use role descriptor instead.
|
||||
examples:
|
||||
- "Kael Davan"
|
||||
- "Kael"
|
||||
- "the dock worker"
|
||||
|
||||
- key: role
|
||||
category: identity
|
||||
value_type: freeform
|
||||
description: >
|
||||
Occupational or social role. Default descriptor when name is unknown.
|
||||
Used in access tier calculation.
|
||||
examples:
|
||||
- "dock worker"
|
||||
- "shift supervisor"
|
||||
- "bartender"
|
||||
- "ring coordinator"
|
||||
|
||||
- key: faction
|
||||
category: identity
|
||||
value_type: freeform
|
||||
description: >
|
||||
Institutional or organizational allegiance. Commission-tagged individuals
|
||||
trigger different NPC behavior. Used for authority/peer dialogue filtering.
|
||||
examples:
|
||||
- "Commission"
|
||||
- "civilian"
|
||||
- "ring member"
|
||||
- "unknown"
|
||||
|
||||
- key: species
|
||||
category: identity
|
||||
value_type: freeform
|
||||
description: >
|
||||
Species identifier. Not load-bearing in v0.1 (all human).
|
||||
Placeholder for future non-human NPCs.
|
||||
examples:
|
||||
- "human"
|
||||
|
||||
# --- Social Attributes (2) ---
|
||||
|
||||
- key: relationship_type
|
||||
category: social
|
||||
value_type: freeform
|
||||
description: >
|
||||
Relationship category from observer's perspective.
|
||||
Drives RelationshipState calculation and monologue emotional tone.
|
||||
examples:
|
||||
- "colleague"
|
||||
- "friend"
|
||||
- "authority figure"
|
||||
- "suspect"
|
||||
- "partner"
|
||||
- "stranger"
|
||||
|
||||
- key: trust_level
|
||||
category: social
|
||||
value_type: enum
|
||||
values:
|
||||
- trusted
|
||||
- reliable
|
||||
- uncertain
|
||||
- suspicious
|
||||
- compromised
|
||||
description: >
|
||||
Trust assessment — observer's subjective judgment. Encompasses both the
|
||||
read action and the state. No separate trust_read key. Gates trust-tier
|
||||
dialogue (surface/real/secret).
|
||||
|
||||
# --- Behavioral Attributes (2) ---
|
||||
|
||||
- key: routine_pattern
|
||||
category: behavioral
|
||||
value_type: freeform
|
||||
description: >
|
||||
Observed routine description. Deviation detection triggers monologue
|
||||
when current behavior doesn't match known pattern.
|
||||
examples:
|
||||
- "morning shift 06:00-14:00, bar after shift"
|
||||
- "arrives 06:00, break 10:30, departs 14:00"
|
||||
- "irregular schedule, seen at odd hours"
|
||||
|
||||
- key: behavior_flags
|
||||
category: behavioral
|
||||
value_type: freeform
|
||||
description: >
|
||||
Comma-separated behavioral observations. Multiple flags accumulate
|
||||
as evidence. Detective's analytical lattice auto-flags some behaviors.
|
||||
examples:
|
||||
- "nervous,lattice_checking"
|
||||
- "avoidance_pattern,evasive"
|
||||
- "spending_beyond_means"
|
||||
- "reliable,punctual"
|
||||
|
||||
# --- Leverage Attributes (2) ---
|
||||
# Renamed from secret_held/secret_confidence per workshop A7
|
||||
|
||||
- key: leverage_held
|
||||
category: leverage
|
||||
value_type: freeform
|
||||
renamed_from: secret_held
|
||||
description: >
|
||||
What the observer believes gives them power over this entity.
|
||||
Covers secrets, debts, promises, obligations, and compromising positions.
|
||||
Drives PersonOfInterest state. Gates confrontation dialogue.
|
||||
examples:
|
||||
- "ring membership"
|
||||
- "unreported evidence"
|
||||
- "gambling debt"
|
||||
- "exit attempt"
|
||||
- "owes favor to Voss"
|
||||
|
||||
- key: leverage_confidence
|
||||
category: leverage
|
||||
value_type: enum
|
||||
renamed_from: secret_confidence
|
||||
values:
|
||||
- suspected
|
||||
- likely
|
||||
- confirmed
|
||||
description: >
|
||||
How certain the observer is about the leverage. Suspected allows
|
||||
fishing questions; confirmed enables direct confrontation.
|
||||
|
||||
# --- Investigation Attributes (2) ---
|
||||
|
||||
- key: tell_observed
|
||||
category: investigation
|
||||
value_type: freeform
|
||||
description: >
|
||||
Specific tell noted by observer. Captures the observer's interpretive
|
||||
conclusion about behavior. Accumulated tells build case strength.
|
||||
Monologue references specific tells when they recur.
|
||||
examples:
|
||||
- "looks left when lying"
|
||||
- "leaves when Torek arrives"
|
||||
- "checks lattice before answering"
|
||||
- "forced casualness after shift supervisor passes"
|
||||
implementation_note: >
|
||||
Server may track tells via behavior_flags internally. tell_observed is
|
||||
retained as a content-authoring key for its distinct narrative purpose.
|
||||
|
||||
- key: contradiction_flagged
|
||||
category: investigation
|
||||
value_type: freeform
|
||||
description: >
|
||||
Boolean-ish flag — presence of value means contradiction detected.
|
||||
THE FRIEND arc trigger. When set, monologue tone shifts, dialogue
|
||||
options change, RelationshipState moves toward PersonOfInterest.
|
||||
examples:
|
||||
- "meeting_unknown_contact"
|
||||
- "avoidance_inconsistency"
|
||||
- "manifest_access_mismatch"
|
||||
implementation_note: >
|
||||
Server may track via behavior_flags with contradiction: prefix.
|
||||
Retained as documented key for arc transition gating.
|
||||
|
||||
# --- Role-Perspective Attributes (4 new per workshop A7) ---
|
||||
# Same enums across all archetypes; interpretation is archetype-dependent.
|
||||
# Smuggler reads "risk" as operational exposure; detective reads it as
|
||||
# threat to investigation. See wiki entity-attributes.md for full mapping.
|
||||
|
||||
- key: risk_assessment
|
||||
category: role-perspective
|
||||
value_type: enum
|
||||
values:
|
||||
- none
|
||||
- low
|
||||
- moderate
|
||||
- high
|
||||
- critical
|
||||
description: >
|
||||
Observer's assessment of how much danger this entity poses or attracts.
|
||||
Gates operational monologue. High-risk triggers cautionary dialogue.
|
||||
Nature of "risk" depends on observer archetype.
|
||||
|
||||
- key: loyalty_assessment
|
||||
category: role-perspective
|
||||
value_type: enum
|
||||
values:
|
||||
- solid
|
||||
- dependable
|
||||
- uncertain
|
||||
- wavering
|
||||
- hostile
|
||||
description: >
|
||||
Observer's assessment of this entity's reliability and loyalty.
|
||||
Determines how much the observer relies on or confides in the target.
|
||||
Wavering triggers monitoring; hostile triggers defensive posture.
|
||||
|
||||
- key: position_integrity
|
||||
category: role-perspective
|
||||
value_type: enum
|
||||
values:
|
||||
- solid
|
||||
- thin
|
||||
- cracking
|
||||
- blown
|
||||
- "N/A"
|
||||
description: >
|
||||
Observer's assessment of this entity's cover, position stability,
|
||||
or facade status. When degraded, monologue reflects increasing anxiety
|
||||
or strategic recalculation. N/A when cover concept does not apply.
|
||||
|
||||
- key: moral_weight
|
||||
category: role-perspective
|
||||
value_type: enum
|
||||
values:
|
||||
- innocent
|
||||
- peripheral
|
||||
- complicit
|
||||
- compromised
|
||||
- willing
|
||||
description: >
|
||||
Observer's assessment of this entity's moral involvement. Shapes the
|
||||
observer's moral arc and internal conflict. Smuggler assesses complicity
|
||||
in ring operations; detective assesses culpability in the crime.
|
||||
Same scale, different lens.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Fact catalog: knowledge
|
||||
# Operational knowledge — ring procedures, social dynamics, institutional mechanics
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.4 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: knowledge.ring_routing_knowledge
|
||||
description: Full knowledge of the ring's drop routes and timing protocols
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
notes: "Smuggler starts at knows_details (Background). Not available to detective through normal investigation paths."
|
||||
|
||||
- fact_id: knowledge.ring_bar_operations
|
||||
description: The bar (The Last Shift) has a role in ring communications or social cover
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: knowledge.bar_back_room_significance
|
||||
description: The bar's back room is significant — meeting space, handoff point, or storage
|
||||
typical_confidence: suspects
|
||||
characters: [detective]
|
||||
|
||||
- fact_id: knowledge.bar_ring_awareness
|
||||
description: Bar regulars have varying levels of awareness of the ring — open secret at some level
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: knowledge.kael_exit_plan
|
||||
description: Kael has a specific exit plan — timing, conditions, contact
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Only available through Kael's deep trust disclosure or independent investigation"
|
||||
@@ -0,0 +1,51 @@
|
||||
# Fact catalog: location
|
||||
# Location-specific access, restrictions, and significance
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.6 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: location.corridor_b7_restricted
|
||||
description: Corridor B-7 is a restricted access zone — requires credentials
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Both characters have this from Background (signage, institutional knowledge)"
|
||||
|
||||
- fact_id: location.corridor_b7_drop_site
|
||||
description: B-7 is an active drop site for the ring
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler from Background. Detective through investigation."
|
||||
|
||||
- fact_id: location.maintenance_corridors_access
|
||||
description: Character knows the maintenance corridor layout and can navigate it
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler at knows_details (Background — daily operational space). Detective at knows_of after investigation."
|
||||
|
||||
- fact_id: location.bay4_sealed
|
||||
description: Bay 4 is sealed — inspection or deliberate cover
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: location.span_gate_schedule_gap
|
||||
description: The span gate schedule has a window that enables unseen cargo drops
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: location.smuggling_route
|
||||
description: The complete physical route the ring uses through the station
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler from Background. Detective from deep investigation."
|
||||
|
||||
- fact_id: location.surveillance_gaps
|
||||
description: Specific camera blind spots in the maintenance corridor network
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler from Background. Detective must discover independently."
|
||||
|
||||
- fact_id: location.dead_drop_location
|
||||
description: Specific dead drop location(s) used for handoffs
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
notes: "Smuggler operational knowledge"
|
||||
@@ -0,0 +1,36 @@
|
||||
# Fact catalog: progress
|
||||
# Narrative milestone flags
|
||||
# Note: progress.* facts are NOT recommended for direct authoring in prerequisite blocks.
|
||||
# Use investigation fact chains and relationship states instead.
|
||||
# These exist for potential storyteller/arc-tracking use.
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §3.2 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: progress.friend_arc_phase_1
|
||||
description: FRIEND arc Phase 1 warmth established (baseline positive relationship)
|
||||
notes: "Set by relationship state transition to Friendly. Do not author directly — use relationship checks."
|
||||
|
||||
- fact_id: progress.friend_arc_phase_2
|
||||
description: FRIEND arc Phase 2 trust built (deep rapport + initial social context)
|
||||
notes: "Set by trust tier advancement. Do not author directly — use investigation + relationship checks."
|
||||
|
||||
- fact_id: progress.friend_arc_contradiction
|
||||
description: FRIEND contradiction discovered (KnowledgeState → Contradicted)
|
||||
notes: "Set by contradiction detection system. Use relationship state person_of_interest + investigation fact instead."
|
||||
|
||||
- fact_id: progress.friend_arc_confrontation
|
||||
description: FRIEND confronted about contradiction
|
||||
notes: "Set post-confrontation by dialogue system."
|
||||
|
||||
- fact_id: progress.opening_complete
|
||||
description: Opening tutorial sequence completed (first 5 minutes)
|
||||
notes: "Set by storyteller after initial objective sequence."
|
||||
|
||||
- fact_id: progress.first_ring_contact
|
||||
description: Smuggler completed first ring operation during play session
|
||||
characters: [smuggler]
|
||||
|
||||
- fact_id: progress.first_evidence_filed
|
||||
description: Detective filed first manifest discrepancy evidence
|
||||
characters: [detective]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Fact catalog: relationship
|
||||
# Inter-NPC connections — what one character knows about relationships between others
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.7 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: relationship.kael_naia_connection
|
||||
description: Kael and Naia are romantically partnered — not just colleagues
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler may know from background or Kael mentioning Naia. Detective through social observation."
|
||||
|
||||
- fact_id: relationship.ring_membership
|
||||
description: Character has identified specific ring members
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "knows_of = partial list; knows_details = full ring membership identified"
|
||||
|
||||
- fact_id: relationship.trust_network
|
||||
description: Understanding of who trusts whom inside the ring — operational reliability mapping
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler]
|
||||
notes: "Smuggler-specific operational knowledge"
|
||||
|
||||
- fact_id: relationship.tamm_family
|
||||
description: Connection between Naia Tamm and her family situation
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
|
||||
- fact_id: relationship.torek_bar_arrangement
|
||||
description: Torek has a financial arrangement related to the bar or its operations
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
@@ -0,0 +1,106 @@
|
||||
# Fact catalog: ring
|
||||
# Smuggling ring module event trail — observable evidence produced by module events.
|
||||
# These facts are made discoverable by specific events in smuggling_ring_v0_1.yaml.
|
||||
# Stub entries — description and confidence level confirmed; progression text TBD.
|
||||
# Ticket: #158 | Sprint 18
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: ring.cargo_discrepancy_pattern
|
||||
description: Cargo manifests at The Terminal show small, systematic discrepancies — weight variances, unverified entries, containers with no return log
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Detective's analytical lattice may flag this automatically during Terminal walkthrough.
|
||||
Smuggler encounters it during operational paperwork checks. Pattern becomes clearer over
|
||||
multiple observations (once: false event — repeats). Starting entry point for both
|
||||
investigation arcs.
|
||||
|
||||
- fact_id: ring.kael_missed_verification
|
||||
description: Kael Davan was absent from a scheduled cargo verification — another dock worker covered without explanation
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Discoverable via cargo manifest review (Terminal office) or analytical lattice flag
|
||||
(detective). Smuggler notices the irregularity in their own paperwork. First concrete
|
||||
evidence that Kael is deviating from his normal operational reliability.
|
||||
|
||||
- fact_id: ring.kael_unauthorized_corridor_access
|
||||
description: Kael was observed in restricted maintenance corridor B-7 during off-shift hours with an unknown contact
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Core FRIEND contradiction observable (D-034). Player must be in visual range of B-7
|
||||
to discover this, OR examine the corridor door access log (investigative action).
|
||||
Both characters can discover this fact — interpreted differently (smuggler: security
|
||||
breach / detective: suspicious contact). Upgrades from suspects to knows_of when
|
||||
contact identity confirmed, knows_details when purpose confirmed.
|
||||
|
||||
- fact_id: ring.kael_unusual_meridian_activity
|
||||
description: Encrypted Meridian packets sent from cargo bay terminals — frequent, patterned, not from personal devices
|
||||
typical_confidence: suspects
|
||||
characters: [detective]
|
||||
notes: >
|
||||
Detective-only. Requires analytical lattice to flag the outgoing packet pattern.
|
||||
Player must be in or adjacent to The Terminal cargo bay. Content not accessible —
|
||||
only the existence and frequency of the encrypted messages. Hints at Kael's
|
||||
off-district contact without revealing who.
|
||||
|
||||
- fact_id: ring.voss_kael_private_meeting
|
||||
description: Voss called Kael into the supervisor's office — closed door, raised voices audible from adjacent position
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Observable via spatial positioning (supervisor's office area). Smuggler can witness
|
||||
the approach and departure without hearing content. Detective observes Kael's visible
|
||||
strain immediately after if present in The Terminal. Upgrade to knows_of via
|
||||
trust-gated dialogue with Kael ("Are you alright?" option unlocks post-meeting).
|
||||
|
||||
- fact_id: ring.voss_accelerating_timeline
|
||||
description: Voss is pushing to close ring operations faster than normal — someone is watching
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Smuggler: Voss mentions "the schedule moving up" in guarded conversation (trust-gated,
|
||||
ring-insider access required). Detective: observe Voss and Nils in two exchanges within
|
||||
the same shift and note behavioral change. Not directly stated — inferred from context.
|
||||
|
||||
- fact_id: ring.commission_cargo_flag
|
||||
description: A Commission internal note flags The Terminal's cargo variance rate as statistically unusual
|
||||
typical_confidence: knows_of
|
||||
characters: [detective]
|
||||
notes: >
|
||||
Detective-only. Accessible via institutional Commission query (authority access tier,
|
||||
Terminal records). Not a formal investigation — just an internal flag from Maret Korr.
|
||||
Confirms the detective's suspicions with institutional weight. Smuggler has no way to
|
||||
know this exists.
|
||||
|
||||
- fact_id: ring.final_shipment_scheduled
|
||||
description: The ring has scheduled a major final drop — after it clears, operations go dark
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Smuggler: direct notification from Voss (ring-insider access). Detective: cargo manifest
|
||||
shows an unusually large entry scheduled 3 days out — no obvious legitimate reason for
|
||||
the volume. This fact creates the closing window — both characters know time is running
|
||||
out, for different reasons.
|
||||
|
||||
- fact_id: ring.sera_avoidance_behavior
|
||||
description: Sera Venn takes longer routes through The Last Shift to avoid standing near Torek Lintar
|
||||
typical_confidence: suspects
|
||||
characters: [detective]
|
||||
notes: >
|
||||
Detective-only observable. Player must observe Sera's movement pattern on two separate
|
||||
occasions — requires forward vision cone and awareness of her baseline route. The
|
||||
avoidance is visible but not explained. Points toward Sera holding information about
|
||||
Torek's investigation without having acted on it.
|
||||
|
||||
- fact_id: ring.nils_unlogged_cargo
|
||||
description: Nils makes regular supply runs to maintenance corridor C-7 with containers that never appear in return logs
|
||||
typical_confidence: suspects
|
||||
characters: [smuggler, detective]
|
||||
notes: >
|
||||
Discoverable by watching Nils's cargo handling pattern over two shifts, or by examining
|
||||
the maintenance corridor C-7 access log directly. The missing return entry is the tell —
|
||||
the container went in but never came back. Both characters can find this; smuggler may
|
||||
already suspect given operational context.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Fact catalog: social
|
||||
# Social positioning facts — standing in a specific social context
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.8 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: social.bar_regular_status
|
||||
description: Character is an established regular at The Last Shift — recognized, trusted in that context
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler may have this from Background. Detective builds it through multiple bar visits."
|
||||
@@ -0,0 +1,35 @@
|
||||
# Fact catalog: world
|
||||
# Faction and institutional knowledge — Commission, lattice regulation, broader context
|
||||
# Source: docs/design/knowledge-vocabulary-v01.md §4.6 (#368)
|
||||
|
||||
facts:
|
||||
|
||||
- fact_id: world.lattice_regulation_active
|
||||
description: Commission actively regulates neural lattice component access in this system
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Both characters have this from Background"
|
||||
|
||||
- fact_id: world.unlicensed_lattice_demand
|
||||
description: High demand for unlicensed lattice upgrades on working-class stations like Sova
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "The moral context of the operation — smuggler knows it as justification; detective may learn it as complication"
|
||||
|
||||
- fact_id: world.commission_sova_jurisdiction
|
||||
description: Commission has jurisdiction over Sova Transit District
|
||||
typical_confidence: knows_details
|
||||
characters: [detective]
|
||||
notes: "Detective Background knowledge — institutional mandate"
|
||||
|
||||
- fact_id: world.ring_cross_system
|
||||
description: The ring operates across multiple star systems — Sova is one node
|
||||
typical_confidence: knows_details
|
||||
characters: [smuggler, detective]
|
||||
notes: "Late-game discovery — changes scope of the operation"
|
||||
|
||||
- fact_id: world.shift_schedule
|
||||
description: Sova Transit District shift schedule — who works when
|
||||
typical_confidence: knows_of
|
||||
characters: [smuggler, detective]
|
||||
notes: "Smuggler from Background (colleague). Detective through investigation."
|
||||
@@ -0,0 +1,49 @@
|
||||
// Trait Modifier Clauses for Voice Pipeline (D-138)
|
||||
//
|
||||
// One clause per PersonalityTrait. Injected into LLM prompts to modify
|
||||
// speech style based on NPC personality. Stacks with culture persona
|
||||
// and tell-state injectors.
|
||||
//
|
||||
// Schema: map of trait name → clause string
|
||||
// Used by: server/src/voice/prompt_builder.rs
|
||||
//
|
||||
// Writing notes:
|
||||
// - Behavioral, not emotional. Never label a feeling.
|
||||
// - Each clause targets a distinct speech dimension so traits stack cleanly.
|
||||
// Bold = delivery force. Cautious = word selection. Curious = sentence shape.
|
||||
// Compassionate = cadence (engaged). Incurious = cadence (flat). Ruthless = position.
|
||||
// A Bold+Compassionate NPC speaks with force but gives the answer room to land — no conflict.
|
||||
// - Kept to 1-2 sentences. LLM context is limited and these share space with
|
||||
// persona, tell-state, and epistemic marker instructions.
|
||||
|
||||
{
|
||||
// Delivery: force and directness. Short sentences land without hedging.
|
||||
"Bold": "This character doesn't soften the landing. Statements arrive short and flat — no qualifiers, no trailing uncertainty.",
|
||||
|
||||
// Word selection: nothing committed without cover. Hedges stay.
|
||||
"Cautious": "This character hedges where the situation allows it. \"Probably,\" \"might,\" \"I'd say\" — these aren't weakness, they're habit.",
|
||||
|
||||
// Sentence shape: questions surface. Interest pulls the line open.
|
||||
"Curious": "This character lets interest show at the end of a line — a beat longer than needed, a question that wasn't required.",
|
||||
|
||||
// Framing: the useful version, not the true version. Nothing false, just selected.
|
||||
"Deceptive": "This character leads with what serves them. The useful detail arrives early; the less useful one doesn't arrive at all.",
|
||||
|
||||
// Framing: no softening, no omission. The whole thing, bluntly.
|
||||
"Honest": "This character gives the whole answer, including the part that doesn't reflect well. Nothing is softened to spare anyone.",
|
||||
|
||||
// Cadence: open, engaged. Responses arrive with momentum and care.
|
||||
"Compassionate": "This character gives the answer room to land. There's no rush past the difficult part — it gets said, plainly, without looking away.",
|
||||
|
||||
// Cadence: flat, unengaged. The response does its job and stops.
|
||||
"Incurious": "This character answers what was asked. Nothing extra surfaces — no follow-up, no interest, no second look at what was just said.",
|
||||
|
||||
// Volume and reach: minimal, inward-facing. Not interested in being heard widely.
|
||||
"Reclusive": "This character answers what was asked and closes the door. There is no invitation for follow-up.",
|
||||
|
||||
// Texture: open, inclusive. Others are assumed to be present and welcome.
|
||||
"Social": "This character addresses the conversation, not just the question. A word or two lands that wasn't strictly necessary — the kind that keeps things warm.",
|
||||
|
||||
// Position: sharp, economical. Nothing is offered that doesn't serve the speaker.
|
||||
"Ruthless": "This character cuts to the useful part. Courtesy is absent, not hostile — just unnecessary. The sentence ends when the point is made.",
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Van Maanen's Star Industrial Zone — location metadata
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: ZoneSpec
|
||||
// Ticket: #609, #630, #661 (copy team)
|
||||
// Validate: tooling/validate-ron content/global/van-maanens-star-industrial-zone.ron zone
|
||||
//
|
||||
// Location-specific metadata for a Van Maanen's Star industrial freight district.
|
||||
// Behavior primitives have moved to the zone-type template:
|
||||
// content/global/zone-types/industrial_freight.ron
|
||||
//
|
||||
// This file holds: zone_type reference, location metadata, role weights, social sites.
|
||||
// The generator reads zone_type to find the primitive pool; overrides come from here.
|
||||
//
|
||||
// Character: freight handling, manufacturing, maintenance. High throughput.
|
||||
// Shift rhythms. Functional over comfortable. Nobody lingers — unless they're on break.
|
||||
|
||||
(
|
||||
zone_type: "industrial_freight",
|
||||
label: "Industrial Zone",
|
||||
description: "Freight handling, manufacturing, and maintenance. High throughput, shift-based work rhythms, functional over comfortable. Faces are known by role and bay number more than name. The work doesn't stop between shifts — people do.",
|
||||
|
||||
// 1-10. Industrial Van Maanen's Star: steady credit flow, but it all goes somewhere.
|
||||
economic_level: 7,
|
||||
// 1-10. Dense. Multiple shifts overlap. Crowds at handover.
|
||||
population_density: 6,
|
||||
|
||||
// Role presence and weights for this location.
|
||||
// skill_focus / combat_eligible / behavior_primitives live in the zone-type template.
|
||||
role_weights: [
|
||||
( id: "dock_worker", weight: 5 ),
|
||||
( id: "technician", weight: 4 ),
|
||||
( id: "foreman", weight: 2 ),
|
||||
( id: "security", weight: 2 ),
|
||||
],
|
||||
|
||||
social_sites: [
|
||||
(
|
||||
site_type: "break_room",
|
||||
label: "Worker Break Room",
|
||||
// Between shifts. Decompression. People stop performing for a moment.
|
||||
min_npcs: 2,
|
||||
max_npcs: 5,
|
||||
),
|
||||
(
|
||||
site_type: "maintenance_bay",
|
||||
label: "Maintenance Bay",
|
||||
// Active work site. Technicians and dock workers cross paths here.
|
||||
min_npcs: 2,
|
||||
max_npcs: 4,
|
||||
),
|
||||
(
|
||||
site_type: "loading_platform",
|
||||
label: "Loading Platform",
|
||||
// The operational center. Busy, loud, coordinated.
|
||||
min_npcs: 3,
|
||||
max_npcs: 8,
|
||||
),
|
||||
],
|
||||
|
||||
abandoned: false,
|
||||
poi_overlay: None,
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
// Van Maanen's Star Rural Zone — location metadata
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: ZoneSpec
|
||||
// Ticket: #609, #630, #661 (copy team)
|
||||
// Validate: tooling/validate-ron content/global/van-maanens-star-rural-zone.ron zone
|
||||
//
|
||||
// Location-specific metadata for a Van Maanen's Star rural agricultural settlement.
|
||||
// Behavior primitives have moved to the zone-type template:
|
||||
// content/global/zone-types/rural_agricultural.ron
|
||||
//
|
||||
// This file holds: zone_type reference, location metadata, role weights, social sites.
|
||||
// The generator reads zone_type to find the primitive pool; overrides come from here.
|
||||
//
|
||||
// Character: scattered homesteads and small workshops. Unhurried. Community-bound.
|
||||
// Low throughput, high familiarity. Everyone knows who belongs here.
|
||||
|
||||
(
|
||||
zone_type: "rural_agricultural",
|
||||
label: "Rural Settlement",
|
||||
description: "Scattered homesteads, small workshops, and communal gathering points. Low population density, strong community bonds, subsistence-plus economy. Work is seasonal and visible — everyone knows what everyone else is doing.",
|
||||
|
||||
// 1-10. Rural Van Maanen's Star: self-sufficient, low cash flow, barter supplements credits.
|
||||
economic_level: 3,
|
||||
// 1-10. Sparse. Faces are familiar. Strangers stand out.
|
||||
population_density: 2,
|
||||
|
||||
// Role presence and weights for this location.
|
||||
// skill_focus / combat_eligible / behavior_primitives live in the zone-type template.
|
||||
role_weights: [
|
||||
( id: "farmer", weight: 5 ),
|
||||
( id: "mechanic", weight: 3 ),
|
||||
( id: "trader", weight: 2 ),
|
||||
( id: "militia", weight: 1 ),
|
||||
],
|
||||
|
||||
social_sites: [
|
||||
( site_type: "tavern", label: "Local Tavern", min_npcs: 3, max_npcs: 6 ),
|
||||
( site_type: "workshop", label: "Community Workshop", min_npcs: 2, max_npcs: 4 ),
|
||||
( site_type: "market_stall", label: "Settlement Market", min_npcs: 1, max_npcs: 3 ),
|
||||
],
|
||||
|
||||
abandoned: false,
|
||||
poi_overlay: None,
|
||||
)
|
||||
@@ -0,0 +1,97 @@
|
||||
// Zone Identity Spec — example file
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: ZoneSpec
|
||||
// Real content: ticket #609 (copy team fills this)
|
||||
// Validate: tooling/validate-ron content/global/zone-identity-spec.example.ron zone
|
||||
//
|
||||
// One file per zone type. The generator reads this to decide NPC count,
|
||||
// role distribution, and social site placement.
|
||||
//
|
||||
// Format: RON (Rusty Object Notation) — the Rust structs ARE the schema.
|
||||
// Comments are allowed. Trailing commas are allowed.
|
||||
|
||||
(
|
||||
zone_type: "rural",
|
||||
label: "Rural Settlement",
|
||||
description: "Scattered homesteads, small workshops, and communal gathering spots. Low population density, strong community bonds, subsistence-plus economy.",
|
||||
|
||||
// 1-10 scale. Rural = low economic activity, mostly self-sufficient.
|
||||
economic_level: 3,
|
||||
// 1-10 scale. Rural = sparse, everyone knows everyone.
|
||||
population_density: 2,
|
||||
|
||||
roles: [
|
||||
(
|
||||
id: "farmer",
|
||||
label: "Farmer",
|
||||
weight: 5,
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
typical_behaviors: [
|
||||
"tends crops in the field",
|
||||
"hauls produce to the market stall",
|
||||
"repairs equipment by hand",
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "mechanic",
|
||||
label: "Settlement Mechanic",
|
||||
weight: 3,
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
typical_behaviors: [
|
||||
"works on machinery with focused intensity",
|
||||
"wipes grease on coveralls between tasks",
|
||||
"explains repairs in terse technical shorthand",
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "trader",
|
||||
label: "Itinerant Trader",
|
||||
weight: 2,
|
||||
skill_focus: ["persuasion", "observation"],
|
||||
combat_eligible: false,
|
||||
typical_behaviors: [
|
||||
"arranges goods on a portable display",
|
||||
"haggles with quiet persistence",
|
||||
"watches foot traffic from market stall",
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "militia",
|
||||
label: "Settlement Militia",
|
||||
weight: 1,
|
||||
skill_focus: ["combat", "observation"],
|
||||
combat_eligible: true,
|
||||
typical_behaviors: [
|
||||
"patrols the settlement perimeter",
|
||||
"checks credentials at the gate",
|
||||
"leans on rifle while scanning the horizon",
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
social_sites: [
|
||||
(
|
||||
site_type: "tavern",
|
||||
label: "Local Tavern",
|
||||
roles: ["farmer", "mechanic", "trader", "militia"],
|
||||
min_npcs: 3,
|
||||
max_npcs: 6,
|
||||
),
|
||||
(
|
||||
site_type: "workshop",
|
||||
label: "Community Workshop",
|
||||
roles: ["mechanic", "farmer"],
|
||||
min_npcs: 2,
|
||||
max_npcs: 4,
|
||||
),
|
||||
(
|
||||
site_type: "market_stall",
|
||||
label: "Market Stall",
|
||||
roles: ["trader", "farmer"],
|
||||
min_npcs: 1,
|
||||
max_npcs: 3,
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,168 @@
|
||||
// Industrial Freight Zone-Type Template
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: ZoneTypeTemplate
|
||||
// Ticket: #661 (copy team)
|
||||
// Validate: tooling/validate-ron server/content/global/zone-types/industrial_freight.ron zone_type
|
||||
//
|
||||
// Sources: D-142 (zone-type template architecture).
|
||||
//
|
||||
// Zone character: freight handling, cargo throughput, shift-based manufacturing.
|
||||
// High throughput, functional over comfortable. Faces known by role and bay number.
|
||||
// The work doesn't stop between shifts — people do.
|
||||
//
|
||||
// Primitives are CULTURE-NEUTRAL. Culture modifiers (culture-*.ron files) append
|
||||
// cultural flavor at assembly time. Do not encode cultural attitudes in action text.
|
||||
//
|
||||
// Roles: dock_worker, technician, foreman, security
|
||||
// Social site types: break_room, maintenance_bay, loading_platform
|
||||
|
||||
(
|
||||
id: "industrial_freight",
|
||||
subtype: None,
|
||||
label: "Industrial Freight Zone",
|
||||
description: "Freight handling, cargo throughput, shift-based work rhythms. Functional over comfortable. Faces known by role and bay number. The work doesn't stop between shifts — people do.",
|
||||
|
||||
// Default economic/density profile. Location specs override.
|
||||
economic_level: 7,
|
||||
population_density: 6,
|
||||
|
||||
roles: [
|
||||
(
|
||||
id: "dock_worker",
|
||||
label: "Dock Worker",
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
// weight omitted — location specs declare role_weights
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "guides a freight container into position with hand signals", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "checks a manifest against a handheld scanner", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "waits at the loading bay apron, watching the clock", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "calls bay numbers to a colleague across the noise of the floor", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "hooks a cargo sling and steps clear before signaling the lift", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "stacks empty pallets against a wall", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "wipes sweat from their face with a forearm and keeps moving", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "drags a heavy case along the deck plating one-handed, leaning into the weight", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "re-checks a seal on a container door after a colleague already checked it", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "flags a damaged pallet to the foreman without stopping the line", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "shoulders a cargo rig harness and clips in without looking down", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "sweeps debris off the loading apron with a long push broom", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "braces a container with a boot while reaching for the locking pin", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "reads the load ticket twice, then flips the handheld over to check the back", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "waves off a crane operator when the angle is wrong, holds up a fist", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "steps over a bundled cable run without breaking stride", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "peels off a work glove with his teeth to check a handheld display", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "exchanges a brief look with a nearby colleague when the supervisor passes", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
// OffDuty / break room
|
||||
( action: "slumps into a break room chair and stares at nothing for a full minute before reaching for a drink", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "unwraps a meal packet in the break room and eats standing at the counter", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "passes a drink to the person next to her without being asked", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "leans back in a break room chair with eyes closed, boots crossed at the ankle", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "laughs at something across the break room table, loud enough to carry", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "shows something on a handheld to a colleague and both of them look at it", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "sits with elbows on knees, turning an empty cup in both hands", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "splashes water on his face at the sink and stands there a moment before turning off the tap", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "talks over the break room noise at volume, gesturing with a fork", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "falls asleep in a break room chair, chin on chest, arms folded", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "technician",
|
||||
label: "Systems Technician",
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "runs a diagnostic routine on a floor console, eyes on the readout", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "traces a conduit run along the ceiling with a handheld light", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "swaps a panel module, tools back in the pouch before the cover is latched", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "logs a fault code into a datapad before moving on", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "presses an ear to a vibrating duct and listens", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "argues quietly with a display that isn't giving the right numbers", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "queries a fault log through a terminal, eyes briefly unfocused", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "taps a wrench against a junction box lid to test for rattle", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "pulls a burnt relay from a panel and holds it up to the light", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "clips a sensor lead to two terminals and watches the readout settle", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "photographs a fault site with a handheld before touching anything", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "threads a cable through a conduit run without looking, hands working by feel", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "checks a pressure gauge by putting a thumb on the dial housing and reading the needle", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "labels a repaired junction with tape and a marker, block letters", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "reads a service manual on a battered datapad, scrolling with one finger", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "kneels under a raised floor panel with a light between her teeth", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "closes a maintenance hatch and shoulder-checks it twice", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "stands on the second rung of a ladder and reaches without climbing higher", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
// OffDuty / break room
|
||||
( action: "stretches both arms overhead in the break room doorway, blocking it without noticing", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "sits sideways in a break room chair, back against the wall, feet on the seat beside her", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "pulls up a schematic on a personal handheld and looks at it between bites", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "refills someone else's cup from the urn without commenting on it", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "puts her boots on the break room table when off-shift", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "describes a problem to a colleague who doesn't follow it but nods anyway", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "argues a point at the break room table, tapping the surface for emphasis", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "reads something on a personal device with his head tipped back and the screen held at arm's length", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "laughs until he has to set down his drink", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "foreman",
|
||||
label: "Shift Foreman",
|
||||
skill_focus: ["observation", "persuasion"],
|
||||
combat_eligible: false,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "walks the floor with a datapad under one arm and says nothing", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "pulls a worker aside for a quiet word near the far wall", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "marks a line off a production board and moves to the next one", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "stands at the mezzanine rail watching throughput without expression", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "reviews shift handover notes and circles something with a stylus", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "speaks to a crew in a low voice — they listen without nodding", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "covers a worker's absence from the shift log by redistributing the bay assignments", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "eats lunch standing at a wall terminal so she can keep an eye on the work", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "takes the call herself instead of forwarding it", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "corrects a worker's form on a piece of equipment without making it a lesson", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "walks a new hire through the handover checklist once, point by point, no shortcuts", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "keeps a junior worker between herself and the inspection team as the inspectors pass through", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "finds a reason to be nearby when a new worker makes her first solo lift", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "absorbs a production shortfall report without passing the frustration down the line", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "hands a worker an extra meal packet and walks away without explaining it", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "signs off on a repair they didn't personally inspect", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "stands at the edge of the loading floor for a long moment before walking back", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "makes a note in the shift log that takes longer to write than it takes to read", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "tells a bad joke to nobody in particular while marking off the production board", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
// OffDuty
|
||||
( action: "sits alone in the break room rubbing the back of her neck, datapad face-down on the table", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "laughs at something a colleague says, then catches herself and goes quiet", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
( action: "sits next to someone in the break room and doesn't talk, just sits", context: OffDuty, modifier_hint: Some("social_signal") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "security",
|
||||
label: "Facility Security",
|
||||
skill_focus: ["combat", "observation"],
|
||||
combat_eligible: true,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "sweeps the access corridor on a timed circuit, same path each time", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "waves a regular through the checkpoint on sight — scans the one behind them out of procedure", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "leans at the restricted bay entrance, arms folded, watching the corridor", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "notes something in a shift log without reacting to it outwardly", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "watches a handover between crews from across the loading floor", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "stops at a junction, looks both ways, and picks the longer route", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "runs an ID check on someone whose face she recognizes, procedure is procedure", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "stands with her back to a structural column where both exits are visible", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "glances at a badge without stopping the person wearing it", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "checks the restricted bay door seal before settling in to watch the corridor", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "runs the same check routine when the supervisor is watching as when she isn't", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "acknowledges the facility manager's walk-through with a nod and doesn't change her position", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
// Valid social site types for this zone type.
|
||||
// Location specs declare which appear, with their own label, min_npcs, max_npcs.
|
||||
social_site_types: [
|
||||
( site_type: "break_room", eligible_roles: ["dock_worker", "technician", "foreman"] ),
|
||||
( site_type: "maintenance_bay", eligible_roles: ["technician", "dock_worker"] ),
|
||||
( site_type: "loading_platform", eligible_roles: ["dock_worker", "foreman", "security"] ),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,165 @@
|
||||
// Rural Agricultural Zone-Type Template
|
||||
//
|
||||
// Schema: server/src/npc/blueprint.rs :: ZoneTypeTemplate
|
||||
// Ticket: #661 (copy team)
|
||||
// Validate: tooling/validate-ron server/content/global/zone-types/rural_agricultural.ron zone_type
|
||||
//
|
||||
// Sources: D-142 (zone-type template architecture).
|
||||
//
|
||||
// Zone character: scattered homesteads, small workshops, open agricultural land.
|
||||
// Seasonal work rhythms, low population density, strong community familiarity.
|
||||
// Work is visible and shared — everyone knows what everyone else is doing.
|
||||
//
|
||||
// Primitives are CULTURE-NEUTRAL. Culture modifiers (culture-*.ron files) append
|
||||
// cultural flavor at assembly time. Do not encode cultural attitudes in action text.
|
||||
//
|
||||
// Roles: farmer, mechanic, trader, militia
|
||||
// Social site types: tavern, workshop, market_stall
|
||||
|
||||
(
|
||||
id: "rural_agricultural",
|
||||
subtype: None,
|
||||
label: "Rural Agricultural Zone",
|
||||
description: "Crop farming, small workshops, communal settlement. Low density, community-bonded, self-sufficient economy. Work is visible to neighbors — everyone knows what everyone else is doing.",
|
||||
|
||||
// Default economic/density profile. Location specs override.
|
||||
economic_level: 3,
|
||||
population_density: 2,
|
||||
|
||||
roles: [
|
||||
(
|
||||
id: "farmer",
|
||||
label: "Farmer",
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
// weight omitted — location specs declare role_weights
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "works back along a crop row, stopping to pull weeds by the root", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "lifts a crate of produce onto a flatbed and reaches for the next before it settles", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "checks the section's light cycle timer before deciding whether to water", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "patches a cracked irrigation pipe with bonding tape", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "calls across a field to a neighbor without looking up from work", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "hauls produce to the market stall before the morning exchange opens", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "crouches over seedling trays and pinches back a leggy stem that grew sideways", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "runs a thumb along the edge of a cracked irrigation seal, then sets it aside", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "stacks empty crates at the end of a row", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "drags a length of hose to a dry section and clamps the fitting by hand", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "kneels at the base of a struggling plant and parts the soil with two fingers", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "leans on a fence post and watches the sky before going back to the row", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "refills a handheld sprayer from a standing drum", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "ties a row marker to a stake with a short length of wire", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "pulls a glove off to wipe her face, pulls it back on without looking", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "loads a wheelbarrow and tips it into a compost bin at the field edge", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "pulls a dead plant by the roots and carries it to the burn pile", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "tests soil moisture by pressing a thumb into the ground beside a seedling", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "walks the field edge, pulling the wire taut at each span to test the tension", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "sets two crates down on the market floor and counts the lids twice", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "answers an inspector's question without breaking the task rhythm", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "waits for the settlement head to finish speaking before going back to work", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
// Social / off-duty
|
||||
( action: "nurses a drink at the end of the bar with both hands wrapped around the glass", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "trades short words with the person at the next seat without turning fully around", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "sits with boots off under the table, socked feet flat on the floor", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "refills a neighbor's cup from her own jug without being asked", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "plays a slow tile game with two others at the corner table", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "slides a credit chit across the bar and waits for change without counting it", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "leans back in the chair and stares at the ceiling for a long moment", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "mechanic",
|
||||
label: "Settlement Mechanic",
|
||||
skill_focus: ["technical"],
|
||||
combat_eligible: false,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "pulls a drive unit from a tiller and examines the worn housing", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "wipes grease on the thigh of her coveralls between jobs", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "explains a repair in clipped shorthand without looking up", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "lines up salvaged parts on a workbench and assesses them", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "welds a seam on a cracked water tank with slow, careful strokes", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "borrows a tool from a neighbor and returns it without being asked", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "taps a seized bolt with a mallet twice before reaching for a longer bar", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "threads a wire through a conduit clip and bites the insulation back with her teeth", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "sets a part down, picks up the spec card beside it, and reads it once", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "uses a straight edge to check the flatness of a repaired flange", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "drains a coolant line into a catch tray and marks the container", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "torques a fastener by feel and then confirms it with a wrench click", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "stacks finished repairs in a corner and photographs them with a handheld", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "jots a note on a strip of tape and sticks it to a part's casing", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "squeezes into a narrow access panel and works by touch", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "straightens up and rolls her neck once before kneeling back down", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
// Social / off-duty
|
||||
( action: "rinses her hands twice at the basin before sitting down at the bar", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "drops her toolkit bag under the stool and orders without looking at the board", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "listens to a complaint about a piece of equipment and nods once or twice", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "draws a rough diagram on a napkin to explain something, then folds it away", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "buys a round for the table and returns to her seat before anyone can thank her", context: Social, modifier_hint: Some("social_signal") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "trader",
|
||||
label: "Itinerant Trader",
|
||||
skill_focus: ["persuasion", "observation"],
|
||||
combat_eligible: false,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "squares goods on a fold-out portable display", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "leans back on a stool and tracks a browser who has checked the same display twice without committing", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "flicks credit chits across the counter with one thumb", context: OnShift, modifier_hint: Some("physical_manner") ),
|
||||
( action: "holds eye contact through a long pause, waiting for the price to land", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "watches a regular browse the same shelf as last time and says nothing", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "packs unsold goods without reacting", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "unrolls a cloth display on the counter and weights the corners with small stones", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "lifts a sample item and sets it in the light where a browser can see it clearly", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "rewraps an unsold item and tucks it back in the case with a specific order", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "counts coins into a small tray and slides it across the counter", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "pulls a ledger from the bag, checks one line, and closes it", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "marks a price down on the board with a grease stylus and steps back", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "holds a cracked item up to show where it failed before handing it back", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "folds the portable display flat and straps it to the pack in two moves", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "lays two items side by side on the counter for a customer to compare", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "wipes down the counter surface with a cloth before setting out the next goods", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
// Social
|
||||
( action: "nurses a drink at the far end of the bar, watching who comes in", context: Social, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "talks price with whoever's sitting nearest without making it a pitch", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "buys a round and tracks who accepts and who doesn't", context: Social, modifier_hint: Some("social_signal") ),
|
||||
( action: "listens more than she talks at the bar, refilling her glass slowly", context: Social, modifier_hint: Some("offduty_posture") ),
|
||||
],
|
||||
),
|
||||
(
|
||||
id: "militia",
|
||||
label: "Settlement Militia",
|
||||
skill_focus: ["combat", "observation"],
|
||||
combat_eligible: true,
|
||||
behavior_primitives: [
|
||||
// OnShift
|
||||
( action: "walks the fence line at a measured, unhurried pace", context: OnShift, modifier_hint: Some("work_pace") ),
|
||||
( action: "leans on the gate post with rifle slung, watching the road", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "waves a familiar face through without checking credentials", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
// on-duty quiet time — context is OnShift but posture reads as off-duty
|
||||
( action: "sits in the shade of the gatehouse reading a local newsline", context: OnShift, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "stops to talk with a passerby, eyes still scanning the perimeter", context: OnShift, modifier_hint: Some("social_signal") ),
|
||||
( action: "checks the charge on a handheld scanner and clips it back to the belt", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "props a boot on the lower fence rail and scans the far end of the road", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "nods to a passing cart and tracks it until it clears the gate", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "marks a log entry on a handheld at the end of a perimeter pass", context: OnShift, modifier_hint: Some("task_completion") ),
|
||||
( action: "steps out of the gatehouse at the sound of an approaching engine", context: OnShift, modifier_hint: Some("environmental_scan") ),
|
||||
( action: "reports a situation to the settlement head without editorializing", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
( action: "straightens slightly when the relief officer arrives for handover", context: OnShift, modifier_hint: Some("authority_response") ),
|
||||
// OffDuty
|
||||
( action: "sits at the end of the bar still in kit, drink in hand, not really off yet", context: OffDuty, modifier_hint: Some("offduty_posture") ),
|
||||
( action: "catches up with a neighbor over a drink without mentioning work", context: Social, modifier_hint: Some("social_signal") ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
// Valid social site types for this zone type.
|
||||
// Location specs declare which appear, with their own label, min_npcs, max_npcs.
|
||||
social_site_types: [
|
||||
( site_type: "tavern", eligible_roles: ["farmer", "mechanic", "trader", "militia"] ),
|
||||
( site_type: "workshop", eligible_roles: ["mechanic", "farmer"] ),
|
||||
( site_type: "market_stall", eligible_roles: ["trader", "farmer"] ),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,692 @@
|
||||
# yaml-language-server: $schema=../../schemas/drama_module.schema.yaml
|
||||
#
|
||||
# Tier 1 Drama Module: The Smuggling Ring (v0.1)
|
||||
# The vertical slice Tier 1 module for D-027.
|
||||
#
|
||||
# NARRATIVE CORE:
|
||||
# A logistics worker (the smuggler PC, if played) is embedded in a small ring
|
||||
# smuggling unlicensed lattice components through Sova Transit District (D-037).
|
||||
# The ring is led by Voss from The Terminal. Kael Davan — a ring member and the
|
||||
# smuggler's FRIEND — is quietly trying to exit to protect his partner Naia Tamm.
|
||||
# Sera Venn (the detective's FRIEND) has noticed Kael's manifest discrepancies
|
||||
# but hasn't reported them, protecting Naia by proxy.
|
||||
#
|
||||
# DUAL-LENS EXPERIENCE:
|
||||
# Smuggler plays INSIDE the ring: manage drops, cover tracks, notice Kael going cold.
|
||||
# Detective plays OUTSIDE: cargo anomalies → follow Kael → witness secret meeting →
|
||||
# confront or protect.
|
||||
#
|
||||
# SUCCESS CRITERIA (D-027):
|
||||
# #1: 30 minutes of daily-life play before the ring activates (min_play_ticks: 2100)
|
||||
# #3: Player names Kael as an NPC they felt conflicted about
|
||||
# #4: observe→notice→follow→discover emerges from systems, not scripts
|
||||
|
||||
module_id: smuggling_ring_v0_1
|
||||
display_name: "The Smuggling Ring"
|
||||
version: "0.1"
|
||||
tier: 1
|
||||
description: >
|
||||
A small ring of logistics workers smuggling unlicensed lattice components through
|
||||
Sova Transit District. The ring's weakest link — Kael Davan — is trying to exit
|
||||
to protect his partner. The detective investigates cargo anomalies. The smuggler
|
||||
manages ring operations and navigates Kael's loyalty crisis. Neither character
|
||||
knows the other's full picture until confrontation forces it.
|
||||
|
||||
notes: >
|
||||
This module IS the vertical slice (D-027). It exercises every system at full depth:
|
||||
dual-lens NPC observation, tell progression, trust-gated dialogue, knowledge graph
|
||||
confidence accumulation, confrontation weight (D-063), walk-away consequences (D-064),
|
||||
and THE FRIEND contradiction arc (D-034). All outcome paths must feel earned.
|
||||
No outcome is "the right answer" — Kael's situation has no clean resolution.
|
||||
|
||||
dual_lens:
|
||||
smuggler: >
|
||||
You're inside the ring. Voss manages operations; you handle logistics cover.
|
||||
Kael used to be reliable. Lately he's absent, distracted, making excuses.
|
||||
The drop schedule is at risk. Do you pressure him, cover for him, or cut him?
|
||||
You don't know he's trying to get out. He doesn't know you've noticed.
|
||||
detective: >
|
||||
Cargo manifest discrepancies in The Terminal. Small, systematic, deniable.
|
||||
Your analytical lattice flags them before your conscious mind does.
|
||||
Follow the thread: discrepancy → dock worker with odd schedule → Kael Davan →
|
||||
maintenance corridors → someone he shouldn't be meeting. And then what?
|
||||
Arrest a man trying to leave a ring he never wanted to join?
|
||||
|
||||
pool:
|
||||
weight: 8
|
||||
compatible_districts:
|
||||
- sova-transit
|
||||
max_concurrent: 1
|
||||
|
||||
# ── ENTRY CONDITIONS ─────────────────────────────────────────────────────────
|
||||
# Ring activity begins after player has had time to establish routine (D-027 #1).
|
||||
# The ring is already running at game start — the module activates when the
|
||||
# storyteller decides the tension has built enough to surface.
|
||||
|
||||
entry_conditions:
|
||||
world_state:
|
||||
- type: npc_present
|
||||
role: ring-leader
|
||||
- type: npc_present
|
||||
role: ring-member-exiting
|
||||
- type: location_accessible
|
||||
location: the-terminal
|
||||
- type: location_accessible
|
||||
location: maintenance-corridors
|
||||
|
||||
activation:
|
||||
trigger: storyteller_push
|
||||
min_play_ticks: 2100 # ~35 minutes at 1 tick/second — D-027 criterion #1
|
||||
# The storyteller pushes activation when player has established presence
|
||||
# in The Terminal or The Last Shift through routine interaction.
|
||||
# Proximity trigger (maintenance-corridors) is a secondary activation path
|
||||
# if the player wanders there early.
|
||||
|
||||
# ── NPC REQUIREMENTS ─────────────────────────────────────────────────────────
|
||||
# All core roles are named (hand-authored NPCs from the vertical slice).
|
||||
# No generated NPC slots in v0.1 — the smuggling ring uses the 15 authored NPCs.
|
||||
|
||||
npc_requirements:
|
||||
- role: ring-leader
|
||||
display_hint: >
|
||||
Runs the ring from The Terminal. Logistics authority = cover.
|
||||
Never handles contraband directly. Pressure source for Kael.
|
||||
binding: named
|
||||
named_npc: "npc:voss"
|
||||
must_have_motivation: HANDLER
|
||||
|
||||
- role: ring-member-exiting
|
||||
display_hint: >
|
||||
Kael Davan. Dock worker, ring member, smuggler's FRIEND.
|
||||
Trying to exit quietly to protect Naia. This is THE FRIEND contradiction.
|
||||
Every event sequence runs through this role.
|
||||
binding: named
|
||||
named_npc: "npc:kael-davan"
|
||||
must_have_pattern: FRIEND
|
||||
must_have_motivation: TURNCOAT
|
||||
|
||||
- role: partner-uninvolved
|
||||
display_hint: >
|
||||
Naia Tamm. Kael's partner. Does not know about the ring.
|
||||
Her safety is Kael's motivation for exiting. Her ignorance is the moral weight.
|
||||
Discovery of her connection to Kael is a late-investigation revelation.
|
||||
binding: named
|
||||
named_npc: "npc:naia-tamm"
|
||||
must_have_motivation: CIVILIAN
|
||||
|
||||
- role: evidence-holder
|
||||
display_hint: >
|
||||
Sera Venn. Detective's FRIEND. Commission field tech.
|
||||
She has noticed Kael's manifest discrepancies but hasn't reported them —
|
||||
she knows Naia, and filing means Kael's arrest and Naia's exposure.
|
||||
Her silence IS the detective's investigation blocker in phase 1.
|
||||
binding: named
|
||||
named_npc: "npc:sera-venn"
|
||||
must_have_pattern: FRIEND
|
||||
must_have_motivation: WITNESS
|
||||
|
||||
- role: ring-operative
|
||||
display_hint: >
|
||||
The ring's operational member in maintenance corridors.
|
||||
Handles physical drops. Not a speaking character — observable behavior only.
|
||||
Can be the anonymous contact Kael meets.
|
||||
binding: named
|
||||
named_npc: "npc:nils-davan"
|
||||
is_optional: false
|
||||
|
||||
- role: institutional-watcher
|
||||
display_hint: >
|
||||
Maret Korr. A Commission observer embedded at The Terminal.
|
||||
Her growing attention is the external pressure that accelerates the timeline.
|
||||
She doesn't know about the ring specifically — she's tracking cargo patterns.
|
||||
binding: named
|
||||
named_npc: "npc:maret-korr"
|
||||
must_have_motivation: OPERATOR
|
||||
is_optional: true # Module runs without Maret, but with degraded tension arc
|
||||
|
||||
# ── EVENTS ───────────────────────────────────────────────────────────────────
|
||||
# Two sequences + one pool.
|
||||
# Sequence A: Kael's exit arc (the FRIEND contradiction backbone)
|
||||
# Sequence B: Investigation pressure arc (escalating discovery opportunities)
|
||||
# Pool: ambient ring activity (fires opportunistically throughout the module)
|
||||
|
||||
events:
|
||||
|
||||
sequences:
|
||||
|
||||
# SEQUENCE A: Kael's Exit Arc
|
||||
# The narrative spine. Each step makes Kael's contradiction more visible.
|
||||
# Observable to both characters, interpreted differently.
|
||||
|
||||
- sequence_id: kael_exit_arc
|
||||
label: "Kael's Exit Arc"
|
||||
description: >
|
||||
Kael Davan's progressive attempt to leave the ring.
|
||||
Tells intensify. Routine deviations appear. The secret meeting is the
|
||||
pivot point — after it fires, both characters' understanding shifts.
|
||||
steps:
|
||||
|
||||
- event_id: kael_goes_cold
|
||||
label: "Kael Goes Cold"
|
||||
description: >
|
||||
Kael starts missing social patterns he'd normally keep — fewer bar visits,
|
||||
shorter responses at The Terminal, leaving early. His tell system activates:
|
||||
the shoulder-check behavior appears. Nothing dramatic. Just absence where
|
||||
there was presence. The smuggler notices because they work together.
|
||||
The detective might notice if they've been tracking Kael's baseline.
|
||||
triggers:
|
||||
- type: ticks_since_activation
|
||||
ticks: 300 # ~5 minutes after module activates
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael skips his usual post-shift drink at The Last Shift.
|
||||
Leaves the terminal 15 minutes early. No explanation.
|
||||
- type: tell_intensify
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael's shoulder-check behavior activates at The Terminal.
|
||||
Visible to any character with forward vision cone in his direction.
|
||||
sets_flag: kael_behavior_changed
|
||||
|
||||
- event_id: drop_happens_without_kael
|
||||
label: "Scheduled Drop — Kael Absent"
|
||||
description: >
|
||||
A ring drop occurs in maintenance corridor C-7. Kael was supposed
|
||||
to verify the cargo. He wasn't there. Nils covered it.
|
||||
The smuggler notices the irregularity in the paperwork.
|
||||
The detective — if watching cargo patterns — sees a manifest entry
|
||||
with no verifying signature where one is normally present.
|
||||
triggers:
|
||||
- type: ticks_since_event
|
||||
after_event: kael_goes_cold
|
||||
ticks: 450 # ~7.5 minutes after goes-cold
|
||||
effects:
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.kael_missed_verification"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Smuggler: check the cargo manifest in The Terminal office.
|
||||
Detective: analytical lattice flags unsigned verification entry.
|
||||
- type: location_state
|
||||
location: maintenance-corridors
|
||||
description: "An unsigned cargo verification entry exists in corridor C-7's log."
|
||||
sets_flag: kael_missed_drop
|
||||
|
||||
- event_id: kael_secret_meeting
|
||||
label: "Kael's Secret Meeting"
|
||||
description: >
|
||||
Kael meets an off-district contact in maintenance corridor B-7.
|
||||
This is the observable contradiction (D-034): Kael, in a restricted
|
||||
area he has no logged reason to be in, talking to someone who's
|
||||
not in any district NPC roster. His body language is tense.
|
||||
If the player is in visual range: this is the pivot moment.
|
||||
If not: the meeting happens anyway — the world doesn't wait.
|
||||
triggers:
|
||||
- type: ticks_since_event
|
||||
after_event: drop_happens_without_kael
|
||||
ticks: 600 # ~10 minutes after the dropped verification
|
||||
- type: player_proximity
|
||||
target_type: location
|
||||
target: maintenance-corridors
|
||||
radius_tiles: 12 # Player wandering near triggers the meeting early
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael enters maintenance corridor B-7. Locked door to restricted
|
||||
supply closet. Emerges with the ring-operative 8 minutes later.
|
||||
Neither acknowledges the encounter publicly.
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.kael_unauthorized_corridor_access"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Player must be in visual range of corridor B-7.
|
||||
Or examine the corridor door access log (investigative action).
|
||||
- type: tell_intensify
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
After the meeting, Kael's shoulder-check frequency doubles.
|
||||
Also: he avoids eye contact with the smuggler at The Terminal.
|
||||
sets_flag: secret_meeting_occurred
|
||||
|
||||
- event_id: kael_sends_message
|
||||
label: "Kael Sends the Message"
|
||||
description: >
|
||||
Kael sends an encrypted Meridian message to an off-district contact.
|
||||
The detective's analytical lattice can detect an anomalous outgoing
|
||||
packet from the district node — not the content, just the pattern
|
||||
(frequent, encrypted, sent from cargo bay terminals, not personal devices).
|
||||
The smuggler won't see this unless they're specifically watching Kael.
|
||||
triggers:
|
||||
- type: ticks_since_event
|
||||
after_event: kael_secret_meeting
|
||||
ticks: 200
|
||||
effects:
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.kael_unusual_meridian_activity"
|
||||
discoverable_by: detective
|
||||
discovery_method: >
|
||||
Detective's analytical lattice flags the outgoing packet pattern.
|
||||
Requires player to be in or adjacent to The Terminal cargo bay.
|
||||
sets_flag: kael_message_sent
|
||||
|
||||
- event_id: ring_leader_confronts_kael
|
||||
label: "Voss Confronts Kael"
|
||||
description: >
|
||||
Voss calls Kael into The Terminal supervisor's office.
|
||||
Closed door. Raised voices (audible only from adjacent room/position).
|
||||
Kael emerges pale. Voss emerges neutral. The smuggler can witness
|
||||
the approach/departure without hearing content. The detective can
|
||||
observe Kael's state immediately after if in The Terminal.
|
||||
This is Voss applying pressure. Kael is now visibly under strain.
|
||||
triggers:
|
||||
- type: flag_set
|
||||
flag: kael_message_sent
|
||||
- type: ticks_since_event
|
||||
after_event: kael_message_sent
|
||||
ticks: 400
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-leader
|
||||
description: "Voss calls Kael into the supervisor's office. Door closed."
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael emerges from the meeting looking strained. His shoulder-check
|
||||
is now constant. He takes an unscheduled break outside, alone.
|
||||
- type: tell_intensify
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael's contentment hits lowest observed level. He now actively avoids
|
||||
the ring-operative (Nils) in public. The disconnection is visible.
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.voss_kael_private_meeting"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Observe the meeting room door (spatial). Or ask Kael directly
|
||||
after (trust-gated dialogue unlocks "Are you alright?" option).
|
||||
sets_flag: voss_pressure_applied
|
||||
|
||||
# SEQUENCE B: Investigation Pressure Arc
|
||||
# External pressure that escalates the timeline.
|
||||
# Fires in parallel with Sequence A.
|
||||
|
||||
- sequence_id: investigation_pressure
|
||||
label: "Investigation Pressure Arc"
|
||||
description: >
|
||||
Maret Korr's institutional attention creates a closing window.
|
||||
Her growing interest is the reason the module can't stay in equilibrium forever.
|
||||
She doesn't know about the ring — she's a pattern-watcher. But patterns
|
||||
are what the detective investigates too. Their paths converge.
|
||||
steps:
|
||||
|
||||
- event_id: maret_flags_anomaly
|
||||
label: "Maret Flags the Cargo Anomaly"
|
||||
description: >
|
||||
Maret Korr files an internal Commission note flagging The Terminal's
|
||||
cargo variance rate as statistically unusual. Not an investigation —
|
||||
just a flag. The detective's institutional access can pull this note.
|
||||
The smuggler has no way to know it exists (unless the detective tells them).
|
||||
triggers:
|
||||
- type: ticks_since_activation
|
||||
ticks: 900 # ~15 minutes after activation
|
||||
effects:
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.commission_cargo_flag"
|
||||
discoverable_by: detective
|
||||
discovery_method: >
|
||||
Detective queries Commission data via institutional access
|
||||
(authority access tier, Terminal records).
|
||||
sets_flag: commission_flag_exists
|
||||
|
||||
- event_id: maret_increases_presence
|
||||
label: "Maret Increases Her Presence"
|
||||
description: >
|
||||
Maret starts spending more time in The Terminal. More frequent
|
||||
walkthroughs during shift changes. Her attention to the cargo bay
|
||||
area is noticeable to anyone watching. Ring members are unnerved.
|
||||
Voss starts accelerating the timeline to close operations before
|
||||
institutional attention becomes formal investigation.
|
||||
triggers:
|
||||
- type: ticks_since_event
|
||||
after_event: maret_flags_anomaly
|
||||
ticks: 600
|
||||
- type: player_action
|
||||
action: examine
|
||||
target_role: institutional-watcher
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: institutional-watcher
|
||||
description: >
|
||||
Maret adds two extra Terminal walkthroughs per shift cycle.
|
||||
Spends 15 minutes studying the cargo bay manifest terminals.
|
||||
- type: tell_intensify
|
||||
npc_role: ring-leader
|
||||
description: >
|
||||
Voss becomes quieter, more deliberate. Less casual conversation.
|
||||
His tell — the stillness before speaking — becomes more frequent.
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.voss_accelerating_timeline"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Smuggler: Voss mentions "the schedule moving up" in a guarded
|
||||
conversation (trust-gated, ring-insider access required).
|
||||
Detective: observe Voss and Nils in two exchanges within same shift.
|
||||
sets_flag: timeline_accelerating
|
||||
|
||||
- event_id: final_shipment_scheduled
|
||||
label: "The Final Shipment Is Scheduled"
|
||||
description: >
|
||||
The ring schedules the last major drop — after this, they go dark.
|
||||
This is the closing window. If the detective hasn't uncovered enough
|
||||
by the time this fires, the ring disperses and the operation closes
|
||||
without exposure (escaped outcome). If they have, confrontation
|
||||
becomes unavoidable. The smuggler knows about this drop. Kael doesn't
|
||||
want to participate. Voss insists.
|
||||
triggers:
|
||||
- type: flag_set
|
||||
flag: timeline_accelerating
|
||||
- type: ticks_since_event
|
||||
after_event: maret_increases_presence
|
||||
ticks: 800
|
||||
effects:
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.final_shipment_scheduled"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Smuggler: direct notification from Voss.
|
||||
Detective: cargo manifest shows an unusual large entry for 3 days out.
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-member-exiting
|
||||
description: >
|
||||
Kael's schedule changes: he's assigned to the cargo bay
|
||||
during the drop window. He doesn't want to be there.
|
||||
sets_flag: final_shipment_known
|
||||
|
||||
pools:
|
||||
|
||||
# POOL: Ambient ring activity — opportunistic events that add texture
|
||||
- pool_id: ambient_ring_activity
|
||||
label: "Ambient Ring Activity"
|
||||
description: >
|
||||
Low-level ring business that happens throughout the module regardless of
|
||||
player engagement. Creates the sense that the ring exists independently.
|
||||
Players who look closely will find more; players who don't still feel the world moving.
|
||||
events:
|
||||
- event_id: cargo_discrepancy_appears
|
||||
label: "Small Cargo Discrepancy Appears"
|
||||
description: >
|
||||
A minor manifest irregularity appears in The Terminal records.
|
||||
Small enough to be deniable. Systematic enough to be a pattern.
|
||||
The detective's analytical lattice may flag it. The smuggler can
|
||||
correct it if they notice it — covering tracks is part of their role.
|
||||
triggers:
|
||||
- type: ticks_since_activation
|
||||
ticks: 150 # Fires early and repeats
|
||||
effects:
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.cargo_discrepancy_pattern"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Detective: analytical lattice flags during Terminal walkthrough.
|
||||
Smuggler: check manifest terminals (or get flagged by the discrepancy
|
||||
in their own work).
|
||||
once: false # Repeats — pattern builds over time
|
||||
|
||||
- event_id: sera_avoids_torek
|
||||
label: "Sera Avoids Torek at The Bar"
|
||||
description: >
|
||||
Sera Venn reroutes her usual path through The Last Shift to avoid
|
||||
standing near Torek Lintar (the Commission enforcement officer).
|
||||
Anyone watching Sera's normal pattern would notice.
|
||||
This is the detective's first clue that Sera's behavior is odd.
|
||||
triggers:
|
||||
- type: ticks_since_activation
|
||||
ticks: 500
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: evidence-holder
|
||||
description: >
|
||||
Sera takes a longer route to her usual seat, passing through
|
||||
the back of the bar to avoid Torek's sightline.
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.sera_avoidance_behavior"
|
||||
discoverable_by: detective
|
||||
discovery_method: >
|
||||
Observe Sera's path through the bar on two separate occasions.
|
||||
Requires forward vision cone and awareness of her baseline route.
|
||||
once: false
|
||||
|
||||
- event_id: nils_makes_supply_run
|
||||
label: "Nils Makes an Unscheduled Supply Run"
|
||||
description: >
|
||||
The ring-operative (Nils) enters the maintenance corridors with a
|
||||
small container logged as "calibration tools". The container isn't
|
||||
logged for return. Someone paying attention to cargo flow would notice.
|
||||
triggers:
|
||||
- type: ticks_since_activation
|
||||
ticks: 700
|
||||
effects:
|
||||
- type: npc_routine_deviation
|
||||
npc_role: ring-operative
|
||||
description: "Nils takes a container to maintenance corridor C-7."
|
||||
- type: fact_becomes_discoverable
|
||||
fact_id: "ring.nils_unlogged_cargo"
|
||||
discoverable_by: any
|
||||
discovery_method: >
|
||||
Watch Nils's cargo handling pattern over two shifts.
|
||||
Or examine maintenance corridor C-7 access log.
|
||||
once: false
|
||||
|
||||
# ── OUTCOMES ─────────────────────────────────────────────────────────────────
|
||||
# Five resolution states. Checked each tick after the first sequence step fires.
|
||||
# Order matters — the storyteller applies the first matching outcome.
|
||||
# is_terminal: true ends the module.
|
||||
|
||||
outcomes:
|
||||
|
||||
# 1. RING EXPOSED
|
||||
# Detective successfully uncovers the operation.
|
||||
# Commission becomes involved. Arrests/flight follow.
|
||||
- outcome_id: ring_exposed
|
||||
label: "Ring Exposed"
|
||||
is_terminal: true
|
||||
description: >
|
||||
The detective accumulates enough evidence to trigger a formal Commission
|
||||
inquiry. The ring collapses: arrests, flight, or both. Voss is detained.
|
||||
Kael's situation is now public. The smuggler (if played) faces consequences.
|
||||
Naia learns what Kael was doing — and why he was trying to leave.
|
||||
No clean endings. The right outcome for the detective who goes all the way.
|
||||
conditions:
|
||||
facts_known:
|
||||
- "ring.cargo_discrepancy_pattern"
|
||||
- "ring.kael_unauthorized_corridor_access"
|
||||
- "ring.voss_kael_private_meeting"
|
||||
flags_set:
|
||||
- secret_meeting_occurred # set by kael_secret_meeting event
|
||||
- commission_flag_exists # Commission was watching before exposure
|
||||
effects:
|
||||
- type: npc_disposition
|
||||
npc_role: ring-leader
|
||||
shift: hostile
|
||||
description: "Voss is detained or flees. Commission inquiry opens."
|
||||
- type: npc_disposition
|
||||
npc_role: ring-member-exiting
|
||||
shift: hostile
|
||||
description: >
|
||||
Kael is arrested or disappears. His exit attempt is now moot.
|
||||
His relationship with Naia is exposed.
|
||||
- type: faction_reaction
|
||||
faction: lattice-commission
|
||||
reaction: grateful
|
||||
description: "Commission credits the detective's investigation."
|
||||
- type: npc_exit
|
||||
npc_role: ring-leader
|
||||
description: "Voss leaves the district — detained, fled, or both."
|
||||
|
||||
# 2. KAEL ESCAPES THE RING
|
||||
# Unique path. Requires the player to engage with Kael directly
|
||||
# and choose to help him rather than expose the ring wholesale.
|
||||
- outcome_id: kael_escapes
|
||||
label: "Kael Escapes the Ring"
|
||||
is_terminal: true
|
||||
description: >
|
||||
Through the player's choices — helping Kael cover his exit, or warning him,
|
||||
or simply choosing not to act on what they know — Kael successfully leaves
|
||||
the ring before the final shipment. He and Naia leave the district quietly.
|
||||
The ring continues without him, smaller and more cautious.
|
||||
This outcome requires discovering Kael's secret AND choosing restraint.
|
||||
The smuggler can engineer this by covering for Kael with Voss.
|
||||
The detective can achieve this by confronting Kael privately rather than
|
||||
filing a report. The most morally complicated path.
|
||||
conditions:
|
||||
facts_known:
|
||||
- "ring.kael_unauthorized_corridor_access"
|
||||
flags_set:
|
||||
- kael_behavior_changed # set by kael_goes_cold — his exit arc begins here
|
||||
- secret_meeting_occurred # set by kael_secret_meeting — the pivot moment
|
||||
- voss_pressure_applied # set by ring_leader_confronts_kael — pressure applied
|
||||
# ring_exposed is checked first in the outcomes list and is terminal,
|
||||
# so kael_escapes only evaluates if ring_exposed hasn't fired.
|
||||
# No flags_not_set needed here — outcome ordering handles priority.
|
||||
effects:
|
||||
- type: npc_disposition
|
||||
npc_role: ring-member-exiting
|
||||
shift: friendly
|
||||
description: "Kael remembers whoever helped him. He's gone, but grateful."
|
||||
- type: npc_exit
|
||||
npc_role: ring-member-exiting
|
||||
description: "Kael and Naia leave Sova Transit District."
|
||||
- type: faction_reaction
|
||||
faction: the-ring
|
||||
reaction: suspicious
|
||||
description: "The ring is destabilized by Kael's exit. Voss is alert to further leaks."
|
||||
|
||||
# 3. RING COMPLETES OPERATION
|
||||
# The ring finishes the final shipment and goes dark before discovery.
|
||||
# Default path if the detective doesn't move fast enough.
|
||||
- outcome_id: ring_completes
|
||||
label: "Ring Completes the Operation"
|
||||
is_terminal: true
|
||||
description: >
|
||||
The final shipment clears. The ring disperses. Voss transfers. Nils goes quiet.
|
||||
Kael stays — he's now out by default, the ring having dissolved around him.
|
||||
The evidence trail goes cold. The detective closes the case as inconclusive.
|
||||
The smuggler completes their last run and waits to see if there's another.
|
||||
Unsatisfying only if you expected a tidy resolution. The world moved on.
|
||||
conditions:
|
||||
flags_set:
|
||||
- final_shipment_known # set by final_shipment_scheduled event
|
||||
- timeline_accelerating # set by maret_increases_presence — Maret forced their hand
|
||||
facts_not_known:
|
||||
- "ring.cargo_discrepancy_pattern" # detective never found the basic pattern — no investigation
|
||||
ticks_since_activation: 3600 # Module ran for ~60 minutes without full exposure
|
||||
# kael_message_sent was previously gated here but auto-fires at tick ~1550,
|
||||
# making this outcome permanently unreachable. Replaced with player-action fact gate.
|
||||
effects:
|
||||
- type: faction_reaction
|
||||
faction: the-ring
|
||||
reaction: neutral
|
||||
description: "The ring successfully completed this operation. They'll be back."
|
||||
- type: npc_exit
|
||||
npc_role: ring-leader
|
||||
description: "Voss transfers to another station for 'career development'."
|
||||
- type: location_access_change
|
||||
location: maintenance-corridors
|
||||
change: open
|
||||
description: "The restricted supply closet is now empty. Access log shows it cleared."
|
||||
|
||||
# 4. RING SPLINTERS
|
||||
# Partial discovery. The ring fractures but doesn't fully collapse.
|
||||
# An incomplete ending that leaves threads for future investigation.
|
||||
- outcome_id: ring_splinters
|
||||
label: "Ring Splinters"
|
||||
is_terminal: false # Not terminal — splinter state can evolve
|
||||
description: >
|
||||
Enough evidence surfaces that the ring knows it's been partially seen.
|
||||
Voss shuts down active operations. Nils disappears. Kael stays — now the
|
||||
one person in the district who knows what happened and has no one to tell.
|
||||
The formal investigation stalls for lack of a clear chain of evidence.
|
||||
The detective has facts but not the complete picture. The smuggler
|
||||
faces an awkward return to normalcy. Both know the ring isn't gone — just quiet.
|
||||
conditions:
|
||||
facts_known:
|
||||
- "ring.cargo_discrepancy_pattern" # detective found some evidence — ring responds
|
||||
events_fired:
|
||||
- kael_goes_cold # event ID — Kael's behavioral shift fired
|
||||
flags_set:
|
||||
- kael_missed_drop # set by drop_happens_without_kael — ring destabilized
|
||||
ticks_since_activation: 2400
|
||||
# Mutually exclusive with ring_completes via facts_known/facts_not_known on
|
||||
# ring.cargo_discrepancy_pattern. No auto-flag gate needed.
|
||||
effects:
|
||||
- type: npc_disposition
|
||||
npc_role: ring-leader
|
||||
shift: suspicious
|
||||
description: "Voss goes quiet. He's watching to see who knows what."
|
||||
- type: npc_exit
|
||||
npc_role: ring-operative
|
||||
description: "Nils stops appearing at The Terminal. Transferred, officially."
|
||||
- type: faction_reaction
|
||||
faction: the-ring
|
||||
reaction: suspicious
|
||||
description: "The ring is alerted to exposure risk. Future operations will be more careful."
|
||||
|
||||
# 5. INVESTIGATION STALLS (post-splinter exit)
|
||||
# The ring splinters but the detective never breaks through to the pivot evidence.
|
||||
# Explicit terminal exit for the non-terminal ring_splinters state.
|
||||
- outcome_id: ring_stalemate
|
||||
label: "Investigation Stalls"
|
||||
is_terminal: true
|
||||
description: >
|
||||
The ring went dark after the splinter. The detective has the cargo discrepancy
|
||||
on record — enough to flag, not enough to pursue. The case stays open but cold.
|
||||
No arrests. No answers. Kael stays in the district, the only person who knows
|
||||
the full shape of what happened, with no one left to tell it to.
|
||||
The ring will reconstitute elsewhere. It always does.
|
||||
conditions:
|
||||
facts_known:
|
||||
- "ring.cargo_discrepancy_pattern" # ring_splinters already fired (same gate)
|
||||
facts_not_known:
|
||||
- "ring.kael_unauthorized_corridor_access" # detective never reached the pivot evidence
|
||||
flags_set:
|
||||
- kael_missed_drop
|
||||
- final_shipment_known # ring finished while investigation stalled
|
||||
ticks_since_activation: 4500 # 2100 ticks after ring_splinters window — investigation ran cold
|
||||
effects:
|
||||
- type: faction_reaction
|
||||
faction: lattice-commission
|
||||
reaction: neutral
|
||||
description: "The discrepancy flag stays in Maret's file. No follow-up action."
|
||||
- type: npc_exit
|
||||
npc_role: ring-leader
|
||||
description: "Voss quietly transfers. No announcement, no incident report."
|
||||
|
||||
# 6. MODULE EXPIRY (quiet exit)
|
||||
# Player never engaged at all. Module times out without drama.
|
||||
# NOTE (Gestalt, Sprint 18): Condition uses facts_not_known, not flags_not_set.
|
||||
# kael_behavior_changed fires automatically at tick 300 (time-triggered), making
|
||||
# flags_not_set: [kael_behavior_changed] permanently false after tick 300.
|
||||
# Gate expiry on player-action-required facts instead.
|
||||
- outcome_id: module_abandoned
|
||||
label: "Module Abandoned"
|
||||
is_terminal: true
|
||||
is_expiry: true
|
||||
description: >
|
||||
The player never engaged with the ring's signals. The final shipment
|
||||
completed without incident. The ring disperses on its own schedule.
|
||||
Kael stays. The world is unchanged. This is not failure — it's the game
|
||||
acknowledging that not every conspiracy needs a protagonist.
|
||||
The 70% mundane majority (D-029) plays out: life continued.
|
||||
conditions:
|
||||
facts_not_known:
|
||||
- "ring.cargo_discrepancy_pattern" # Only known via player examination of terminal
|
||||
- "ring.kael_unauthorized_corridor_access" # Only known via player observing Kael in B-7
|
||||
ticks_since_activation: 5400 # ~90 minutes with zero player investigation
|
||||
effects:
|
||||
- type: faction_reaction
|
||||
faction: the-ring
|
||||
reaction: neutral
|
||||
description: "The ring closed operations without incident. No record of compromise."
|
||||
@@ -0,0 +1,832 @@
|
||||
# NPC-to-NPC Overheard Dialogue Pool
|
||||
# Ticket: #536 | Author: Mellanie | Sprint: 14
|
||||
# Decision refs: D-078, D-018, D-071, D-035
|
||||
#
|
||||
# LEGACY CONTENT — v0.1 hand-authored pool. DO NOT DELETE.
|
||||
# All entries in this file reference named NPCs from the Sova Transit District
|
||||
# vertical slice (Kael Davan, Sera Venn, Naia Tamm, Voss, Nils, Maret Korr, etc.).
|
||||
# These named NPCs were authored for the drama module system (ticket #158), which
|
||||
# was superseded before implementation. The named references in this file are
|
||||
# therefore broken — they reference characters that do not exist in the generated
|
||||
# NPC population.
|
||||
# Ticket #664 tracks replacement of this file with a zone-type-aware overheard
|
||||
# generator that produces conversations from role pairs rather than named characters.
|
||||
# Until #664 ships, this file is not served at runtime.
|
||||
#
|
||||
# These are conversations the player can overhear when stationary near two
|
||||
# NPCs. The passive dialogue panel (D-078) displays them with per-word
|
||||
# occlusion based on distance, ambient noise, and ListeningFocus stance.
|
||||
#
|
||||
# OCCLUSION-RESILIENT AUTHORING RULES (D-078):
|
||||
# 1. Front-load key information — most important word in first third.
|
||||
# 2. Short declarative sentences — one idea per turn.
|
||||
# 3. No pronoun-first openers — first word has highest drop risk; a dropped
|
||||
# pronoun without antecedent is unresolvable. Use names or nouns.
|
||||
# 4. Each turn is self-contained — a player who hears only one side gets
|
||||
# a complete thought.
|
||||
#
|
||||
# SCHEMA NOTE:
|
||||
# `relationship_type` and `knowledge_payload` are custom extensions to the
|
||||
# D-035 schema for this content type. NPC-sourced lines use `role: npc`,
|
||||
# `access` and `trust` apply normally. Monologue-specific tags (`character`,
|
||||
# `trigger`, `prerequisite`) are not used here.
|
||||
# Schema confirmation with Gestalt (#168) before CI validation.
|
||||
#
|
||||
# REGISTERS:
|
||||
# social — idle chat, personal news, relationship talk
|
||||
# work — shift logistics, job gripes, operational notes
|
||||
# gossip — third-party information with player-relevant knowledge payload
|
||||
#
|
||||
# RELATIONSHIP TYPES:
|
||||
# colleague, friend, hostile, romantic
|
||||
#
|
||||
# KNOWLEDGE PAYLOAD FORMAT:
|
||||
# Plain English description of what the player can infer from hearing this
|
||||
# exchange clearly — or the key fragment they retain under partial occlusion.
|
||||
# null if the exchange is social noise with no investigative value.
|
||||
|
||||
pairs:
|
||||
|
||||
# ===================================================================
|
||||
# SOCIAL register — personal news, idle chat, relationships
|
||||
# ===================================================================
|
||||
|
||||
- id: overheard_001
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Kael brought food for the whole bay yesterday. Just showed up with it."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Naia must have made him feel guilty about something. Again."
|
||||
relationship_type: colleague
|
||||
topic: personal-gossip
|
||||
location_hints: [the-terminal, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael and Naia have a relationship dynamic. Naia has leverage or emotional pull over Kael's behavior."
|
||||
tags: [kael, naia, social, atmosphere]
|
||||
|
||||
- id: overheard_002
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Lera's keeping the kitchen open late this week. Span gate delay — crews stuck here."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Good for Lera. Bad for everyone waiting on the gate."
|
||||
relationship_type: friend
|
||||
topic: station-life
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [lera, span-gate, atmosphere, social]
|
||||
|
||||
- id: overheard_003
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Drin's applying for the transfer. Again. Third time he's tried."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Drin's never getting off Sova. Some people just belong to a place."
|
||||
relationship_type: colleague
|
||||
topic: career-gossip
|
||||
location_hints: [the-terminal, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Drin wants out of Sova Transit. Unhappy enough to pursue transfer requests."
|
||||
tags: [drin, transfer, social, atmosphere]
|
||||
|
||||
- id: overheard_004
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Naia and Kael had a fight. Loud enough that Lera had to step in."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Kael looked rough this morning. That tracks."
|
||||
relationship_type: friend
|
||||
topic: relationship-drama
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael and Naia are under relationship strain. Kael's mood is affected. Cross-reference: behavioral.kael_behavioral_change."
|
||||
tags: [kael, naia, relationship, friend-arc-adjacent, dual-lens]
|
||||
|
||||
- id: overheard_005
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Renn's finally getting his lattice service done. Been putting it off for years."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Commission clinic wait time is eight months. Renn found someone faster."
|
||||
relationship_type: colleague
|
||||
topic: lattice-access
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Renn found a non-Commission lattice service provider. Points toward unlicensed lattice components market."
|
||||
tags: [renn, lattice, contraband-adjacent, atmosphere]
|
||||
|
||||
- id: overheard_006
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Maret's promotion came through. Third level supervisor."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Good. Maret actually knows what she's doing. Unlike some."
|
||||
relationship_type: colleague
|
||||
topic: career-news
|
||||
location_hints: [the-last-shift, the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [maret, promotion, atmosphere, social]
|
||||
|
||||
- id: overheard_007
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Voss has been in a mood all week. Something from upstairs."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Voss is always in a mood. Week ends in -day, Voss is in a mood."
|
||||
relationship_type: colleague
|
||||
topic: supervisor-gossip
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Voss is under external pressure from 'upstairs.' Something is stressing management."
|
||||
tags: [voss, management, atmosphere, social]
|
||||
|
||||
- id: overheard_008
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Sera's been coming here every evening this week. Thought Commission people didn't drink."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Sera's different. She fits here better than she fits over there."
|
||||
relationship_type: colleague
|
||||
topic: social-observation
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Sera Venn is a regular at The Last Shift, unusual for a Commission employee. She has social roots in the district."
|
||||
tags: [sera, commission, atmosphere, social, dual-lens]
|
||||
|
||||
- id: overheard_009
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Nils covered Kael's morning slot yesterday. No explanation, just a roster note."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Kael's been doing that a lot lately. Taking sick leave, then showing up mid-shift."
|
||||
relationship_type: colleague
|
||||
topic: roster-anomaly
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael's schedule is irregular. Unexplained absences and late arrivals. Cross-reference: observe_anomaly triggers for Kael."
|
||||
tags: [kael, nils, roster, friend-arc-adjacent]
|
||||
|
||||
- id: overheard_010
|
||||
register: social
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Olin's kid got into the Commission cadet program. Starts next cycle."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Olin must be pleased. Cost of living here, Commission pay makes sense."
|
||||
relationship_type: colleague
|
||||
topic: family-news
|
||||
location_hints: [maintenance-corridors, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [olin, commission, atmosphere, social]
|
||||
|
||||
# ===================================================================
|
||||
# WORK register — shift logistics, operational gripes, job talk
|
||||
# ===================================================================
|
||||
|
||||
- id: overheard_011
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Maret shifted the roster again. Bay four to bay seven, no reason given."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Bay seven's the low-traffic slot. Somebody wanted less oversight over there."
|
||||
relationship_type: colleague
|
||||
topic: roster-change
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Bay seven has been given a low-oversight crew deliberately. This is operationally significant."
|
||||
tags: [maret, roster, bay-seven, investigation-adjacent]
|
||||
|
||||
- id: overheard_012
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Loading arm three is grinding again. Filed the report last week."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Maintenance says next cycle. Means nothing gets done until someone loses a hand."
|
||||
relationship_type: colleague
|
||||
topic: equipment-maintenance
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [equipment, maintenance, atmosphere, work]
|
||||
|
||||
- id: overheard_013
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Container 4471's been in temp for three days. Someone should move it."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Routing says hold. Don't ask me, I just process what the board says."
|
||||
relationship_type: colleague
|
||||
topic: container-routing
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Container 4471 is being deliberately held in temp storage. 'Routing says hold' — someone modified the routing instruction."
|
||||
tags: [container-4471, routing, contraband-adjacent, investigation-payload]
|
||||
|
||||
- id: overheard_014
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Voss cut the B-section crew by two. Says it's budget. Doesn't feel like budget."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Less crew in B-section means less oversight. Could be budget. Could be something else."
|
||||
relationship_type: colleague
|
||||
topic: crew-reduction
|
||||
location_hints: [the-terminal, maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "B-section is understaffed. The speaker suspects this is deliberate. Cross-reference: ring oversight windows."
|
||||
tags: [voss, b-section, oversight, investigation-payload]
|
||||
|
||||
- id: overheard_015
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Span gate's backed up again. Forty-minute delay on the freight queue."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Forty minutes is nothing. Last month it was three hours. Patience."
|
||||
relationship_type: colleague
|
||||
topic: span-gate-delay
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [span-gate, freight, atmosphere, work]
|
||||
|
||||
- id: overheard_016
|
||||
register: work
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Junction C-2's camera was repositioned. Nobody filed a maintenance request."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Someone moved it without logging it. That's a compliance violation."
|
||||
relationship_type: colleague
|
||||
topic: camera-anomaly
|
||||
location_hints: [maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Camera at junction C-2 was moved without documentation. Suggests deliberate surveillance manipulation. Cross-reference: awareness.surveillance_change."
|
||||
tags: [camera, surveillance, c-2, investigation-payload]
|
||||
|
||||
- id: overheard_017
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Commission wants another inspection. Fourth one this quarter."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Inspections mean overtime. Inspections mean everyone's watching everyone."
|
||||
relationship_type: colleague
|
||||
topic: commission-inspection
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Commission is conducting elevated inspections of the terminal. Something has drawn their attention."
|
||||
tags: [commission, inspection, atmosphere, investigation-adjacent]
|
||||
|
||||
- id: overheard_018
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Weight manifest on the morning freight came in five hundred kilos short. Recalibration error."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Five hundred kilos doesn't disappear from a calibration error. It disappears from somewhere else."
|
||||
relationship_type: colleague
|
||||
topic: weight-discrepancy
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Manifest weight discrepancy exists and dock workers are suspicious of the official explanation."
|
||||
tags: [manifest, weight-discrepancy, contraband-adjacent, investigation-payload]
|
||||
|
||||
- id: overheard_019
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Shift handover's going to be rough tonight. Torek's team hasn't filed."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Torek's team never files on time. Let Maret handle it."
|
||||
relationship_type: colleague
|
||||
topic: shift-handover
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [torek, maret, shift, atmosphere, work]
|
||||
|
||||
- id: overheard_020
|
||||
register: work
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Sub-level access has been requested twice this week. Both times outside shift hours."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Scheduled maintenance happens in-shift. Off-hours access needs sign-off."
|
||||
relationship_type: colleague
|
||||
topic: sub-level-access
|
||||
location_hints: [maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Someone has been accessing sub-level spaces outside normal hours. No sign-off implies unauthorized use. Cross-reference: smuggling-hold activity."
|
||||
tags: [sub-level, access, maintenance, investigation-payload]
|
||||
|
||||
- id: overheard_021
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Bay four's been sealed for inspection since yesterday morning."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Commission authorized it. Not Voss. Commission went over his head."
|
||||
relationship_type: colleague
|
||||
topic: bay-inspection
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Bay four inspection bypassed Voss's authority. Commission is operating independently of local management."
|
||||
tags: [bay-four, commission, voss, inspection, investigation-payload]
|
||||
|
||||
- id: overheard_022
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Kael's running Renn's route today. Renn's supposed to be on that."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Kael volunteered. Said Renn had something come up."
|
||||
relationship_type: colleague
|
||||
topic: route-substitution
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael is voluntarily taking routes outside his assignment. Could be covering for Renn, could be operational flexibility needed by the ring."
|
||||
tags: [kael, renn, route, ring-adjacent]
|
||||
|
||||
- id: overheard_023
|
||||
register: work
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Lera's dealing with the supply chain issue again. Grain spirit supplier changed terms."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Lera will figure it out. Lera always figures it out."
|
||||
relationship_type: friend
|
||||
topic: supply-chain
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [lera, supply, atmosphere, work]
|
||||
|
||||
- id: overheard_024
|
||||
register: work
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Condensation in the B-corridor's gotten worse. Someone's running heat-generation equipment down there."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Heat-gen in a maintenance corridor. That's either a storage issue or a very bad idea."
|
||||
relationship_type: colleague
|
||||
topic: corridor-anomaly
|
||||
location_hints: [maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Heat-generating equipment is being used in maintenance corridors unofficially. Points toward the smuggling hold or ring operations."
|
||||
tags: [b-corridor, heat, maintenance, smuggling-adjacent]
|
||||
|
||||
- id: overheard_025
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Torek's been doing double manifests for a month. Every container logged twice."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Double logging means one version goes somewhere it shouldn't."
|
||||
relationship_type: colleague
|
||||
topic: manifest-anomaly
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Torek is maintaining duplicate manifest records. One version is falsified. Direct evidence of ring operation at the administrative level."
|
||||
tags: [torek, manifest, contraband, investigation-payload, high-value]
|
||||
|
||||
# ===================================================================
|
||||
# GOSSIP register — third-party knowledge with investigative payload
|
||||
# ===================================================================
|
||||
|
||||
- id: overheard_026
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Kael was in corridor B-7 last night. Saw him myself. Off shift, wrong time, wrong place."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Kael lives near B-section. Could have been heading home the long way."
|
||||
relationship_type: colleague
|
||||
topic: kael-location
|
||||
location_hints: [the-terminal, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael was in corridor B-7 off-shift. Eyewitness account."
|
||||
tags: [kael, corridor-b7, investigation-payload, friend-arc, high-value]
|
||||
|
||||
- id: overheard_027
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Kael was talking to someone near B-7 last night. Person I didn't recognize."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Unknown people at junction B-7 at night. That's not normal."
|
||||
relationship_type: friend
|
||||
topic: kael-unknown-contact
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael met with an unidentified person at corridor B-7 during off-hours."
|
||||
tags: [kael, unknown-contact, corridor-b7, friend-arc, high-value, investigation-payload]
|
||||
|
||||
- id: overheard_028
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Torek spent three thousand credits at Lera's last week. Three thousand. On a dock worker's salary."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Torek's either very lucky or very stupid. Either way, someone's going to notice."
|
||||
relationship_type: friend
|
||||
topic: torek-spending
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Torek is spending significantly above his salary at The Last Shift."
|
||||
tags: [torek, spending, lera, investigation-payload, ring-adjacent]
|
||||
|
||||
- id: overheard_029
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Renn got new boots. Commission-grade. Renn can't afford Commission-grade boots."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Renn's been running extra shifts. Or extra something."
|
||||
relationship_type: colleague
|
||||
topic: renn-spending
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Renn has unexplained extra income. Cross-reference: ring membership payments."
|
||||
tags: [renn, income, ring-adjacent, investigation-adjacent]
|
||||
|
||||
- id: overheard_030
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Sera Venn avoids Torek every time he's here. Every single time. Noticed it three weeks straight."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Torek does that to people. He talks too much and says things he shouldn't."
|
||||
relationship_type: colleague
|
||||
topic: sera-torek-avoidance
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Sera Venn has a consistent avoidance pattern toward Torek Lintar. Cross-reference: behavioral.sera_avoidance_pattern."
|
||||
tags: [sera, torek, avoidance, friend-arc, investigation-payload, high-value]
|
||||
|
||||
- id: overheard_031
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Commission officer's been asking questions at The Terminal. Polite questions. Thorough ones."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Polite and thorough is the worst combination. That's someone who has time."
|
||||
relationship_type: colleague
|
||||
topic: commission-investigation
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "A Commission officer is conducting a quiet investigation at The Terminal. Cross-reference: awareness.detective_presence."
|
||||
tags: [commission, detective, investigation-payload, awareness]
|
||||
|
||||
- id: overheard_032
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Lattice components went through last month. Unlicensed grade. Manifest said 'mechanical parts.'"
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Mechanical parts. Right. People find ways when the Commission won't."
|
||||
relationship_type: colleague
|
||||
topic: contraband-transit
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Unlicensed lattice components are moving through The Terminal under falsified manifests. Cross-reference: contraband operation confirmed."
|
||||
tags: [lattice, contraband, manifest, investigation-payload, high-value]
|
||||
|
||||
- id: overheard_033
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Sub-level temp storage has been accessed four times this week. Door log shows it."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Four times and no maintenance ticket filed. Someone's using it off-book."
|
||||
relationship_type: colleague
|
||||
topic: unauthorized-access
|
||||
location_hints: [maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "The sub-level storage (smuggling hold) has heavy unauthorized use documented in door logs. Direct evidence of ring operations."
|
||||
tags: [sub-level, access-log, ring, investigation-payload, high-value]
|
||||
|
||||
- id: overheard_034
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Voss changed the B-section rotation. Kael and Renn are both on the overnight slot now."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Kael and Renn on overnight in B-section. That's a very specific combination."
|
||||
relationship_type: colleague
|
||||
topic: roster-combination
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael and Renn have been placed on the same overnight B-section rotation. This creates the oversight gap the ring needs."
|
||||
tags: [kael, renn, voss, roster, ring-adjacent, investigation-payload]
|
||||
|
||||
- id: overheard_035
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Sera asked me about container routing last week. Wanted to know who approves temp storage extensions."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Commission tech asking about temp storage approvals. That's outside her job scope."
|
||||
relationship_type: colleague
|
||||
topic: sera-investigation
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Sera Venn is conducting her own investigation into temp storage approvals, outside her Commission mandate. Cross-reference: behavioral.sera_kiosk_pattern."
|
||||
tags: [sera, storage, investigation-payload, friend-arc, dual-lens, high-value]
|
||||
|
||||
- id: overheard_036
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Naia told me Kael hasn't been sleeping. Says he's up late, doesn't explain where."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Naia's worried about him. Kael won't talk about whatever it is."
|
||||
relationship_type: friend
|
||||
topic: kael-behavioral-change
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael's behavior has changed at home. Sleepless, secretive, won't explain to Naia. Cross-reference: behavioral.kael_behavioral_change."
|
||||
tags: [kael, naia, behavior, friend-arc, investigation-payload]
|
||||
|
||||
- id: overheard_037
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Someone flagged a manifest discrepancy on the morning freight. Voss cleared the flag himself."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Voss doesn't clear flags. That's Maret's job. Or Commission's."
|
||||
relationship_type: colleague
|
||||
topic: manifest-flag-cleared
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Voss manually cleared a manifest discrepancy flag, bypassing protocol. Suggests Voss is actively covering for ring activity."
|
||||
tags: [voss, manifest, flag-cleared, investigation-payload, high-value]
|
||||
|
||||
- id: overheard_038
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Kael's been asking about exit options. Not just talk — actually asking Lera if she knows anyone who could help someone disappear quietly."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Kael wants out of something. That's the only reason people ask that kind of question."
|
||||
relationship_type: friend
|
||||
topic: kael-exit-attempt
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Kael is actively trying to leave 'something' — most likely the ring. He's researching options."
|
||||
tags: [kael, exit, ring-adjacent, friend-arc, investigation-payload, high-value]
|
||||
|
||||
- id: overheard_039
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Medical lattice upgrades are showing up on the black-side. Commission-grade, no paperwork."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "People who need them can't wait on Commission approval. Someone's doing a service."
|
||||
relationship_type: colleague
|
||||
topic: black-market-lattice
|
||||
location_hints: [the-terminal, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Medical-grade lattice components are available outside Commission channels. Confirms the moral framing of the contraband operation — it serves real medical need."
|
||||
tags: [lattice, medical, black-market, contraband, moral-framing]
|
||||
|
||||
- id: overheard_040
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Torek told someone he had a meeting last night. Past midnight. In maintenance."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Torek having midnight maintenance meetings. Sure. That's completely normal."
|
||||
relationship_type: colleague
|
||||
topic: torek-late-meeting
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Torek met with someone in the maintenance corridors late at night."
|
||||
tags: [torek, maintenance, midnight, ring-adjacent, investigation-payload]
|
||||
|
||||
# ===================================================================
|
||||
# BONUS PAIRS — social and work depth
|
||||
# ===================================================================
|
||||
|
||||
- id: overheard_041
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Lera's thinking about expanding. Back room could seat twenty more."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Back room's the only reason this place has any privacy. Expand it and we lose that."
|
||||
relationship_type: friend
|
||||
topic: bar-expansion
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [lera, bar, atmosphere, social]
|
||||
|
||||
- id: overheard_042
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Shift change is late again. Maret says weather on Velen is delaying the span gate."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Weather on Velen. Which means fog. Which means the morning run is going to be rough."
|
||||
relationship_type: colleague
|
||||
topic: weather-delay
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [velen, fog, span-gate, weather, atmosphere]
|
||||
|
||||
- id: overheard_043
|
||||
register: social
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Olin's getting a commendation from Commission. Ten years of service."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Ten years. Commission gives you a piece of paper and a handshake."
|
||||
relationship_type: colleague
|
||||
topic: commission-recognition
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [olin, commission, atmosphere, social]
|
||||
|
||||
- id: overheard_044
|
||||
register: work
|
||||
speaker_a:
|
||||
role: maintenance-tech
|
||||
text: "Power fluctuation in sub-level B last night. Lasted three minutes."
|
||||
speaker_b:
|
||||
role: maintenance-tech
|
||||
text: "Three minutes is enough to blind the cameras if you know the timing."
|
||||
relationship_type: colleague
|
||||
topic: power-fluctuation
|
||||
location_hints: [maintenance-corridors]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "A power fluctuation in sub-level B temporarily disabled cameras. Could be timed to enable unobserved access."
|
||||
tags: [power, cameras, sub-level, timing, investigation-adjacent]
|
||||
|
||||
- id: overheard_045
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Commission officer asked Drin about cargo manifest irregularities. Drin told him everything he knows, which isn't much."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Drin doesn't know much by design. That's why Drin's on inspection detail."
|
||||
relationship_type: colleague
|
||||
topic: commission-inquiry
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "The Commission officer is asking dock workers directly about manifest irregularities. Drin has been interviewed. Cross-reference: awareness.detective_presence."
|
||||
tags: [drin, commission, detective, interview, investigation-payload]
|
||||
|
||||
- id: overheard_046
|
||||
register: gossip
|
||||
speaker_a:
|
||||
role: bar-regular
|
||||
text: "Sera's been keeping a list. Someone told Naia. Private list, personal data."
|
||||
speaker_b:
|
||||
role: bar-regular
|
||||
text: "Sera keeping a list of what? And why would Naia know?"
|
||||
relationship_type: friend
|
||||
topic: sera-documentation
|
||||
location_hints: [the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Sera is documenting something privately — possibly her own investigation or evidence she's sitting on."
|
||||
tags: [sera, naia, list, evidence, friend-arc, investigation-payload, dual-lens]
|
||||
|
||||
- id: overheard_047
|
||||
register: social
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Sova's been home for twenty years. Still don't know if I love it or just got used to it."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Twenty years is the same thing."
|
||||
relationship_type: friend
|
||||
topic: station-life
|
||||
location_hints: [the-terminal, the-last-shift]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: null
|
||||
tags: [sova, atmosphere, personal, social]
|
||||
|
||||
- id: overheard_048
|
||||
register: work
|
||||
speaker_a:
|
||||
role: dock-worker
|
||||
text: "Voss approved overtime for two extra heads on the B-section night shift. Unusual."
|
||||
speaker_b:
|
||||
role: dock-worker
|
||||
text: "Overtime plus extra bodies in B at night. Either something's wrong or something's being made to look right."
|
||||
relationship_type: colleague
|
||||
topic: overtime-approval
|
||||
location_hints: [the-terminal]
|
||||
access: [public]
|
||||
trust: surface
|
||||
knowledge_payload: "Voss approved extra overnight staffing in B-section — possibly to create cover or provide legitimate-looking explanation for movement in that area."
|
||||
tags: [voss, overtime, b-section, night-shift, investigation-adjacent]
|
||||
@@ -0,0 +1,718 @@
|
||||
# Drama Module Schema — Tier 1 Content (D-023)
|
||||
# YAML expression of JSON Schema 2020-12
|
||||
# Validated against this schema: server/content/modules/tier1/*.yaml
|
||||
#
|
||||
# Ownership:
|
||||
# Dramatic structure (this file): Paula
|
||||
# YAML validation tooling / serde structs: Gestalt / Tyre
|
||||
# Authoring ergonomics review: Mellanie
|
||||
#
|
||||
# See: docs/design/tier1-module-authoring.md for field-by-field guide.
|
||||
|
||||
$schema: "https://json-schema.org/draft/2020-12/schema"
|
||||
$id: "drama_module.schema.yaml"
|
||||
title: "Tier 1 Drama Module"
|
||||
description: >
|
||||
A hand-authored drama module drawn from the pool at game start.
|
||||
The storyteller activates one or more modules per playthrough based on
|
||||
entry conditions, then fires events and detects outcomes. Tier 1 modules
|
||||
are the conspiracy layer of D-023 — authored, optional, relocatable.
|
||||
type: object
|
||||
required:
|
||||
- module_id
|
||||
- display_name
|
||||
- version
|
||||
- tier
|
||||
- pool
|
||||
- entry_conditions
|
||||
- npc_requirements
|
||||
- events
|
||||
- outcomes
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
|
||||
# ── IDENTITY ────────────────────────────────────────────────────────────────
|
||||
|
||||
module_id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*_v[0-9]+_[0-9]+$"
|
||||
description: >
|
||||
Stable unique slug. Format: {name}_v{major}_{minor}.
|
||||
Never reuse IDs. Increment version on breaking structural changes.
|
||||
Example: "smuggling_ring_v0_1"
|
||||
|
||||
display_name:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: "Human-readable title shown in dev/debug tooling."
|
||||
|
||||
version:
|
||||
type: string
|
||||
pattern: "^[0-9]+\\.[0-9]+$"
|
||||
description: "Authoring version. Semantic: major.minor."
|
||||
|
||||
tier:
|
||||
type: integer
|
||||
const: 1
|
||||
description: "Always 1 for Tier 1 drama modules."
|
||||
|
||||
description:
|
||||
type: string
|
||||
description: "One-paragraph authoring summary. Not shown in-game."
|
||||
|
||||
# ── POOL METADATA ─────────────────────────────────────────────────────────
|
||||
# Controls how the storyteller includes this module in the per-playthrough pool.
|
||||
|
||||
pool:
|
||||
type: object
|
||||
required:
|
||||
- weight
|
||||
additionalProperties: false
|
||||
description: "How the storyteller samples this module from the pool."
|
||||
properties:
|
||||
weight:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 10
|
||||
description: >
|
||||
Relative selection probability (1–10). Higher = more likely to be
|
||||
included in a given playthrough's active module set. Default: 5.
|
||||
compatible_districts:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >
|
||||
District slugs where this module can activate, or omit for "any".
|
||||
Example: ["sova-transit"]
|
||||
incompatible_with:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*_v[0-9]+_[0-9]+$"
|
||||
description: >
|
||||
Module IDs that cannot run concurrently with this one.
|
||||
The storyteller will not activate both in the same playthrough.
|
||||
max_concurrent:
|
||||
type: integer
|
||||
minimum: 1
|
||||
default: 1
|
||||
description: >
|
||||
Maximum simultaneous active instances. Almost always 1.
|
||||
Set to 2+ only for modules designed to stack (rare).
|
||||
|
||||
# ── ENTRY CONDITIONS ──────────────────────────────────────────────────────
|
||||
# All listed conditions must be true for the module to become activatable.
|
||||
# The storyteller checks these each tick after min_play_ticks.
|
||||
|
||||
entry_conditions:
|
||||
type: object
|
||||
required:
|
||||
- activation
|
||||
additionalProperties: false
|
||||
description: >
|
||||
World-state prerequisites. The storyteller activates the module when
|
||||
ALL conditions are satisfied AND the activation trigger fires.
|
||||
properties:
|
||||
world_state:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/world_state_condition"
|
||||
description: "World-state conditions checked each tick."
|
||||
player:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/player_condition"
|
||||
description: >
|
||||
Optional player-state conditions. Module can activate without
|
||||
player engagement — these gate on player-specific world state,
|
||||
not on player noticing the module.
|
||||
activation:
|
||||
type: object
|
||||
required:
|
||||
- trigger
|
||||
additionalProperties: false
|
||||
description: "How and when activation is evaluated."
|
||||
properties:
|
||||
trigger:
|
||||
type: string
|
||||
enum:
|
||||
- proximity # Player comes within range of a key NPC/location
|
||||
- storyteller_push # Storyteller activates on its own schedule
|
||||
- player_action # Player performs a specific action
|
||||
description: "What pushes the module from 'eligible' to 'active'."
|
||||
min_play_ticks:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: >
|
||||
Minimum ticks of game time before this module can activate.
|
||||
Enforces D-027 success criterion #1: 30 minutes of daily-life
|
||||
breathing room. At 1 tick/second, 30 minutes ≈ 1800 ticks.
|
||||
proximity_location:
|
||||
type: string
|
||||
description: >
|
||||
Required when trigger = proximity. Location slug the player
|
||||
must enter or approach. Example: "maintenance-corridors"
|
||||
proximity_radius_tiles:
|
||||
type: integer
|
||||
minimum: 1
|
||||
description: >
|
||||
Required when trigger = proximity. Tile radius around the
|
||||
location's anchor point.
|
||||
player_action_required:
|
||||
type: string
|
||||
description: >
|
||||
Required when trigger = player_action. The action that fires
|
||||
activation. Example: "examine:cargo-manifest"
|
||||
|
||||
# ── NPC REQUIREMENTS ──────────────────────────────────────────────────────
|
||||
# NPC slots this module requires. Each slot is filled at module load time.
|
||||
# Named bindings resolve to specific authored NPCs; generated bindings
|
||||
# are filled from the district's generated NPC pool.
|
||||
|
||||
npc_requirements:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
$ref: "#/$defs/npc_slot"
|
||||
description: >
|
||||
Module-internal NPC role slots. Roles are referenced by slug throughout
|
||||
the rest of this document. Hand-authored NPCs use named bindings.
|
||||
Generated NPCs use constraint-based bindings.
|
||||
|
||||
# ── EVENTS ────────────────────────────────────────────────────────────────
|
||||
# Ordered sequences and unordered event pools the storyteller can fire.
|
||||
# Sequences are narrative beats in a defined order.
|
||||
# Pools are events the storyteller can fire in any order when conditions are met.
|
||||
|
||||
events:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: "Event sequences and pools the storyteller manages."
|
||||
properties:
|
||||
sequences:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/event_sequence"
|
||||
description: >
|
||||
Ordered event sequences. Steps fire in order; the next step
|
||||
becomes eligible only after the previous one fires.
|
||||
pools:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/event_pool"
|
||||
description: >
|
||||
Unordered event pools. The storyteller may fire any eligible
|
||||
event in the pool when its trigger conditions are met.
|
||||
|
||||
# ── OUTCOMES ──────────────────────────────────────────────────────────────
|
||||
# Resolution states the module can reach. The storyteller checks outcome
|
||||
# conditions each tick. First matching outcome wins.
|
||||
# Every module MUST include an expiry outcome.
|
||||
|
||||
outcomes:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
$ref: "#/$defs/outcome"
|
||||
description: >
|
||||
Terminal and transitional resolution states. The storyteller checks
|
||||
these each tick and applies the first matching outcome.
|
||||
|
||||
# ── AUTHORING NOTES ───────────────────────────────────────────────────────
|
||||
|
||||
notes:
|
||||
type: string
|
||||
description: "Authoring-only field. Design rationale, cross-references. Ignored at load time."
|
||||
|
||||
dual_lens:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: "Authoring-only. How smuggler vs detective experience this module."
|
||||
properties:
|
||||
smuggler: { type: string }
|
||||
detective: { type: string }
|
||||
|
||||
# ── SHARED DEFINITIONS ────────────────────────────────────────────────────────
|
||||
|
||||
$defs:
|
||||
|
||||
# World-state condition types
|
||||
|
||||
world_state_condition:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
description: "A single world-state prerequisite for module activation."
|
||||
oneOf:
|
||||
- # NPC with the given module role is present in the district
|
||||
properties:
|
||||
type: { type: string, const: "npc_present" }
|
||||
role: { type: string, description: "Module-internal NPC role slug." }
|
||||
required: [type, role]
|
||||
additionalProperties: false
|
||||
|
||||
- # A specific location is accessible to the player
|
||||
properties:
|
||||
type: { type: string, const: "location_accessible" }
|
||||
location: { type: string, description: "Location slug." }
|
||||
required: [type, location]
|
||||
additionalProperties: false
|
||||
|
||||
- # Player has NOT yet discovered a specific fact
|
||||
properties:
|
||||
type: { type: string, const: "fact_not_known" }
|
||||
fact_id: { type: string, description: "Fact ID from global/knowledge/." }
|
||||
required: [type, fact_id]
|
||||
additionalProperties: false
|
||||
|
||||
- # No other Tier 1 module of the given ID is currently active
|
||||
properties:
|
||||
type: { type: string, const: "no_active_module" }
|
||||
module_id: { type: string }
|
||||
required: [type, module_id]
|
||||
additionalProperties: false
|
||||
|
||||
- # A named fact IS known (module requires precondition awareness)
|
||||
properties:
|
||||
type: { type: string, const: "fact_known" }
|
||||
fact_id: { type: string }
|
||||
known_by: { type: string, enum: [smuggler, detective, any] }
|
||||
required: [type, fact_id]
|
||||
additionalProperties: false
|
||||
|
||||
# Player-state condition types
|
||||
|
||||
player_condition:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
description: "A player-state prerequisite."
|
||||
oneOf:
|
||||
- # Player has reached minimum relationship threshold with an NPC
|
||||
properties:
|
||||
type: { type: string, const: "relationship_threshold" }
|
||||
npc_role: { type: string, description: "Module-internal NPC role." }
|
||||
min_state:
|
||||
type: string
|
||||
enum: [stranger, known, friendly]
|
||||
description: "Minimum RelationshipState required."
|
||||
required: [type, npc_role, min_state]
|
||||
additionalProperties: false
|
||||
|
||||
- # Minimum game ticks elapsed
|
||||
properties:
|
||||
type: { type: string, const: "min_ticks" }
|
||||
ticks: { type: integer, minimum: 0 }
|
||||
required: [type, ticks]
|
||||
additionalProperties: false
|
||||
|
||||
# NPC slot definition
|
||||
|
||||
npc_slot:
|
||||
type: object
|
||||
required:
|
||||
- role
|
||||
- binding
|
||||
additionalProperties: false
|
||||
description: >
|
||||
One NPC slot in the module. Named binding = specific authored NPC.
|
||||
Generated binding = constraint-matched NPC from district pool.
|
||||
properties:
|
||||
role:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*$"
|
||||
description: >
|
||||
Module-internal role slug. Referenced in events, outcomes, and
|
||||
triggers. Example: "ring-leader", "ring-member-exiting", "witness"
|
||||
display_hint:
|
||||
type: string
|
||||
description: "Authoring note. What this role is narratively."
|
||||
binding:
|
||||
type: string
|
||||
enum: [named, generated]
|
||||
description: >
|
||||
named = resolves to a specific authored NPC (use named_npc).
|
||||
generated = any district NPC matching the axis constraints.
|
||||
named_npc:
|
||||
type: string
|
||||
pattern: "^npc:[a-z][a-z0-9-]*$"
|
||||
description: >
|
||||
Required when binding = named. Short-form NPC canonical ID.
|
||||
Example: "npc:kael-davan"
|
||||
axes:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/axis_constraint"
|
||||
description: >
|
||||
Required when binding = generated. The NPC must satisfy all
|
||||
listed axis constraints to fill this slot.
|
||||
must_have_pattern:
|
||||
type: string
|
||||
enum: [FRIEND, MIRROR, ANCHOR, GHOST, CATALYST, THRESHOLD, REMNANT, SYSTEM, NOBODY]
|
||||
description: "Optional: NPC must have this pattern (D-024)."
|
||||
must_have_motivation:
|
||||
type: string
|
||||
enum: [HANDLER, WITNESS, TURNCOAT, CIVILIAN, OPERATOR, SKEPTIC]
|
||||
description: "Optional: NPC must have this motivation (D-024)."
|
||||
is_optional:
|
||||
type: boolean
|
||||
default: false
|
||||
description: >
|
||||
If true, the module can activate without this slot filled.
|
||||
Optional slots produce degraded but valid module runs.
|
||||
|
||||
# NPC axis constraint (used in generated bindings)
|
||||
|
||||
axis_constraint:
|
||||
type: object
|
||||
required:
|
||||
- axis
|
||||
- constraint
|
||||
additionalProperties: false
|
||||
properties:
|
||||
axis:
|
||||
type: string
|
||||
enum: [want, secret, relationships, tolerance, routine, information, contentment, personality, tells, skills]
|
||||
description: "Which NPC axis to constrain (D-024)."
|
||||
constraint:
|
||||
type: string
|
||||
description: >
|
||||
Constraint expression. Freeform string interpreted by the storyteller.
|
||||
Convention: "has_{value}", "min_{N}", "not_{value}".
|
||||
Examples: "has_major_secret", "min_contentment_-3", "not_combat_trained"
|
||||
|
||||
# Event sequence
|
||||
|
||||
event_sequence:
|
||||
type: object
|
||||
required:
|
||||
- sequence_id
|
||||
- steps
|
||||
additionalProperties: false
|
||||
description: "An ordered sequence of narrative events."
|
||||
properties:
|
||||
sequence_id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9_-]*$"
|
||||
label:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
steps:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
$ref: "#/$defs/event_step"
|
||||
|
||||
# Unordered event pool
|
||||
|
||||
event_pool:
|
||||
type: object
|
||||
required:
|
||||
- pool_id
|
||||
- events
|
||||
additionalProperties: false
|
||||
properties:
|
||||
pool_id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9_-]*$"
|
||||
label:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
events:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
$ref: "#/$defs/event_step"
|
||||
|
||||
# Individual event step
|
||||
|
||||
event_step:
|
||||
type: object
|
||||
required:
|
||||
- event_id
|
||||
- triggers
|
||||
additionalProperties: false
|
||||
description: "A single storyteller-managed event with triggers and effects."
|
||||
properties:
|
||||
event_id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9_-]*$"
|
||||
description: "Unique within this module. Used in outcome conditions."
|
||||
label:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
description: "What happens narratively when this event fires."
|
||||
triggers:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
$ref: "#/$defs/event_trigger"
|
||||
description: "ANY trigger being true fires this event."
|
||||
effects:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/event_effect"
|
||||
description: "What changes in the world when this event fires."
|
||||
once:
|
||||
type: boolean
|
||||
default: true
|
||||
description: "If true, fires only once. If false, may repeat when conditions reset."
|
||||
sets_flag:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9_-]*$"
|
||||
description: "Module-internal flag set when this event fires. Queryable in outcomes."
|
||||
|
||||
# Event trigger conditions
|
||||
|
||||
event_trigger:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
description: "A condition that causes an event to fire."
|
||||
oneOf:
|
||||
- # Ticks elapsed since module activation
|
||||
properties:
|
||||
type: { type: string, const: "ticks_since_activation" }
|
||||
ticks: { type: integer, minimum: 1 }
|
||||
required: [type, ticks]
|
||||
additionalProperties: false
|
||||
|
||||
- # Ticks elapsed since a previous event fired
|
||||
properties:
|
||||
type: { type: string, const: "ticks_since_event" }
|
||||
after_event: { type: string }
|
||||
ticks: { type: integer, minimum: 1 }
|
||||
required: [type, after_event, ticks]
|
||||
additionalProperties: false
|
||||
|
||||
- # Player enters a location or comes within range of NPC
|
||||
properties:
|
||||
type: { type: string, const: "player_proximity" }
|
||||
target_type: { type: string, enum: [location, npc_role] }
|
||||
target: { type: string }
|
||||
radius_tiles: { type: integer, minimum: 1 }
|
||||
required: [type, target_type, target]
|
||||
additionalProperties: false
|
||||
|
||||
- # Player performs an interaction
|
||||
properties:
|
||||
type: { type: string, const: "player_action" }
|
||||
action:
|
||||
type: string
|
||||
enum: [talk, examine, confront, follow, observe]
|
||||
target_role: { type: string, description: "Module NPC role or location slug." }
|
||||
required: [type, action, target_role]
|
||||
additionalProperties: false
|
||||
|
||||
- # Player has discovered a specific fact
|
||||
properties:
|
||||
type: { type: string, const: "fact_known_by_player" }
|
||||
fact_id: { type: string }
|
||||
required: [type, fact_id]
|
||||
additionalProperties: false
|
||||
|
||||
- # A module flag has been set
|
||||
properties:
|
||||
type: { type: string, const: "flag_set" }
|
||||
flag: { type: string }
|
||||
required: [type, flag]
|
||||
additionalProperties: false
|
||||
|
||||
- # NPC enters a specific mood state
|
||||
properties:
|
||||
type: { type: string, const: "npc_mood" }
|
||||
npc_role: { type: string }
|
||||
mood:
|
||||
type: string
|
||||
enum: [anxious, frustrated, content, suspicious, warm, hostile, relieved, focused]
|
||||
required: [type, npc_role, mood]
|
||||
additionalProperties: false
|
||||
|
||||
# Event effects
|
||||
|
||||
event_effect:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
description: "A world change triggered by an event."
|
||||
oneOf:
|
||||
- # NPC deviates from their normal routine
|
||||
properties:
|
||||
type: { type: string, const: "npc_routine_deviation" }
|
||||
npc_role: { type: string }
|
||||
description: { type: string, description: "What the deviation looks like." }
|
||||
duration_ticks: { type: integer }
|
||||
required: [type, npc_role, description]
|
||||
additionalProperties: false
|
||||
|
||||
- # A fact becomes discoverable (moves to Rumoured confidence)
|
||||
properties:
|
||||
type: { type: string, const: "fact_becomes_discoverable" }
|
||||
fact_id: { type: string }
|
||||
discoverable_by:
|
||||
type: string
|
||||
enum: [smuggler, detective, any]
|
||||
discovery_method:
|
||||
type: string
|
||||
description: "How the player can discover this. Authoring note."
|
||||
required: [type, fact_id, discoverable_by]
|
||||
additionalProperties: false
|
||||
|
||||
- # NPC tell behavior becomes more pronounced
|
||||
properties:
|
||||
type: { type: string, const: "tell_intensify" }
|
||||
npc_role: { type: string }
|
||||
description: { type: string }
|
||||
required: [type, npc_role]
|
||||
additionalProperties: false
|
||||
|
||||
- # A module-internal flag is set
|
||||
properties:
|
||||
type: { type: string, const: "flag_set" }
|
||||
flag: { type: string, pattern: "^[a-z][a-z0-9_-]*$" }
|
||||
required: [type, flag]
|
||||
additionalProperties: false
|
||||
|
||||
- # Something changes about a location
|
||||
properties:
|
||||
type: { type: string, const: "location_state" }
|
||||
location: { type: string }
|
||||
description: { type: string }
|
||||
required: [type, location, description]
|
||||
additionalProperties: false
|
||||
|
||||
- # NPC's access to information changes
|
||||
properties:
|
||||
type: { type: string, const: "npc_knowledge_update" }
|
||||
npc_role: { type: string }
|
||||
fact_id: { type: string }
|
||||
description: { type: string }
|
||||
required: [type, npc_role, fact_id]
|
||||
additionalProperties: false
|
||||
|
||||
# Module outcome definition
|
||||
|
||||
outcome:
|
||||
type: object
|
||||
required:
|
||||
- outcome_id
|
||||
- label
|
||||
- is_terminal
|
||||
additionalProperties: false
|
||||
description: >
|
||||
A resolution state the module can reach. Conditions are checked each tick.
|
||||
The first matching outcome is applied. is_terminal = true ends the module.
|
||||
properties:
|
||||
outcome_id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9_-]*$"
|
||||
label:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
description: "What this outcome means narratively."
|
||||
is_terminal:
|
||||
type: boolean
|
||||
description: "If true, this outcome ends the module permanently."
|
||||
is_expiry:
|
||||
type: boolean
|
||||
default: false
|
||||
description: >
|
||||
If true, this is the quiet-exit outcome when the player never engages.
|
||||
Every module must include exactly one expiry outcome.
|
||||
conditions:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: "ALL conditions must be true to reach this outcome."
|
||||
properties:
|
||||
facts_known:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "Player must know all these facts."
|
||||
facts_not_known:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "Player must NOT know any of these facts."
|
||||
flags_set:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "All these module flags must be set."
|
||||
flags_not_set:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "None of these module flags may be set."
|
||||
events_fired:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "All these events must have fired."
|
||||
ticks_since_activation:
|
||||
type: integer
|
||||
description: "Module has been active for at least this many ticks."
|
||||
effects:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/$defs/outcome_effect"
|
||||
description: "Effects applied when this outcome is reached."
|
||||
|
||||
# Outcome-level effects (broader scope than event effects)
|
||||
|
||||
outcome_effect:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
oneOf:
|
||||
- # NPC disposition toward player changes
|
||||
properties:
|
||||
type: { type: string, const: "npc_disposition" }
|
||||
npc_role: { type: string }
|
||||
shift:
|
||||
type: string
|
||||
enum: [hostile, suspicious, neutral, friendly]
|
||||
description: { type: string }
|
||||
required: [type, npc_role, shift]
|
||||
additionalProperties: false
|
||||
|
||||
- # Faction reaction
|
||||
properties:
|
||||
type: { type: string, const: "faction_reaction" }
|
||||
faction: { type: string }
|
||||
reaction:
|
||||
type: string
|
||||
enum: [hostile, suspicious, neutral, friendly, grateful]
|
||||
description: { type: string }
|
||||
required: [type, faction, reaction]
|
||||
additionalProperties: false
|
||||
|
||||
- # Location becomes restricted or opens up
|
||||
properties:
|
||||
type: { type: string, const: "location_access_change" }
|
||||
location: { type: string }
|
||||
change:
|
||||
type: string
|
||||
enum: [restricted, locked, open]
|
||||
description: { type: string }
|
||||
required: [type, location, change]
|
||||
additionalProperties: false
|
||||
|
||||
- # A fact is now permanently known/unknown
|
||||
properties:
|
||||
type: { type: string, const: "fact_state" }
|
||||
fact_id: { type: string }
|
||||
state:
|
||||
type: string
|
||||
enum: [known, hidden, destroyed]
|
||||
description: { type: string }
|
||||
required: [type, fact_id, state]
|
||||
additionalProperties: false
|
||||
|
||||
- # NPC leaves the district or changes role
|
||||
properties:
|
||||
type: { type: string, const: "npc_exit" }
|
||||
npc_role: { type: string }
|
||||
description: { type: string }
|
||||
required: [type, npc_role]
|
||||
additionalProperties: false
|
||||
@@ -8,8 +8,8 @@
|
||||
//!
|
||||
//! Run twice, compare side-by-side:
|
||||
//! ```sh
|
||||
//! cargo run --bin generator_spike -- --zone rural --seed 42
|
||||
//! cargo run --bin generator_spike -- --zone industrial --seed 42
|
||||
//! cargo run --bin generator_spike -- --zone-type rural --seed 42
|
||||
//! cargo run --bin generator_spike -- --zone-type industrial --seed 42
|
||||
//! ```
|
||||
//!
|
||||
//! The test: can you tell which is which from the output alone?
|
||||
@@ -22,7 +22,7 @@
|
||||
//! functions that work without ECS. Full ECS integration is deferred.
|
||||
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
|
||||
use clap::Parser;
|
||||
@@ -31,9 +31,9 @@ use rand::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
||||
use settled_reach_server::npc::blueprint::{
|
||||
assemble_behaviors, BlueprintRelationship, CultureProfile, CulturalMarkers, CulturalValues,
|
||||
assemble_behaviors, BlueprintRelationship, CulturalMarkers, CulturalValues, CultureProfile,
|
||||
NamingConventions, NpcBlueprint, NpcWant, RoleSpec, SocialSiteSpec, SpeechPatterns,
|
||||
SpikeOutput, ZoneSpec,
|
||||
SpikeOutput, ZoneSpec, ZoneTypeTemplate,
|
||||
};
|
||||
use settled_reach_server::npc::PersonalityTrait;
|
||||
use settled_reach_server::simulation::rng::SimRng;
|
||||
@@ -48,9 +48,11 @@ use settled_reach_server::simulation::rng::SimRng;
|
||||
about = "NPC generator proof-of-life — Sprint 25 (#612)"
|
||||
)]
|
||||
struct Args {
|
||||
/// Zone type to generate ("rural" or "industrial").
|
||||
/// Zone type to generate.
|
||||
/// Phase 1 (hardcoded): "rural" or "industrial".
|
||||
/// Phase 2 (--from-files): a zone-type ID like "rural_agricultural" or "industrial_freight".
|
||||
#[arg(long)]
|
||||
zone: String,
|
||||
zone_type: String,
|
||||
|
||||
/// Deterministic seed — same seed produces identical output.
|
||||
#[arg(long)]
|
||||
@@ -60,8 +62,8 @@ struct Args {
|
||||
#[arg(long, default_value = "van-maanens-star")]
|
||||
culture: String,
|
||||
|
||||
/// Phase 2: load zone spec and culture from RON files on disk.
|
||||
/// Requires content/global/<zone>-zone-spec.ron and content/global/culture-<culture>.ron.
|
||||
/// Phase 2: load zone-type template and culture from RON files on disk.
|
||||
/// Requires server/content/global/zone-types/<zone_type>.ron and server/content/global/culture-<culture>.ron.
|
||||
#[arg(long)]
|
||||
from_files: bool,
|
||||
|
||||
@@ -268,21 +270,60 @@ fn hardcoded_van_maanens_star_culture() -> CultureProfile {
|
||||
// Phase 2: file loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn load_zone_from_file(content_root: &PathBuf, zone_type: &str) -> ZoneSpec {
|
||||
let path = content_root.join("global").join(format!("{}-zone-spec.ron", zone_type));
|
||||
fn load_zone_from_file(content_root: &Path, zone_type: &str) -> ZoneSpec {
|
||||
let path = content_root
|
||||
.join("global")
|
||||
.join("zone-types")
|
||||
.join(format!("{}.ron", zone_type));
|
||||
let content = std::fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||
eprintln!("Error loading zone spec from {:?}: {}", path, e);
|
||||
eprintln!("Tip: copy team fills this file (ticket #609).");
|
||||
eprintln!("Error loading zone-type template from {:?}: {}", path, e);
|
||||
eprintln!("Tip: zone-type RON files live in content/global/zone-types/.");
|
||||
process::exit(1);
|
||||
});
|
||||
ron::from_str(&content).unwrap_or_else(|e| {
|
||||
eprintln!("Invalid ZoneSpec in {:?}: {}", path, e);
|
||||
let template: ZoneTypeTemplate = ron::from_str(&content).unwrap_or_else(|e| {
|
||||
eprintln!("Invalid ZoneTypeTemplate in {:?}: {}", path, e);
|
||||
process::exit(1);
|
||||
})
|
||||
});
|
||||
zone_type_template_to_zone_spec(template)
|
||||
}
|
||||
|
||||
fn load_culture_from_file(content_root: &PathBuf, culture_id: &str) -> CultureProfile {
|
||||
let path = content_root.join("global").join(format!("culture-{}.ron", culture_id));
|
||||
/// Convert a `ZoneTypeTemplate` to a `ZoneSpec` compatible with the spike generation pipeline.
|
||||
///
|
||||
/// `ZoneTypeTemplate` roles have no weight (weights live on `LocationSpec`).
|
||||
/// The spike assigns weight=1 uniformly — all roles equally likely.
|
||||
fn zone_type_template_to_zone_spec(template: ZoneTypeTemplate) -> ZoneSpec {
|
||||
ZoneSpec {
|
||||
zone_type: template.id,
|
||||
label: template.label,
|
||||
description: template.description,
|
||||
economic_level: template.economic_level,
|
||||
population_density: template.population_density,
|
||||
roles: template
|
||||
.roles
|
||||
.into_iter()
|
||||
.map(|mut r| {
|
||||
r.weight = 1; // uniform weight — location specs provide real weights
|
||||
r
|
||||
})
|
||||
.collect(),
|
||||
social_sites: template
|
||||
.social_site_types
|
||||
.into_iter()
|
||||
.map(|s| SocialSiteSpec {
|
||||
site_type: s.site_type,
|
||||
label: String::new(),
|
||||
roles: s.eligible_roles,
|
||||
min_npcs: 1,
|
||||
max_npcs: 6,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_culture_from_file(content_root: &Path, culture_id: &str) -> CultureProfile {
|
||||
let path = content_root
|
||||
.join("global")
|
||||
.join(format!("culture-{}.ron", culture_id));
|
||||
let content = std::fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||
eprintln!("Error loading culture from {:?}: {}", path, e);
|
||||
eprintln!("Tip: copy team fills this file (ticket #610).");
|
||||
@@ -386,7 +427,11 @@ fn gen_traits(rng: &mut SimRng, culture: &CultureProfile) -> Vec<PersonalityTrai
|
||||
/// main-RNG word before reaching the Fisher-Yates step.
|
||||
fn name_pool_seed(seed: u64, zone_type: &str, culture_id: &str) -> u64 {
|
||||
let mut h: u64 = seed ^ 0xcbf29ce484222325; // FNV-1a offset basis, XOR'd with seed
|
||||
for b in zone_type.bytes().chain(std::iter::once(b':')).chain(culture_id.bytes()) {
|
||||
for b in zone_type
|
||||
.bytes()
|
||||
.chain(std::iter::once(b':'))
|
||||
.chain(culture_id.bytes())
|
||||
{
|
||||
h ^= b as u64;
|
||||
h = h.wrapping_mul(0x100000001b3); // FNV-1a prime
|
||||
}
|
||||
@@ -549,7 +594,8 @@ const MAX_FILLER_WORDS: usize = 2;
|
||||
|
||||
fn gen_cultural_markers(rng: &mut SimRng, culture: &CultureProfile) -> CulturalMarkers {
|
||||
let filler_count = if culture.speech.filler_words.len() > 1 {
|
||||
rng.rng.random_range(1_usize..=MAX_FILLER_WORDS.min(culture.speech.filler_words.len()))
|
||||
rng.rng
|
||||
.random_range(1_usize..=MAX_FILLER_WORDS.min(culture.speech.filler_words.len()))
|
||||
} else {
|
||||
culture.speech.filler_words.len()
|
||||
};
|
||||
@@ -595,7 +641,10 @@ fn gen_relationships(
|
||||
) -> Vec<BlueprintRelationship> {
|
||||
use settled_reach_server::npc::blueprint::RelationshipValence;
|
||||
|
||||
let other_names: Vec<&String> = all_names.iter().filter(|n| n.as_str() != this_name).collect();
|
||||
let other_names: Vec<&String> = all_names
|
||||
.iter()
|
||||
.filter(|n| n.as_str() != this_name)
|
||||
.collect();
|
||||
if other_names.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
@@ -649,9 +698,7 @@ fn generate_npc_blueprint(
|
||||
let role = pick_role(rng, zone);
|
||||
let traits = gen_traits(rng, culture);
|
||||
let want = gen_want(rng, &traits, role);
|
||||
let pool = behavior_pools
|
||||
.entry(role.id.clone())
|
||||
.or_insert_with(VecDeque::new);
|
||||
let pool = behavior_pools.entry(role.id.clone()).or_default();
|
||||
let observable_behaviors = gen_behaviors(rng, role, pool);
|
||||
let cultural_markers = gen_cultural_markers(rng, culture);
|
||||
// Relationships and Want tells assigned in later passes.
|
||||
@@ -807,9 +854,15 @@ fn gen_want_tell(rng: &mut SimRng, npc: &NpcBlueprint) -> Option<String> {
|
||||
AvoidingSomeone => match avoid_target {
|
||||
Some(name) => {
|
||||
let options = [
|
||||
format!("routes around {}'s usual area without looking toward it", name),
|
||||
format!(
|
||||
"routes around {}'s usual area without looking toward it",
|
||||
name
|
||||
),
|
||||
format!("takes the long way past {}'s position", name),
|
||||
format!("keeps a surface or cluster of people between them and {}", name),
|
||||
format!(
|
||||
"keeps a surface or cluster of people between them and {}",
|
||||
name
|
||||
),
|
||||
];
|
||||
options[rng.rng.random_range(0..options.len())].clone()
|
||||
}
|
||||
@@ -870,7 +923,10 @@ fn apply_relationship_behaviors(rng: &mut SimRng, npc: &mut NpcBlueprint) {
|
||||
}
|
||||
("friend", _) => format!("drifts toward {} between tasks", target),
|
||||
("subordinate", RelationshipValence::Negative) => {
|
||||
format!("complies when {} speaks, but doesn't volunteer anything", target)
|
||||
format!(
|
||||
"complies when {} speaks, but doesn't volunteer anything",
|
||||
target
|
||||
)
|
||||
}
|
||||
("subordinate", _) => format!("defers to {} before moving on", target),
|
||||
("superior", RelationshipValence::Negative) => {
|
||||
@@ -992,17 +1048,20 @@ fn main() {
|
||||
// Load zone spec and culture profile
|
||||
let (zone, culture) = if args.from_files {
|
||||
eprintln!("Phase 2: loading from files...");
|
||||
let zone = load_zone_from_file(&args.content_root, &args.zone);
|
||||
let zone = load_zone_from_file(&args.content_root, &args.zone_type);
|
||||
let culture = load_culture_from_file(&args.content_root, &args.culture);
|
||||
(zone, culture)
|
||||
} else {
|
||||
// Phase 1: hardcoded stubs
|
||||
let zone = match args.zone.as_str() {
|
||||
let zone = match args.zone_type.as_str() {
|
||||
"rural" => hardcoded_rural_zone(),
|
||||
"industrial" => hardcoded_industrial_zone(),
|
||||
other => {
|
||||
eprintln!("Unknown zone type: '{}'. Use 'rural' or 'industrial'.", other);
|
||||
eprintln!("(Phase 2 with --from-files supports additional zone types from disk)");
|
||||
eprintln!(
|
||||
"Unknown zone type: '{}'. Use 'rural' or 'industrial'.",
|
||||
other
|
||||
);
|
||||
eprintln!("(Phase 2 with --from-files supports zone-type IDs like 'rural_agricultural')");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
@@ -1046,7 +1105,7 @@ fn main() {
|
||||
// Pre-generate unique names using a derived RNG (#628 fix: first-pick bias).
|
||||
// Name ordering is a pure function of (seed, zone_type, culture) — independent
|
||||
// of how many words the main RNG has consumed for NPC count or other purposes.
|
||||
let name_pool = build_name_pool(args.seed, &args.zone, &culture, npc_count);
|
||||
let name_pool = build_name_pool(args.seed, &args.zone_type, &culture, npc_count);
|
||||
|
||||
// Pre-shuffle behavior pools per role (#629 fix: dedup within a zone run).
|
||||
// D-139: uses assemble_behaviors() when behavior_primitives are present.
|
||||
@@ -1055,9 +1114,7 @@ fn main() {
|
||||
// First pass: generate all NPCs (no relationships yet)
|
||||
let mut npcs: Vec<NpcBlueprint> = name_pool
|
||||
.into_iter()
|
||||
.map(|name| {
|
||||
generate_npc_blueprint(&mut rng, &zone, &culture, name, &mut behavior_pools)
|
||||
})
|
||||
.map(|name| generate_npc_blueprint(&mut rng, &zone, &culture, name, &mut behavior_pools))
|
||||
.collect();
|
||||
|
||||
// Collect all names for relationship pass.
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
//!
|
||||
//! ```sh
|
||||
//! # Via wrapper script (recommended):
|
||||
//! tooling/validate-ron content/global/zone-identity-spec.example.ron zone
|
||||
//! tooling/validate-ron content/global/culture-van-maanens-star.example.ron culture
|
||||
//! tooling/validate-ron server/content/global/zone-identity-spec.example.ron zone
|
||||
//! tooling/validate-ron server/content/global/culture-van-maanens-star.example.ron culture
|
||||
//!
|
||||
//! # Direct:
|
||||
//! cargo run --bin validate_ron -- <file.ron> <zone|culture>
|
||||
@@ -19,7 +19,7 @@ use std::process;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use settled_reach_server::npc::blueprint::{CultureProfile, ZoneSpec};
|
||||
use settled_reach_server::npc::blueprint::{CultureProfile, ZoneSpec, ZoneTypeTemplate};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
@@ -29,7 +29,7 @@ use settled_reach_server::npc::blueprint::{CultureProfile, ZoneSpec};
|
||||
struct Args {
|
||||
/// Path to the RON file to validate.
|
||||
file: String,
|
||||
/// Schema type: "zone" (ZoneSpec) or "culture" (CultureProfile).
|
||||
/// Schema type: "zone" (ZoneSpec), "zone_type" (ZoneTypeTemplate), or "culture" (CultureProfile).
|
||||
schema: String,
|
||||
}
|
||||
|
||||
@@ -48,7 +48,11 @@ fn main() {
|
||||
"zone" => match ron::from_str::<ZoneSpec>(&content) {
|
||||
Ok(spec) => {
|
||||
println!("Valid ZoneSpec: {} ({})", spec.label, spec.zone_type);
|
||||
println!(" {} roles, {} social sites", spec.roles.len(), spec.social_sites.len());
|
||||
println!(
|
||||
" {} roles, {} social sites",
|
||||
spec.roles.len(),
|
||||
spec.social_sites.len()
|
||||
);
|
||||
let mut warnings = 0;
|
||||
if spec.roles.is_empty() {
|
||||
eprintln!(" WARNING: no roles defined — generator will reject this");
|
||||
@@ -105,8 +109,43 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
"zone_type" => match ron::from_str::<ZoneTypeTemplate>(&content) {
|
||||
Ok(template) => {
|
||||
println!("Valid ZoneTypeTemplate: {} ({})", template.label, template.id);
|
||||
println!(
|
||||
" {} roles, {} social site types",
|
||||
template.roles.len(),
|
||||
template.social_site_types.len()
|
||||
);
|
||||
let mut warnings = 0;
|
||||
if template.roles.is_empty() {
|
||||
eprintln!(" WARNING: no roles defined — generator will reject this");
|
||||
warnings += 1;
|
||||
}
|
||||
for role in &template.roles {
|
||||
if role.behavior_primitives.is_empty() {
|
||||
eprintln!(
|
||||
" WARNING: role '{}' has no behavior_primitives",
|
||||
role.id
|
||||
);
|
||||
warnings += 1;
|
||||
}
|
||||
}
|
||||
if warnings > 0 {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Invalid ZoneTypeTemplate in {}:", args.file);
|
||||
eprintln!(" {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
other => {
|
||||
eprintln!("Unknown schema type: '{}'. Use 'zone' or 'culture'.", other);
|
||||
eprintln!(
|
||||
"Unknown schema type: '{}'. Use 'zone', 'zone_type', or 'culture'.",
|
||||
other
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user