|
| 1 | +// ─── Non-Combat Challenge Cast Pools ────────────────────────────── |
| 2 | +// Content library for the social framework in src/lib/challenge- |
| 3 | +// frameworks/social.ts. PR 2's counterpart to noncombat-scenarios.ts. |
| 4 | +// Personas, wants, secrets, and leverage combine to build a principal |
| 5 | +// NPC; complications and interruptions add live pressure to the scene. |
| 6 | +// |
| 7 | +// Authoring rule: no dice expressions or DC numbers in these strings |
| 8 | +// — the engines attach the numbers. Keep prose vivid and table-ready, |
| 9 | +// D&D fantasy register, no anachronisms. |
| 10 | + |
| 11 | +export interface Persona { archetype: string; quirk: string; speech: string } |
| 12 | +export interface Leverage { |
| 13 | + kind: 'coin' | 'flattery' | 'threat' | 'logic' | 'favor' | 'secret-for-secret'; |
| 14 | + approach: string; // what moves them |
| 15 | + counter: string; // what backfires |
| 16 | +} |
| 17 | + |
| 18 | +// ─── Personas ─────────────────────────────────────────────────────── |
| 19 | +export const PERSONAS: Persona[] = [ |
| 20 | + { archetype: 'a guild clerk drowning in ledgers', quirk: 'stacks and restacks papers when nervous', speech: 'answers questions with smaller questions' }, |
| 21 | + { archetype: 'a retired sell-sword turned innkeep', quirk: 'polishes the same tankard throughout', speech: 'short sentences, long pauses' }, |
| 22 | + { archetype: 'a temple acolyte assigned to watch the visitors', quirk: 'touches the holy symbol before every lie', speech: 'quotes scripture that never quite fits' }, |
| 23 | + { archetype: 'a caravan master counting losses', quirk: 'taps a tally stick against their palm', speech: 'talks in freight weights and travel days' }, |
| 24 | + { archetype: "a minor noble's steward doing the noble's real work", quirk: 'straightens whatever is nearest before speaking', speech: 'formal to the point of stiffness, until provoked' }, |
| 25 | + { archetype: 'a midwife who has delivered half the district', quirk: 'sizes up strangers like she is checking for a fever', speech: 'blunt, and unbothered by rank' }, |
| 26 | + { archetype: 'a river ferryman who has seen every kind of passenger', quirk: 'spits over the rail before naming a price', speech: 'trades in questions that are really answers' }, |
| 27 | + { archetype: 'a disgraced hedge-mage scraping by on charms', quirk: 'mutters the end of every sentence twice', speech: 'over-explains the harmless magic and dodges the rest' }, |
| 28 | + { archetype: 'a town crier who knows more than the news he shouts', quirk: 'clears his throat before anything true', speech: 'performs even private conversations' }, |
| 29 | + { archetype: 'a mercenary captain between contracts', quirk: 'counts exits in any room within a breath of entering', speech: 'names a price for everything, including advice' }, |
| 30 | + { archetype: 'a widowed innfolk running the family trade alone', quirk: 'wipes hands on an apron that is never actually dirty', speech: 'warm until money or the dead come up' }, |
| 31 | + { archetype: "a gatehouse sergeant tired of being no one's priority", quirk: 'rolls a shoulder that never quite healed right', speech: 'clipped orders even when making requests' }, |
| 32 | + { archetype: 'a traveling relic-appraiser with a magnifying loupe on a chain', quirk: 'squints at anything shiny, mid-sentence', speech: 'prices things aloud without meaning to' }, |
| 33 | + { archetype: 'a shrine keeper tending a god whose worship has thinned', quirk: 'lights a candle for every visitor, believer or not', speech: 'gentle, but circles back to the same worry' }, |
| 34 | +]; |
| 35 | + |
| 36 | +// ─── Wants ────────────────────────────────────────────────────────── |
| 37 | +export const WANTS: string[] = [ |
| 38 | + 'safe passage for a wagon that must not be inspected', |
| 39 | + "a rival's letter retrieved before it is read aloud at council", |
| 40 | + 'proof that a debt was already paid, before the collectors arrive', |
| 41 | + 'someone trustworthy to carry a message no courier will touch', |
| 42 | + 'a missing relative found before the harvest festival', |
| 43 | + 'a rival merchant undercut without anyone tracing it back', |
| 44 | + 'an old promise called in, quietly, without witnesses', |
| 45 | + 'a stolen heirloom returned before its absence is noticed', |
| 46 | + 'a dangerous rumor about them stopped before it spreads further', |
| 47 | + 'an outsider to vouch for them at a hearing they cannot attend', |
| 48 | + 'safe escort past a watch post they would rather not explain', |
| 49 | + 'a poisoned business deal undone before the ink dries', |
| 50 | + 'someone willing to take the blame for a mistake that was not theirs', |
| 51 | + 'a family grudge settled without it turning into a feud', |
| 52 | + 'a shipment recovered before its owner learns it went missing', |
| 53 | + 'a confession pried from someone who owes them nothing', |
| 54 | + 'protection for a family member who has made dangerous enemies', |
| 55 | + 'a forged document quietly destroyed before it is used against them', |
| 56 | + 'an old rival humiliated in a way that cannot be traced back to them', |
| 57 | + 'the truth about a death that the authorities have already closed the book on', |
| 58 | +]; |
| 59 | + |
| 60 | +// ─── Secrets ──────────────────────────────────────────────────────── |
| 61 | +export const SECRETS: string[] = [ |
| 62 | + 'the debt they owe is to someone who does not forgive in coin', |
| 63 | + 'they witnessed the crime everyone is asking about — from the wrong side', |
| 64 | + 'they are not who their papers say they are', |
| 65 | + 'they once took a bribe that got someone else killed', |
| 66 | + 'they are hiding a sibling the law wants for a crime long past', |
| 67 | + 'the object everyone assumes was stolen was theirs to give away', |
| 68 | + 'they have been forging a signature for years and no one has noticed', |
| 69 | + 'they know exactly where the missing person is, and why they left', |
| 70 | + 'they broke a vow to a temple that has not forgotten', |
| 71 | + 'the accident that maimed them was no accident at all', |
| 72 | + 'they are quietly buying up debts to seize the land beneath them', |
| 73 | + 'they have been feeding information to the very people they claim to fear', |
| 74 | + "they buried a body and have told three different lies about the grave since", |
| 75 | + 'their claim to the family trade rests on a will that was never real', |
| 76 | + 'they let an innocent take the blame once, and would do it again', |
| 77 | + 'they are dying of something slow, and no one else knows yet', |
| 78 | + 'the ledger they keep so carefully is a second, false set of books', |
| 79 | + 'they once loved someone the party is now hunting', |
| 80 | + 'they arranged the very disaster they are now offering to help solve', |
| 81 | + 'they have been quietly stockpiling supplies for a war no one else believes is coming', |
| 82 | +]; |
| 83 | + |
| 84 | +// ─── Leverage ─────────────────────────────────────────────────────── |
| 85 | +export const LEVERAGE: Leverage[] = [ |
| 86 | + { kind: 'coin', approach: 'a fair price named plainly, half up front', counter: 'haggling insults them — the price rises' }, |
| 87 | + { kind: 'secret-for-secret', approach: 'trade a confidence of equal weight', counter: 'a hollow or invented secret ends all trust' }, |
| 88 | + { kind: 'flattery', approach: 'genuine praise for the one thing they are actually proud of', counter: 'flattery that misses the mark reads as mockery' }, |
| 89 | + { kind: 'threat', approach: 'a quiet, specific warning of a consequence they cannot afford', counter: 'an empty or exaggerated threat makes them call the bluff — and remember the insult' }, |
| 90 | + { kind: 'logic', approach: 'lay out the plain arithmetic of their own self-interest', counter: 'condescension in the explanation turns agreement into stubbornness' }, |
| 91 | + { kind: 'favor', approach: 'call on a debt they already owe, or offer one worth collecting later', counter: 'a favor that costs them more than it is worth breeds resentment, not gratitude' }, |
| 92 | +]; |
| 93 | + |
| 94 | +// ─── Social Complications ─────────────────────────────────────────── |
| 95 | +export const SOCIAL_COMPLICATIONS: string[] = [ |
| 96 | + 'a rival faction watches the conversation and will act on whatever is agreed', |
| 97 | + 'the persona is being watched by someone they are terrified of, and it shows', |
| 98 | + 'a mutual acquaintance arrives and expects to be included in the conversation', |
| 99 | + 'the persona has already promised the same thing to someone else', |
| 100 | + 'guards patrol close enough to overhear anything said too loudly', |
| 101 | + 'the persona is testing the party to see if they can be trusted with something larger', |
| 102 | + 'someone in earshot is taking notes for a person the party has not met yet', |
| 103 | + 'the persona is only half paying attention — a real crisis is unfolding elsewhere', |
| 104 | + 'the deal being struck would ruin a friend of the persona if it ever got out', |
| 105 | + 'a child or apprentice of the persona is present and old enough to understand everything', |
| 106 | +]; |
| 107 | + |
| 108 | +// ─── Interruptions ────────────────────────────────────────────────── |
| 109 | +export const INTERRUPTIONS: string[] = [ |
| 110 | + 'a third party arrives mid-conversation with a competing offer', |
| 111 | + 'a messenger bursts in with news that changes the persona\'s priorities entirely', |
| 112 | + 'the persona is suddenly and visibly recognized by someone they hoped to avoid', |
| 113 | + 'a loud commotion outside draws everyone\'s attention for a crucial moment', |
| 114 | + 'someone the persona owes money to walks in and demands to be paid on the spot', |
| 115 | + 'a piece of evidence relevant to the secret is dropped, delivered, or overheard in the room', |
| 116 | + 'the persona receives a note that visibly unsettles them and changes their tone', |
| 117 | + 'an old enemy of the persona recognizes one of the party and says so, loudly', |
| 118 | +]; |
0 commit comments