Add a macro to perform generic checks

This commit is contained in:
Johannes Loher 2021-05-13 21:39:42 +02:00
parent 441e907b8a
commit 24725c15f9
9 changed files with 138 additions and 29 deletions

View file

@ -21,7 +21,8 @@
"DS4.HeadingSpecialCreatureAbilities": "Besondere Fähigkeiten", "DS4.HeadingSpecialCreatureAbilities": "Besondere Fähigkeiten",
"DS4.AttackType": "Angriffsart", "DS4.AttackType": "Angriffsart",
"DS4.AttackTypeAbbr": "AA", "DS4.AttackTypeAbbr": "AA",
"DS4.AttackTypeSelection": "Welche Angriffsart?", "DS4.DialogAttackTypeSelection": "Welche Angriffsart?",
"DS4.DialogAttributeTraitSelection": "Welches Attribut und welche Eigenschaft?",
"DS4.WeaponBonus": "Waffenbonus", "DS4.WeaponBonus": "Waffenbonus",
"DS4.WeaponBonusAbbr": "WB", "DS4.WeaponBonusAbbr": "WB",
"DS4.OpponentDefense": "Gegnerabwehr", "DS4.OpponentDefense": "Gegnerabwehr",
@ -122,9 +123,11 @@
"DS4.SpellPrice": "Preis (Gold)", "DS4.SpellPrice": "Preis (Gold)",
"DS4.ActorTypeCharacter": "Charakter", "DS4.ActorTypeCharacter": "Charakter",
"DS4.ActorTypeCreature": "Kreatur", "DS4.ActorTypeCreature": "Kreatur",
"DS4.Attribute": "Attribut",
"DS4.AttributeBody": "Körper", "DS4.AttributeBody": "Körper",
"DS4.AttributeMobility": "Agilität", "DS4.AttributeMobility": "Agilität",
"DS4.AttributeMind": "Geist", "DS4.AttributeMind": "Geist",
"DS4.Trait": "Eigenschaft",
"DS4.TraitStrength": "Stärke", "DS4.TraitStrength": "Stärke",
"DS4.TraitConstitution": "Härte", "DS4.TraitConstitution": "Härte",
"DS4.TraitAgility": "Bewegung", "DS4.TraitAgility": "Bewegung",
@ -204,6 +207,8 @@
"DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.", "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.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.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.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.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.", "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.UnitCustomAbbr": " ",
"DS4.GenericOkButton": "OK", "DS4.GenericOkButton": "OK",
"DS4.GenericCancelButton": "Abbrechen", "DS4.GenericCancelButton": "Abbrechen",
"DS4.RollDialogDefaultTitle": "Proben-Optionen", "DS4.DialogRollOptionsDefaultTitle": "Proben-Optionen",
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.", "DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde '{exType}', tatsächlich erhalten wurde '{realType}'.",
"DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.", "DS4.ErrorCouldNotFindForm": "Konnte HTML Element '{htmlElement}' nicht finden.",
"DS4.ErrorActorDoesNotHaveItem": "Der Aktor '{actor}' hat kein Item mit der ID '{id}'.", "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.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.ErrorItemDoesNotHaveEffect": "Das Item '{item}' hat keinen Effekt mit der ID '{id}'.",
"DS4.RollDialogCheckTargetNumberLabel": "Probenwert", "DS4.DialogRollOptionsCheckTargetNumberLabel": "Probenwert",
"DS4.RollDialogGMModifierLabel": "SL-Modifikator", "DS4.DialogRollOptionsGMModifierLabel": "SL-Modifikator",
"DS4.RollDialogMaximumCoupResultLabel": "Immersieg bis", "DS4.DialogRollOptionsMaximumCoupResultLabel": "Immersieg bis",
"DS4.RollDialogMinimumFumbleResultLabel": "Patzer ab", "DS4.DialogRollOptionsMinimumFumbleResultLabel": "Patzer ab",
"DS4.RollDialogRollModeLabel": "Sichtbarkeit", "DS4.DialogRollOptionsRollModeLabel": "Sichtbarkeit",
"DS4.TooltipBaseValue": "Basiswert", "DS4.TooltipBaseValue": "Basiswert",
"DS4.TooltipModifier": "Modifikator", "DS4.TooltipModifier": "Modifikator",
"DS4.TooltipEffects": "Effekte", "DS4.TooltipEffects": "Effekte",
@ -279,5 +284,6 @@
"DS4.ChecksWakeUp": "Erwachen", "DS4.ChecksWakeUp": "Erwachen",
"DS4.ChecksWorkMechanism": "Mechanismus Öffnen", "DS4.ChecksWorkMechanism": "Mechanismus Öffnen",
"DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe.", "DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe.",
"DS4.ActorGenericCheckFlavor": "{actor} würfelt eine Probe gegen {attribute} + {trait}.",
"DS4.CheckTooltip": "{check} Probe würfeln" "DS4.CheckTooltip": "{check} Probe würfeln"
} }

