Initial block-out: Hench data model + playbooks

This commit is contained in:
walcutt 2024-11-27 19:39:42 -05:00
parent 753052aac3
commit ac59cc57df
8 changed files with 388 additions and 1 deletions

View File

@ -1 +1,11 @@
# hench
# HENCH
An implementation of *HENCH (2025)* by Chloe Herd (myself) and Cassandra Grove.
## Current Release Version:
N/A (still in development).
## Next Target Release Version:
Draft 0
Discord Link: N/A

15
hench.mjs Normal file
View File

@ -0,0 +1,15 @@
import { HenchDataModel } from "./module/data-models.mjs";
import { HenchDebugSheet } from "./module/sheets/hench-debug.mjs";
Hooks.once("init", () => {
CONFIG.Actor.dataModels = {
hench: HenchDataModel,
};
Actors.unregisterSheet('core', ActorSheet);
Actors.registerSheet('henchDebug', HenchDebugSheet, {
makeDefault: true,
label: 'Hench Debug Sheet',
});
});

105
module/data-models.mjs Normal file
View File

@ -0,0 +1,105 @@
const { HTMLField, SchemaField, NumberField, StringField, BooleanField, FilePathField, ArrayField } = foundry.data.fields;
import { nullPlaybookKey, playbookKeys, lookupPlaybook } from './playbooks.mjs';
const textField = () => new StringField({ required: true, blank: true });
const promptField = () => new SchemaField({
question: textField(),
answer: textField()
});
const harmField = () => new SchemaField({
marked: new BooleanField({ required: true }),
description: textField(),
});
export class HenchDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
name: textField(),
look: textField(),
detailAnswers: new SchemaField({
one: textField(),
two: textField(),
}),
customInclination: textField(),
harm: new SchemaField({
levelOne: new SchemaField({
one: harmField(),
two: harmField(),
}),
levelTwo: new SchemaField({
one: harmField(),
two: harmField(),
}),
levelThree: new SchemaField({
one: harmField(),
}),
levelFour: new SchemaField({
one: harmField(),
}),
}),
stress: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 12 }),
experience: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 5 }),
playbook: new StringField({ required: true, blank: false, initial: nullPlaybookKey, options: playbookKeys }),
};
}
static migrateData(source) {
// No migrations yet - base case.
return super.migrateData(source);
}
get dead() {
return !!this.harm.levelFour.marked;
}
get playbookDetails() {
return lookupPlaybook(this.playbook);
}
// TODO IMPLEMENT advancements
get gearLimit() {
return 3;
}
get hasPlaybookSelected() {
return this.playbook !== nullPlaybookKey;
}
get detailQuestions() {
return this.playbookDetails?.detailQuestions;
}
get inclinations() {
const base = this.playbookDetails?.inclinations ?? {};
return {
...base,
custom: this.customInclination
};
}
get missionPlanningQuestions() {
return this.playbookDetails?.missionPlanningQuestions;
}
get expTriggers() {
const fromPlaybook = this.playbookDetails?.expTrigger;
let triggers = {
one: "You acted on your inclinations.",
two: "You made the boss proud.",
three: "Your home life interfered with the mission."
};
if(fromPlaybook) {
triggers.four = fromPlaybook;
}
return triggers;
}
}

132
module/playbooks.mjs Normal file
View File

