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 { 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 { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
|
||||
import { createCheckRoll } from "./rolls/check-factory";
|
||||
import { registerSystemSettings } from "./settings";
|
||||
import { migration } from "./migrations";
|
||||
import { DS4 } from "./config";
|
||||
import registerHandlebarsHelpers from "./handlebars/handlebars-helpers";
|
||||
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 () => {
|
||||
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.terms.s = DS4Check;
|
||||
|
||||
registerSlayingDiceModifier();
|
||||
|
||||
registerSystemSettings();
|
||||
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
import evaluateCheck, { getRequiredNumberOfDice } from "./check-evaluation";
|
||||
|
||||
interface DS4CheckTermData extends DiceTerm.TermData {
|
||||
canFumble: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements DS4 Checks as an emulated "dice throw".
|
||||
*
|
||||
* @example
|
||||
* - 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 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`
|
||||
*/
|
||||
export class DS4Check extends DiceTerm {
|
||||
constructor({ modifiers = [], options = {}, canFumble = true }: Partial<DS4CheckTermData> = {}) {
|
||||
constructor({ modifiers = [], options }: Partial<DiceTerm.TermData> = {}) {
|
||||
super({
|
||||
faces: 20,
|
||||
modifiers: modifiers,
|
||||
options: options,
|
||||
});
|
||||
|
||||
this.canFumble = canFumble;
|
||||
|
||||
// Parse and store check target number
|
||||
const checkTargetNumberModifier = this.modifiers.filter((m) => m[0] === "v")[0];
|
||||
const ctnRgx = new RegExp("v([0-9]+)?");
|
||||
|
@ -38,10 +32,11 @@ export class DS4Check extends DiceTerm {
|
|||
|
||||
// Parse and store maximumCoupResult and minimumFumbleResult
|
||||
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);
|
||||
if (cfmMatch) {
|
||||
const [parseMaximumCoupResult, parseMinimumFumbleResult] = cfmMatch.slice(1);
|
||||
const parseMaximumCoupResult = cfmMatch[1];
|
||||
const parseMinimumFumbleResult = cfmMatch[3];
|
||||
this.maximumCoupResult = parseMaximumCoupResult
|
||||
? parseInt(parseMaximumCoupResult)
|
||||
: DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
||||
|
@ -51,11 +46,17 @@ export class DS4Check extends DiceTerm {
|
|||
if (this.minimumFumbleResult <= this.maximumCoupResult)
|
||||
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;
|
||||
fumble: boolean | null = null;
|
||||
canFumble: boolean;
|
||||
canFumble = true;
|
||||
checkTargetNumber = DS4Check.DEFAULT_CHECK_TARGET_NUMBER;
|
||||
minimumFumbleResult = DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
|
||||
maximumCoupResult = DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
|
||||
|
@ -107,7 +108,7 @@ export class DS4Check extends DiceTerm {
|
|||
/**
|
||||
* @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);
|
||||
term.results = results;
|
||||
term.evaluateResults();
|
||||
|
@ -122,5 +123,6 @@ export class DS4Check extends DiceTerm {
|
|||
static MODIFIERS = {
|
||||
c: (): void => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult
|
||||
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