View file

@ -21,7 +21,8 @@
"DS4.HeadingSpecialCreatureAbilities": "Special Abilities", "DS4.HeadingSpecialCreatureAbilities": "Special Abilities",
"DS4.AttackType": "Attack Type", "DS4.AttackType": "Attack Type",
"DS4.AttackTypeAbbr": "AT", "DS4.AttackTypeAbbr": "AT",
"DS4.AttackTypeSelection": "Which Attack Type?", "DS4.DialogAttackTypeSelection": "Which Attack Type?",
"DS4.DialogAttributeTraitSelection": "Which Attribute and Trait?",
"DS4.WeaponBonus": "Weapon Bonus", "DS4.WeaponBonus": "Weapon Bonus",
"DS4.WeaponBonusAbbr": "WB", "DS4.WeaponBonusAbbr": "WB",
"DS4.OpponentDefense": "Opponent Defense", "DS4.OpponentDefense": "Opponent Defense",
@ -122,9 +123,11 @@
"DS4.SpellPrice": "Price (Gold)", "DS4.SpellPrice": "Price (Gold)",
"DS4.ActorTypeCharacter": "Character", "DS4.ActorTypeCharacter": "Character",
"DS4.ActorTypeCreature": "Creature", "DS4.ActorTypeCreature": "Creature",
"DS4.Attribute": "Attribute",
"DS4.AttributeBody": "Body", "DS4.AttributeBody": "Body",
"DS4.AttributeMobility": "Mobility", "DS4.AttributeMobility": "Mobility",
"DS4.AttributeMind": "Mind", "DS4.AttributeMind": "Mind",
"DS4.Trait": "Trait",
"DS4.TraitStrength": "Strength", "DS4.TraitStrength": "Strength",
"DS4.TraitConstitution": "Constitution", "DS4.TraitConstitution": "Constitution",
"DS4.TraitAgility": "Agility", "DS4.TraitAgility": "Agility",
@ -204,6 +207,8 @@
"DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.", "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.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.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.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.",
"DS4.ErrorCannotDragMissingCheck": "Trying to drag the check '{check}' but no such check exists.", "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.", "DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.",
@ -232,17 +237,17 @@
"DS4.UnitCustomAbbr": " ", "DS4.UnitCustomAbbr": " ",
"DS4.GenericOkButton": "Ok", "DS4.GenericOkButton": "Ok",
"DS4.GenericCancelButton": "Cancel", "DS4.GenericCancelButton": "Cancel",
"DS4.RollDialogDefaultTitle": "Roll Options", "DS4.DialogRollOptionsDefaultTitle": "Roll Options",
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.", "DS4.ErrorUnexpectedHtmlType": "Type Error: Expected '{exType}' but got '{realType}'.",
"DS4.ErrorCouldNotFindForm": "Could not find HTML element '{htmlElement}'.", "DS4.ErrorCouldNotFindForm": "Could not find HTML element '{htmlElement}'.",
"DS4.ErrorActorDoesNotHaveItem": "The actor '{actor}' does not have any item with the id '{id}'.", "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.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.ErrorItemDoesNotHaveEffect": "The item '{item}' does not have any effect with the id '{id}'.",
"DS4.RollDialogCheckTargetNumberLabel": "Check Target Number", "DS4.DialogRollOptionsCheckTargetNumberLabel": "Check Target Number",
"DS4.RollDialogGMModifierLabel": "Game Master Modifier", "DS4.DialogRollOptionsGMModifierLabel": "Game Master Modifier",
"DS4.RollDialogMaximumCoupResultLabel": "Coup to", "DS4.DialogRollOptionsMaximumCoupResultLabel": "Coup to",
"DS4.RollDialogMinimumFumbleResultLabel": "Fumble from", "DS4.DialogRollOptionsMinimumFumbleResultLabel": "Fumble from",
"DS4.RollDialogRollModeLabel": "Visibility", "DS4.DialogRollOptionsRollModeLabel": "Visibility",
"DS4.TooltipBaseValue": "Base Value", "DS4.TooltipBaseValue": "Base Value",
"DS4.TooltipModifier": "Modifier", "DS4.TooltipModifier": "Modifier",
"DS4.TooltipEffects": "Effects", "DS4.TooltipEffects": "Effects",
@ -279,5 +284,6 @@
"DS4.ChecksWakeUp": "Wake Up", "DS4.ChecksWakeUp": "Wake Up",
"DS4.ChecksWorkMechanism": "Work Mechanism", "DS4.ChecksWorkMechanism": "Work Mechanism",
"DS4.ActorCheckFlavor": "{actor} rolls a {check} check.", "DS4.ActorCheckFlavor": "{actor} rolls a {check} check.",
"DS4.ActorGenericCheckFlavor": "{actor} rolls a check against {attribute} + {trait}.",
"DS4.CheckTooltip": "Roll a {check} check" "DS4.CheckTooltip": "Roll a {check} check"
} }

