hench/module/playbooks.mjs
2025-05-23 15:16:22 -04:00

892 lines
33 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const nullPlaybookKey = "NONE";
export const playbookKeys = [
nullPlaybookKey,
"SUPERFAN",
"BADASS",
"INVENTOR",
"DEMOTED",
"OUTCAST",
"USURPER",
"TIMECARD",
];
const playbooks = {
[nullPlaybookKey]: {
details: [
{
question: "**** *** ******* ** ******* **?",
answer: "",
},
{
question: "*** *** *** **** ****** ********** ** *** ** *?",
answer: "",
}
],
fixedInclinations: [
"**** ***** ** *** *********** ** ***** **** *** *.",
"******* *** *** ** *** ****** *** **.",
],
missionPlanningQuestions: [
"**** *** ******* ** ******* ** **** ***** ** *** *********** ** ***** **** *** *.",
"*** *** *** **** ****** ********** ** *** ** * ******* *** *** ** *** ****** *** **.",
"****** ** ******** ***** *** **** ***** *** * ********* ********* **** ******** **** ***.",
],
expTrigger: {
marked: false,
description: "*** * ********* ********* **** ******** **** ***.",
},
advancement: {
marked: false,
description: "****** ********** ** *** ** * ******* *** *** ** ***. Retire."
},
gear: [
{
marked: false,
description: "***** ********* ** ***",
},
{
marked: false,
description: "***** ***",
},
{
marked: false,
description: "******* ******",
},
{
marked: false,
description: "************** ******",
},
],
moves: [
{
marked: false,
name: "*********** ********",
description: `**** *** ***** **** * *** ****** ** ********** *********, *** **** *** *** *** *** ** *** *********. ***'** ***** ** **** ******: ******. ******** *** ***** ** ****.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "*********** **************",
description: `**** *** ***** *** ** ******* ********* ******, **** ** **** ** **** **** **** ****** ******* **.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "*********",
description: `**** *** *** *** ** ******* ** *** **** ******* *** *** * ******** ***** *** ****** ******** ** *** **** ********.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "***************** ******* ********** **",
description: `** *** **** ****** ******* *** **** ****** ***** **, *** *** ** *** ***** ******* **** *** ************ ***** *****. ******* *** **** ********.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "*************!",
description: `** * ******** **** *** *** ***** *** *** *** *********** ***** * ***** ** *** *******. ***** * ****** **** ***** **** ***** *** ****. ****** *** ***, *** *** *** ****:
<ul class="hench-list-1-col">
<li>** * *-*, *** ***** **** *********** ** ******* *** ******.</li>
<li>** * *-*, *** ***** **** *********** ** ***** *** ***** * ****** ** *****.</li>
<li>** * *-* ******* ***** ****** ** **** ****** *** **** ********* **** **** **** ********** ** *******.</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
]
},
SUPERFAN: {
details: [
{
question: "What's the coolest part of being a hench?",
answer: "",
},
{
question: "What's your favorite story about the boss?",
answer: "",
}
],
fixedInclinations: [
"Drawing on your knowledge of comic lore",
"Acting with naive optimism",
],
missionPlanningQuestions: [
"You've read an issue about the target. Choose a power or a weakness of the target, and explain it.",
"Confidently state a poorly thought out plan to achieve the goal.",
"Ask the boss: is the target really as cool as it's made out to be?",
],
expTrigger: {
marked: false,
description: "You faced the grim realities of the job.",
},
advancement: {
marked: false,
description: "Sign a comic book deal, become your own star. Retire."
},
gear: [
{
marked: false,
description: "Replica Prop",
},
{
marked: false,
description: "Camera",
},
{
marked: false,
description: "Lame Getaway Car",
},
{
marked: false,
description: "Cheap Comic",
},
],
moves: [
{
marked: false,
name: "Encyclopedic Knowledge",
description: `When you first meet a new heroic or villainous character, you can decide you've heard of them before, and choose to answer one of the below. Whatever you state is true.
<ul class="hench-list-2-col">
<li>What is their power?</li>
<li>Why are they here?</li>
<li>Who do they work with?</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Lampshade",
description: `When you point out an obvious superhero cliche, take +1 card to your next draw acting against it.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Just an Average Citizen",
description: `When you are out of uniform no one can identify you as a henchman until you reveal yourself or act with violence.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Please Don't Be Dead, Please Don't Be Dead...",
description: `If you have killed someone and felt guilty about it, you may at any point declare they are miraculously still alive. Explain how they survived.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Forum Lurker",
description: `As a prep action you can trawl the web for information about a piece of the setting. State a single fact about this thing and draw 2. Ignore the cue, and use the rank:
<ul class="hench-list-1-col">
<li>1-2 — You learn your information is correct but flawed.</li>
<li>3-6 — You learn your information is false but holds a kernel of truth.</li>
<li>5-6 — Someone takes notice of your search and will interfere with your next home life or mission.</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
]
},
BADASS: {
details: [
{
question: "What's the worst fight you ever got into?",
answer: "",
},
{
question: "What's your favorite story about the boss?",
answer: "",
}
],
fixedInclinations: [
"Applying brute force",
"Making a threat",
],
missionPlanningQuestions: [
"You've fought the target before. State where they left a scar, and how they did it.",
"The target is an old friend. State how you met, and how it ended.",
"Ask the boss: what are we not allowed to break?",
],
expTrigger: {
marked: false,
description: "Your strength couldn't solve the problem at hand.",
},
advancement: {
marked: false,
description: "Get caught up in a grudge you tried to forget. Retire."
},
gear: [
{
marked: false,
description: "An Old Battle Trophy",
},
{
marked: false,
description: "Body Armor",
},
{
marked: false,
description: "A Sweet Ride",
},
{
marked: false,
description: "Live Grenade",
},
],
moves: [
{
marked: false,
name: "Go Overboard",
description: `Once per session, when you throw yourself into overwhelming danger, you can treat any played card as -1 rank and ignore any harm as consequence. This does not extend to your team. <em>(This wont change the cue of a 2).</em>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Signature Weapon",
description: `You gain a new gear. Name it, then choose two properties it has.
<ul class="hench-list-2-col">
<li>Scary</li>
<li>Deadly</li>
<li>Concealable</li>
<li>Quick</li>
<li>Unbreakable</li>
<li>Explosive</li>
<li>Untraceable</li>
<li>Sniping</li>
</ul>`,
hasWriteIn: true,
writeIn: "",
},
{
marked: false,
name: "Meathead",
description: `Whenever you play a card and follow the cue, you can mark 1 stress to treat the card's suit as ♠ instead.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Parasocial",
description: `Once per session, when you first meet a character, you can declare that you have wronged them in the past. The Boss will explain how. They will obsessively try to enact revenge against you for the slight.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Phone a Friend",
description: `As a prep action, you can ask an old friend to complete a favor. State what you want, then draw 2. Ignore the cue, and instead use the rank:
<ul class="hench-list-1-col">
<li>1-2 — They do it cleanly.</li>
<li>3-4 — They do it sloppily. A new complication arises.</li>
<li>5-6 — You get roped into doing a favor for them during the next mission. If you fail to follow through, take 2 harm, “Damaged Reputation.”</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
],
},
INVENTOR: {
details: [
{
question: "What was your first invention?",
answer: "",
},
{
question: "What secret project does the boss fund? Why is it doomed?",
answer: "",
}
],
fixedInclinations: [
"Using your intellect",
"Doing something unexpected",
],
missionPlanningQuestions: [
"Explain an unconventional or secret weakness of the target.”",
"State a fact about the target's defense systems.",
"Ask the boss: what secret research is the target hiding?",
],
expTrigger: {
marked: false,
description: "Your invention failed fantastically.",
},
advancement: {
marked: false,
description: "You finally finish your life's work. Retire."
},
gear: [
{
marked: false,
description: "Remote Tracker",
},
{
marked: false,
description: "Hacking Suite",
},
{
marked: false,
description: "Dangerous Chemicals",
},
{
marked: false,
description: "Surgical Tools",
},
],
moves: [
{
marked: false,
name: "Unorthodox",
description: `When you use your gear in a manner other than its intended purpose, and you play a 2, 3, or face card, clear 3 stress.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Wirey",
description: `Treat all played ♣ cards as -1 rank. Treat all played ♠ cards as +1 rank. <em>(This won't turn a 2 into a 1, or a 6 into a 7).</em>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Hammerspace",
description: `You can mark any number of gear slots.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "I'm a PROFESSIONAL.",
description: `When you take the tinker prep action, mark 2 ticks instead of one.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Always Prepared",
description: `As a prep action, you can design a new prototype invention. Write it in below and pick 1 flaw. You may use it as gear this mission. Then, draw 2. Ignore the cue, and use the rank:
<ul class="hench-list-1-col">
<li>1-2 — You make enough prototypes for the whole team to use. Each player may write it in as an extra gear option for this mission.</li>
<li>3-4 — You're able to make one for yourself.</li>
<li>5-6 — You barely get it out of the lab. Pick an additional flaw.</li>
</ul>
<p><strong>Flaws:</strong></p>
<ul class="hench-list-2-col">
<li>Inaccurate</li>
<li>Slow</li>
<li>Unstable</li>
<li>Cumbersome</li>
</ul>`,
hasWriteIn: true,
writeIn: "",
},
],
},
DEMOTED: {
details: [
{
question: "What was your old villain name? Why are you just a hench, now?",
answer: "",
},
{
question: "What leverage does the boss have on you?",
answer: "",
}
],
fixedInclinations: [
"Acting with excessive flair",
"Attempting to reclaim your status",
],
missionPlanningQuestions: [
"The target is sloppy. State an obvious flaw with their approach or gimmick.",
"You've bested the target before. State how, and why you can't just do that again.",
"Ask the boss: why hasn't the Guild taken out the target?",
],
expTrigger: {
marked: false,
description: "Your old tricks weren't cut out for the job.",
},
advancement: {
marked: false,
description: "Earn back your title as supervillain. Retire."
},
gear: [
{
marked: false,
description: "Gimmick Weapon",
},
{
marked: false,
description: "Old Uniform",
},
{
marked: false,
description: "Pyrotechnics",
},
{
marked: false,
description: "Your own calling card",
},
],
moves: [
{
marked: false,
name: "Villain-ish",
description: `You have a minor power or gimmick. Describe it. You may mark 1 stress at any time to use it.`,
hasWriteIn: true,
writeIn: "",
},
{
marked: false,
name: "You're Dead, Kiddo",
description: `When you intimidate someone by describing a past villainous deed, treat any ♥ as -1 rank. <em>(This won't change the cue for a 2)</em>.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "I've Seen Worse",
description: `When you team up with a fellow hench, describe a similar memory from your past that has prepared you for this moment. Take +1 to the draw (possibly up to 4 cards), and you take one stress.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Overqualified",
description: `When a teammate fails to complete a task, and you confidently take control, you may treat your played card as any suit, but you cannot resist the consequences.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Like Old Times",
description: `As a prep action, you can take some henches on a minor crime spree. Explain your petty crimes, then draw 2. Ignore the cue, and instead use the rank:
<ul class="hench-list-1-col">
<li>1-2 — You stir up some recognition and pride. Mark 2 experience, and describe something you've found on the scene.</li>
<li>3-4 — The night is a blur. Mark 1 experience, then describe something you've lost, and something youve gained.</li>
<li>5-6 — You cause a complete ruckus. When you draw engagement, treat the result as one level worse. How have you put the mission in jeopardy?</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
],
},
OUTCAST: {
details: [
{
question: "Why has society rejected you?",
answer: "",
},
{
question: "Why has the boss taken you in?",
answer: "",
}
],
fixedInclinations: [
"Attempting to impress someone",
"Acting against all social norms",
],
missionPlanningQuestions: [
"The target has treated you cruelly. State when, and how.",
"You and the target have something in common. State what, and how that makes you feel.",
"Ask the boss: Why do you hate the target?",
],
expTrigger: {
marked: false,
description: "An attempt at connection was rejected.",
},
advancement: {
marked: false,
description: "A kinder community takes you in. Retire."
},
gear: [
{
marked: false,
description: "Something Sentimental",
},
{
marked: false,
description: "Something of The Boss's",
},
{
marked: false,
description: "Something Misshapen",
},
{
marked: false,
description: "Something Dangerous",
},
],
moves: [
{
marked: false,
name: "Teacher's Pet",
description: `You live at the base, and are specially favored by the boss. Describe a privilege that only you receive. You cannot clock out.`,
hasWriteIn: true,
writeIn: "",
},
{
marked: false,
name: "Sore Thumb",
description: `When you follow a cue, you can take 1 stress to gain an additional cue: “You attract the attention of everyone around.” <em>(Follow both cues).</em>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Mutation",
description: `Your body has been changed, somehow. Describe it. This alteration can be counted as gear, and does not take a slot.`,
hasWriteIn: true,
writeIn: "",
},
{
marked: false,
name: "Folie à Deux",
description: `At the beginning of the action, choose another hench. Whenever they help you or show support, clear 2 stress. Whenever they fail or are hurt, take 2 stress.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Eavesdropper",
description: `As a prep action, you can lurk around where you shouldn't be and listen for secrets. Choose a target or an ally, then draw 2. Ignore the cue, and instead use the rank:
<ul class="hench-list-1-col">
<li>1-2 — You overhear something no one else knows - a future plan, a haunting deed, or their shame.</li>
<li>3-4 — You overhear a common rumor - a worst held secret, a trick they've pulled, or a romantic tryst.</li>
<li>5-6 — You are discovered, and learn nothing. You make a personal enemy of them.</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
],
},
USURPER: {
details: [
{
question: "What is your ultimate ambition?",
answer: "",
},
{
question: "What habit of the boss infuriates you the most?",
answer: "",
}
],
fixedInclinations: [
"Acting on hidden agendas.",
"Putting yourself before anyone else.",
],
missionPlanningQuestions: [
"Describe how the target is uniquely equipped to defeat the boss.",
"Describe how you are uniquely equipped to defeat the target.",
"Ask the boss: are you really prepared for this mission?",
],
expTrigger: {
marked: false,
description: "You had to act selflessly.",
},
advancement: {
marked: false,
description: "You finally overthrow the boss. Replace them."
},
gear: [
{
marked: false,
description: "Concealed Weapon",
},
{
marked: false,
description: "Imitation Boss Uniform",
},
{
marked: false,
description: "Vial of Poison",
},
{
marked: false,
description: "False Evidence",
},
],
moves: [
{
marked: false,
name: "Defiant",
description: `When you act in opposition to the boss's orders, take 2 less stress if you resist consequences.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Steal the Spotlight",
description: `When you claim the boss's actions as your own, clear 2 stress. If your audience believes you, clear 3.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Fall Guy",
description: `When you would take harm, you may mark stress equal to the level to have a nearby hench or bystander take the harm instead.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Splinter Cell",
description: `You have loyal henches on the team. When you take the team-building prep time action, you can draw 3 and hold two of the cards for the mission.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Machinations",
description: `As a prep action, secretly note your own goal for this mission, and show it to the boss. During the mission, if you complete this goal, reveal it, then choose one:
<ul class="hench-list-1-col">
<li>Clear all stress.</li>
<li>Reduce all marked harm by 1 level. <em>(Level 1 harm is cleared).</em></li>
<li>Mark 2 ticks on one of your tinker clocks.</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
],
},
TIMECARD: {
details: [
{
question: "Who relies on you?",
answer: "",
},
{
question: "How did the boss convince you to take the job?",
answer: "",
}
],
fixedInclinations: [
"Invoking rules or regulations.",
"Draw on skills from your mundane life.",
],
missionPlanningQuestions: [
"State something that, by Guild rule, the target cannot do to you this mission.",
"State something you refuse to do this mission.",
"Ask the boss: How can we get a bonus on this job?",
],
expTrigger: {
marked: false,
description: "You had to act above and beyond your usual duties.",
},
advancement: {
marked: false,
description: "Force them to fire you, and get your severance pay. Retire."
},
gear: [
{
marked: false,
description: "An Old Getaway Car",
},
{
marked: false,
description: "Civilian Clothing",
},
{
marked: false,
description: "Corporate Credit Card",
},
{
marked: false,
description: "Household Tool",
},
],
moves: [
{
marked: false,
name: "Icebreaker",
description: `When you crack a joke while talking to an adversary, you may take 1 stress. If you do, they will not retaliate with violence. <em>(They might retaliate in other ways).</em>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Time Theft",
description: `When you haven't spoken in a while, you can use a flashback to reveal you've left the group to do something else. Take 0 stress for this flashback, and explain what frivolous task you've been occupied with. Then, you may reappear at the time and place of your choosing.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Fail Upward",
description: `When you play a 6 and follow the cue, gain an additional cue: "Despite this, you succeed." <em>(Follow both cues).</em>`,
hasWriteIn: false,
writeIn: null,
},
{
marked: false,
name: "Payday Advance",
description: `You may treat any 2 you play as ♦.`,
hasWriteIn: false,
writeIn: null,
},
{
marked: true,
locked: true,
name: "Hobbyist",
description: `As a prep action, you can invite a fellow hench to join you in your hobby. Name the hobby, and how they're involved, then draw 2. Ignore the cue, and instead use the rank:
<ul class="hench-list-1-col">
<li>1-2 — You have a wonderful time and brighten each others' spirits. You each clear 2 stress.</li>
<li>3-4 — You have a pleasant, but ordinary, time. Clear 1 stress; they mark 1 experience.</li>
<li>5-6 — You completely lose track of time. Lose a remaining prep action, or take 1 harm "sleep deprived."</li>
</ul>`,
hasWriteIn: false,
writeIn: null,
},
],
},
};
export function lookupPlaybook(key) {
return playbooks[key];
}
export function validatePlaybookKey(key) {
return playbookKeys.includes(key);
}
export function getGear(playbookGear) {
const nullSafePlaybookGear = playbookGear ?? [];
return [
{
marked: false,
description: "Nonlethal Gun",
},
{
marked: false,
description: "A Small Weapon or Two",
},
{
marked: false,
description: "Spare Uniform",
},
{
marked: false,
description: "Lockpicks",
},
{
marked: false,
description: "The Boss's Calling Card",
},
...nullSafePlaybookGear
];
}
export function getExpTriggers(playbookTrigger) {
const baseTriggers = [
{
marked: false,
description: "You acted on your inclinations.",
},
{
marked: false,
description: "You made the boss proud.",
},
{
marked: false,
description: "You got on the boss's nerves.",
},
{
marked: false,
description: "Your home life interfered with the mission.",
},
];
if(playbookTrigger && playbookTrigger.description) {
return [...baseTriggers, playbookTrigger];
} else {
return baseTriggers;
}
}
export function getAdvancements(playbookAdvancement) {
const baseAdvancements = [
{
marked: false,
description: "Take another ability",
},
{
marked: false,
description: "Take another ability",
},
{
marked: false,
description: "Gear limit +1",
},
{
marked: false,
description: "Write in a 3rd inclination",
},
{
marked: false,
description: "Take an ability from another playbook.",
},
];
const exAdvancements = [
{
marked: false,
description: "Change playbooks.",
},
{
marked: false,
description: "Quit for good. Retire.",
},
];
const hasValidPlaybookAdvancement = playbookAdvancement && playbookAdvancement.description;
const finalExAdvancements = hasValidPlaybookAdvancement
? [...exAdvancements, playbookAdvancement]
: exAdvancements;
return {
base: baseAdvancements,
ex: finalExAdvancements
};
}
export function getPlaybookMutation(playbookKey, prefix = '') {
if(!validatePlaybookKey(playbookKey)) {
return getPlaybookMutation(nullPlaybookKey, prefix);
}
const playbookInfo = lookupPlaybook(playbookKey);
const advancements = getAdvancements(playbookInfo.advancement);
const gear = getGear(playbookInfo.gear);
const expTriggers = getExpTriggers(playbookInfo.expTrigger);
let mutation = {};
mutation[`${prefix}playbook`] = playbookKey;
mutation[`${prefix}details`] = playbookInfo.details;
mutation[`${prefix}fixedInclinations`] = playbookInfo.fixedInclinations;
mutation[`${prefix}missionPlanning`] = playbookInfo.missionPlanningQuestions;
mutation[`${prefix}fixedGear`] = gear;
mutation[`${prefix}experienceTriggers`] = expTriggers;
mutation[`${prefix}baseAdvancements`] = advancements.base;
mutation[`${prefix}exAdvancements`] = advancements.ex;
mutation[`${prefix}moves`] = playbookInfo.moves;
return mutation;
}