@ -0,0 +1,132 @@
export const nullPlaybookKey = "NONE";
export const playbookKeys = [
nullPlaybookKey,
"SUPERFAN",
"BADASS",
"LABMAN",
"DEMOTED",
"OUTCAST",
"USURPER",
"TIMECARD",
];
const playbooks = {
NONE: null,
SUPERFAN: {
detailQuestions: {
one: "What's the coolest thing you've ever done?",
two: "What's your favorite story about the boss? ",
},
inclinations: {
one: "Drawing on your knowledge of comic lore",
two: "Acting with naive optimism",
},
missionPlanningQuestions: {
one: "You've read an issue about the target. Choose a power or a weakness of the target, and explain it.",
two: "Confidently state a poorly thought out plan to achieve the goal.",
three: "Ask the boss: is the target really as cool as it's made out to be?",
},
expTrigger: "You faced the grim realities of the job.",
},
BADASS: {
detailQuestions: {
one: "Have you killed on the job? If so, what was your first? ",
two: "How did the boss earn your respect?",
},
inclinations: {
one: "Applying brute force",
two: "Making a threat",
},
missionPlanningQuestions: {
one: "You've fought the target before. State where they left a scar, and how they did it.",
two: "The target is an old friend. State how you met, and how it ended.",
three: "Ask the boss: can we kill the target?",
},
expTrigger: "Your strength couldn't solve the problem at hand.",
},
LABMAM: {
detailQuestions: {
one: "What was your first invention? ",
two: "What secret project does the boss fund? Why is it doomed? ",
},
inclinations: {
one: "Using your intellect",
two: "Doing something unexpected",
},
missionPlanningQuestions: {
one: "Present your new prototype weapon, which the target is probably weak to.",
two: "State a fact about the target's defense systems.",
three: "Ask the boss: what secret research is the target hiding?",
},
expTrigger: "Your invention failed fantastically.",
},
DEMOTED: {
detailQuestions: {
one: "What was your old villain name? Why are you just a hench, now?",
two: "What leverage does the boss have on you?",
},
inclinations: {
one: "Acting with excessive flair",
two: "Attempting to reclaim your status",
},
missionPlanningQuestions: {
one: "The target's clearly a rookie. State an obvious flaw with their approach or gimmick.",
two: "You've bested the target before. State how, and why you can't just do that again.",
three: "Ask the boss: why hasn't the Guild taken out the target?",
},
expTrigger: "Your old tricks weren't cut out for the job.",
},
OUTCAST: {
detailQuestions: {
one: "Why has society rejected you? ",
two: "Why has the boss taken you in? ",
},
inclinations: {
one: "Attempting to impress someone",
two: "Acting against all social norms",
},
missionPlanningQuestions: {
one: "The target has treated you cruelly. State when, and how.",
two: "You and the target have something in common. State what, and how that makes you feel.",
three: "Ask the boss: Why do you hate the target?",
},
expTrigger: "An attempt at connection was rejected.",
},
USURPER: {
detailQuestions: {
one: "What is your ultimate ambition? ",
two: "What habit of the boss infuriates you the most? ",
},
inclinations: {
one: "Acting on hidden agendas.",
two: "Putting yourself before anyone else.",
},
missionPlanningQuestions: {
one: "Describe how the target is uniquely equipped to defeat the boss.",
two: "Describe how you are uniquely equipped to defeat the target.",
three: "Ask the boss: are you really prepared for this mission?",
},
expTrigger: "You had to act selflessly.",
},
TIMECARD: {
detailQuestions: {
one: "Who relies on you? ",
two: "How did the boss convince you to take the job?",
},
inclinations: {
one: "Invoking rules or regulations.",
two: "Taking the laziest approach.",
},
missionPlanningQuestions: {
one: "State something that, by Guild rule, the target cannot do to you this mission.",
two: "State something you refuse to do this mission.",
three: "Ask the boss: How can we get a bonus on this job?",
},
expTrigger: "You had to act above and beyond your usual duties.",
},
};
export function lookupPlaybook(key) {
return playbooks[key];
}

View File

@ -0,0 +1,11 @@
export class HenchDebugSheet extends ActorSheet {
/** @override */
get template() {
return `systems/hench/templates/hench-debug.hbs`;
}
/** @override */
getData() {
return super.getData();
}
}

33
system.json Normal file
View File

@ -0,0 +1,33 @@
{
"id": "hench",
"title": "HENCH",
"description": "HENCH (2025)",
"version": "0.0.0",
"compatibility": {
"minimum": "11",
"verified": "11"
},
"authors": [
{
"id": "walcutt",
"name": "walcutt",
"discord": "walcutt"
}
],
"esmodules": [
"hench.mjs"
],
"styles": [],
"packs": [],
"languages": [],
"documentTypes": {
"Actor": {
"hench": {},
"boss": {}
},
"Item": {
"move": {}
}
},
"url": "https://github.com/walcutt/hench"
}

