Add slying dice modifier to DicePool
This commit is contained in:
parent
eeb1aa61f4
commit
7f973e7de8
3 changed files with 53 additions and 19 deletions
|
@ -1,15 +1,16 @@
|
||||||
import { DS4Actor } from "./actor/actor";
|
import { DS4Actor } from "./actor/actor";
|
||||||
import { DS4Item } from "./item/item";
|
|
||||||
import { DS4ItemSheet } from "./item/item-sheet";
|
|
||||||
import { DS4 } from "./config";
|
|
||||||
import { DS4Check } from "./rolls/check";
|
|
||||||
import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet";
|
import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet";
|
||||||
import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
|
import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
|
||||||
import { createCheckRoll } from "./rolls/check-factory";
|
import { DS4 } from "./config";
|
||||||
import { registerSystemSettings } from "./settings";
|
|
||||||
import { migration } from "./migrations";
|
|
||||||
import registerHandlebarsHelpers from "./handlebars/handlebars-helpers";
|
import registerHandlebarsHelpers from "./handlebars/handlebars-helpers";
|
||||||
import registerHandlebarsPartials from "./handlebars/handlebars-partials";
|
import registerHandlebarsPartials from "./handlebars/handlebars-partials";
|
||||||
|
import { DS4Item } from "./item/item";
|
||||||
|
import { DS4ItemSheet } from "./item/item-sheet";
|
||||||
|
import { migration } from "./migrations";
|
||||||
|
import { DS4Check } from "./rolls/check";
|
||||||
|
import { createCheckRoll } from "./rolls/check-factory";
|
||||||
|
import registerSlayingDiceModifier from "./rolls/slaying-dice-modifier";
|
||||||
|
import { registerSystemSettings } from "./settings";
|
||||||
|
|
||||||
Hooks.once("init", async () => {
|
Hooks.once("init", async () => {
|
||||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||||
|
@ -33,6 +34,8 @@ Hooks.once("init", async () => {
|
||||||
CONFIG.Dice.types.push(DS4Check);
|
CONFIG.Dice.types.push(DS4Check);
|
||||||
CONFIG.Dice.terms.s = DS4Check;
|
CONFIG.Dice.terms.s = DS4Check;
|
||||||
|
|
||||||
|
registerSlayingDiceModifier();
|
||||||
|
|
||||||
registerSystemSettings();
|
registerSystemSettings();
|
||||||
|
|
||||||
Actors.unregisterSheet("core", ActorSheet);
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
|
|
@ -1,28 +1,22 @@
|
||||||
import evaluateCheck, { getRequiredNumberOfDice } from "./check-evaluation";
|
import evaluateCheck, { getRequiredNumberOfDice } from "./check-evaluation";
|
||||||
|
|
||||||
interface DS4CheckTermData extends DiceTerm.TermData {
|
|
||||||
canFumble: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements DS4 Checks as an emulated "dice throw".
|
* Implements DS4 Checks as an emulated "dice throw".
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* - Roll a check against a Check Target Number (CTN) of 18: `/r dsv18`
|
* - Roll a check against a Check Target Number (CTN) of 18: `/r dsv18`
|
||||||
* - Roll a check with multiple dice against a CTN of 34: `/r dsv34`
|
* - Roll a check with multiple dice against a CTN of 34: `/r dsv34`
|
||||||
* - Roll a check with a racial ability that makes `2` a coup and `19` a fumble: `/r dsv19c2,19`
|
* - Roll a check with a racial ability that makes `2` a coup and `19` a fumble: `/r dsv19c2:19`
|
||||||
* - Roll a check with a racial ability that makes `5` a coup and default fumble: `/r dsv19c5`
|
* - Roll a check with a racial ability that makes `5` a coup and default fumble: `/r dsv19c5`
|
||||||
*/
|
*/
|
||||||
export class DS4Check extends DiceTerm {
|
export class DS4Check extends DiceTerm {
|
||||||
constructor({ modifiers = [], options = {}, canFumble = true }: Partial<DS4CheckTermData> = {}) {
|
constructor({ modifiers = [], options }: Partial<DiceTerm.TermData> = {}) {
|
||||||
super({
|
super({
|
||||||
faces: 20,
|
faces: 20,
|
||||||
modifiers: modifiers,
|
modifiers: modifiers,
|
||||||
options: options,
|
options: options,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canFumble = canFumble;
|
|
||||||
|
|
||||||
// Parse and store check target number
|
// Parse and store check target number
|
||||||
const checkTargetNumberModifier = this.modifiers.filter((m) => m[0] === "v")[0];
|
const checkTargetNumberModifier = this.modifiers.filter((m) => m[0] === "v")[0];
|
||||||
const ctnRgx = new RegExp("v([0-9]+)?");
|
const ctnRgx = new RegExp("v([0-9]+)?");
|
||||||
|
@ -38,10 +32,11 @@ export class DS4Check extends DiceTerm {
|
||||||
|
|
||||||
// Parse and store maximumCoupResult and minimumFumbleResult
|
// Parse and store maximumCoupResult and minimumFumbleResult
|
||||||
const coupFumbleModifier = this.modifiers.filter((m) => m[0] === "c")[0];
|
const coupFumbleModifier = this.modifiers.filter((m) => m[0] === "c")[0];
|
||||||
const cfmRgx = new RegExp("c([0-9]+)?,([0-9]+)?");
|
const cfmRgx = new RegExp("c([0-9]+)?(:([0-9]+))?");
|
||||||
const cfmMatch = coupFumbleModifier?.match(cfmRgx);
|
const cfmMatch = coupFumbleModifier?.match(cfmRgx);
|
||||||
if (cfmMatch) {
|
if (cfmMatch) {
|
||||||
const [parseMaximumCoupResult, parseMinimumFumbleResult] = cfmMatch.slice(1);
|
const parseMaximumCoupResult = cfmMatch[1];
|
||||||
|
const parseMinimumFumbleResult = cfmMatch[3];
|
||||||
this.maximumCoupResult = parseMaximumCoupResult
|
this.maximumCoupResult = parseMaximumCoupResult
|
||||||
? parseInt(parseMaximumCoupResult)
|
? parseInt(parseMaximumCoupResult)
|
||||||
: DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
: DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
||||||
|
@ -51,11 +46,17 @@ export class DS4Check extends DiceTerm {
|
||||||
if (this.minimumFumbleResult <= this.maximumCoupResult)
|
if (this.minimumFumbleResult <= this.maximumCoupResult)
|
||||||
throw new SyntaxError(game.i18n.localize("DS4.ErrorDiceCritOverlap"));
|
throw new SyntaxError(game.i18n.localize("DS4.ErrorDiceCritOverlap"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse and store no fumble
|
||||||
|
const noFumbleModifier = this.modifiers.filter((m) => m[0] === "n")[0];
|
||||||
|
if (noFumbleModifier) {
|
||||||
|
this.canFumble = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coup: boolean | null = null;
|
coup: boolean | null = null;
|
||||||
fumble: boolean | null = null;
|
fumble: boolean | null = null;
|
||||||
canFumble: boolean;
|
canFumble = true;
|
||||||
checkTargetNumber = DS4Check.DEFAULT_CHECK_TARGET_NUMBER;
|
checkTargetNumber = DS4Check.DEFAULT_CHECK_TARGET_NUMBER;
|
||||||
minimumFumbleResult = DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
|
minimumFumbleResult = DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
|
||||||
maximumCoupResult = DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
maximumCoupResult = DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
||||||
|
@ -107,7 +108,7 @@ export class DS4Check extends DiceTerm {
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
static fromResults(options: Partial<DS4CheckTermData>, results: DiceTerm.Result[]): DS4Check {
|
static fromResults(options: Partial<DiceTerm.TermData>, results: DiceTerm.Result[]): DS4Check {
|
||||||
const term = new this(options);
|
const term = new this(options);
|
||||||
term.results = results;
|
term.results = results;
|
||||||
term.evaluateResults();
|
term.evaluateResults();
|
||||||
|
@ -122,5 +123,6 @@ export class DS4Check extends DiceTerm {
|
||||||
static MODIFIERS = {
|
static MODIFIERS = {
|
||||||
c: (): void => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult
|
c: (): void => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult
|
||||||
v: (): void => undefined, // Modifier is consumed in constructor for checkTargetNumber
|
v: (): void => undefined, // Modifier is consumed in constructor for checkTargetNumber
|
||||||
|
n: (): void => undefined, // Modifier is consumed in constructor for canFumble
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
29
src/module/rolls/slaying-dice-modifier.ts
Normal file
29
src/module/rolls/slaying-dice-modifier.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { DS4Check } from "./check";
|
||||||
|
|
||||||
|
export default function registerSlayingDiceModifier(): void {
|
||||||
|
// TODO(types): Adjust types to allow extension of DiceTerm.MODIFIERS
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
DicePool.MODIFIERS.x = slay;
|
||||||
|
DicePool.POOL_REGEX = /^{([^}]+)}([A-z]([A-z0-9<=>]+)?)?$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
function slay(this: DicePool, modifier: string): void {
|
||||||
|
const rgx = /[xX]/;
|
||||||
|
const match = modifier.match(rgx);
|
||||||
|
if (!match || !this.rolls) return;
|
||||||
|
|
||||||
|
let checked = 0;
|
||||||
|
while (checked < (this.dice.length ?? 0)) {
|
||||||
|
const diceTerm = this.dice[checked];
|
||||||
|
checked++;
|
||||||
|
if (diceTerm instanceof DS4Check && diceTerm.coup) {
|
||||||
|
const formula = `dsv${diceTerm.checkTargetNumber}c${diceTerm.maximumCoupResult}:${diceTerm.minimumFumbleResult}n`;
|
||||||
|
const additionalRoll = Roll.create(formula).evaluate();
|
||||||
|
|
||||||
|
this.rolls.push(additionalRoll);
|
||||||
|
this.results.push({ result: additionalRoll.total ?? 0, active: true });
|
||||||
|
}
|
||||||
|
if (checked > 1000) throw new Error(game.i18n.localize("DS4.ErrorExplodingRecursionLimitExceeded"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue