diff --git a/src/lang/de.json b/src/lang/de.json index 7b7ecad3..e19dbadd 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -66,6 +66,8 @@ "DS4.ItemTypeAlphabetPlural": "Schriftzeichen", "DS4.ItemTypeSpecialCreatureAbility": "Besondere Kreaturenfähigkeit", "DS4.ItemTypeSpecialCreatureAbilityPlural": "Besondere Kreaturenfähigkeiten", + "DS4.ItemWeaponCheckFlavor": "{actor} greift mit {weapon} an.", + "DS4.ItemSpellCheckFlavor": "{actor} wirkt {spell}.", "DS4.ArmorType": "Panzerungstyp", "DS4.ArmorTypeAbbr": "PAT", "DS4.ArmorMaterialType": "Materialtyp", @@ -231,5 +233,7 @@ "DS4.RollDialogRollModeLabel": "Sichtbarkeit", "DS4.TooltipBaseValue": "Basiswert", "DS4.TooltipModifier": "Modifikator", - "DS4.TooltipEffects": "Effekte" + "DS4.TooltipEffects": "Effekte", + "DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slayende Würfel", + "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben." } diff --git a/src/lang/en.json b/src/lang/en.json index c1718b7c..a3ae4f71 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -66,6 +66,8 @@ "DS4.ItemTypeAlphabetPlural": "Alphabets", "DS4.ItemTypeSpecialCreatureAbility": "Special Creature Ability", "DS4.ItemTypeSpecialCreatureAbilityPlural": "Special Creature Abilities", + "DS4.ItemWeaponCheckFlavor": "{actor} attacks with {weapon}.", + "DS4.ItemSpellCheckFlavor": "{actor} casts {spell}.", "DS4.ArmorType": "Armor Type", "DS4.ArmorTypeAbbr": "AT", "DS4.ArmorMaterialType": "Material Type", @@ -231,5 +233,7 @@ "DS4.RollDialogRollModeLabel": "Visibility", "DS4.TooltipBaseValue": "Base Value", "DS4.TooltipModifier": "Modifier", - "DS4.TooltipEffects": "Effects" + "DS4.TooltipEffects": "Effects", + "DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slaying Dice", + "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks." } diff --git a/src/module/item/item.ts b/src/module/item/item.ts index 0501fd1d..07b5dc47 100644 --- a/src/module/item/item.ts +++ b/src/module/item/item.ts @@ -48,7 +48,7 @@ export class DS4Item extends Item { } /** - * Roll a check for a action with this item. + * Roll a check for an action with this item. */ async roll(): Promise { if (!this.isOwnedItem()) { @@ -87,14 +87,17 @@ export class DS4Item extends Item { ); } - 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 combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); - const checkTargetValue = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus; - await createCheckRoll(checkTargetValue, { + const checkTargetNumber = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus; + + await createCheckRoll(checkTargetNumber, { rollMode: game.settings.get("core", "rollMode"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, + flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, weapon: this.name }), }); } @@ -120,7 +123,8 @@ export class DS4Item extends Item { ); } - 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; if (spellBonus === undefined) { notifications.info( @@ -131,12 +135,13 @@ export class DS4Item extends Item { ); } 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"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, + flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, spell: this.name }), }); } diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts index fa26b2ae..5498acab 100644 --- a/src/module/rolls/check-factory.ts +++ b/src/module/rolls/check-factory.ts @@ -6,6 +6,7 @@ class DefaultCheckOptions implements DS4CheckFactoryOptions { readonly minimumFumbleResult = 20; readonly useSlayingDice = false; readonly rollMode: Const.DiceRollMode = "roll"; + readonly flavor: undefined; mergeWith(other: Partial): DS4CheckFactoryOptions { return { ...this, ...other }; @@ -22,41 +23,39 @@ const defaultCheckOptions = new DefaultCheckOptions(); */ class CheckFactory { constructor( - private checkTargetValue: number, + private checkTargetNumber: number, private gmModifier: number, - passedOptions: Partial = {}, + options: Partial = {}, ) { - this.checkOptions = defaultCheckOptions.mergeWith(passedOptions); + this.options = defaultCheckOptions.mergeWith(options); } - private checkOptions: DS4CheckFactoryOptions; + private options: DS4CheckFactoryOptions; - async execute(): Promise { - const innerFormula = ["ds", this.createTargetValueTerm(), this.createCritTerm()].filterJoin(""); - const formula = this.checkOptions.useSlayingDice ? `{${innerFormula}}x` : innerFormula; + async execute(): Promise { + const innerFormula = ["ds", this.createCheckTargetNumberModifier(), this.createCoupFumbleModifier()].filterJoin( + "", + ); + const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const roll = Roll.create(formula); - const rollModeTemplate = this.checkOptions.rollMode; - return roll.toMessage({}, { rollMode: rollModeTemplate, create: true }); + return roll.toMessage( + { speaker: ChatMessage.getSpeaker(), flavor: this.options.flavor }, + { rollMode: this.options.rollMode, create: true }, + ); } - // Term generators - createTargetValueTerm(): string | null { - if (this.checkTargetValue !== null) { - return "v" + (this.checkTargetValue + this.gmModifier); - } else { - return null; - } + createCheckTargetNumberModifier(): string | null { + return "v" + (this.checkTargetNumber + this.gmModifier); } - createCritTerm(): string | null { - const minCritRequired = this.checkOptions.minimumFumbleResult !== defaultCheckOptions.minimumFumbleResult; - const maxCritRequired = this.checkOptions.maximumCoupResult !== defaultCheckOptions.maximumCoupResult; + createCoupFumbleModifier(): string | null { + const isMinimumFumbleResultRequired = + this.options.minimumFumbleResult !== defaultCheckOptions.minimumFumbleResult; + const isMaximumCoupResultRequired = this.options.maximumCoupResult !== defaultCheckOptions.maximumCoupResult; - if (minCritRequired || maxCritRequired) { - return ( - "c" + (this.checkOptions.maximumCoupResult ?? "") + ":" + (this.checkOptions.minimumFumbleResult ?? "") - ); + if (isMinimumFumbleResultRequired || isMaximumCoupResultRequired) { + return "c" + (this.options.maximumCoupResult ?? "") + ":" + (this.options.minimumFumbleResult ?? ""); } else { return null; } @@ -65,23 +64,24 @@ class CheckFactory { /** * Asks the user for all unknown/necessary information and passes them on to perform a roll. - * @param targetValue - The Check Target Number ("CTN") - * @param options - Options changing the behavior of the roll and message. + * @param checkTargetNumber - The Check Target Number ("CTN") + * @param options - Options changing the behavior of the roll and message. */ export async function createCheckRoll( - targetValue: number, + checkTargetNumber: number, options: Partial = {}, ): Promise { // 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 newOptions: Partial = { - maximumCoupResult: gmModifierData.maximumCoupResult ?? options.maximumCoupResult ?? undefined, - minimumFumbleResult: gmModifierData.minimumFumbleResult ?? options.minimumFumbleResult ?? undefined, - useSlayingDice: gmModifierData.useSlayingDice ?? options.useSlayingDice ?? undefined, - rollMode: gmModifierData.rollMode ?? options.rollMode ?? undefined, + maximumCoupResult: gmModifierData.maximumCoupResult ?? options.maximumCoupResult, + minimumFumbleResult: gmModifierData.minimumFumbleResult ?? options.minimumFumbleResult, + useSlayingDice: game.settings.get("ds4", "useSlayingDiceForAutomatedChecks") ?? false, + rollMode: gmModifierData.rollMode ?? options.rollMode, + flavor: options.flavor, }; // Create Factory @@ -106,7 +106,6 @@ async function askGmModifier( options: Partial = {}, { template, title }: { template?: string; title?: string } = {}, ): Promise> { - // 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 = { @@ -114,7 +113,7 @@ async function askGmModifier( checkTargetNumber: checkTargetNumber, maximumCoupResult: options.maximumCoupResult ?? defaultCheckOptions.maximumCoupResult, minimumFumbleResult: options.minimumFumbleResult ?? defaultCheckOptions.minimumFumbleResult, - rollMode: options.rollMode, + rollMode: options.rollMode ?? game.settings.get("core", "rollMode"), rollModes: CONFIG.Dice.rollModes, }; const renderedHtml = await renderTemplate(usedTemplate, templateData); @@ -200,12 +199,8 @@ interface GmModifierData { */ interface IntermediateGmModifierData extends GmModifierData { checkTargetNumber: number; - gmModifier: number; maximumCoupResult: number; minimumFumbleResult: number; - // TODO: In final version from system settings - useSlayingDice: boolean; - rollMode: Const.DiceRollMode; } /** @@ -216,4 +211,5 @@ export interface DS4CheckFactoryOptions { minimumFumbleResult: number; useSlayingDice: boolean; rollMode: Const.DiceRollMode; + flavor?: string; } diff --git a/src/module/settings.ts b/src/module/settings.ts index 644033f0..938be7c0 100644 --- a/src/module/settings.ts +++ b/src/module/settings.ts @@ -9,4 +9,13 @@ export function registerSystemSettings(): void { type: Number, default: -1, }); + + game.settings.register("ds4", "useSlayingDiceForAutomatedChecks", { + name: "DS4.SettingUseSlayingDiceForAutomatedChecksName", + hint: "DS4.SettingUseSlayingDiceForAutomatedChecksHint", + scope: "world", + config: true, + type: Boolean, + default: false, + }); }