Improve CheckFactory

This commit is contained in:
Johannes Loher 2021-03-18 08:52:02 +01:00
parent a2014c53d4
commit d5b872e74b
5 changed files with 63 additions and 47 deletions

View file

@ -66,6 +66,8 @@
"DS4.ItemTypeAlphabetPlural": "Schriftzeichen", "DS4.ItemTypeAlphabetPlural": "Schriftzeichen",
"DS4.ItemTypeSpecialCreatureAbility": "Besondere Kreaturenfähigkeit", "DS4.ItemTypeSpecialCreatureAbility": "Besondere Kreaturenfähigkeit",
"DS4.ItemTypeSpecialCreatureAbilityPlural": "Besondere Kreaturenfähigkeiten", "DS4.ItemTypeSpecialCreatureAbilityPlural": "Besondere Kreaturenfähigkeiten",
"DS4.ItemWeaponCheckFlavor": "{actor} greift mit {item} an.",
"DS4.ItemSpellCheckFlavor": "{actor} wirkt {item}.",
"DS4.ArmorType": "Panzerungstyp", "DS4.ArmorType": "Panzerungstyp",
"DS4.ArmorTypeAbbr": "PAT", "DS4.ArmorTypeAbbr": "PAT",
"DS4.ArmorMaterialType": "Materialtyp", "DS4.ArmorMaterialType": "Materialtyp",
@ -231,5 +233,7 @@
"DS4.RollDialogRollModeLabel": "Sichtbarkeit", "DS4.RollDialogRollModeLabel": "Sichtbarkeit",
"DS4.TooltipBaseValue": "Basiswert", "DS4.TooltipBaseValue": "Basiswert",
"DS4.TooltipModifier": "Modifikator", "DS4.TooltipModifier": "Modifikator",
"DS4.TooltipEffects": "Effekte" "DS4.TooltipEffects": "Effekte",
"DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slayende Würfel",
"DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben."
} }

View file

@ -66,6 +66,8 @@
"DS4.ItemTypeAlphabetPlural": "Alphabets", "DS4.ItemTypeAlphabetPlural": "Alphabets",
"DS4.ItemTypeSpecialCreatureAbility": "Special Creature Ability", "DS4.ItemTypeSpecialCreatureAbility": "Special Creature Ability",
"DS4.ItemTypeSpecialCreatureAbilityPlural": "Special Creature Abilities", "DS4.ItemTypeSpecialCreatureAbilityPlural": "Special Creature Abilities",
"DS4.ItemWeaponCheckFlavor": "{actor} attacks with {item}.",
"DS4.ItemSpellCheckFlavor": "{actor} casts {item}.",
"DS4.ArmorType": "Armor Type", "DS4.ArmorType": "Armor Type",
"DS4.ArmorTypeAbbr": "AT", "DS4.ArmorTypeAbbr": "AT",
"DS4.ArmorMaterialType": "Material Type", "DS4.ArmorMaterialType": "Material Type",
@ -231,5 +233,7 @@
"DS4.RollDialogRollModeLabel": "Visibility", "DS4.RollDialogRollModeLabel": "Visibility",
"DS4.TooltipBaseValue": "Base Value", "DS4.TooltipBaseValue": "Base Value",
"DS4.TooltipModifier": "Modifier", "DS4.TooltipModifier": "Modifier",
"DS4.TooltipEffects": "Effects" "DS4.TooltipEffects": "Effects",
"DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slaying Dice",
"DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks."
} }

View file

@ -48,7 +48,7 @@ export class DS4Item extends Item<DS4ItemData> {
} }
/** /**
* Roll a check for a action with this item. * Roll a check for an action with this item.
*/ */
async roll(): Promise<void> { async roll(): Promise<void> {
if (!this.isOwnedItem()) { if (!this.isOwnedItem()) {
@ -87,14 +87,17 @@ export class DS4Item extends Item<DS4ItemData> {
); );
} }
const ownerDataData = ((this.actor as unknown) as DS4Actor).data.data; // TODO(types): Improve so that the concrete Actor type is known here const actor = (this.actor as unknown) as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here
const ownerDataData = actor.data.data;
const weaponBonus = this.data.data.weaponBonus; const weaponBonus = this.data.data.weaponBonus;
const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType);
const checkTargetValue = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus; const checkTargetNumber = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus;
await createCheckRoll(checkTargetValue, {
await createCheckRoll(checkTargetNumber, {
rollMode: game.settings.get("core", "rollMode"), rollMode: game.settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, item: this.name }),
}); });
} }
@ -120,7 +123,8 @@ export class DS4Item extends Item<DS4ItemData> {
); );
} }
const ownerDataData = ((this.actor as unknown) as DS4Actor).data.data; // TODO(types): Improve so that the concrete Actor type is known here const actor = (this.actor as unknown) as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here
const ownerDataData = actor.data.data;
const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined; const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined;
if (spellBonus === undefined) { if (spellBonus === undefined) {
notifications.info( notifications.info(
@ -131,12 +135,13 @@ export class DS4Item extends Item<DS4ItemData> {
); );
} }
const spellType = this.data.data.spellType; const spellType = this.data.data.spellType;
const checkTargetValue = (ownerDataData.combatValues[spellType].total as number) + (spellBonus ?? 0); const checkTargetNumber = (ownerDataData.combatValues[spellType].total as number) + (spellBonus ?? 0);
await createCheckRoll(checkTargetValue, { await createCheckRoll(checkTargetNumber, {
rollMode: game.settings.get("core", "rollMode"), rollMode: game.settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, item: this.name }),
}); });
} }