71
templates/hench-debug.hbs Normal file
View File

@ -0,0 +1,71 @@
<form>
<h1> {{ actor.name }} </h1>
<h2> Explicit Details: </h2>
<div id="hench-explicit-details">
<p>Name: {{ actor.system.name }}</p>
<p>Look: {{ actor.system.look }}</p>
<h3> Detail Answers: </h3>
<div>
<p>Answer 1: {{ actor.system.detailAnswers.one }}</p>
<p>Answer 2: {{ actor.system.detailAnswers.two }}</p>
</div>
<p>Custom Inclination: {{ actor.system.customInclination }}</p>
<h3> Harm: </h3>
<div>
<h4>Level 1:</h4>
<div>
<p>1 {{#if actor.system.harm.levelOne.one.marked }} X {{ else }} O {{/if}}: {{actor.system.harm.levelOne.one.description }}</p>
<p>1 {{#if actor.system.harm.levelOne.two.marked }} X {{ else }} O {{/if}}: {{actor.system.harm.levelOne.two.description }}</p>
</div>
<h4>Level 2:</h4>
<div>
<p>1 {{#if actor.system.harm.levelTwo.one.marked }} X {{ else }} O {{/if}}: {{ actor.system.harm.levelTwo.one.description }}</p>
<p>1 {{#if actor.system.harm.levelTwo.two.marked }} X {{ else }} O {{/if}}: {{ actor.system.harm.levelTwo.two.description }}</p>
</div>
<h4>Level 3:</h4>
<div>
<p>1 {{#if actor.system.harm.levelThree.one.marked }} X {{ else }} O {{/if}}: {{actor.system.harm.levelThree.one.description }}</p>
</div>
<h4>Level 3:</h4>
<div>
<p>1 {{#if actor.system.harm.levelFour.one.marked }} X {{ else }} O {{/if}}: {{actor.system.harm.levelFour.one.description }}</p>
</div>
</div>
<p>Stress: {{ actor.system.stress }}</p>
<p>Experience: {{ actor.system.experience }}</p>
<p>Playbook: {{ actor.system.playbook }}</p>
</div>
<h2>Implicit Details:</h2>
<div>
<p>Is Dead: {{ actor.system.dead }}</p>
<p>Gear Limit: {{ actor.system.gearLimit }}</p>
<p>Has playbook selected: {{ actor.system.hasPlaybookSelected }}</p>
<h3> Detail Questions: </h3>
<div>
<p>Question 1: {{ actor.system.detailQuestions.one }}</p>
<p>Question 2: {{ actor.system.detailQuestions.two }}</p>
</div>
<h3> Inclinations: </h3>
<div>
<p>Inclination 1: {{ actor.system.inclinations.one }}</p>
<p>Inclination 2: {{ actor.system.inclinations.two }}</p>
<p>Inclination 3: {{ actor.system.inclinations.custom }}</p>
</div>
<h3>Mission Planning</h3>
<div>
<p>Question 1: {{ actor.system.missionPlanningQuestions.one }}</p>
<p>Question 2: {{ actor.system.missionPlanningQuestions.two }}</p>
<p>Question 3: {{ actor.system.missionPlanningQuestions.three }}</p>
</div>
<h3>Experience Triggers:</h3>
<div>
<p>Trigger 1: {{ actor.system.expTriggers.one }}</p>
<p>Trigger 2: {{ actor.system.expTriggers.two }}</p>
<p>Trigger 3: {{ actor.system.expTriggers.three }}</p>
<p>Trigger 4: {{ actor.system.expTriggers.four }}</p>
</div>
</div>
</form>

10
todo.md Normal file
View File

@ -0,0 +1,10 @@
- [] Define Move Model
- [] Define Hench Model
- [] Define Boss Model
- [x] Define Prompt Model (DEFUNCT)
- [] Define Storyline Model
- [] ? Define Table Model
- [] Design Hench sheet
- [] Design Boss sheet
- [] Implement Hench sheet
- [] Implement Boss sheet