Implement a custom DS4 "dice" class.
Includes exploding and crit dice modifier, but not tested yet, as some required types are still missing.
This commit is contained in:
parent
9feaa5216d
commit
c885d2d405
5 changed files with 112 additions and 6 deletions
|
@ -4,6 +4,7 @@ import { DS4ActorSheet } from "./actor/actor-sheet";
|
|||
import { DS4Item } from "./item/item";
|
||||
import { DS4ItemSheet } from "./item/item-sheet";
|
||||
import { DS4 } from "./config";
|
||||
import { DS4Roll } from "./rolls/ds4roll";
|
||||
|
||||
Hooks.once("init", async function () {
|
||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||
|
@ -21,6 +22,14 @@ Hooks.once("init", async function () {
|
|||
CONFIG.Actor.entityClass = DS4Actor as typeof Actor;
|
||||
CONFIG.Item.entityClass = DS4Item as typeof Item;
|
||||
|
||||
// Configure Dice
|
||||
CONFIG.Dice.types = [Die, DS4Roll];
|
||||
CONFIG.Dice.terms = {
|
||||
c: Coin,
|
||||
d: Die,
|
||||
s: DS4Roll,
|
||||
};
|
||||
|
||||
// Register sheet application classes
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("ds4", DS4ActorSheet, { makeDefault: true });
|
||||
|
|
95
src/module/rolls/ds4roll.ts
Normal file
95
src/module/rolls/ds4roll.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { RollResult, RollResultStatus } from "./roll-data";
|
||||
import { ds4roll } from "./roll-executor";
|
||||
|
||||
export class DS4Roll extends DiceTerm {
|
||||
constructor({ faces = 20, modifiers = [], options = {} } = {}) {
|
||||
super({ number: 1, faces: faces, modifiers: modifiers, options: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param param0
|
||||
*/
|
||||
roll({ minimize = false, maximize = false } = {}): RollResult {
|
||||
return this.rollWithDifferentBorders(1, 20, { minimize, maximize });
|
||||
}
|
||||
|
||||
rollWithDifferentBorders(
|
||||
maxCritSuccess: number,
|
||||
minCritFail: number,
|
||||
{ minimize = false, maximize = false } = {},
|
||||
): RollResult {
|
||||
if (minimize) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20], true);
|
||||
} else if (maximize) {
|
||||
return new RollResult(
|
||||
this.faces,
|
||||
RollResultStatus.CRITICAL_SUCCESS,
|
||||
Array(Math.ceil(this.faces / 20)).fill(1),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
return ds4roll(this.faces, { maxCritSucc: maxCritSuccess, minCritFail: minCritFail });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get values(): Array<number> {
|
||||
return (this.results as Array<RollResult>)
|
||||
.filter((r) => r.active)
|
||||
.map((r) => r.dice)
|
||||
.reduce((acc: Array<number>, cur: Array<number>) => {
|
||||
return acc.concat(cur);
|
||||
}, []);
|
||||
}
|
||||
|
||||
/** Term Modifiers */
|
||||
crits(modifier: string): this {
|
||||
const rgx: RegExp = /[c([0-9]+)?,([0-9]+)?]/;
|
||||
const match = modifier.match(rgx);
|
||||
if (!match) return this;
|
||||
const [parseCritSucc, parsedCritFail] = match.slice(1);
|
||||
|
||||
const maxCritSuccess = parseCritSucc ? parseInt(parseCritSucc) : 1;
|
||||
const minCritFail = parsedCritFail ? parseInt(parsedCritFail) : 20;
|
||||
|
||||
const newResults: Array<RollResult> = (this.results as Array<RollResult>).map((r) => {
|
||||
return ds4roll(this.faces, { minCritFail: minCritFail, maxCritSucc: maxCritSuccess }, r.dice);
|
||||
});
|
||||
|
||||
this.results = newResults;
|
||||
}
|
||||
|
||||
// DS4 only allows recursive explosions
|
||||
explode(modifier: string): this {
|
||||
// There should only ever be a single dice in the results-array at this point!
|
||||
if (this.results.length != 1) {
|
||||
// TODO: Add 'expression' to types!
|
||||
// console.error(`Skipped explode for term ${this.expression}`);
|
||||
return this;
|
||||
}
|
||||
|
||||
const rgx = /[xX]([0-9]+)?/;
|
||||
const match = modifier.match(rgx);
|
||||
if (!match) return this;
|
||||
const [parsedCritSucc] = match.slice(1);
|
||||
|
||||
const maxCritSucc = parsedCritSucc ? parseInt(parsedCritSucc) : 1;
|
||||
|
||||
let checked = 0;
|
||||
while (checked < this.results.length) {
|
||||
const r = (this.results as Array<RollResult>)[checked];
|
||||
checked++;
|
||||
if (!r.active) continue;
|
||||
|
||||
if (r.dice[0] <= maxCritSucc) {
|
||||
r.exploded = true;
|
||||
this.rollWithDifferentBorders(maxCritSucc, 21);
|
||||
}
|
||||
|
||||
if (checked > 1000) throw new Error("Maximum recursion depth for explodign dice roll exceeded");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ export class RollResult {
|
|||
public status: RollResultStatus,
|
||||
public dice: Array<number>,
|
||||
public active: boolean = true,
|
||||
public exploded: boolean = false,
|
||||
) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,15 +48,15 @@ export function rollCheckSingleDie(
|
|||
dice = [new DS4RollProvider().getNextRoll()];
|
||||
}
|
||||
const usedDice = dice;
|
||||
const roll = usedDice[0];
|
||||
const rolledDie = usedDice[0];
|
||||
|
||||
if (roll <= usedOptions.maxCritSucc) {
|
||||
if (rolledDie <= usedOptions.maxCritSucc) {
|
||||
return new RollResult(checkTargetValue, RollResultStatus.CRITICAL_SUCCESS, usedDice, true);
|
||||
} else if (roll >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) {
|
||||
} else if (rolledDie >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
|
||||
} else {
|
||||
if (roll <= checkTargetValue) {
|
||||
return new RollResult(roll, RollResultStatus.SUCCESS, usedDice, true);
|
||||
if (rolledDie <= checkTargetValue) {
|
||||
return new RollResult(rolledDie, RollResultStatus.SUCCESS, usedDice, true);
|
||||
} else {
|
||||
return new RollResult(0, RollResultStatus.FAILURE, usedDice, true);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
*/
|
||||
export class DS4RollProvider implements RollProvider {
|
||||
getNextRoll(): number {
|
||||
return new Roll("1d20").roll().total;
|
||||
const rand = CONFIG.Dice.randomUniform();
|
||||
return Math.ceil(rand * 20);
|
||||
}
|
||||
|
||||
getNextRolls(amount: number): Array<number> {
|
||||
|
|
Loading…
Reference in a new issue