View file

@ -6,6 +6,7 @@ class DefaultCheckOptions implements DS4CheckFactoryOptions {
readonly minimumFumbleResult = 20; readonly minimumFumbleResult = 20;
readonly useSlayingDice = false; readonly useSlayingDice = false;
readonly rollMode: Const.DiceRollMode = "roll"; readonly rollMode: Const.DiceRollMode = "roll";
readonly flavor: undefined;
mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions { mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions {
return { ...this, ...other }; return { ...this, ...other };
@ -22,41 +23,37 @@ const defaultCheckOptions = new DefaultCheckOptions();
*/ */
class CheckFactory { class CheckFactory {
constructor( constructor(
private checkTargetValue: number, private checkTargetNumber: number,
private gmModifier: number, private gmModifier: number,
passedOptions: Partial<DS4CheckFactoryOptions> = {}, options: Partial<DS4CheckFactoryOptions> = {},
) { ) {
this.checkOptions = defaultCheckOptions.mergeWith(passedOptions); this.options = defaultCheckOptions.mergeWith(options);
} }
private checkOptions: DS4CheckFactoryOptions; private options: DS4CheckFactoryOptions;
async execute(): Promise<ChatMessage | unknown> { async execute(): Promise<ChatMessage> {
const innerFormula = ["ds", this.createTargetValueTerm(), this.createCritTerm()].filterJoin(""); const innerFormula = ["ds", this.createTargetNumberModifier(), this.createCoupFumbleModifier()].filterJoin("");
const formula = this.checkOptions.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula;
const roll = Roll.create(formula); const roll = Roll.create(formula);
const rollModeTemplate = this.checkOptions.rollMode; return roll.toMessage(
return roll.toMessage({}, { rollMode: rollModeTemplate, create: true }); { speaker: ChatMessage.getSpeaker(), flavor: this.options.flavor },
{ rollMode: this.options.rollMode, create: true },
);
} }
// Term generators createTargetNumberModifier(): string | null {
createTargetValueTerm(): string | null { return "v" + (this.checkTargetNumber + this.gmModifier);
if (this.checkTargetValue !== null) {
return "v" + (this.checkTargetValue + this.gmModifier);
} else {
return null;
}
} }
createCritTerm(): string | null { createCoupFumbleModifier(): string | null {
const minCritRequired = this.checkOptions.minimumFumbleResult !== defaultCheckOptions.minimumFumbleResult; const isMinimumFumbleResultRequired =
const maxCritRequired = this.checkOptions.maximumCoupResult !== defaultCheckOptions.maximumCoupResult; this.options.minimumFumbleResult !== defaultCheckOptions.minimumFumbleResult;
const isMaximumCoupResultRequired = this.options.maximumCoupResult !== defaultCheckOptions.maximumCoupResult;
if (minCritRequired || maxCritRequired) { if (isMinimumFumbleResultRequired || isMaximumCoupResultRequired) {
return ( return "c" + (this.options.maximumCoupResult ?? "") + ":" + (this.options.minimumFumbleResult ?? "");
"c" + (this.checkOptions.maximumCoupResult ?? "") + ":" + (this.checkOptions.minimumFumbleResult ?? "")
);
} else { } else {
return null; return null;
} }
@ -65,23 +62,24 @@ class CheckFactory {
/** /**
* Asks the user for all unknown/necessary information and passes them on to perform a roll. * Asks the user for all unknown/necessary information and passes them on to perform a roll.
* @param targetValue - The Check Target Number ("CTN") * @param checkTargetNumber - The Check Target Number ("CTN")
* @param options - Options changing the behavior of the roll and message. * @param options - Options changing the behavior of the roll and message.
*/ */
export async function createCheckRoll( export async function createCheckRoll(
targetValue: number, checkTargetNumber: number,
options: Partial<DS4CheckFactoryOptions> = {}, options: Partial<DS4CheckFactoryOptions> = {},
): Promise<ChatMessage | unknown> { ): Promise<ChatMessage | unknown> {
// Ask for additional required data; // Ask for additional required data;
const gmModifierData = await askGmModifier(targetValue, options); const gmModifierData = await askGmModifier(checkTargetNumber, options);
const newTargetValue = gmModifierData.checkTargetNumber ?? targetValue; const newTargetValue = gmModifierData.checkTargetNumber ?? checkTargetNumber;
const gmModifier = gmModifierData.gmModifier ?? 0; const gmModifier = gmModifierData.gmModifier ?? 0;
const newOptions: Partial<DS4CheckFactoryOptions> = { const newOptions: Partial<DS4CheckFactoryOptions> = {
maximumCoupResult: gmModifierData.maximumCoupResult ?? options.maximumCoupResult ?? undefined, maximumCoupResult: gmModifierData.maximumCoupResult ?? options.maximumCoupResult,
minimumFumbleResult: gmModifierData.minimumFumbleResult ?? options.minimumFumbleResult ?? undefined, minimumFumbleResult: gmModifierData.minimumFumbleResult ?? options.minimumFumbleResult,
useSlayingDice: gmModifierData.useSlayingDice ?? options.useSlayingDice ?? undefined, useSlayingDice: game.settings.get("ds4", "useSlayingDiceForAutomatedChecks") ?? false,
rollMode: gmModifierData.rollMode ?? options.rollMode ?? undefined, rollMode: gmModifierData.rollMode ?? options.rollMode,
flavor: options.flavor,
}; };
// Create Factory // Create Factory
@ -106,7 +104,6 @@ async function askGmModifier(
options: Partial<DS4CheckFactoryOptions> = {}, options: Partial<DS4CheckFactoryOptions> = {},
{ template, title }: { template?: string; title?: string } = {}, { template, title }: { template?: string; title?: string } = {},
): Promise<Partial<IntermediateGmModifierData>> { ): Promise<Partial<IntermediateGmModifierData>> {
// Render model interface and return value
const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs"; const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle"); const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle");
const templateData = { const templateData = {
@ -114,7 +111,7 @@ async function askGmModifier(
checkTargetNumber: checkTargetNumber, checkTargetNumber: checkTargetNumber,
maximumCoupResult: options.maximumCoupResult ?? defaultCheckOptions.maximumCoupResult, maximumCoupResult: options.maximumCoupResult ?? defaultCheckOptions.maximumCoupResult,
minimumFumbleResult: options.minimumFumbleResult ?? defaultCheckOptions.minimumFumbleResult, minimumFumbleResult: options.minimumFumbleResult ?? defaultCheckOptions.minimumFumbleResult,
rollMode: options.rollMode, rollMode: options.rollMode ?? game.settings.get("core", "rollMode"),
rollModes: CONFIG.Dice.rollModes, rollModes: CONFIG.Dice.rollModes,
}; };
const renderedHtml = await renderTemplate(usedTemplate, templateData); const renderedHtml = await renderTemplate(usedTemplate, templateData);
@ -200,12 +197,8 @@ interface GmModifierData {
*/ */
interface IntermediateGmModifierData extends GmModifierData { interface IntermediateGmModifierData extends GmModifierData {
checkTargetNumber: number; checkTargetNumber: number;
gmModifier: number;
maximumCoupResult: number; maximumCoupResult: number;
minimumFumbleResult: number; minimumFumbleResult: number;
// TODO: In final version from system settings
useSlayingDice: boolean;
rollMode: Const.DiceRollMode;
} }
/** /**
@ -216,4 +209,5 @@ export interface DS4CheckFactoryOptions {
minimumFumbleResult: number; minimumFumbleResult: number;
useSlayingDice: boolean; useSlayingDice: boolean;
rollMode: Const.DiceRollMode; rollMode: Const.DiceRollMode;
flavor?: string;
} }

View file

@ -9,4 +9,13 @@ export function registerSystemSettings(): void {
type: Number, type: Number,
default: -1, default: -1,
}); });
game.settings.register("ds4", "useSlayingDiceForAutomatedChecks", {
name: "DS4.SettingUseSlayingDiceForAutomatedChecksName",
hint: "DS4.SettingUseSlayingDiceForAutomatedChecksHint",
scope: "world",
config: true,
type: Boolean,
default: false,
});
} }