Initial commit: Theriapolis baseline at port/godot branch point

Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Christopher Wiebe
2026-04-30 20:40:51 -07:00
commit b451f83174
525 changed files with 75786 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
{
"id": "generic_guard",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "Halt. State your business in {npc.role}.",
"options": [
{ "text": "Just passing through. Travelling to learn the country.", "next": "passes" },
{
"text": "I'm here on business with the magistrate.",
"next": "passes",
"conditions": [
{ "kind": "rep_at_least", "faction": "covenant_enforcers", "value": 0 }
]
},
{
"text": "[Persuasion DC 13] Come now, friend — do I look like trouble?",
"skill_check": { "skill": "persuasion", "dc": 13 },
"next_on_success": "passes_warmly",
"next_on_failure": "guard_suspicious",
"effects_on_success": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": 2, "faction": "covenant_enforcers", "note": "smooth talker" } }
]
},
{
"text": "[Intimidate DC 16] Stand aside.",
"skill_check": { "skill": "intimidation", "dc": 16 },
"next_on_success": "guard_steps_aside",
"next_on_failure": "guard_calls_for_help",
"effects_on_success": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": -3, "faction": "covenant_enforcers", "note": "intimidated a guard" } }
],
"effects_on_failure": [
{ "kind": "rep_event", "event": { "kind": "Crime", "magnitude": -8, "faction": "covenant_enforcers", "note": "tried to bully a guard" } }
]
},
{ "text": "Apologies. Goodbye.", "next": "<end>" }
]
},
{
"id": "passes",
"speaker": "npc",
"text": "On your way then. Mind the curfew. Stranger faces have come through this season; we're keeping count.",
"options": [
{ "text": "Thank you.", "next": "<end>" }
]
},
{
"id": "passes_warmly",
"speaker": "npc",
"text": "Hah — alright, alright, on with you. Mind yourself out there.",
"options": [
{ "text": "Thank you, friend.", "next": "<end>" }
]
},
{
"id": "guard_suspicious",
"speaker": "npc",
"text": "Mm. I'll be remembering your face. Move along.",
"options": [
{ "text": "Thank you.", "next": "<end>" }
]
},
{
"id": "guard_steps_aside",
"speaker": "npc",
"text": "(They step aside, jaw tight.) Pass. Don't make me regret it.",
"options": [
{ "text": "(Walk past.)", "next": "<end>" }
]
},
{
"id": "guard_calls_for_help",
"speaker": "npc",
"text": "(They reach for a whistle.) That's enough. Constable! Constable, here!",
"options": [
{ "text": "(Back away.)", "next": "<end>" }
]
}
]
}
@@ -0,0 +1,109 @@
{
"id": "generic_merchant",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "{disposition_label} face today. What brings you to my counter, friend?",
"options": [
{
"text": "Show me what you have for sale.",
"next": "shop_open",
"effects": [{ "kind": "open_shop" }]
},
{
"text": "What's the news?",
"next": "lore"
},
{
"text": "[Persuasion DC 12] Surely a regular customer earns better prices?",
"skill_check": { "skill": "persuasion", "dc": 12 },
"next_on_success": "shopkeeper_warmed",
"next_on_failure": "shopkeeper_unmoved",
"effects_on_success": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": 2, "note": "polite haggle" } }
],
"effects_on_failure": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": -1, "note": "weak pitch" } }
]
},
{
"text": "[Intimidate DC 14] Discount, or I tell people about the rats in your stockroom.",
"skill_check": { "skill": "intimidation", "dc": 14 },
"next_on_success": "shopkeeper_caves",
"next_on_failure": "shopkeeper_dismisses",
"conditions": [
{ "kind": "not_has_flag", "flag": "intimidation_used_at_npc" }
],
"effects_on_success": [
{ "kind": "set_flag", "flag": "shopkeeper_intimidated" },
{ "kind": "set_flag", "flag": "intimidation_used_at_npc" }
],
"effects_on_failure": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": -3, "note": "tried to bully" } },
{ "kind": "set_flag", "flag": "intimidation_used_at_npc" }
]
},
{
"text": "Goodbye.",
"next": "<end>"
}
]
},
{
"id": "shop_open",
"speaker": "npc",
"text": "Take a look. Quality goods, fair prices.",
"options": [
{ "text": "[Browse]", "next": "intro", "effects": [{ "kind": "open_shop" }] },
{ "text": "Actually, never mind.", "next": "intro" }
]
},
{
"id": "lore",
"speaker": "npc",
"text": "Times are uneasy. Rumours of strange scents on the wind. Travellers come through asking after old names. Don't ask me which. I trade in goods, not gossip. (Well — mostly.)",
"options": [
{ "text": "Back to business.", "next": "intro" },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "shopkeeper_warmed",
"speaker": "npc",
"text": "Hah. You've a tongue, that's certain. Tell you what — pick something out, I'll knock a bit off.",
"options": [
{ "text": "Show me what you have.", "next": "shop_open", "effects": [{ "kind": "open_shop" }] },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "shopkeeper_unmoved",
"speaker": "npc",
"text": "Heard better lines from the rats in my stockroom. Prices are prices.",
"options": [
{ "text": "Show me what you have anyway.", "next": "shop_open", "effects": [{ "kind": "open_shop" }] },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "shopkeeper_caves",
"speaker": "npc",
"text": "Easy. Easy. There's no need for that. Tell you what — take a look, I'll be reasonable.",
"options": [
{ "text": "Show me what you have.", "next": "shop_open", "effects": [{ "kind": "open_shop" }] },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "shopkeeper_dismisses",
"speaker": "npc",
"text": "I've heard better threats from a wet matchstick. Buy something or get out.",
"options": [
{ "text": "Show me what you have.", "next": "shop_open", "effects": [{ "kind": "open_shop" }] },
{ "text": "Goodbye.", "next": "<end>" }
]
}
]
}
@@ -0,0 +1,62 @@
{
"id": "generic_villager",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "Stranger. You're a {disposition_label} sort, far as I can tell. What's on your mind?",
"options": [
{ "text": "What's it like living here?", "next": "lore_town" },
{ "text": "Heard any unusual news?", "next": "lore_news" },
{
"text": "[Insight DC 12] Are you well? You seem distracted.",
"skill_check": { "skill": "insight", "dc": 12 },
"next_on_success": "villager_confides",
"next_on_failure": "villager_brushes_off",
"effects_on_success": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": 2, "note": "noticed unease" } }
]
},
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "lore_town",
"speaker": "npc",
"text": "Quiet, mostly. We mind our pasture, the constable minds the road, magistrate minds the rest. Used to be quieter still, before the news started.",
"options": [
{ "text": "What news?", "next": "lore_news" },
{ "text": "Back.", "next": "intro" }
]
},
{
"id": "lore_news",
"speaker": "npc",
"text": "Travellers from west of here, asking after old names. Strange scents some mornings. Constable's twitchy. We don't talk about it much. You shouldn't either.",
"options": [
{ "text": "Thanks.", "next": "intro" },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "villager_confides",
"speaker": "npc",
"text": "(Quietly.) You've got an eye on you. Two days back, neighbours saw a coyote-folk on the road who didn't smell right — like a city perfume but ten years stale. Made my fur stand up.",
"options": [
{ "text": "Did you tell the constable?", "next": "intro",
"effects": [{ "kind": "set_flag", "flag": "villager_warned_about_coyote" }] },
{ "text": "Stay safe. Goodbye.", "next": "<end>" }
]
},
{
"id": "villager_brushes_off",
"speaker": "npc",
"text": "Nothing wrong. Long day, that's all.",
"options": [
{ "text": "Back.", "next": "intro" },
{ "text": "Goodbye.", "next": "<end>" }
]
}
]
}
@@ -0,0 +1,74 @@
{
"id": "millhaven_constable",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "\"Constable Aldous Fenn. Magistrate told me to expect you.\"\n\nThe coyote-folk constable has the worn calm of someone who has already had two long days this week. His Covenant brassard is impeccable; his cuffs are not. \"I'm sorry for your loss. Anything you need from the office, you ask me first.\"",
"options": [
{ "text": "Show me your file on my parents.", "next": "case_file" },
{ "text": "What's the Old Howl mine?", "next": "old_howl" },
{ "text": "I heard there's some kind of dispute on the south fence.", "next": "fence_dispute" },
{ "text": "I'll come back later.", "next": "<end>" }
]
},
{
"id": "case_file",
"speaker": "npc",
"text": "He pulls a thin folder. \"Open-field rawfang attack. No witnesses, no print pattern, no one missing from the local population. I closed it as such. Magistrate disagreed. He didn't tell me what he disagreed with.\"\n\nA pause. \"If you find something my eyes missed, I want to know. I'd rather rewrite a closed case than work the wrong one.\"",
"options": [
{ "text": "I will. Thank you.", "next": "intro" },
{ "text": "Why didn't the magistrate tell you?", "next": "magistrate_split" }
]
},
{
"id": "magistrate_split",
"speaker": "npc",
"text": "He almost smiles. \"Because the magistrate is older than me, and he learned policing in a generation that didn't trust paperwork. He's careful with what he writes down. I am too, when I have to be.\"",
"options": [
{ "text": "Understood.", "next": "intro" }
]
},
{
"id": "old_howl",
"speaker": "npc",
"text": "\"Collapsed mine, due south. Used to ship copper. The shaft fell in fifty years ago. Officially closed. Unofficially, somebody's been camping in the entrance — campfire ash and a path beaten in the brush. Brigands, I expect. Take care if you go.\"",
"options": [
{ "text": "Brigands? On their own, this close to town?", "next": "brigand_concern" },
{ "text": "Good to know.", "next": "intro" }
]
},
{
"id": "brigand_concern",
"speaker": "npc",
"text": "\"On their own, no. Some of them work to a contract. I would like to know who pays for camp ash this close to town. If you find out and choose to tell me, the office will remember.\"",
"options": [
{ "text": "I'll look.", "next": "intro" }
]
},
{
"id": "fence_dispute",
"speaker": "npc",
"text": "\"Cervid farmer named Mira and a Canid rancher named Voss. Same fence line for thirty years. This summer it became a war. Both sides are right about something. Neither will speak to me without the other in the room. I am very tired of fences.\"\n\n\"If you can mediate, I will accept any honest outcome.\"",
"options": [
{
"text": "I'll go talk to them.",
"next": "fence_accept",
"effects": [
{ "kind": "start_quest", "quest": "side_act_i_fence_lines" }
]
},
{ "text": "Not my problem.", "next": "intro" }
]
},
{
"id": "fence_accept",
"speaker": "npc",
"text": "\"Mira's farm is the second left out the south gate. Voss runs his herd along the eastern ridge. Pick a side, pick a compromise — I'll back any solution that holds for a season.\"",
"options": [
{ "text": "Understood.", "next": "<end>" }
]
}
]
}
@@ -0,0 +1,122 @@
{
"id": "millhaven_grandmother_asha",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "\"Sit, sit. The chair likes company.\"\n\nGrandmother Asha is a wolf-folk older than the timbers of her cottage. Her eyes are pale and watchful. A small fire burns even in the warm hour. \"You smell like a long road, child. What pulls you to this old door?\"",
"options": [
{ "text": "I'm here about my parents.", "next": "parents" },
{ "text": "The magistrate said you'd know things about Millhaven.", "next": "lore" },
{
"text": "[Has Briarstead Journal] My parents kept this journal. Did you know them?",
"conditions": [{ "kind": "has_item", "id": "briarstead_journal" }],
"next": "journal_recognised"
},
{ "text": "I should be on my way.", "next": "<end>" }
]
},
{
"id": "parents",
"speaker": "npc",
"text": "\"I knew them when the millpond still froze every winter. Quiet people. Good with chemistry. Your mother could tell a forged Pheromone from a real one before the salesman finished his pitch.\" Her ears flatten briefly. \"They were not killed by a wild thing. I have walked this country my whole life. I know what a real rawfang scent leaves on the wind.\"",
"options": [
{ "text": "What did the wind smell like, then?", "next": "wind_smell" },
{ "text": "What do you mean?", "next": "wind_smell" }
]
},
{
"id": "wind_smell",
"speaker": "npc",
"text": "\"Manufactured. Adrenaline without fear. Saliva without hunger. Someone deployed a pheromone compound to write the wrong story across the field.\" She watches your face for a long moment. \"You are not the first to ask. Read what they left you. Then come back and tell me whose names are on the list.\"",
"options": [
{ "text": "I'd like to ask a favour, if I may.", "next": "favour_offer",
"conditions": [{ "kind": "has_flag", "flag": "asha_offered_favour" }] },
{ "text": "Will you help me?", "next": "favour" },
{ "text": "I'll come back.", "next": "<end>" }
]
},
{
"id": "favour",
"speaker": "npc",
"text": "\"I am old, child, and the old keep accounts. There is a stone in the Old Howl mine — your magistrate told you the lane forks. Left to the farm, right to the mine. The mine has been collapsed since my mother's time. My father's Howl-stone is in there. Bring it to me, and I will tell you what I know about the people whose names you carry.\"\n\n\"You'll need to be careful. The mine has not been empty. Someone has been using it. Bring no one with you who you do not trust completely.\"",
"options": [
{
"text": "I'll fetch the stone.",
"next": "<end>",
"effects": [
{ "kind": "set_flag", "flag": "asha_offered_favour", "value": 1 },
{ "kind": "start_quest", "quest": "side_act_i_old_howl" }
]
},
{ "text": "Maybe later.", "next": "<end>" }
]
},
{
"id": "favour_offer",
"speaker": "npc",
"text": "\"The mine, the stone, my father's hand. Either you've gone, or you're remembering. Speak, then.\"",
"options": [
{
"text": "[Has Howl-stone] I brought your stone back.",
"conditions": [{ "kind": "has_item", "id": "howl_stone" }],
"next": "stone_returned",
"effects": [
{ "kind": "take_item", "id": "howl_stone", "qty": 1 },
{ "kind": "set_flag", "flag": "asha_received_howl_stone", "value": 1 },
{ "kind": "rep_event", "event": { "kind": "Aid", "magnitude": 30, "role_tag": "millhaven.grandmother_asha", "note": "returned the Howl-stone" } },
{ "kind": "give_xp", "xp": 150 }
]
},
{ "text": "Still working on it.", "next": "<end>" }
]
},
{
"id": "stone_returned",
"speaker": "npc",
"text": "She closes her hand around the stone and is silent for a long beat. The fire behind her snaps.\n\n\"Thank you, child. The Old Howl is quieter now.\" She presses something thin and folded into your palm — a name, an old address, a route. \"This is who I would speak to in Thornfield, if I were going. Don't show it to a stranger.\"\n\n\"Now ask me what you came for.\"",
"options": [
{ "text": "Tell me about the Maw.", "next": "maw_lore",
"conditions": [{ "kind": "has_item", "id": "maw_sigil" }] },
{ "text": "What do you know about Sable Vasik?", "next": "vasik_lore",
"conditions": [{ "kind": "has_flag", "flag": "asha_received_howl_stone" }] },
{ "text": "Just thank you.", "next": "<end>" }
]
},
{
"id": "vasik_lore",
"speaker": "npc",
"text": "\"That name. I have heard it twice in fifty years, and both times the speaker apologised for saying it aloud.\" She thinks. \"A Covenant-Keeper of the Bridge oath, retired thirty years. People who knew the work say the work was good. People who knew the keeper say the keeper was tired. Be careful with what tired people decide is true.\"",
"options": [
{ "text": "Thank you. Goodbye.", "next": "<end>" }
]
},
{
"id": "maw_lore",
"speaker": "npc",
"text": "\"A circle of teeth. Predators of every clade biting the same prey at the same moment. The pictograph is older than the Covenant — it used to mean a hunting truce. Whoever uses it now has read history badly, or read it too well.\"",
"options": [
{ "text": "Thank you for your help.", "next": "<end>" }
]
},
{
"id": "journal_recognised",
"speaker": "npc",
"text": "She turns the journal over in her hands as carefully as a wounded bird.\n\n\"Yes. Your parent's hand. Read the early entries first — they were a careful writer. The later entries are not as careful. Something had begun to frighten them. By the time they crossed names off this list, they were sleeping in shifts.\" Her eyes meet yours. \"I am sorry, child. I am old enough to recognise what fear looks like in handwriting.\"",
"options": [
{ "text": "Will you help me?", "next": "favour" },
{ "text": "I'll come back when I know more.", "next": "<end>" }
]
},
{
"id": "lore",
"speaker": "npc",
"text": "\"Millhaven sits where the southern road and the millrace meet. Cervid farmers and Canid ranchers, with a few of every other clade between. Most years, the grain is good and the constable is bored. This year is not most years.\"\n\n\"Ask me something specific and I'll answer the part I know.\"",
"options": [
{ "text": "What about my parents?", "next": "parents" },
{ "text": "Goodbye.", "next": "<end>" }
]
}
]
}
@@ -0,0 +1,149 @@
{
"id": "millhaven_lacroix",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "\"Evening, friend. Fen Lacroix, traveling merchant. Saw your face in the inn last night and thought I'd say hello.\"\n\nThe coyote-folk smiles too widely, talks too smoothly, and stands too close. There's a road-pack at his feet, but the only goods inside are two flat parcels that don't rattle.",
"options": [
{ "text": "What are you really doing in Millhaven?", "next": "what_doing" },
{
"text": "[Has Briarstead Journal] You were in my parents' workshop.",
"conditions": [{ "kind": "has_item", "id": "briarstead_journal" }],
"next": "accuse"
},
{ "text": "Just passing through. Goodbye.", "next": "<end>" }
]
},
{
"id": "what_doing",
"speaker": "npc",
"text": "\"Buying, selling, listening. Roads have ears, and ears get hungry.\" His smile doesn't change. \"You should be careful who you talk to. This town has a habit of remembering things badly.\"",
"options": [
{ "text": "Is that a warning?", "next": "warning" },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "warning",
"speaker": "npc",
"text": "\"It's a road-merchant's discount. Listen well or pay later.\" He starts to lift his pack.",
"options": [
{ "text": "Wait. We're not done.", "next": "accuse",
"conditions": [{ "kind": "has_item", "id": "briarstead_journal" }] },
{ "text": "Goodbye, then.", "next": "<end>" }
]
},
{
"id": "accuse",
"speaker": "npc",
"text": "His face stops smiling.\n\n\"Ah. So you read the journal already. That's quicker than they thought.\" He shifts his weight. The hand near his pack is no longer holding it.\n\n\"Three options, friend. You walk away and forget my name. You take me to your constable, and I tell him a story that won't satisfy you. Or we settle it here.\"",
"options": [
{
"text": "[Intimidate DC 14] Talk first. The story you tell me, or you don't walk out of this room.",
"skill_check": { "skill": "intimidation", "dc": 14 },
"next_on_success": "interrogate",
"next_on_failure": "fight",
"effects_on_success": [
{ "kind": "set_flag", "flag": "lacroix_interrogated", "value": 1 },
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": 4, "role_tag": "millhaven.lacroix", "note": "broke under threat" } }
],
"effects_on_failure": [
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": -3, "role_tag": "millhaven.lacroix", "note": "called your bluff" } }
]
},
{
"text": "Settle it here.",
"next": "fight"
},
{
"text": "Walk away. (Both of us.)",
"next": "let_go",
"effects": [
{ "kind": "set_flag", "flag": "lacroix_let_go", "value": 1 },
{ "kind": "give_item", "id": "maw_sigil", "qty": 1 },
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": 5, "faction": "maw", "note": "let the operative go" } },
{ "kind": "rep_event", "event": { "kind": "Dialogue", "magnitude": -8, "faction": "covenant_enforcers", "note": "did not detain a known suspect" } }
]
}
]
},
{
"id": "interrogate",
"speaker": "npc",
"text": "He sits — slowly — on the edge of the bed.\n\n\"Fine. I was sent to confirm your parents' notes were destroyed. Someone wanted them dead before they finished writing. I was cleanup.\" He looks tired. \"I don't know who paid. I take a name and a wax seal and money in my hand. The seal is teeth in a circle. The Maw, they call themselves. That's all I know. Ask the people in Thornfield. Ask Sable.\"\n\nHe opens his coat slowly and produces a folded letter sealed in black wax. The mark is a circle of teeth meeting at the centre. He sets it on the bed.",
"options": [
{
"text": "Take the letter and let him go.",
"next": "<end>",
"effects": [
{ "kind": "give_item", "id": "maw_sigil", "qty": 1 },
{ "kind": "set_flag", "flag": "lacroix_climax_resolved", "value": 1 },
{ "kind": "set_flag", "flag": "act_i_briarstead_searched", "value": 1 },
{ "kind": "give_xp", "xp": 250 },
{ "kind": "rep_event", "event": { "kind": "Quest", "magnitude": 4, "faction": "covenant_enforcers", "note": "extracted intel from a Maw operative" } },
{ "kind": "rep_event", "event": { "kind": "Quest", "magnitude": -10, "faction": "maw", "note": "extracted intel from one of their operatives" } }
]
},
{
"text": "Take the letter and end this.",
"next": "kill_after_interrogate",
"effects": [
{ "kind": "give_item", "id": "maw_sigil", "qty": 1 },
{ "kind": "set_flag", "flag": "lacroix_killed", "value": 1 },
{ "kind": "set_flag", "flag": "lacroix_climax_resolved", "value": 1 },
{ "kind": "set_flag", "flag": "act_i_briarstead_searched", "value": 1 },
{ "kind": "give_xp", "xp": 200 },
{ "kind": "rep_event", "event": { "kind": "Death", "magnitude": -25, "faction": "maw", "note": "killed an operative after extraction" } },
{ "kind": "rep_event", "event": { "kind": "Crime", "magnitude": -8, "faction": "covenant_enforcers", "note": "executed a prisoner without trial" } }
]
}
]
},
{
"id": "kill_after_interrogate",
"speaker": "narration",
"text": "He doesn't fight back. He didn't think you'd actually do it.",
"options": [
{ "text": "Continue.", "next": "<end>" }
]
},
{
"id": "fight",
"speaker": "narration",
"text": "Lacroix moves first. The room is small. There is no escape — only a fight that ends one way or the other.\n\n(For Phase 6 M6, the fight is resolved narratively here as a single decisive blow; Phase 7's PoI work will replace this with the full tactical encounter the design calls for.)",
"options": [
{
"text": "[Decisive blow]",
"next": "<end>",
"effects": [
{ "kind": "give_item", "id": "maw_sigil", "qty": 1 },
{ "kind": "set_flag", "flag": "lacroix_killed", "value": 1 },
{ "kind": "set_flag", "flag": "lacroix_climax_resolved", "value": 1 },
{ "kind": "set_flag", "flag": "act_i_briarstead_searched", "value": 1 },
{ "kind": "give_xp", "xp": 250 },
{ "kind": "rep_event", "event": { "kind": "Death", "magnitude": -30, "faction": "maw", "note": "killed Lacroix in self-defence" } },
{ "kind": "rep_event", "event": { "kind": "Combat", "magnitude": 4, "faction": "covenant_enforcers", "note": "stopped a Maw operative mid-mission" } }
]
}
]
},
{
"id": "let_go",
"speaker": "npc",
"text": "He nods, slowly, and slides a folded letter across the floor with his boot. The wax seal is a circle of teeth.\n\n\"You have a long road. Keep it warm.\" He shoulders his pack and goes.\n\n(The Maw now knows you exist. They will adjust.)",
"options": [
{
"text": "Continue.",
"next": "<end>",
"effects": [
{ "kind": "set_flag", "flag": "lacroix_climax_resolved", "value": 1 },
{ "kind": "set_flag", "flag": "act_i_briarstead_searched", "value": 1 },
{ "kind": "give_xp", "xp": 150 }
]
}
]
}
]
}
@@ -0,0 +1,86 @@
{
"id": "millhaven_magistrate",
"root": "intro",
"nodes": [
{
"id": "intro",
"speaker": "npc",
"text": "\"You'll be the one I wrote to. Take a breath. There's a small letter on my desk waiting for you, and a longer story behind it.\"\n\nMagistrate Vossler is an elderly elk-folk in modest robes. The Covenant seal on his collar is old enough that the gilding has worn down. He nods at a chair without inviting you to sit.",
"options": [
{ "text": "Tell me what happened to my parents.", "next": "case" },
{ "text": "Why did you write to me, of all people?", "next": "why_you" },
{ "text": "I'd like to look around the town first.", "next": "<end>" },
{ "text": "Goodbye.", "next": "<end>" }
]
},
{
"id": "why_you",
"speaker": "npc",
"text": "\"You're the next of kin on file. They never named anyone else. The estate paperwork required your hand. The case file...\" He pauses. \"That's why I wrote you a personal letter and not a courier slip.\"",
"options": [
{ "text": "What's in the case file?", "next": "case" },
{ "text": "I'll think on it. Goodbye.", "next": "<end>" }
]
},
{
"id": "case",
"speaker": "npc",
"text": "\"The constabulary report calls it a rawfang attack. Open-field, predator profile, no witnesses. Constable Fenn ran the investigation himself. He's a good Canid — pack-trained, methodical. He believes what he wrote.\"\n\nThe magistrate folds his hands. \"I do not. The scene was wrong. Your parents had been preparing for something — papers boxed, maps annotated. I had the boxes brought here for safekeeping. I'd like you to take them with you.\"",
"options": [
{
"text": "I'll take them. What did they leave?",
"next": "hand_over",
"effects": [
{ "kind": "give_item", "id": "briarstead_journal", "qty": 1 },
{ "kind": "give_item", "id": "formula_partial", "qty": 1 },
{ "kind": "give_item", "id": "names_list", "qty": 1 },
{ "kind": "set_flag", "flag": "spoke_to_millhaven_magistrate", "value": 1 },
{ "kind": "rep_event", "event": { "kind": "Quest", "magnitude": 4, "faction": "covenant_enforcers", "note": "called on the Millhaven magistrate" } }
]
},
{ "text": "Why didn't you go to the Enforcers in the capital?", "next": "why_not_capital" }
]
},
{
"id": "why_not_capital",
"speaker": "npc",
"text": "\"Because if my hunch is right, then the wrong office in the capital will hand the file to the wrong person. I'd rather you know what they wrote down before anyone else does.\"\n\nHe sighs. \"Take the boxes. Read the journal first. The names list will mean nothing today. It will mean a great deal in a month.\"",
"options": [
{
"text": "Alright. Hand them over.",
"next": "hand_over",
"effects": [
{ "kind": "give_item", "id": "briarstead_journal", "qty": 1 },
{ "kind": "give_item", "id": "formula_partial", "qty": 1 },
{ "kind": "give_item", "id": "names_list", "qty": 1 },
{ "kind": "set_flag", "flag": "spoke_to_millhaven_magistrate", "value": 1 },
{ "kind": "rep_event", "event": { "kind": "Quest", "magnitude": 4, "faction": "covenant_enforcers", "note": "called on the Millhaven magistrate" } }
]
}
]
},
{
"id": "hand_over",
"speaker": "npc",
"text": "He brings out a small chest from behind the desk and slides it across.\n\n\"There's a journal, three sheets of formula, and a list of names. I've also kept the keys to the farmhouse — Briarstead, if you remember the road. South of town, past the millpond. Constable Fenn can show you the path; Grandmother Asha can tell you who your parents were before this happened. Do both, in either order. Then come back here and tell me what you found.\"",
"options": [
{ "text": "Where exactly is Briarstead?", "next": "directions" },
{
"text": "I'll go.",
"next": "<end>"
}
]
},
{
"id": "directions",
"speaker": "npc",
"text": "\"South gate, then the rutted lane along the millpond. Twenty minutes if you walk steadily. The lane forks at the old fence — left for the farmhouse, right for the Old Howl mine. The mine's collapsed. Don't go there alone.\"",
"options": [
{
"text": "Understood.",
"next": "<end>"
}
]
}
]
}