Merge branch '043-rollBuilder' into 'master'
Resolve "Roll Info Interfaces" Closes #43 See merge request dungeonslayers/ds4!43
This commit is contained in:
commit
bbf9c35a6b
11 changed files with 318 additions and 27 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -159,9 +159,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/socket.io-client": {
|
"@types/socket.io-client": {
|
||||||
"version": "1.4.34",
|
"version": "1.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.35.tgz",
|
||||||
"integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==",
|
"integrity": "sha512-MI8YmxFS+jMkIziycT5ickBWK1sZwDwy16mgH/j99Mcom6zRG/NimNGQ3vJV0uX5G6g/hEw0FG3w3b3sT5OUGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/tinymce": {
|
"@types/tinymce": {
|
||||||
|
@ -2717,7 +2717,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"foundry-pc-types": {
|
"foundry-pc-types": {
|
||||||
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f84074f63d1aeeb9229e441e8c3ccaa9cba64142",
|
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#5fcca4e4327b558d5eeeb962f05470c994a394be",
|
||||||
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
|
@ -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]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -157,5 +157,18 @@
|
||||||
"DS4.UnitKilometers": "Kilometer",
|
"DS4.UnitKilometers": "Kilometer",
|
||||||
"DS4.UnitKilometersAbbr": "km",
|
"DS4.UnitKilometersAbbr": "km",
|
||||||
"DS4.UnitCustom": "individuell",
|
"DS4.UnitCustom": "individuell",
|
||||||
"DS4.UnitCustomAbbr": " "
|
"DS4.UnitCustomAbbr": " ",
|
||||||
|
"DS4.RollDialogDefaultTitle": "Proben-Optionen",
|
||||||
|
"DS4.RollDialogOkButton": "Ok",
|
||||||
|
"DS4.RollDialogCancelButton": "Abbrechen",
|
||||||
|
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde {exType}, tatsächlich erhalten wurde {realType}",
|
||||||
|
"DS4.RollDialogTargetLabel": "Probenwert",
|
||||||
|
"DS4.RollDialogModifierLabel": "SL-Modifikator",
|
||||||
|
"DS4.RollDialogCoupLabel": "Immersieg bis",
|
||||||
|
"DS4.RollDialogFumbleLabel": "Patzer ab",
|
||||||
|
"DS4.RollDialogVisibilityLabel": "Sichtbarkeit",
|
||||||
|
"DS4.ChatVisibilityRoll": "Alle",
|
||||||
|
"DS4.ChatVisibilityGmRoll": "Selbst & SL",
|
||||||
|
"DS4.ChatVisibilityBlindRoll": "Nur SL",
|
||||||
|
"DS4.ChatVisibilitySelfRoll": "Nur selbst"
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,5 +157,18 @@
|
||||||
"DS4.UnitKilometers": "Kilometers",
|
"DS4.UnitKilometers": "Kilometers",
|
||||||
"DS4.UnitKilometersAbbr": "km",
|
"DS4.UnitKilometersAbbr": "km",
|
||||||
"DS4.UnitCustom": "Custom Unit",
|
"DS4.UnitCustom": "Custom Unit",
|
||||||
"DS4.UnitCustomAbbr": " "
|
"DS4.UnitCustomAbbr": " ",
|
||||||
|
"DS4.RollDialogDefaultTitle": "Roll Options",
|
||||||
|
"DS4.RollDialogOkButton": "Ok",
|
||||||
|
"DS4.RollDialogCancelButton": "Cancel",
|
||||||
|
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected {exType}, got {realType}",
|
||||||
|
"DS4.RollDialogTargetLabel": "Check Target Number",
|
||||||
|
"DS4.RollDialogModifierLabel": "Game Master Modifier",
|
||||||
|
"DS4.RollDialogCoupLabel": "Coup to",
|
||||||
|
"DS4.RollDialogFumbleLabel": "Fumble from",
|
||||||
|
"DS4.RollDialogVisibilityLabel": "Visibility",
|
||||||
|
"DS4.ChatVisibilityRoll": "All",
|
||||||
|
"DS4.ChatVisibilityGmRoll": "Self & GM",
|
||||||
|
"DS4.ChatVisibilityBlindRoll": "GM only",
|
||||||
|
"DS4.ChatVisibilitySelfRoll": "Self only"
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ export const DS4 = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the profile info types for hanndlebars of a character
|
* Define the profile info types for handlebars of a character
|
||||||
*/
|
*/
|
||||||
profileDTypes: {
|
profileDTypes: {
|
||||||
gender: "String",
|
gender: "String",
|
||||||
|
@ -253,4 +253,14 @@ export const DS4 = {
|
||||||
days: "DS4.UnitDaysAbbr",
|
days: "DS4.UnitDaysAbbr",
|
||||||
custom: "DS4.UnitCustomAbbr",
|
custom: "DS4.UnitCustomAbbr",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define localization strings for Chat Visibility
|
||||||
|
*/
|
||||||
|
chatVisibilities: {
|
||||||
|
roll: "DS4.ChatVisibilityRoll",
|
||||||
|
gmroll: "DS4.ChatVisibilityGmRoll",
|
||||||
|
blindroll: "DS4.ChatVisibilityBlindRoll",
|
||||||
|
selfroll: "DS4.ChatVisibilitySelfRoll",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { DS4Item } from "./item/item";
|
||||||
import { DS4ItemSheet } from "./item/item-sheet";
|
import { DS4ItemSheet } from "./item/item-sheet";
|
||||||
import { DS4 } from "./config";
|
import { DS4 } from "./config";
|
||||||
import { DS4Check } from "./rolls/check";
|
import { DS4Check } from "./rolls/check";
|
||||||
|
import { createCheckRoll } from "./rolls/check-factory";
|
||||||
|
|
||||||
Hooks.once("init", async function () {
|
Hooks.once("init", async function () {
|
||||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||||
|
@ -13,6 +14,7 @@ Hooks.once("init", async function () {
|
||||||
DS4Actor,
|
DS4Actor,
|
||||||
DS4Item,
|
DS4Item,
|
||||||
DS4,
|
DS4,
|
||||||
|
createCheckRoll,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record configuration
|
// Record configuration
|
||||||
|
@ -90,6 +92,7 @@ Hooks.once("setup", function () {
|
||||||
"temporalUnitsAbbr",
|
"temporalUnitsAbbr",
|
||||||
"distanceUnits",
|
"distanceUnits",
|
||||||
"distanceUnitsAbbr",
|
"distanceUnitsAbbr",
|
||||||
|
"chatVisibilities",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Exclude some from sorting where the default order matters
|
// Exclude some from sorting where the default order matters
|
||||||
|
|
237
src/module/rolls/check-factory.ts
Normal file
237
src/module/rolls/check-factory.ts
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
import { DS4 } from "../config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides default values for all arguments the `CheckFactory` expects.
|
||||||
|
*/
|
||||||
|
class DefaultCheckOptions implements DS4CheckFactoryOptions {
|
||||||
|
maxCritSuccess = 1;
|
||||||
|
minCritFailure = 20;
|
||||||
|
useSlayingDice = false;
|
||||||
|
rollMode: DS4RollMode = "roll";
|
||||||
|
|
||||||
|
mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions {
|
||||||
|
return { ...this, ...other } as DS4CheckFactoryOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton reference for default value extraction.
|
||||||
|
*/
|
||||||
|
const defaultCheckOptions = new DefaultCheckOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
passedOptions: Partial<DS4CheckFactoryOptions> = {},
|
||||||
|
) {
|
||||||
|
this.checkOptions = new DefaultCheckOptions().mergeWith(passedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkOptions: DS4CheckFactoryOptions;
|
||||||
|
|
||||||
|
async execute(): Promise<ChatMessage | any> {
|
||||||
|
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;
|
||||||
|
console.log(rollModeTemplate);
|
||||||
|
return roll.toMessage({}, { rollMode: rollModeTemplate, create: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Term generators
|
||||||
|
createTargetValueTerm(): string | null {
|
||||||
|
if (this.checkTargetValue !== null) {
|
||||||
|
return "v" + (this.checkTargetValue + this.gmModifier);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createCritTerm(): string | null {
|
||||||
|
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 | null {
|
||||||
|
return this.checkOptions.useSlayingDice ? "x" : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<ChatMessage | any> {
|
||||||
|
// Ask for additional required data;
|
||||||
|
const gmModifierData = await askGmModifier(targetValue, options);
|
||||||
|
|
||||||
|
const newOptions: Partial<DS4CheckFactoryOptions> = {
|
||||||
|
maxCritSuccess: gmModifierData.maxCritSuccess ?? options.maxCritSuccess ?? undefined,
|
||||||
|
minCritFailure: gmModifierData.minCritFailure ?? options.minCritFailure ?? undefined,
|
||||||
|
useSlayingDice: gmModifierData.useSlayingDice ?? options.useSlayingDice ?? undefined,
|
||||||
|
rollMode: gmModifierData.rollMode ?? options.rollMode ?? undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Factory
|
||||||
|
const cf = new CheckFactory(gmModifierData.checkTargetValue, gmModifierData.gmModifier, newOptions);
|
||||||
|
|
||||||
|
// Possibly additional processing
|
||||||
|
|
||||||
|
// Execute roll
|
||||||
|
await cf.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for rendering the modal interface asking for the modifier specified by GM and (currently) additional data.
|
||||||
|
*
|
||||||
|
* @notes
|
||||||
|
* At the moment, this asks for more data than it will do after some iterations.
|
||||||
|
*
|
||||||
|
* @returns {Promise<IntermediateGmModifierData>} The data given by the user.
|
||||||
|
*/
|
||||||
|
async function askGmModifier(
|
||||||
|
targetValue: number,
|
||||||
|
options: Partial<DS4CheckFactoryOptions> = {},
|
||||||
|
{ template, title }: { template?: string; title?: string } = {},
|
||||||
|
): Promise<IntermediateGmModifierData> {
|
||||||
|
// Render model interface and return value
|
||||||
|
const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
|
||||||
|
const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle");
|
||||||
|
const templateData = {
|
||||||
|
cssClass: "roll-option",
|
||||||
|
title: usedTitle,
|
||||||
|
checkTargetValue: targetValue,
|
||||||
|
maxCritSuccess: options.maxCritSuccess ?? defaultCheckOptions.maxCritSuccess,
|
||||||
|
minCritFailure: options.minCritFailure ?? defaultCheckOptions.minCritFailure,
|
||||||
|
rollModes: rollModes,
|
||||||
|
config: DS4,
|
||||||
|
};
|
||||||
|
const renderedHtml = await renderTemplate(usedTemplate, templateData);
|
||||||
|
|
||||||
|
const dialogPromise = new Promise<HTMLFormElement>((resolve) => {
|
||||||
|
new Dialog(
|
||||||
|
{
|
||||||
|
title: usedTitle,
|
||||||
|
close: () => {
|
||||||
|
// Don't do anything
|
||||||
|
},
|
||||||
|
content: renderedHtml,
|
||||||
|
buttons: {
|
||||||
|
ok: {
|
||||||
|
label: game.i18n.localize("DS4.RollDialogOkButton"),
|
||||||
|
callback: (html: HTMLElement | JQuery) => {
|
||||||
|
if (!("jquery" in html)) {
|
||||||
|
throw new Error(
|
||||||
|
game.i18n.format("DS4.ErrorUnexpectedHtmlType", {
|
||||||
|
exType: "JQuery",
|
||||||
|
realType: "HTMLElement",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const innerForm = html[0].querySelector("form");
|
||||||
|
resolve(innerForm);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
label: game.i18n.localize("DS4.RollDialogCancelButton"),
|
||||||
|
callback: () => {
|
||||||
|
// Don't do anything
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: "ok",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
).render(true);
|
||||||
|
});
|
||||||
|
const dialogForm = await dialogPromise;
|
||||||
|
return parseDialogFormData(dialogForm, targetValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts Dialog data from the returned DOM element.
|
||||||
|
* @param formData {HTMLFormElement} The filed dialog
|
||||||
|
* @param targetValue {number} The previously known target value (slated for removal once data automation is available)
|
||||||
|
*/
|
||||||
|
function parseDialogFormData(formData: HTMLFormElement, targetValue: number): IntermediateGmModifierData {
|
||||||
|
return {
|
||||||
|
checkTargetValue: parseInt(formData["ctv"]?.value) ?? targetValue,
|
||||||
|
gmModifier: parseInt(formData["gmmod"]?.value) ?? 0,
|
||||||
|
maxCritSuccess: parseInt(formData["maxcoup"]?.value) ?? defaultCheckOptions.maxCritSuccess,
|
||||||
|
minCritFailure: parseInt(formData["minfumble"]?.value) ?? defaultCheckOptions.minCritFailure,
|
||||||
|
useSlayingDice: false,
|
||||||
|
rollMode: formData["visibility"]?.value ?? defaultCheckOptions.rollMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains data that needs retrieval from an interactive Dialog.
|
||||||
|
*/
|
||||||
|
interface GmModifierData {
|
||||||
|
gmModifier: number;
|
||||||
|
rollMode: DS4RollMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains *CURRENTLY* necessary Data for drafting a roll.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* Quite a lot of this information is requested due to a lack of automation:
|
||||||
|
* - maxCritSuccess
|
||||||
|
* - minCritFailure
|
||||||
|
* - useSlayingDice
|
||||||
|
* - checkTargetValue
|
||||||
|
*
|
||||||
|
* They will and should be removed once effects and data retrieval is in place.
|
||||||
|
* If a "raw" roll dialog is necessary, create another pre-porcessing Dialog
|
||||||
|
* class asking for the required information.
|
||||||
|
* This interface should then be replaced with the `GmModifierData`.
|
||||||
|
*/
|
||||||
|
interface IntermediateGmModifierData extends GmModifierData {
|
||||||
|
checkTargetValue: number;
|
||||||
|
gmModifier: number;
|
||||||
|
maxCritSuccess: number;
|
||||||
|
minCritFailure: number;
|
||||||
|
// TODO: In final version from system settings
|
||||||
|
useSlayingDice: boolean;
|
||||||
|
rollMode: DS4RollMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum behavioural options that need to be passed to the factory.
|
||||||
|
*/
|
||||||
|
export interface DS4CheckFactoryOptions {
|
||||||
|
maxCritSuccess: number;
|
||||||
|
minCritFailure: number;
|
||||||
|
useSlayingDice: boolean;
|
||||||
|
rollMode: DS4RollMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines all possible roll modes, both for iterating and typing.
|
||||||
|
*/
|
||||||
|
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,
|
||||||
});
|
});
|
||||||
|
@ -132,7 +132,6 @@ export class DS4Check extends DiceTerm {
|
||||||
static readonly DEFAULT_TARGET_VALUE = 10;
|
static readonly DEFAULT_TARGET_VALUE = 10;
|
||||||
static readonly DEFAULT_MAX_CRIT_SUCCESS = 1;
|
static readonly DEFAULT_MAX_CRIT_SUCCESS = 1;
|
||||||
static readonly DEFAULT_MIN_CRIT_FAILURE = 20;
|
static readonly DEFAULT_MIN_CRIT_FAILURE = 20;
|
||||||
// TODO: add to Type declarations
|
|
||||||
static DENOMINATION = "s";
|
static DENOMINATION = "s";
|
||||||
static MODIFIERS = {
|
static MODIFIERS = {
|
||||||
x: "explode",
|
x: "explode",
|
||||||
|
|
|
@ -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">{{localize "DS4.RollDialogTargetLabel"}}</label>
|
||||||
|
<input id="ctv" data-type="Number" type="number" name="ctv" value="{{checkTargetValue}}" />
|
||||||
|
<label for="gmmod">{{localize "DS4.RollDialogModifierLabel"}}</label>
|
||||||
|
<input id="gmmod" data-type="Number" type="number" name="gmmod" value="0" />
|
||||||
|
<label for="maxcoup">{{localize "DS4.RollDialogCoupLabel"}}</label>
|
||||||
|
<input id="maxcoup" data-type="Number" type="number" name="maxcoup" value="{{maxCritSuccess}}" />
|
||||||
|
<label for="minfumble">{{localize "DS4.RollDialogFumbleLabel"}}</label>
|
||||||
|
<input id="minfumble" data-type="Number" type="number" name="minfumble" value="{{minCritFailure}}" />
|
||||||
|
<label for="visibility">{{localize "DS4.RollDialogVisibilityLabel"}}</label>
|
||||||
|
<select id="visibility" data-type="String">
|
||||||
|
{{#each rollModes as |rollMode|}}
|
||||||
|
<option value="{{rollMode}}">{{lookup ../config.chatVisibilities rollMode}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</form>
|
Loading…
Reference in a new issue