View file

@ -291,4 +291,80 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
flavor: game.i18n.format("DS4.ActorCheckFlavor", { actor: this.name, check: DS4.i18n.checks[check] }), 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<void> {
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,
};
},
});
}
} }

View file

@ -156,13 +156,16 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
if (attackType === "meleeRanged") { if (attackType === "meleeRanged") {
const { melee, ranged } = { ...DS4.i18n.attackTypes }; const { melee, ranged } = { ...DS4.i18n.attackTypes };
const identifier = "attack-type-selection"; const identifier = "attack-type-selection";
const label = game.i18n.localize("DS4.AttackType");
const answer = Dialog.prompt({ 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", { content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", {
label, selects: [
identifier, {
options: { melee, ranged }, label: game.i18n.localize("DS4.AttackType"),
identifier,
options: { melee, ranged },
},
],
}), }),
label: game.i18n.localize("DS4.GenericOkButton"), label: game.i18n.localize("DS4.GenericOkButton"),
callback: (html) => { callback: (html) => {

View file

@ -1,7 +1,9 @@
import { rollCheck } from "./roll-check"; import { rollCheck } from "./roll-check";
import { rollGenericCheck } from "./roll-generic-check";
import { rollItem } from "./roll-item"; import { rollItem } from "./roll-item";
export const macros = { export const macros = {
rollCheck, rollCheck,
rollGenericCheck,
rollItem, rollItem,
}; };

View file

@ -0,0 +1,13 @@
import notifications from "../ui/notifications";
import { getActiveActor } from "./helpers";
/**
* Executes the roll generic check macro.
*/
export async function rollGenericCheck(): Promise<void> {
const actor = getActiveActor();
if (!actor) {
return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro"));
}
return actor.rollGenericCheck();
}

View file

@ -107,7 +107,7 @@ async function askGmModifier(
{ template, title }: { template?: string; title?: string } = {}, { template, title }: { template?: string; title?: string } = {},
): Promise<Partial<IntermediateGmModifierData>> { ): Promise<Partial<IntermediateGmModifierData>> {
const usedTemplate = template ?? "systems/ds4/templates/dialogs/roll-options.hbs"; 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 = { const templateData = {
title: usedTitle, title: usedTitle,
checkTargetNumber: checkTargetNumber, checkTargetNumber: checkTargetNumber,

View file

@ -8,26 +8,26 @@
--}} --}}
<form class="ds4-roll-options"> <form class="ds4-roll-options">
<div class="form-group"> <div class="form-group">
<label for="check-target-number">{{localize "DS4.RollDialogCheckTargetNumberLabel"}}</label> <label for="check-target-number">{{localize "DS4.DialogRollOptionsCheckTargetNumberLabel"}}</label>
<input id="check-target-number" data-type="Number" type="number" name="check-target-number" <input id="check-target-number" data-type="Number" type="number" name="check-target-number"
value="{{checkTargetNumber}}" /> value="{{checkTargetNumber}}" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="gm-modifier">{{localize "DS4.RollDialogGMModifierLabel"}}</label> <label for="gm-modifier">{{localize "DS4.DialogRollOptionsGMModifierLabel"}}</label>
<input id="gm-modifier" data-type="Number" type="number" name="gm-modifier" value="0" /> <input id="gm-modifier" data-type="Number" type="number" name="gm-modifier" value="0" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="maximum-coup-result">{{localize "DS4.RollDialogMaximumCoupResultLabel"}}</label> <label for="maximum-coup-result">{{localize "DS4.DialogRollOptionsMaximumCoupResultLabel"}}</label>
<input id="maximum-coup-result" data-type="Number" type="number" name="maximum-coup-result" <input id="maximum-coup-result" data-type="Number" type="number" name="maximum-coup-result"
value="{{maximumCoupResult}}" /> value="{{maximumCoupResult}}" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="minimum-fumble-result">{{localize "DS4.RollDialogMinimumFumbleResultLabel"}}</label> <label for="minimum-fumble-result">{{localize "DS4.DialogRollOptionsMinimumFumbleResultLabel"}}</label>
<input id="minimum-fumble-result" data-type="Number" type="number" name="minimum-fumble-result" <input id="minimum-fumble-result" data-type="Number" type="number" name="minimum-fumble-result"
value="{{minimumFumbleResult}}" /> value="{{minimumFumbleResult}}" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="roll-mode">{{localize "DS4.RollDialogRollModeLabel"}}</label> <label for="roll-mode">{{localize "DS4.DialogRollOptionsRollModeLabel"}}</label>
<div class="form-fields"> <div class="form-fields">
<select id="roll-mode" name="roll-mode" data-type="String"> <select id="roll-mode" name="roll-mode" data-type="String">
{{#select rollMode}} {{#select rollMode}}

View file

@ -1,11 +1,13 @@
{{!-- {{!--
!-- Render a simple form with a single select element. It uses the default form classes of Foundry VTT. !-- Render a simple form with select elements. It uses the default form classes of Foundry VTT.
!-- @param identifier: The identifier to use as id for the select element. Can be used to query the value later on. !-- @param selects: An array of objects that each contain the following:
!-- @param label: Text to display as the label for the select element. !---- identifier: The identifier to use as id for the select element. Can be used to query the value later on.
!-- @param options: Key-value pairs that describe the options. The keys are used for the value attribute of the !---- label: Text to display as the label for the select element.
!---- options: Key-value pairs that describe the options. The keys are used for the value attribute of the
options, the values are used as content. options, the values are used as content.
--}} --}}
<form class="ds4-simple-form"> <form class="ds4-simple-form">
{{#each selects}}
<div class="form-group"> <div class="form-group">
<label for="{{identifier}}">{{label}}</label> <label for="{{identifier}}">{{label}}</label>
<div class="form-fields"> <div class="form-fields">
@ -16,4 +18,5 @@ options, the values are used as content.
</select> </select>
</div> </div>
</div> </div>
{{/each}}
</form> </form>