Display (only!) selection options to the user.
This commit is contained in:
parent
c94ff4a67a
commit
a19a996d1d
6 changed files with 88 additions and 28 deletions
|
@ -65,37 +65,37 @@ describe("DS4 Rolls with one die and slaying dice, followup throw.", () => {
|
||||||
|
|
||||||
describe("DS4 Rolls with one die and crit roll modifications.", () => {
|
describe("DS4 Rolls with one die and crit roll modifications.", () => {
|
||||||
it("Should do a crit success on `1`.", () => {
|
it("Should do a crit success on `1`.", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [1])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [1])).toEqual(
|
||||||
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]),
|
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should do a crit success on `maxCritSucc`.", () => {
|
it("Should do a crit success on `maxCritSucc`.", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [2])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [2])).toEqual(
|
||||||
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]),
|
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should do a success on lower edge case `3`.", () => {
|
it("Should do a success on lower edge case `3`.", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [3])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [3])).toEqual(
|
||||||
new RollResult(3, RollResultStatus.SUCCESS, [3]),
|
new RollResult(3, RollResultStatus.SUCCESS, [3]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should do a success on upper edge case `18`.", () => {
|
it("Should do a success on upper edge case `18`.", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [18])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [18])).toEqual(
|
||||||
new RollResult(0, RollResultStatus.FAILURE, [18]),
|
new RollResult(0, RollResultStatus.FAILURE, [18]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should do a crit fail on `minCritFail`.", () => {
|
it("Should do a crit fail on `minCritFailure`.", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [19])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [19])).toEqual(
|
||||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]),
|
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should do a crit fail on `20`", () => {
|
it("Should do a crit fail on `20`", () => {
|
||||||
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [20])).toEqual(
|
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [20])).toEqual(
|
||||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]),
|
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -171,37 +171,37 @@ describe("DS4 Rolls with multiple dice and no modifiers.", () => {
|
||||||
|
|
||||||
describe("DS4 Rolls with multiple dice and min/max modifiers.", () => {
|
describe("DS4 Rolls with multiple dice and min/max modifiers.", () => {
|
||||||
it("Should do a crit fail on `19` for first roll.", () => {
|
it("Should do a crit fail on `19` for first roll.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [19, 15, 6])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [19, 15, 6])).toEqual(
|
||||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should succeed with all rolls crit successes (1 and 2).", () => {
|
it("Should succeed with all rolls crit successes (1 and 2).", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [2, 1, 2])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [2, 1, 2])).toEqual(
|
||||||
new RollResult(48, RollResultStatus.CRITICAL_SUCCESS, [2, 1, 2]),
|
new RollResult(48, RollResultStatus.CRITICAL_SUCCESS, [2, 1, 2]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should succeed with the last roll not being sufficient.", () => {
|
it("Should succeed with the last roll not being sufficient.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 15, 15])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 15, 15])).toEqual(
|
||||||
new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]),
|
new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should succeed with the last roll a crit success `2`.", () => {
|
it("Should succeed with the last roll a crit success `2`.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 15, 2])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 15, 2])).toEqual(
|
||||||
new RollResult(38, RollResultStatus.SUCCESS, [15, 15, 2]),
|
new RollResult(38, RollResultStatus.SUCCESS, [15, 15, 2]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should succeed with the last roll being `20` and one crit success '2'.", () => {
|
it("Should succeed with the last roll being `20` and one crit success '2'.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 2, 20])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 2, 20])).toEqual(
|
||||||
new RollResult(43, RollResultStatus.SUCCESS, [15, 2, 20]),
|
new RollResult(43, RollResultStatus.SUCCESS, [15, 2, 20]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should succeed with the last roll being `19` and one crit success '2'.", () => {
|
it("Should succeed with the last roll being `19` and one crit success '2'.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 2, 19])).toEqual(
|
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 2, 19])).toEqual(
|
||||||
new RollResult(42, RollResultStatus.SUCCESS, [15, 2, 19]),
|
new RollResult(42, RollResultStatus.SUCCESS, [15, 2, 19]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -209,7 +209,7 @@ describe("DS4 Rolls with multiple dice and min/max modifiers.", () => {
|
||||||
|
|
||||||
describe("DS4 Rolls with multiple dice and fail modifiers.", () => {
|
describe("DS4 Rolls with multiple dice and fail modifiers.", () => {
|
||||||
it("Should do a crit fail on `19` for first roll.", () => {
|
it("Should do a crit fail on `19` for first roll.", () => {
|
||||||
expect(rollCheckMultipleDice(48, { minCritFail: 19 }, [19, 15, 6])).toEqual(
|
expect(rollCheckMultipleDice(48, { minCritFailure: 19 }, [19, 15, 6])).toEqual(
|
||||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,8 @@ class DefaultCheckOptions implements DS4CheckFactoryOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultCheckOptions = new DefaultCheckOptions();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Most basic class responsible for generating the chat formula and passing it to the chat as roll.
|
* Most basic class responsible for generating the chat formula and passing it to the chat as roll.
|
||||||
*/
|
*/
|
||||||
|
@ -53,8 +55,8 @@ class CheckFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
createCritTerm(): string {
|
createCritTerm(): string {
|
||||||
const minCritRequired = this.checkOptions.minCritFailure !== CheckFactory.defaultCheckOptions.minCritFailure;
|
const minCritRequired = this.checkOptions.minCritFailure !== defaultCheckOptions.minCritFailure;
|
||||||
const maxCritRequired = this.checkOptions.maxCritSuccess !== CheckFactory.defaultCheckOptions.maxCritSuccess;
|
const maxCritRequired = this.checkOptions.maxCritSuccess !== defaultCheckOptions.maxCritSuccess;
|
||||||
|
|
||||||
if (minCritRequired || maxCritRequired) {
|
if (minCritRequired || maxCritRequired) {
|
||||||
return "c" + (this.checkOptions.maxCritSuccess ?? "") + "," + (this.checkOptions.minCritFailure ?? "");
|
return "c" + (this.checkOptions.maxCritSuccess ?? "") + "," + (this.checkOptions.minCritFailure ?? "");
|
||||||
|
@ -66,8 +68,6 @@ class CheckFactory {
|
||||||
createSlayingDiceTerm(): string {
|
createSlayingDiceTerm(): string {
|
||||||
return this.checkOptions.useSlayingDice ? "x" : null;
|
return this.checkOptions.useSlayingDice ? "x" : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultCheckOptions = new DefaultCheckOptions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Figure out return of roll (void should be Ok, tough?)
|
// TODO: Figure out return of roll (void should be Ok, tough?)
|
||||||
|
@ -78,10 +78,10 @@ class CheckFactory {
|
||||||
*/
|
*/
|
||||||
export async function createCheckRoll(targetValue: number, options: Partial<DS4CheckFactoryOptions>): Promise<void> {
|
export async function createCheckRoll(targetValue: number, options: Partial<DS4CheckFactoryOptions>): Promise<void> {
|
||||||
// Ask for additional required data;
|
// Ask for additional required data;
|
||||||
const gmModifier = await askGmModifier();
|
const gmModifierData = await askGmModifier(targetValue, options);
|
||||||
|
|
||||||
// Create Factory
|
// Create Factory
|
||||||
const cf = new CheckFactory(targetValue, gmModifier, options);
|
const cf = new CheckFactory(targetValue, gmModifierData.gmModifier, options);
|
||||||
|
|
||||||
// Possibly additional processing
|
// Possibly additional processing
|
||||||
|
|
||||||
|
@ -92,11 +92,53 @@ export async function createCheckRoll(targetValue: number, options: Partial<DS4C
|
||||||
/**
|
/**
|
||||||
* Responsible for rendering the modal interface asking for the modifier specified by GM.
|
* 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.
|
||||||
|
*
|
||||||
* @returns {Promise<number>} The number by the user.
|
* @returns {Promise<number>} The number by the user.
|
||||||
*/
|
*/
|
||||||
async function askGmModifier(): Promise<number> {
|
async function askGmModifier(
|
||||||
|
targetValue: number,
|
||||||
|
options: Partial<DS4CheckFactoryOptions>,
|
||||||
|
{ template, title }: { template?: string; title?: string } = {},
|
||||||
|
): Promise<GmModifierData> {
|
||||||
// Render model interface and return value
|
// Render model interface and return value
|
||||||
return 0;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DS4CheckFactoryOptions {
|
export interface DS4CheckFactoryOptions {
|
||||||
|
@ -106,4 +148,6 @@ export interface DS4CheckFactoryOptions {
|
||||||
rollMode: DS4RollMode;
|
rollMode: DS4RollMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DS4RollMode = "roll" | "gmroll" | "blindroll" | "selfroll";
|
const rollModes = ["roll", "gmroll", "blindroll", "selfroll"] as const;
|
||||||
|
type DS4RollModeTuple = typeof rollModes;
|
||||||
|
export type DS4RollMode = DS4RollModeTuple[number];
|
||||||
|
|
|
@ -86,7 +86,7 @@ export class DS4Check extends DiceTerm {
|
||||||
} else {
|
} else {
|
||||||
return ds4roll(targetValueToUse, {
|
return ds4roll(targetValueToUse, {
|
||||||
maxCritSuccess: this.maxCritSuccess,
|
maxCritSuccess: this.maxCritSuccess,
|
||||||
minCritFail: this.minCritFailure,
|
minCritFailure: this.minCritFailure,
|
||||||
slayingDiceRepetition: slayingDiceRepetition,
|
slayingDiceRepetition: slayingDiceRepetition,
|
||||||
useSlayingDice: slayingDiceRepetition,
|
useSlayingDice: slayingDiceRepetition,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
export interface RollOptions {
|
export interface RollOptions {
|
||||||
maxCritSuccess: number;
|
maxCritSuccess: number;
|
||||||
minCritFail: number;
|
minCritFailure: number;
|
||||||
useSlayingDice: boolean;
|
useSlayingDice: boolean;
|
||||||
slayingDiceRepetition: boolean;
|
slayingDiceRepetition: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultRollOptions implements RollOptions {
|
export class DefaultRollOptions implements RollOptions {
|
||||||
public maxCritSuccess = 1;
|
public maxCritSuccess = 1;
|
||||||
public minCritFail = 20;
|
public minCritFailure = 20;
|
||||||
public useSlayingDice = false;
|
public useSlayingDice = false;
|
||||||
public slayingDiceRepetition = false;
|
public slayingDiceRepetition = false;
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ export function rollCheckSingleDie(
|
||||||
|
|
||||||
if (rolledDie <= usedOptions.maxCritSuccess) {
|
if (rolledDie <= usedOptions.maxCritSuccess) {
|
||||||
return new RollResult(checkTargetValue, RollResultStatus.CRITICAL_SUCCESS, usedDice, true);
|
return new RollResult(checkTargetValue, RollResultStatus.CRITICAL_SUCCESS, usedDice, true);
|
||||||
} else if (rolledDie >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) {
|
} else if (rolledDie >= usedOptions.minCritFailure && !isSlayingDiceRepetition(usedOptions)) {
|
||||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
|
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
|
||||||
} else {
|
} else {
|
||||||
if (rolledDie <= checkTargetValue) {
|
if (rolledDie <= checkTargetValue) {
|
||||||
|
@ -90,7 +90,7 @@ export function rollCheckMultipleDice(
|
||||||
const slayingDiceRepetition = isSlayingDiceRepetition(usedOptions);
|
const slayingDiceRepetition = isSlayingDiceRepetition(usedOptions);
|
||||||
|
|
||||||
// Slaying Dice require a different handling.
|
// Slaying Dice require a different handling.
|
||||||
if (firstResult >= usedOptions.minCritFail && !slayingDiceRepetition) {
|
if (firstResult >= usedOptions.minCritFailure && !slayingDiceRepetition) {
|
||||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
|
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/templates/roll/roll-options.hbs
Normal file
16
src/templates/roll/roll-options.hbs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<form class="{{cssClass}} grid">
|
||||||
|
<label for="ctv">Check Target Value</label>
|
||||||
|
<input id="ctv" type="number" name="ctv" value="{{checkTargetValue}}" />
|
||||||
|
<label for="gmmod">Game Master Modifier</label>
|
||||||
|
<input id="gmmod" type="number" name="gmmod" value="0" />
|
||||||
|
<label for="maxcoup">Coup to</label>
|
||||||
|
<input id="maxcoup" type="number" name="maxcoup" value="{{maxCritSuccess}}" />
|
||||||
|
<label for="minfumble">Fumble from</label>
|
||||||
|
<input id="minfumble" type="number" name="minfumble" value="{{minCritFailure}}" />
|
||||||
|
<label for="visibility">Visibility</label>
|
||||||
|
<select id="visibility" data-type="String">
|
||||||
|
{{#each rollModes as |rollMode|}}
|
||||||
|
<option value="{{rollMode}}">{{rollMode}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</form>
|
Loading…
Reference in a new issue