ds4/src/module/rolls/check-factory.ts

154 lines
4.8 KiB
TypeScript
Raw Normal View History

// TODO: Rename to something sane.
2021-01-09 23:21:57 +01:00
/**
* Provides default values for all arguments the `CheckFactory` expects.
*/
class DefaultCheckOptions implements DS4CheckFactoryOptions {
maxCritSuccess = 1;
minCritFailure = 20;
useSlayingDice = false;
2021-01-09 23:21:57 +01:00
rollMode: DS4RollMode = "roll";
2021-01-09 23:21:57 +01:00
mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions {
return { ...this, ...other } as DS4CheckFactoryOptions;
}
}
const defaultCheckOptions = new DefaultCheckOptions();
2021-01-09 23:21:57 +01:00
/**
* Most basic class responsible for generating the chat formula and passing it to the chat as roll.
*/
class CheckFactory {
constructor(
private checkTargetValue: number,
private gmModifier: number,
2021-01-09 23:21:57 +01:00
passedOptions: Partial<DS4CheckFactoryOptions> = {},
) {
this.checkOptions = new DefaultCheckOptions().mergeWith(passedOptions);
}
2021-01-09 23:21:57 +01:00
private checkOptions: DS4CheckFactoryOptions;
async execute(): Promise<void> {
const rollCls: typeof Roll = CONFIG.Dice.rolls[0];
const formula = [
"ds",
this.createTargetValueTerm(),
this.createCritTerm(),
this.createSlayingDiceTerm(),
].filterJoin("");
const roll = new rollCls(formula);
const rollModeTemplate = this.checkOptions.rollMode;
return roll.toMessage({}, { rollMode: rollModeTemplate, create: true });
}
// Term generators
createTargetValueTerm(): string {
if (this.checkTargetValue != null) {
return "v" + this.checkTargetValue;
} else {
return null;
}
}
createCritTerm(): string {
const minCritRequired = this.checkOptions.minCritFailure !== defaultCheckOptions.minCritFailure;
const maxCritRequired = this.checkOptions.maxCritSuccess !== defaultCheckOptions.maxCritSuccess;
if (minCritRequired || maxCritRequired) {
return "c" + (this.checkOptions.maxCritSuccess ?? "") + "," + (this.checkOptions.minCritFailure ?? "");
} else {
return null;
}
}
createSlayingDiceTerm(): string {
return this.checkOptions.useSlayingDice ? "x" : null;
}
}
// TODO: Figure out return of roll (void should be Ok, tough?)
2021-01-09 23:21:57 +01:00
/**
* Asks the user for all unknown/necessary information and passes them on to perform a roll.
* @param targetValue {number} The Check Target Number ("CTN")
* @param options {Partial<DS4CheckFactoryOptions>} Options changing the behaviour of the roll and message.
*/
export async function createCheckRoll(targetValue: number, options: Partial<DS4CheckFactoryOptions>): Promise<void> {
// Ask for additional required data;
const gmModifierData = await askGmModifier(targetValue, options);
// Create Factory
const cf = new CheckFactory(targetValue, gmModifierData.gmModifier, options);
// Possibly additional processing
// Execute roll
await cf.execute();
}
2021-01-09 23:21:57 +01:00
/**
* Responsible for rendering the modal interface asking for the modifier specified by GM.
*
* @notes
* At the moment, this asks for more data than it will do after some iterations.
*
2021-01-09 23:21:57 +01:00
* @returns {Promise<number>} The number by the user.
*/
async function askGmModifier(
targetValue: number,
options: Partial<DS4CheckFactoryOptions>,
{ template, title }: { template?: string; title?: string } = {},
): Promise<GmModifierData> {
// Render model interface and return value
const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
const templateData = {
cssClass: "roll-option",
title: title ?? "Roll Options",
checkTargetValue: targetValue,
maxCritSuccess: options.maxCritSuccess ?? defaultCheckOptions.maxCritSuccess,
minCritFailure: options.minCritFailure ?? defaultCheckOptions.minCritFailure,
rollModes: rollModes,
};
const renderedHtml = await renderTemplate(usedTemplate, templateData);
// TODO: Localize
const dialogData: DialogData = {
title: title ?? "Roll Options",
close: (html) => null,
content: renderedHtml,
buttons: {
ok: {
label: "OK",
callback: (html) => null,
},
cancel: {
label: "Cancel",
callback: (html) => null,
},
},
default: "ok",
};
const dialog = new Dialog(dialogData, {}).render(true);
return { gmModifier: 0 };
}
interface GmModifierData {
gmModifier: number;
}
2021-01-09 23:21:57 +01:00
export interface DS4CheckFactoryOptions {
maxCritSuccess: number;
minCritFailure: number;
useSlayingDice: boolean;
2021-01-09 23:21:57 +01:00
rollMode: DS4RollMode;
}
const rollModes = ["roll", "gmroll", "blindroll", "selfroll"] as const;
type DS4RollModeTuple = typeof rollModes;
export type DS4RollMode = DS4RollModeTuple[number];