From b0fec9af09c08151d36b66070c4ad3c8bf4cf549 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 13 Feb 2026 01:19:02 +0100 Subject: [PATCH 1/9] data(content): populate global enums and entity attributes (#387, #388) Populate 9 enum YAML files (situations, topics, moods, triggers, access-tiers, trust-tiers, activities, patterns, motivations) from D-035 taxonomy and 16 canonical entity attribute keys from D-024 with A7 workshop updates. Co-Authored-By: Claude Opus 4.6 --- content/global/enums/access-tiers.yaml | 42 +++- content/global/enums/activities.yaml | 55 ++++ content/global/enums/moods.yaml | 37 ++- content/global/enums/motivations.yaml | 42 +++- content/global/enums/patterns.yaml | 58 ++++- content/global/enums/situations.yaml | 54 +++- content/global/enums/topics.yaml | 45 +++- content/global/enums/triggers.yaml | 57 ++++- content/global/enums/trust-tiers.yaml | 29 ++- .../global/knowledge/entity-attributes.yaml | 236 +++++++++++++++++- 10 files changed, 646 insertions(+), 9 deletions(-) create mode 100644 content/global/enums/activities.yaml diff --git a/content/global/enums/access-tiers.yaml b/content/global/enums/access-tiers.yaml index 21412494d..a0ee720cc 100644 --- a/content/global/enums/access-tiers.yaml +++ b/content/global/enums/access-tiers.yaml @@ -1 +1,41 @@ -# Access tiers: public, insider, authority, peer, hostile +# 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. diff --git a/content/global/enums/activities.yaml b/content/global/enums/activities.yaml new file mode 100644 index 000000000..601365c27 --- /dev/null +++ b/content/global/enums/activities.yaml @@ -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 diff --git a/content/global/enums/moods.yaml b/content/global/enums/moods.yaml index c676a3f57..270015040 100644 --- a/content/global/enums/moods.yaml +++ b/content/global/enums/moods.yaml @@ -1 +1,36 @@ -# 8 mood values +# 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: fond + description: Warm, affectionate. Genuine positive feeling toward the listener. + + - value: comfortable + description: At ease, relaxed. Default positive-neutral state. + + - value: worried + description: Anxious about something specific. Distracted, checking surroundings. + + - value: suspicious + description: Guarded, measuring words. Something feels off to this NPC. + + - value: analytical + description: Thoughtful, assessing. Processing information rather than emoting. + + - value: conflicted + description: > + Torn between competing impulses. Loyalty vs. self-preservation, duty vs. + friendship. The mood of someone who hasn't decided what to do yet. + + - value: concerned + description: Worried about someone else's wellbeing. Empathetic distress. + + - value: relieved + description: Tension has eased. Something feared didn't happen, or a burden lifted. diff --git a/content/global/enums/motivations.yaml b/content/global/enums/motivations.yaml index aec994dca..8aff21d5c 100644 --- a/content/global/enums/motivations.yaml +++ b/content/global/enums/motivations.yaml @@ -1 +1,41 @@ -# 6 functional motivations (System B) +# 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. diff --git a/content/global/enums/patterns.yaml b/content/global/enums/patterns.yaml index 60ea55e39..b5ed47083 100644 --- a/content/global/enums/patterns.yaml +++ b/content/global/enums/patterns.yaml @@ -1 +1,57 @@ -# 9 thematic patterns (System A) +# 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. diff --git a/content/global/enums/situations.yaml b/content/global/enums/situations.yaml index f1c8245c7..820b44eb7 100644 --- a/content/global/enums/situations.yaml +++ b/content/global/enums/situations.yaml @@ -1 +1,53 @@ -# 13 situation values (D-035) +# 13 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 diff --git a/content/global/enums/topics.yaml b/content/global/enums/topics.yaml index c610c1bfc..0ec722333 100644 --- a/content/global/enums/topics.yaml +++ b/content/global/enums/topics.yaml @@ -1 +1,44 @@ -# 9 topic values +# 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. diff --git a/content/global/enums/triggers.yaml b/content/global/enums/triggers.yaml index c74009f32..7de1783b9 100644 --- a/content/global/enums/triggers.yaml +++ b/content/global/enums/triggers.yaml @@ -1 +1,56 @@ -# 9 monologue trigger types +# 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." diff --git a/content/global/enums/trust-tiers.yaml b/content/global/enums/trust-tiers.yaml index d5e8f8ae8..00f5c7b9f 100644 --- a/content/global/enums/trust-tiers.yaml +++ b/content/global/enums/trust-tiers.yaml @@ -1 +1,28 @@ -# Trust tiers: surface, real, secret +# 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. diff --git a/content/global/knowledge/entity-attributes.yaml b/content/global/knowledge/entity-attributes.yaml index c99baed2c..daf2070d7 100644 --- a/content/global/knowledge/entity-attributes.yaml +++ b/content/global/knowledge/entity-attributes.yaml @@ -1 +1,235 @@ -# Entity attributes — 16 canonical EntityKnowledge keys (D-055) +# 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 +# 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. From 3d1bf54fcc2ccc0d858a067c4d0f9fdb4d2d42b1 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 13 Feb 2026 01:19:10 +0100 Subject: [PATCH 2/9] data(content): add pools, templates, and triangles for v0.1 (#389, #390, #391) 5 seed-time pools (single-candidate for deterministic v0.1 testing), 3 social site templates (logistics-hub, bar, smuggling-ring) with role slot definitions per D-025, and 5 triangles (3 active fork, 2 passive) per D-024 workshop synthesis. Co-Authored-By: Claude Opus 4.6 --- .../sova/districts/transit/pools.yaml | 78 +++++++++++++ .../sova/districts/transit/templates/bar.yaml | 101 +++++++++++++++++ .../transit/templates/logistics-hub.yaml | 106 ++++++++++++++++++ .../transit/templates/smuggling-ring.yaml | 88 +++++++++++++++ .../transit/triangles/bar-tensions.yaml | 50 ++++++++- .../transit/triangles/hub-power.yaml | 71 +++++++++++- .../transit/triangles/informant-question.yaml | 57 +++++++++- .../transit/triangles/worried-knowledge.yaml | 64 ++++++++++- .../transit/triangles/worried-partner.yaml | 68 ++++++++++- 9 files changed, 678 insertions(+), 5 deletions(-) create mode 100644 content/campaigns/main/systems/krenn/stations/sova/districts/transit/pools.yaml create mode 100644 content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/bar.yaml create mode 100644 content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/logistics-hub.yaml create mode 100644 content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/smuggling-ring.yaml diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/pools.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/pools.yaml new file mode 100644 index 000000000..729963f03 --- /dev/null +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/pools.yaml @@ -0,0 +1,78 @@ +# Pool definitions for Sova Transit District +# Seed-time selection system. Architecture supports N candidates; v0.1 has 1 each. +# Format per seed-configuration-schema.md section 5.2 +# Referenced by: seed config generation (Sprint 5+), content loader (#408) + +pools: + - pool_id: "transit:friend_smuggler" + category: npc_role + description: > + Smuggler's FRIEND (D-034) — closest colleague, emotional anchor. + Production-level complex NPC exercising every content pipeline. + v0.1: Kael Davan (dock worker, ring member). v0.2+: expand to + multiple dock worker candidates with different contradiction arcs. + constraints: + - must_be_in_template: "logistics-hub" + - must_have_pattern: "FRIEND" + - bonded_character: "smuggler" + candidates: + - npc_id: "npc:kael-davan" + weight: 1 + + - pool_id: "transit:friend_detective" + category: npc_role + description: > + Detective's FRIEND (D-034) — social anchor, information holder. + Production-level complex NPC. Contradiction: sitting on unreported + evidence about Kael's manifest discrepancies, protecting Naia. + v0.1: Sera Venn (Commission field tech, bar regular). + constraints: + - must_have_pattern: "FRIEND" + - bonded_character: "detective" + candidates: + - npc_id: "npc:sera-venn" + weight: 1 + + - pool_id: "transit:bar_regulars" + category: npc_group + description: > + NPCs who frequent The Last Shift as regulars. Fills bar-regular + template slots. v0.1: fixed set (all selected). v0.2+: seeder + draws a subset from a larger candidate pool for variety. + Note: regulars include cross-template visitors (reference links) + who frequent the bar location but are owned by other templates. + constraints: + - frequents_location: "the-last-shift" + candidates: + - npc_id: "npc:sera-venn" + weight: 1 + - npc_id: "npc:resha" + weight: 1 + - npc_id: "npc:kael-davan" + weight: 1 + - npc_id: "npc:torek-lintar" + weight: 1 + - npc_id: "npc:drin" + weight: 1 + + - pool_id: "transit:compromised_inspector" + category: npc_role + description: > + The Commission inspector compromised by the ring. Access to authority + systems, gambling debt makes them vulnerable. v0.1: Torek Lintar. + constraints: + - must_have_access: "authority" + - must_have_faction: "Commission" + candidates: + - npc_id: "npc:torek-lintar" + weight: 1 + + - pool_id: "transit:primary_contraband" + category: contraband + description: > + What the ring is smuggling (D-037). v0.1: unlicensed lattice + components — aftermarket neural lattice modifications. Moral + ambiguity: the ring is smuggling access, not weapons. + candidates: + - id: "contraband:lattice-components" + weight: 1 diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/bar.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/bar.yaml new file mode 100644 index 000000000..d92357c1f --- /dev/null +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/bar.yaml @@ -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] diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/logistics-hub.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/logistics-hub.yaml new file mode 100644 index 000000000..68368f688 --- /dev/null +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/logistics-hub.yaml @@ -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] diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/smuggling-ring.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/smuggling-ring.yaml new file mode 100644 index 000000000..e139f8f7f --- /dev/null +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/templates/smuggling-ring.yaml @@ -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] diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/bar-tensions.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/bar-tensions.yaml index 230477d94..9c8fe9ae0 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/bar-tensions.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/bar-tensions.yaml @@ -1 +1,49 @@ -# Triangle: bar-tensions +# 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. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/hub-power.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/hub-power.yaml index e6b04973e..ee4d6ed13 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/hub-power.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/hub-power.yaml @@ -1 +1,70 @@ -# Triangle: hub-power +# 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. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/informant-question.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/informant-question.yaml index 6f074c355..d5739506d 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/informant-question.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/informant-question.yaml @@ -1 +1,56 @@ -# Triangle: informant-question +# Triangle 5: Informant Question — mundane workplace gossip and suspicion +# Passive — runs on background simulation, provides investigation noise +canonical_id: informant-question +display_name: "The Informant Question" +description: > + Torek Lintar (dock inspector, compromised by gambling debt) is observed + by Drin (dock worker) and Olin (bystander/new arrival). Drin has noticed + Torek's inspection patterns are irregular. Olin overhears workplace gossip + and doesn't know what's significant. This triangle generates the kind + of low-level workplace suspicion that exists in any tight community — + people notice things, talk about them, and form theories. For the + detective, it's signal mixed with noise. + +members: + - npc: "npc:torek-lintar" + role: "inspector — compromised, inspection patterns irregular" + - npc: "npc:drin" + role: "worker — notices Torek's odd behavior, gossips about it" + - npc: "npc:olin" + role: "bystander — overhears gossip, forms theories, asks questions" + +forks: + - id: gossip-spreads + condition: > + Drin's observations about Torek's inspection patterns reach + enough people that the gossip becomes ambient social knowledge. + outcomes: + - id: dismissed-as-gossip + description: > + The community dismisses Drin's observations as workplace gossip. + Torek's reputation survives. The information exists but isn't + treated as significant — noise floor functioning as intended. + effects: + - "torek.position_integrity unchanged" + - "community treats observations as routine gossip" + - "detective must distinguish signal from noise" + - id: torek-noticed + description: > + Enough people notice Torek's patterns that his behavior becomes + a topic of casual conversation. Not accusatory — just curious. + The detective can pick up on ambient awareness. + effects: + - "torek.behavior_flags += noticed,defensive" + - "ambient dialogue includes references to inspection patterns" + - "detective gains investigation.inspection_anomaly (suspects level)" + +resolution_states: + - id: gossip-fades + description: > + The topic loses interest. Workplace gossip moves on. Torek's + irregularities are forgotten by everyone except potentially + the detective. + - id: ambient-suspicion + description: > + A low-level awareness persists in the community. Nothing actionable, + but Torek is slightly more careful and slightly more defensive. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-knowledge.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-knowledge.yaml index 107e0a5c9..77745045c 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-knowledge.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-knowledge.yaml @@ -1 +1,63 @@ -# Triangle: worried-knowledge +# Triangle 2: Worried Knowledge — unreported evidence and protective silence +# Active fork — Sera's choice about what to do with what she knows +canonical_id: worried-knowledge +display_name: "Worried Knowledge" +description: > + Sera Venn (Commission field tech, detective's FRIEND) is sitting on + unreported evidence about Kael's manifest discrepancies. She's protecting + Naia Tamm (Kael's partner, who confided in her about Kael's late nights). + Torek Lintar (compromised inspector) is the subject — his gambling debt + makes him the institutional weak point, and Sera's evidence implicates + his inspection lapses. Sera's avoidance of Torek at the bar is the + observable tell. + +members: + - npc: "npc:sera-venn" + role: "holder — Commission tech sitting on unreported evidence" + - npc: "npc:torek-lintar" + role: "subject — compromised inspector whose lapses Sera has documented" + - npc: "npc:naia-tamm" + role: "protected — Kael's partner, confided worries to Sera" + +forks: + - id: evidence-disclosure + condition: > + Detective builds enough trust with Sera to trigger the disclosure + decision. Sera must choose between institutional duty, friendship + with Naia, and protecting the detective from a compromised colleague. + outcomes: + - id: sera-reports + description: > + Sera reports her evidence through official channels. Torek's + inspection lapses are flagged. The investigation gains institutional + data but Naia loses her protective buffer. + effects: + - "torek.position_integrity = cracking" + - "sera.trust_level (from naia) drops" + - "detective gains investigation.inspection_lapses fact" + - id: sera-protects + description: > + Sera stays silent to protect Naia. The evidence remains buried. + Detective must find the institutional thread through other means. + Sera's avoidance pattern intensifies as she manages the secret. + effects: + - "sera.behavior_flags += avoidance_pattern,evasive" + - "sera.position_integrity = thin" + - "naia remains protected" + - id: sera-confides + description: > + Sera tells the detective privately, off the record. The detective + gains the information but must decide what to do with it. Tests + the detective's institutional loyalty. + effects: + - "detective gains investigation.inspection_lapses fact (informal)" + - "sera.trust_level (toward detective) = trusted" + - "sera.risk_assessment = moderate (off-book disclosure)" + +resolution_states: + - id: torek-exposed + description: Torek's compromised status becomes known to the investigation. + - id: knowledge-buried + description: Sera's evidence stays unreported. Avoidance pattern is the only tell. + - id: informal-disclosure + description: Detective knows but has no official evidence. Must find formal proof. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-partner.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-partner.yaml index de7748a3e..2a2d31e2f 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-partner.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/triangles/worried-partner.yaml @@ -1 +1,67 @@ -# Triangle: worried-partner +# Triangle 4: Worried Partner — ring pressure on personal relationships +# Active fork — the emotional heart of the smuggler's FRIEND arc +canonical_id: worried-partner +display_name: "Worried Partner" +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. From 90e6dcb8e93d66fde32b46fdf320b937c58f9183 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 13 Feb 2026 01:19:15 +0100 Subject: [PATCH 3/9] fix(content): update dialogue-pool schema arrays per D-035 situation, topic, and mood fields changed from single string to array type to match D-035 specification (list). access and trust were already correct as arrays. Co-Authored-By: Claude Opus 4.6 --- content/_schema/dialogue-pool.schema.json | 51 ++++++++++++++--------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/content/_schema/dialogue-pool.schema.json b/content/_schema/dialogue-pool.schema.json index da764d30d..de3ee9aee 100644 --- a/content/_schema/dialogue-pool.schema.json +++ b/content/_schema/dialogue-pool.schema.json @@ -59,29 +59,42 @@ "description": "Minimum trust level required" }, "situation": { - "type": "string", - "enum": [ - "arrival", "shift_start", "shift_end", "shift_transition", - "bar_evening", "night_shift", "investigation", "confrontation", - "social", "alone", "emergency", "routine", "observation" - ], - "description": "Situation context when this line can fire" + "type": "array", + "items": { + "type": "string", + "enum": [ + "arrival", "shift_start", "shift_end", "shift_transition", + "bar_evening", "night_shift", "investigation", "confrontation", + "social", "alone", "emergency", "routine", "observation" + ] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Situation contexts when this line can fire (D-035: list)" }, "topic": { - "type": "string", - "enum": [ - "colleague", "routine", "cargo", "money", "trust", - "danger", "institution", "personal", "investigation" - ], - "description": "Topic tag for selection weighting" + "type": "array", + "items": { + "type": "string", + "enum": [ + "colleague", "routine", "cargo", "money", "trust", + "danger", "institution", "personal", "investigation" + ] + }, + "uniqueItems": true, + "description": "Topic tags for selection weighting (D-035: list)" }, "mood": { - "type": "string", - "enum": [ - "fond", "comfortable", "worried", "suspicious", - "analytical", "conflicted", "concerned", "relieved" - ], - "description": "Mood tag for selection weighting" + "type": "array", + "items": { + "type": "string", + "enum": [ + "fond", "comfortable", "worried", "suspicious", + "analytical", "conflicted", "concerned", "relieved" + ] + }, + "uniqueItems": true, + "description": "Mood tags for selection weighting (D-035: list)" }, "tags": { "type": "array", From 4c008d619122aa4549214167d15f6adbf7d22aed Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 13 Feb 2026 01:19:21 +0100 Subject: [PATCH 4/9] data(content): use WantKind enum keywords in NPC profiles Change want.primary from narrative strings to WantKind enum keywords (Safety, Wealth, Knowledge, etc.) across all 20 NPC profiles. Move narrative text to want.description. Add Happiness to WantKind enum. Add enum constraint to npc-profile.schema.json so make validate-content catches invalid values at authoring time. Fixes silent Want component drop where parse_want_kind() returned None for all real NPCs. Co-Authored-By: Claude Opus 4.6 --- content/_schema/npc-profile.schema.json | 2 +- .../krenn/stations/sova/districts/transit/npcs/devra.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/drin.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/harek.yaml | 3 ++- .../stations/sova/districts/transit/npcs/kael-davan.yaml | 3 ++- .../stations/sova/districts/transit/npcs/lera-sessik.yaml | 3 ++- .../stations/sova/districts/transit/npcs/maret-korr.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/naia-tamm.yaml | 3 ++- .../stations/sova/districts/transit/npcs/nils-davan.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/olin.yaml | 3 ++- .../stations/sova/districts/transit/npcs/pc-detective.yaml | 3 ++- .../stations/sova/districts/transit/npcs/pc-smuggler.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/pell.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/renn.yaml | 4 +++- .../krenn/stations/sova/districts/transit/npcs/resha.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/sabel.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/sera-venn.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/sess.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/tav.yaml | 3 ++- .../stations/sova/districts/transit/npcs/torek-lintar.yaml | 3 ++- .../krenn/stations/sova/districts/transit/npcs/voss.yaml | 3 ++- 21 files changed, 42 insertions(+), 21 deletions(-) diff --git a/content/_schema/npc-profile.schema.json b/content/_schema/npc-profile.schema.json index 5b23c3a97..bfa7964b1 100644 --- a/content/_schema/npc-profile.schema.json +++ b/content/_schema/npc-profile.schema.json @@ -38,7 +38,7 @@ "type": "object", "description": "Primary want/need driving this NPC", "properties": { - "primary": { "type": "string" }, + "primary": { "type": "string", "enum": ["Wealth", "Safety", "Knowledge", "Connection", "Power", "Freedom", "Justice", "Revenge", "Happiness"] }, "intensity": { "type": "integer", "minimum": 0, "maximum": 10 }, "description": { "type": "string" } }, diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/devra.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/devra.yaml index 7df338aeb..39236b58a 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/devra.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/devra.yaml @@ -17,9 +17,10 @@ description: > regular seat at The Last Shift. want: - primary: "keep the operation running smoothly and profitably" + primary: "Wealth" intensity: 7 description: > + Keep the operation running smoothly and profitably. Devra is the ring's operational brain. She manages external supply chain coordination, schedules handoffs, and maintains timing between gate transits and dock processing. She is good at this — better than Nils, though she would diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/drin.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/drin.yaml index 7df3e3ac9..0ec31a592 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/drin.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/drin.yaml @@ -18,9 +18,10 @@ description: > commission. want: - primary: "clear the debt and stop looking over his shoulder" + primary: "Freedom" intensity: 8 description: > + Clear the debt and stop looking over his shoulder. Drin wants to return to when the job was just a job — maintenance rounds, dock inspections, clocking out, going home. The work was repetitive and that was fine. Now every inspection cycle carries a second calculation: which containers diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/harek.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/harek.yaml index 160c6afb5..7540684bf 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/harek.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/harek.yaml @@ -18,9 +18,10 @@ description: > because investigating would mean confronting what the card game created. want: - primary: "maintain his comfortable position" + primary: "Happiness" intensity: 4 description: > + Maintain his comfortable position. Harek has a good thing: security officer with a predictable schedule, card games at the bar, social capital. He doesn't want promotions (more responsibility, more visibility). He doesn't want trouble. Secondary want: keep Drin's debt diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/kael-davan.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/kael-davan.yaml index ee0602ebc..35c57edfd 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/kael-davan.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/kael-davan.yaml @@ -20,9 +20,10 @@ description: > contradiction surfaces. want: - primary: "protect the operation and the people in it" + primary: "Safety" intensity: 8 description: > + Protect the operation and the people in it. Two years in, Kael is shifting from "keep things running" to "keep people safe." The operation is escalating — higher volume, tighter margins, more risk. Secondary want: protect Naia from the truth and the consequences. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/lera-sessik.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/lera-sessik.yaml index 7772541f7..7e8aede39 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/lera-sessik.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/lera-sessik.yaml @@ -18,9 +18,10 @@ description: > solvent. want: - primary: "keep the bar viable" + primary: "Wealth" intensity: 7 description: > + Keep the bar viable. The Last Shift operates on thin margins. The after-shift crowd is what keeps the lights on. Lera knows some regulars have grey-market income — she does not ask where the money comes from because the spending keeps her solvent. Secondary diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/maret-korr.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/maret-korr.yaml index 7c7eb2050..d39d4b7b5 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/maret-korr.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/maret-korr.yaml @@ -22,9 +22,10 @@ description: > on her personal lattice storage. want: - primary: "understand the manifest discrepancies" + primary: "Knowledge" intensity: 7 description: > + Understand the manifest discrepancies. Maret processes freight schedules and the numbers are not adding up. Container counts do not reconcile, manifests are revised after Voss's schedule adjustments, Drin's inspection reports are suspiciously clean. She is paralyzed between duty diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/naia-tamm.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/naia-tamm.yaml index 67c69f0c2..2075e46a5 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/naia-tamm.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/naia-tamm.yaml @@ -16,9 +16,10 @@ description: > exactly what she appears to be: a teacher worried about her partner. want: - primary: "understand what is happening with Kael" + primary: "Knowledge" intensity: 8 description: > + Understand what is happening with Kael. Naia and Kael have been together 6 years. It was good — stable, warm, predictable. Then Kael started coming home late. Evasive answers. Money that did not match the hours. Stress that Kael will not explain. Naia does diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/nils-davan.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/nils-davan.yaml index 67f3984a3..6a8cf600b 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/nils-davan.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/nils-davan.yaml @@ -18,8 +18,9 @@ description: > without seeing him. Tier 1 in schema; functionally off-stage (tier 0). want: - primary: "control" + primary: "Power" description: > + Control. Grow the operation. Increase volume. Maintain authority over the ring from a distance. Nils sets volume targets, applies escalation pressure, and makes personnel decisions. The ring's growth imperative comes from Nils; the diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/olin.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/olin.yaml index 5d6658050..94b142458 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/olin.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/olin.yaml @@ -15,8 +15,9 @@ description: > attention, convenience is suspicious. Ambiguity is deliberate and unresolved. want: - primary: "employment" + primary: "Safety" description: > + Employment. Find work. Settle in. Start over. Whether this is genuine or a cover story is left deliberately ambiguous for v0.1. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-detective.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-detective.yaml index 0bcddf0c2..149738648 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-detective.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-detective.yaml @@ -18,9 +18,10 @@ description: > threat to avoid. Second playthrough: "That's me. That's what I was doing." want: - primary: "answers" + primary: "Knowledge" intensity: 5 description: > + Answers. Wants to understand the freight discrepancies. Follows the investigation methodically. Does not make dramatic decisions on the neutral path -- no confrontations, no leverage plays, no dramatic revelations. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-smuggler.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-smuggler.yaml index 04c20b8e4..bd385f8d6 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-smuggler.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pc-smuggler.yaml @@ -19,9 +19,10 @@ description: > neutral path and the player's actual choices IS the narrative distance traveled. want: - primary: "stability" + primary: "Safety" intensity: 4 description: > + Stability. Keep the day smooth. Keep the operation running, the team safe, the shift uneventful. Does not make dramatic decisions on the neutral path. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pell.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pell.yaml index 5d260a9f6..7976ec4bf 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pell.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/pell.yaml @@ -18,9 +18,10 @@ description: > a handler. want: - primary: "get out without getting killed" + primary: "Freedom" intensity: 9 description: > + Get out without getting killed. Pell is past the point of wanting to stay. The escalating volume, the increasing risk, the paranoia — the ring has gone from manageable supplement to existential threat. Secondary want: find someone — anyone — who can offer protection in diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/renn.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/renn.yaml index 356999624..bda51636d 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/renn.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/renn.yaml @@ -18,9 +18,11 @@ description: > wavering to Devra. want: - primary: "stay useful to the ring without getting caught" + primary: "Power" intensity: 6 description: > + Prove loyalty to the ring and earn trust. + Stay useful without getting caught. Renn is the physical courier — lifting, carrying, timing runs through maintenance corridors during Meridian dead spots. Takes pride in the execution. The job feels important. Secondary want: prove loyalty to Nils and Devra. Renn diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/resha.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/resha.yaml index b2c22fd0a..1d908d7fd 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/resha.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/resha.yaml @@ -18,9 +18,10 @@ description: > match what she observes. want: - primary: "prove herself at the job" + primary: "Power" intensity: 5 description: > + Prove herself at the job. Resha wants to be competent, reliable, valued. She studies shift protocols, asks questions about procedures, and takes notes. Some of those questions — about locked bays, irregular schedule changes, containers with routing codes diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sabel.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sabel.yaml index 8374db6d5..b9379a25f 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sabel.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sabel.yaml @@ -17,8 +17,9 @@ description: > ring members' dialogue. want: - primary: "profit" + primary: "Wealth" description: > + Profit. Deliver. Get paid. Maintain the supply chain. If the operation on Sova is compromised, Sabel cuts the connection and finds another distribution point. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sera-venn.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sera-venn.yaml index df1c7b338..2a726615f 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sera-venn.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sera-venn.yaml @@ -21,9 +21,10 @@ description: > evidence from the detective she calls a friend. The quiet life she wanted is gone. want: - primary: "keep her life stable and her friendships intact" + primary: "Connection" intensity: 8 description: > + Keep her life stable and her friendships intact. Sova was supposed to be quiet — routine calibration checks, scheduled inspections, paperwork. For 12 months, it was exactly that. Then Naia started confiding about Kael's late nights, and Sera's competence made her curious, and diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sess.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sess.yaml index bce7a2716..cf54927eb 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sess.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/sess.yaml @@ -14,8 +14,9 @@ description: > full of people with secrets, Sess's lack of hidden agenda is camouflage. want: - primary: "stability" + primary: "Safety" description: > + Stability. Do the job. Go home. Don't get involved. The work is steady, the tips are adequate, the regulars are predictable. No ambitions beyond maintaining this equilibrium. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/tav.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/tav.yaml index 0bf147eb1..e50b72b0b 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/tav.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/tav.yaml @@ -16,8 +16,9 @@ description: > on watch but visibly nervous to anyone who knows what to look for. want: - primary: "belonging" + primary: "Connection" description: > + Belonging. Do the watch. Don't get noticed. Prove useful. The role is simple — watch, signal, move — but it is the first thing Tav has been trusted with and the consequences of failure are immediate and severe. diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/torek-lintar.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/torek-lintar.yaml index 495c99a9d..8852103e3 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/torek-lintar.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/torek-lintar.yaml @@ -17,9 +17,10 @@ description: > without intent. want: - primary: "enjoy the good life while it lasts" + primary: "Wealth" intensity: 5 description: > + Enjoy the good life while it lasts. Torek is young, single, and flush for the first time. He buys rounds, tips well, has a new jacket and lattice accessories. He is performing generosity — and the performance is the problem. In a district where everyone knows everyone's salary, diff --git a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/voss.yaml b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/voss.yaml index ae682fb10..670849133 100644 --- a/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/voss.yaml +++ b/content/campaigns/main/systems/krenn/stations/sova/districts/transit/npcs/voss.yaml @@ -18,9 +18,10 @@ description: > Voss's schedule first. want: - primary: "operational stability" + primary: "Safety" intensity: 6 description: > + Operational stability. Keep his shift running without incident. Clean handoffs, zero incident reports. The smuggling operation introduces variables he cannot control, but the cut he receives smooths the rough edges of a supervisor's salary. Secondary want: From 267d4151739dfcaf8d2311fdd6ba1ab5382ab98d Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 13 Feb 2026 01:19:29 +0100 Subject: [PATCH 5/9] feat(simulation): implement content loader Phase 2 (#408) Extend content loader to read and instantiate real YAML content into ECS entities. 2-phase spawn pipeline: Phase 1 creates entities with core components (Want, Tolerance, Contentment, Personality, Tells, Skills), Phase 2 resolves cross-references (Relationships, Secrets, Information via KnowledgeGraph, DailyRoutine). Loads enums, entity attributes, pools, templates, triangles, and NPC profiles. Handles stub files gracefully. 7 integration tests against real content files plus edge case tests for invalid data. Co-Authored-By: Claude Opus 4.6 --- server/Cargo.lock | 32 + server/Cargo.toml | 1 + server/src/content/loader.rs | 619 ++++++++++++++++++ server/src/content/mod.rs | 85 +++ server/src/content/spawn.rs | 1041 +++++++++++++++++++++++++++++++ server/src/content/types.rs | 555 ++++++++++++++++ server/src/lib.rs | 1 + server/src/npc/mod.rs | 1 + server/tests/content_loading.rs | 405 ++++++++++++ 9 files changed, 2740 insertions(+) create mode 100644 server/src/content/loader.rs create mode 100644 server/src/content/mod.rs create mode 100644 server/src/content/spawn.rs create mode 100644 server/src/content/types.rs create mode 100644 server/tests/content_loading.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index f397f2e21..13d7040f3 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -616,6 +616,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "js-sys" version = "0.3.85" @@ -902,6 +908,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "semver" version = "1.0.27" @@ -938,6 +950,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "settled-reach-server" version = "0.1.0" @@ -950,6 +975,7 @@ dependencies = [ "rand_chacha", "rmp-serde", "serde", + "serde_yaml", "thiserror", "tracing", "tracing-subscriber", @@ -1164,6 +1190,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "uuid" version = "1.20.0" diff --git a/server/Cargo.toml b/server/Cargo.toml index 61e535a49..57af583e8 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" bevy_ecs = "0.18" bevy_app = "0.18" serde = { version = "1", features = ["derive"] } +serde_yaml = "0.9" rmp-serde = "1" bincode = "1" rand = "0.9" diff --git a/server/src/content/loader.rs b/server/src/content/loader.rs new file mode 100644 index 000000000..868d0e9cc --- /dev/null +++ b/server/src/content/loader.rs @@ -0,0 +1,619 @@ +//! Content discovery and deserialization. +//! +//! Reads content.yaml, discovers campaigns and districts via directory +//! structure, deserializes YAML files into intermediate content types. +//! Comment-only YAML files (stubs) are skipped gracefully. + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; + +use crate::content::types::*; + +/// All content loaded from disk, organized by district. +/// Inserted as a bevy Resource after loading completes. +#[derive(Debug, Default)] +pub struct ContentStore { + pub manifest: Option, + pub districts: BTreeMap, +} + +/// Content for a single district. +#[derive(Debug, Default)] +pub struct DistrictContent { + pub meta: Option, + pub district_path: PathBuf, + pub pools: Vec, + pub templates: Vec