From 24725c15f9fc22f63d4bc62c8b40144f46f47e27 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Thu, 13 May 2021 21:39:42 +0200 Subject: [PATCH] Add a macro to perform generic checks --- src/lang/de.json | 20 ++++-- src/lang/en.json | 20 ++++-- src/module/actor/actor.ts | 76 ++++++++++++++++++++ src/module/item/item.ts | 13 ++-- src/module/macros/macros.ts | 2 + src/module/macros/roll-generic-check.ts | 13 ++++ src/module/rolls/check-factory.ts | 2 +- src/templates/dialogs/roll-options.hbs | 10 +-- src/templates/dialogs/simple-select-form.hbs | 11 +-- 9 files changed, 138 insertions(+), 29 deletions(-) create mode 100644 src/module/macros/roll-generic-check.ts diff --git a/src/lang/de.json b/src/lang/de.json index 12f2708e..76c9b3e7 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -21,7 +21,8 @@ "DS4.HeadingSpecialCreatureAbilities": "Besondere Fähigkeiten", "DS4.AttackType": "Angriffsart", "DS4.AttackTypeAbbr": "AA", - "DS4.AttackTypeSelection": "Welche Angriffsart?", + "DS4.DialogAttackTypeSelection": "Welche Angriffsart?", + "DS4.DialogAttributeTraitSelection": "Welches Attribut und welche Eigenschaft?", "DS4.WeaponBonus": "Waffenbonus", "DS4.WeaponBonusAbbr": "WB", "DS4.OpponentDefense": "Gegnerabwehr", @@ -122,9 +123,11 @@ "DS4.SpellPrice": "Preis (Gold)", "DS4.ActorTypeCharacter": "Charakter", "DS4.ActorTypeCreature": "Kreatur", + "DS4.Attribute": "Attribut", "DS4.AttributeBody": "Körper", "DS4.AttributeMobility": "Agilität", "DS4.AttributeMind": "Geist", + "DS4.Trait": "Eigenschaft", "DS4.TraitStrength": "Stärke", "DS4.TraitConstitution": "Härte", "DS4.TraitAgility": "Bewegung", @@ -204,6 +207,8 @@ "DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.", "DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffsarten: {expectedTypes}", + "DS4.ErrorUnexpectedAttribute": "Unerwartetes Attribut '{actualType}', erwartete Attribute: {expectedTypes}", + "DS4.ErrorUnexpectedTrait": "Unerwartete Eigenschaft '{actualType}', erwartete Eigenschaften: {expectedTypes}", "DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.", "DS4.ErrorCannotDragMissingCheck": "Die Probe '{check}' per Drag & Drop zu ziehen ist nicht möglich, denn sie existiert nicht.", "DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.", @@ -232,17 +237,17 @@ "DS4.UnitCustomAbbr": " ", "DS4.GenericOkButton": "OK", "DS4.GenericCancelButton": "Abbrechen", - "DS4.RollDialogDefaultTitle": "Proben-Optionen", + "DS4.DialogRollOptionsDefaultTitle": "Proben-Optionen", "DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.", "DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.", "DS4.ErrorActorDoesNotHaveItem": "Der Aktor '{actor}' hat kein Item mit der ID '{id}'.", "DS4.ErrorUnexpectedError": "Es gab einen unerwarteten Fehler im Dungeonslayers 4 System. Für mehr Details schauen Sie bitte in die Konsole (F12).", "DS4.ErrorItemDoesNotHaveEffect": "Das Item '{item}' hat keinen Effekt mit der ID '{id}'.", - "DS4.RollDialogCheckTargetNumberLabel": "Probenwert", - "DS4.RollDialogGMModifierLabel": "SL-Modifikator", - "DS4.RollDialogMaximumCoupResultLabel": "Immersieg bis", - "DS4.RollDialogMinimumFumbleResultLabel": "Patzer ab", - "DS4.RollDialogRollModeLabel": "Sichtbarkeit", + "DS4.DialogRollOptionsCheckTargetNumberLabel": "Probenwert", + "DS4.DialogRollOptionsGMModifierLabel": "SL-Modifikator", + "DS4.DialogRollOptionsMaximumCoupResultLabel": "Immersieg bis", + "DS4.DialogRollOptionsMinimumFumbleResultLabel": "Patzer ab", + "DS4.DialogRollOptionsRollModeLabel": "Sichtbarkeit", "DS4.TooltipBaseValue": "Basiswert", "DS4.TooltipModifier": "Modifikator", "DS4.TooltipEffects": "Effekte", @@ -279,5 +284,6 @@ "DS4.ChecksWakeUp": "Erwachen", "DS4.ChecksWorkMechanism": "Mechanismus Öffnen", "DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe.", + "DS4.ActorGenericCheckFlavor": "{actor} würfelt eine Probe gegen {attribute} + {trait}.", "DS4.CheckTooltip": "{check} Probe würfeln" } diff --git a/src/lang/en.json b/src/lang/en.json index 39ad913b..903b13be 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -21,7 +21,8 @@ "DS4.HeadingSpecialCreatureAbilities": "Special Abilities", "DS4.AttackType": "Attack Type", "DS4.AttackTypeAbbr": "AT", - "DS4.AttackTypeSelection": "Which Attack Type?", + "DS4.DialogAttackTypeSelection": "Which Attack Type?", + "DS4.DialogAttributeTraitSelection": "Which Attribute and Trait?", "DS4.WeaponBonus": "Weapon Bonus", "DS4.WeaponBonusAbbr": "WB", "DS4.OpponentDefense": "Opponent Defense", @@ -122,9 +123,11 @@ "DS4.SpellPrice": "Price (Gold)", "DS4.ActorTypeCharacter": "Character", "DS4.ActorTypeCreature": "Creature", + "DS4.Attribute": "Attribute", "DS4.AttributeBody": "Body", "DS4.AttributeMobility": "Mobility", "DS4.AttributeMind": "Mind", + "DS4.Trait": "Trait", "DS4.TraitStrength": "Strength", "DS4.TraitConstitution": "Constitution", "DS4.TraitAgility": "Agility", @@ -204,6 +207,8 @@ "DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.", "DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}", + "DS4.ErrorUnexpectedAttribute": "Unexpected attribute '{actualType}', expected it to be one of: {expectedTypes}", + "DS4.ErrorUnexpectedTrait": "Unexpected trait '{actualType}', expected it to be one of: {expectedTypes}", "DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.", "DS4.ErrorCannotDragMissingCheck": "Trying to drag the check '{check}' but no such check exists.", "DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.", @@ -232,17 +237,17 @@ "DS4.UnitCustomAbbr": " ", "DS4.GenericOkButton": "Ok", "DS4.GenericCancelButton": "Cancel", - "DS4.RollDialogDefaultTitle": "Roll Options", + "DS4.DialogRollOptionsDefaultTitle": "Roll Options", "DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.", "DS4.ErrorCouldNotFindForm": "Could not find HTML element '{htmlElement}'.", "DS4.ErrorActorDoesNotHaveItem": "The actor '{actor}' does not have any item with the id '{id}'.", "DS4.ErrorUnexpectedError": "There was an unexpected error in the Dungeonslayers 4 system. For more details, please take a look at the console (F12).", "DS4.ErrorItemDoesNotHaveEffect": "The item '{item}' does not have any effect with the id '{id}'.", - "DS4.RollDialogCheckTargetNumberLabel": "Check Target Number", - "DS4.RollDialogGMModifierLabel": "Game Master Modifier", - "DS4.RollDialogMaximumCoupResultLabel": "Coup to", - "DS4.RollDialogMinimumFumbleResultLabel": "Fumble from", - "DS4.RollDialogRollModeLabel": "Visibility", + "DS4.DialogRollOptionsCheckTargetNumberLabel": "Check Target Number", + "DS4.DialogRollOptionsGMModifierLabel": "Game Master Modifier", + "DS4.DialogRollOptionsMaximumCoupResultLabel": "Coup to", + "DS4.DialogRollOptionsMinimumFumbleResultLabel": "Fumble from", + "DS4.DialogRollOptionsRollModeLabel": "Visibility", "DS4.TooltipBaseValue": "Base Value", "DS4.TooltipModifier": "Modifier", "DS4.TooltipEffects": "Effects", @@ -279,5 +284,6 @@ "DS4.ChecksWakeUp": "Wake Up", "DS4.ChecksWorkMechanism": "Work Mechanism", "DS4.ActorCheckFlavor": "{actor} rolls a {check} check.", + "DS4.ActorGenericCheckFlavor": "{actor} rolls a check against {attribute} + {trait}.", "DS4.CheckTooltip": "Roll a {check} check" } diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index f7e4120d..00339761 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -291,4 +291,80 @@ export class DS4Actor extends Actor flavor: game.i18n.format("DS4.ActorCheckFlavor", { actor: this.name, check: DS4.i18n.checks[check] }), }); } + + /** + * Roll a generic check. A dialog is presented to select the combination of + * Attribute and Trait to perform the check against. + */ + async rollGenericCheck(): Promise { + const { attribute, trait } = await this.selectAttributeAndTrait(); + const checkTargetNumber = this.data.data.attributes[attribute].total + this.data.data.traits[trait].total; + await createCheckRoll(checkTargetNumber, { + rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream + maximumCoupResult: this.data.data.rolling.maximumCoupResult, + minimumFumbleResult: this.data.data.rolling.minimumFumbleResult, + flavor: game.i18n.format("DS4.ActorGenericCheckFlavor", { + actor: this.name, + attribute: DS4.i18n.attributes[attribute], + trait: DS4.i18n.traits[trait], + }), + }); + } + + protected async selectAttributeAndTrait(): Promise<{ + attribute: keyof typeof DS4.i18n.attributes; + trait: keyof typeof DS4.i18n.traits; + }> { + const attributeIdentifier = "attribute-trait-selection-attribute"; + const traitIdentifier = "attribute-trait-selection-trait"; + return Dialog.prompt({ + title: game.i18n.localize("DS4.DialogAttributeTraitSelection"), + content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { + selects: [ + { + label: game.i18n.localize("DS4.Attribute"), + identifier: attributeIdentifier, + options: DS4.i18n.attributes, + }, + { + label: game.i18n.localize("DS4.Trait"), + identifier: traitIdentifier, + options: DS4.i18n.traits, + }, + ], + }), + label: game.i18n.localize("DS4.GenericOkButton"), + callback: (html) => { + const selectedAttribute = html.find(`#${attributeIdentifier}`).val(); + if (selectedAttribute !== "body" && selectedAttribute !== "mobility" && selectedAttribute !== "mind") { + throw new Error( + game.i18n.format("DS4.ErrorUnexpectedAttribute", { + actualAttribute: selectedAttribute, + expectedTypes: "'body', 'agility', 'mind'", + }), + ); + } + const selectedTrait = html.find(`#${traitIdentifier}`).val(); + if ( + selectedTrait !== "strength" && + selectedTrait !== "constitution" && + selectedTrait !== "agility" && + selectedTrait !== "dexterity" && + selectedTrait !== "intellect" && + selectedTrait !== "aura" + ) { + throw new Error( + game.i18n.format("DS4.ErrorUnexpectedTrait", { + actualAttribute: selectedAttribute, + expectedTypes: "'strength', 'constitution', 'agility', 'dexterity', 'intellect', 'aura'", + }), + ); + } + return { + attribute: selectedAttribute, + trait: selectedTrait, + }; + }, + }); + } } diff --git a/src/module/item/item.ts b/src/module/item/item.ts index c7db80fd..e5861ee7 100644 --- a/src/module/item/item.ts +++ b/src/module/item/item.ts @@ -156,13 +156,16 @@ export class DS4Item extends Item { if (attackType === "meleeRanged") { const { melee, ranged } = { ...DS4.i18n.attackTypes }; const identifier = "attack-type-selection"; - const label = game.i18n.localize("DS4.AttackType"); const answer = Dialog.prompt({ - title: game.i18n.localize("DS4.AttackTypeSelection"), + title: game.i18n.localize("DS4.DialogAttackTypeSelection"), content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { - label, - identifier, - options: { melee, ranged }, + selects: [ + { + label: game.i18n.localize("DS4.AttackType"), + identifier, + options: { melee, ranged }, + }, + ], }), label: game.i18n.localize("DS4.GenericOkButton"), callback: (html) => { diff --git a/src/module/macros/macros.ts b/src/module/macros/macros.ts index e2a43e9b..8488914c 100644 --- a/src/module/macros/macros.ts +++ b/src/module/macros/macros.ts @@ -1,7 +1,9 @@ import { rollCheck } from "./roll-check"; +import { rollGenericCheck } from "./roll-generic-check"; import { rollItem } from "./roll-item"; export const macros = { rollCheck, + rollGenericCheck, rollItem, }; diff --git a/src/module/macros/roll-generic-check.ts b/src/module/macros/roll-generic-check.ts new file mode 100644 index 00000000..75ee9162 --- /dev/null +++ b/src/module/macros/roll-generic-check.ts @@ -0,0 +1,13 @@ +import notifications from "../ui/notifications"; +import { getActiveActor } from "./helpers"; +/** + * Executes the roll generic check macro. + */ +export async function rollGenericCheck(): Promise { + const actor = getActiveActor(); + if (!actor) { + return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); + } + + return actor.rollGenericCheck(); +} diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts index 86b29776..51286e65 100644 --- a/src/module/rolls/check-factory.ts +++ b/src/module/rolls/check-factory.ts @@ -107,7 +107,7 @@ async function askGmModifier( { template, title }: { template?: string; title?: string } = {}, ): Promise> { const usedTemplate = template ?? "systems/ds4/templates/dialogs/roll-options.hbs"; - const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle"); + const usedTitle = title ?? game.i18n.localize("DS4.DialogRollOptionsDefaultTitle"); const templateData = { title: usedTitle, checkTargetNumber: checkTargetNumber, diff --git a/src/templates/dialogs/roll-options.hbs b/src/templates/dialogs/roll-options.hbs index 76c840aa..badac01d 100644 --- a/src/templates/dialogs/roll-options.hbs +++ b/src/templates/dialogs/roll-options.hbs @@ -8,26 +8,26 @@ --}}
- +
- +
- +
- +
- +
+ {{/each}}