From f038509910ac1df6e05fc90c024ed862a5c871e0 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Wed, 24 Mar 2021 09:19:26 +0100 Subject: [PATCH 1/5] Add functionality for common checks, which can be affected by effects and be performed as macros --- src/lang/de.json | 30 +++++++++++- src/lang/en.json | 30 +++++++++++- src/module/actor/actor-prepared-data.ts | 8 ++++ src/module/actor/actor.ts | 64 +++++++++++++++++++++++-- src/module/config.ts | 29 +++++++++++ src/module/macros/macros.ts | 4 +- src/module/macros/roll-check.ts | 43 +++++++++++++++++ src/module/macros/roll-item.ts | 1 - 8 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 src/module/macros/roll-check.ts diff --git a/src/lang/de.json b/src/lang/de.json index e19dbadd..1da1dea4 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -198,6 +198,7 @@ "DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.", "DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.", "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item Würfel Makro zu nutzen muss ein Aktor kontolliert werden.", + "DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein ein Proben Würfel Makro zu nutzen muss ein Aktor kontolliert werden.", "DS4.WarningControlledActorDoesNotHaveItem": "Der kontrollierte Aktor '{actorName}' ({actorId}) hat kein Item mit der ID '{itemId}'.", "DS4.WarningItemIsNotRollable": "Für das Item '{name}' ({id}) vom Typ '{type}' kann nicht gewürfelt werden.", "DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Makros können nur für besessene Items angelegt werden.", @@ -235,5 +236,32 @@ "DS4.TooltipModifier": "Modifikator", "DS4.TooltipEffects": "Effekte", "DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slayende Würfel", - "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben." + "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben.", + "DS4.ChecksAppraise": "Schätzen", + "DS4.ChangeSpell": "Zauber Wechseln", + "DS4.ChecksClimb": "Klettern", + "DS4.ChecksCommunicate": "Verständigen", + "DS4.ChecksDecipherScript": "Inschrift Entziffern", + "DS4.ChecksDefend": "Abwehren", + "DS4.ChecksDefyPoison": "Gift Trotzen", + "DS4.ChecksDisableTraps": "Fallen Entschärfen", + "DS4.ChecksFeatOfStrength": "Kraftakt", + "DS4.ChecksFlirt": "Flirten", + "DS4.ChecksHaggle": "Feilschen", + "DS4.ChecksHide": "Verbergen", + "DS4.ChecksJump": "Springen", + "DS4.ChecksKnowledge": "Wissen", + "DS4.ChecksOpenLock": "Schlösser Öffnen", + "DS4.ChecksPerception": "Bemerken", + "DS4.ChecksPickPocket": "Tschendiebstahl", + "DS4.ChecksReadTracks": "Spuren Lesen", + "DS4.ChecksResistDisease": "Krankheit Trotzen", + "DS4.ChecksRide": "Reiten", + "DS4.ChecksSearch": "Suchen", + "DS4.ChecksSneak": "Sneak", + "DS4.ChecksStartFire": "Feuer Machen", + "DS4.ChecksSwim": "Schwimmen", + "DS4.ChecksWakeUp": "Erwachen", + "DS4.ChecksWorkMechanism": "Mechanismus Öffnen", + "DS4.ActorCheckFlavor": "{actor} würfelt eine {check} Probe." } diff --git a/src/lang/en.json b/src/lang/en.json index a3ae4f71..f5d456c7 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -198,6 +198,7 @@ "DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.", "DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.", "DS4.WarningMustControlActorToUseRollItemMacro": "You must control an actor to be able to use a roll item macro.", + "DS4.WarningMustControlActorToUseRollCheckMacro": "You must control an actor to be able to use a roll check macro.", "DS4.WarningControlledActorDoesNotHaveItem": "Your controlled actor '{actorName}' ({actorId}) does not have any item with the id '{itemId}'.", "DS4.WarningItemIsNotRollable": "Item '{name}' ({id}) of type '{type}' is not rollable.", "DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Macros can only be created for owned items.", @@ -235,5 +236,32 @@ "DS4.TooltipModifier": "Modifier", "DS4.TooltipEffects": "Effects", "DS4.SettingUseSlayingDiceForAutomatedChecksName": "Slaying Dice", - "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks." + "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks.", + "DS4.ChecksAppraise": "Appraise", + "DS4.ChangeSpell": "Change Spell", + "DS4.ChecksClimb": "Climb", + "DS4.ChecksCommunicate": "Communicate", + "DS4.ChecksDecipherScript": "Decipher Script", + "DS4.ChecksDefend": "Defend", + "DS4.ChecksDefyPoison": "Defy Poison", + "DS4.ChecksDisableTraps": "Disable Traps", + "DS4.ChecksFeatOfStrength": "Feat of Strength", + "DS4.ChecksFlirt": "Flirt", + "DS4.ChecksHaggle": "Haggle", + "DS4.ChecksHide": "Hide", + "DS4.ChecksJump": "Jump", + "DS4.ChecksKnowledge": "Knowledge", + "DS4.ChecksOpenLock": "Open Lock", + "DS4.ChecksPerception": "Perception", + "DS4.ChecksPickPocket": "Pick Pocket", + "DS4.ChecksReadTracks": "Read Tracks", + "DS4.ChecksResistDisease": "Resist Disease", + "DS4.ChecksRide": "Ride", + "DS4.ChecksSearch": "Search", + "DS4.ChecksSneak": "Sneak", + "DS4.ChecksStartFire": "Start Fire", + "DS4.ChecksSwim": "Swim", + "DS4.ChecksWakeUp": "Wake Up", + "DS4.ChecksWorkMechanism": "Work Mechanism", + "DS4.ActorCheckFlavor": "{actor} rolls a {check} check." } diff --git a/src/module/actor/actor-prepared-data.ts b/src/module/actor/actor-prepared-data.ts index fcbe0139..85080dad 100644 --- a/src/module/actor/actor-prepared-data.ts +++ b/src/module/actor/actor-prepared-data.ts @@ -1,4 +1,5 @@ import { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from "../common/common-data"; +import { DS4 } from "../config"; import { DS4ActorDataHelper, DS4CharacterDataDataBaseInfo, @@ -21,6 +22,7 @@ interface DS4ActorPreparedDataDataBase { traits: DS4ActorPreparedDataDataTraits; combatValues: DS4ActorPreparedDataDataCombatValues; rolling: DS4ActorPreparedDataDataRolling; + checks: DS4ActorPreparedDataDataChecks; } interface DS4ActorPreparedDataDataAttributes { @@ -54,6 +56,12 @@ interface DS4ActorPreparedDataDataRolling { minimumFumbleResult: number; } +export type Check = keyof typeof DS4.i18n.checks; + +type DS4ActorPreparedDataDataChecks = { + [key in Check]: number; +}; + // types interface DS4CharacterPreparedDataData extends DS4ActorPreparedDataDataBase { diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index a8515444..8123cd5b 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -2,8 +2,9 @@ import { ModifiableDataBaseTotal } from "../common/common-data"; import { DS4 } from "../config"; import { DS4Item } from "../item/item"; import { ItemType } from "../item/item-data"; +import { createCheckRoll } from "../rolls/check-factory"; import { DS4ActorData } from "./actor-data"; -import { DS4ActorPreparedData } from "./actor-prepared-data"; +import { Check, DS4ActorPreparedData } from "./actor-prepared-data"; /** * The Actor class for DS4 @@ -108,13 +109,20 @@ export class DS4Actor extends Actor */ prepareDerivedData(): void { this._prepareCombatValues(); + this._prepareChecks(); } /** * The list of properties that are derived from others, given in dot notation. */ get derivedDataProperties(): Array { - return Object.keys(DS4.i18n.combatValues).map((combatValue) => `data.combatValues.${combatValue}.total`); + return Object.keys(DS4.i18n.combatValues) + .map((combatValue) => `data.combatValues.${combatValue}.total`) + .concat( + Object.keys(DS4.i18n.checks) + .filter((check) => check !== "defend") + .map((check) => `data.checks.${check}`), + ); } /** @@ -122,6 +130,7 @@ export class DS4Actor extends Actor */ prepareFinalDerivedData(): void { this.data.data.combatValues.hitPoints.max = this.data.data.combatValues.hitPoints.total; + this.data.data.checks.defend = this.data.data.combatValues.defense.total; } /** @@ -129,7 +138,7 @@ export class DS4Actor extends Actor * given in dot notation. */ get finalDerivedDataProperties(): string[] { - return ["data.combatValues.hitPoints.max"]; + return ["data.combatValues.hitPoints.max", "data.checks.defend"]; } /** @@ -204,6 +213,42 @@ export class DS4Actor extends Actor .reduce((a, b) => a + b, 0); } + /** + * Prepares the check target numbers of checks for the actor. + */ + protected _prepareChecks(): void { + const data = this.data.data; + data.checks = { + appraise: data.attributes.mind.total + data.traits.intellect.total, + changeSpell: data.attributes.mind.total + data.traits.intellect.total, + climb: data.attributes.mobility.total + data.traits.strength.total, + communicate: data.attributes.mind.total + data.traits.dexterity.total, + decipherScript: data.attributes.mind.total + data.traits.intellect.total, + defend: 0, // assigned in prepareFinalDerivedData as it must always match data.combatValues.defense.total and is not changeable by effects + defyPoison: data.attributes.body.total + data.traits.constitution.total, + disableTraps: data.attributes.mind.total + data.traits.dexterity.total, + featOfStrength: data.attributes.body.total + data.traits.strength.total, + flirt: data.attributes.mind.total + data.traits.aura.total, + haggle: data.attributes.mind.total + Math.max(data.traits.intellect.total, data.traits.intellect.total), + hide: data.attributes.mobility.total + data.traits.agility.total, + jump: data.attributes.mobility.total + data.traits.agility.total, + knowledge: data.attributes.mind.total + data.traits.intellect.total, + openLock: data.attributes.mind.total + data.traits.dexterity.total, + perception: Math.max(data.attributes.mind.total + data.traits.intellect.total, 8), + pickPocket: data.attributes.mobility.total + data.traits.dexterity.total, + readTracks: data.attributes.mind.total + data.traits.intellect.total, + resistDisease: data.attributes.body.total + data.traits.constitution.total, + ride: data.attributes.mobility.total + Math.max(data.traits.agility.total, data.traits.aura.total), + search: Math.max(data.attributes.mind.total + data.traits.intellect.total, 8), + sneak: data.attributes.mobility.total + data.traits.agility.total, + startFire: data.attributes.mind.total + data.traits.dexterity.total, + swim: data.attributes.mobility.total + data.traits.strength.total, + wakeUp: data.attributes.mind.total + data.traits.intellect.total, + workMechanism: + data.attributes.mind.total + Math.max(data.traits.dexterity.total, data.traits.intellect.total), + }; + } + /** * Handle how changes to a Token attribute bar are applied to the Actor. * This only differs from the base implementation by also allowing negative values. @@ -226,4 +271,17 @@ export class DS4Actor extends Actor const allowed = Hooks.call("modifyTokenAttribute", { attribute, value, isDelta, isBar }, updates); return allowed !== false ? this.update(updates) : this; } + + /** + * Roll for a given check. + * @param check The check to perform + */ + async rollCheck(check: Check): Promise { + await createCheckRoll(this.data.data.checks[check], { + 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.ActorCheckFlavor", { actor: this.name, check: DS4.i18n.checks[check] }), + }); + } } diff --git a/src/module/config.ts b/src/module/config.ts index ead4be51..c2990358 100644 --- a/src/module/config.ts +++ b/src/module/config.ts @@ -281,6 +281,35 @@ export const DS4 = { days: "DS4.UnitDaysAbbr", custom: "DS4.UnitCustomAbbr", }, + + checks: { + appraise: "DS4.ChecksAppraise", + changeSpell: "DS4.ChangeSpell", + climb: "DS4.ChecksClimb", + communicate: "DS4.ChecksCommunicate", + decipherScript: "DS4.ChecksDecipherScript", + defend: "DS4.ChecksDefend", + defyPoison: "DS4.ChecksDefyPoison", + disableTraps: "DS4.ChecksDisableTraps", + featOfStrength: "DS4.ChecksFeatOfStrength", + flirt: "DS4.ChecksFlirt", + haggle: "DS4.ChecksHaggle", + hide: "DS4.ChecksHide", + jump: "DS4.ChecksJump", + knowledge: "DS4.ChecksKnowledge", + openLock: "DS4.ChecksOpenLock", + perception: "DS4.ChecksPerception", + pickPocket: "DS4.ChecksPickPocket", + readTracks: "DS4.ChecksReadTracks", + resistDisease: "DS4.ChecksResistDisease", + ride: "DS4.ChecksRide", + search: "DS4.ChecksSearch", + sneak: "DS4.ChecksSneak", + startFire: "DS4.ChecksStartFire", + swim: "DS4.ChecksSwim", + wakeUp: "DS4.ChecksWakeUp", + workMechanism: "DS4.ChecksWorkMechanism", + }, }, /** diff --git a/src/module/macros/macros.ts b/src/module/macros/macros.ts index 95b47854..e2a43e9b 100644 --- a/src/module/macros/macros.ts +++ b/src/module/macros/macros.ts @@ -1,5 +1,7 @@ +import { rollCheck } from "./roll-check"; import { rollItem } from "./roll-item"; export const macros = { - rollItem: rollItem, + rollCheck, + rollItem, }; diff --git a/src/module/macros/roll-check.ts b/src/module/macros/roll-check.ts new file mode 100644 index 00000000..93bcd400 --- /dev/null +++ b/src/module/macros/roll-check.ts @@ -0,0 +1,43 @@ +import { DS4Actor } from "../actor/actor"; +import { Check } from "../actor/actor-prepared-data"; +import { DS4 } from "../config"; +import { getCanvas } from "../helpers"; +import notifications from "../ui/notifications"; + +/** + * Creates a macro from a check drop. + * Get an existing roll check macro if one exists, otherwise create a new one. + * @param check - The name of the check to perform. + * @param slot - The hotbar slot to use. + */ +export async function createRollCheckMacro(check: Check, slot: string): Promise { + const command = `game.ds4.macros.rollCheck("${check}");`; + const macro = + game.macros?.entities.find((m) => m.name === DS4.i18n.checks[check] && m.data.command === command) ?? + (await Macro.create( + { + command, + name: DS4.i18n.checks[check], + type: "script", + // TODO: img, should be addressed in https://git.f3l.de/dungeonslayers/ds4/-/issues/79 + flags: { "ds4.checkMacro": true }, + }, + { displaySheet: false }, + )); + game.user?.assignHotbarMacro(macro, slot); +} + +/** + * Executes the roll check macro for the given check. + */ +export async function rollCheck(check: Check): Promise { + const speaker = ChatMessage.getSpeaker(); + const actor = (getCanvas().tokens.get(speaker.token ?? "")?.actor ?? game.actors?.get(speaker.actor ?? "")) as + | DS4Actor + | null + | undefined; + if (!actor) { + return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); + } + return actor.rollCheck(check); +} diff --git a/src/module/macros/roll-item.ts b/src/module/macros/roll-item.ts index 9d8af10e..3dac24b1 100644 --- a/src/module/macros/roll-item.ts +++ b/src/module/macros/roll-item.ts @@ -28,7 +28,6 @@ export async function createRollItemMacro(itemData: DS4ItemData, slot: string): /** * Executes the roll item macro for the given itemId. - * @param itemId */ export async function rollItem(itemId: string): Promise { const speaker = ChatMessage.getSpeaker(); From 30aed22c031f62938cc1f972a13d8e9e90c53a87 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Wed, 24 Mar 2021 09:24:14 +0100 Subject: [PATCH 2/5] Fix TSDoc string --- src/module/actor/actor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index 8123cd5b..1cf678f4 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -274,7 +274,7 @@ export class DS4Actor extends Actor /** * Roll for a given check. - * @param check The check to perform + * @param check - The check to perform */ async rollCheck(check: Check): Promise { await createCheckRoll(this.data.data.checks[check], { From e545e3f0308d4e967ffe6d621201471e15495fc5 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Mon, 29 Mar 2021 21:20:08 +0200 Subject: [PATCH 3/5] Refactor for better readability (addressed CR comments) --- src/lang/de.json | 4 +-- src/module/actor/actor.ts | 14 +++++----- src/module/macros/helpers.ts | 21 +++++++++++++++ src/module/macros/roll-check.ts | 46 +++++++++++++++++++-------------- src/module/macros/roll-item.ts | 44 +++++++++++++++++-------------- 5 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 src/module/macros/helpers.ts diff --git a/src/lang/de.json b/src/lang/de.json index 1da1dea4..aa8c2685 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -197,8 +197,8 @@ "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}", "DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.", "DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.", - "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item Würfel Makro zu nutzen muss ein Aktor kontolliert werden.", - "DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein ein Proben Würfel Makro zu nutzen muss ein Aktor kontolliert werden.", + "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", + "DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein ein Proben-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", "DS4.WarningControlledActorDoesNotHaveItem": "Der kontrollierte Aktor '{actorName}' ({actorId}) hat kein Item mit der ID '{itemId}'.", "DS4.WarningItemIsNotRollable": "Für das Item '{name}' ({id}) vom Typ '{type}' kann nicht gewürfelt werden.", "DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Makros können nur für besessene Items angelegt werden.", diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index 1cf678f4..17b03b9a 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -116,13 +116,13 @@ export class DS4Actor extends Actor * The list of properties that are derived from others, given in dot notation. */ get derivedDataProperties(): Array { - return Object.keys(DS4.i18n.combatValues) - .map((combatValue) => `data.combatValues.${combatValue}.total`) - .concat( - Object.keys(DS4.i18n.checks) - .filter((check) => check !== "defend") - .map((check) => `data.checks.${check}`), - ); + const combatValueProperties = Object.keys(DS4.i18n.combatValues).map( + (combatValue) => `data.combatValues.${combatValue}.total`, + ); + const checkProperties = Object.keys(DS4.i18n.checks) + .filter((check) => check !== "defend") + .map((check) => `data.checks.${check}`); + return combatValueProperties.concat(checkProperties); } /** diff --git a/src/module/macros/helpers.ts b/src/module/macros/helpers.ts new file mode 100644 index 00000000..73ee8092 --- /dev/null +++ b/src/module/macros/helpers.ts @@ -0,0 +1,21 @@ +import { DS4Actor } from "../actor/actor"; +import { getCanvas } from "../helpers"; + +/** + * Gets the currently active actor based on how {@link ChatMessage} determines + * the current speaker. + * @returns The currently active {@link DS4Actor} if any, and `undefined` otherwise. + */ +export function getActiveActor(): DS4Actor | undefined { + const speaker = ChatMessage.getSpeaker(); + + const speakerToken = speaker.token ? getCanvas().tokens.get(speaker.token) : undefined; + if (speakerToken) { + return speakerToken.actor as DS4Actor; + } + + const speakerActor = speaker.actor ? game.actors?.get(speaker.actor) : undefined; + if (speakerActor) { + return speakerActor as DS4Actor; + } +} diff --git a/src/module/macros/roll-check.ts b/src/module/macros/roll-check.ts index 93bcd400..e6d191b3 100644 --- a/src/module/macros/roll-check.ts +++ b/src/module/macros/roll-check.ts @@ -1,8 +1,7 @@ -import { DS4Actor } from "../actor/actor"; import { Check } from "../actor/actor-prepared-data"; import { DS4 } from "../config"; -import { getCanvas } from "../helpers"; import notifications from "../ui/notifications"; +import { getActiveActor } from "./helpers"; /** * Creates a macro from a check drop. @@ -11,33 +10,40 @@ import notifications from "../ui/notifications"; * @param slot - The hotbar slot to use. */ export async function createRollCheckMacro(check: Check, slot: string): Promise { - const command = `game.ds4.macros.rollCheck("${check}");`; - const macro = - game.macros?.entities.find((m) => m.name === DS4.i18n.checks[check] && m.data.command === command) ?? - (await Macro.create( - { - command, - name: DS4.i18n.checks[check], - type: "script", - // TODO: img, should be addressed in https://git.f3l.de/dungeonslayers/ds4/-/issues/79 - flags: { "ds4.checkMacro": true }, - }, - { displaySheet: false }, - )); + const macro = await getOrCreateRollCheckMacro(check); game.user?.assignHotbarMacro(macro, slot); } +async function getOrCreateRollCheckMacro(check: Check): Promise { + const command = `game.ds4.macros.rollCheck("${check}");`; + + const existingMacro = game.macros?.entities.find( + (m) => m.name === DS4.i18n.checks[check] && m.data.command === command, + ); + if (existingMacro) { + return existingMacro; + } + + return Macro.create( + { + command, + name: DS4.i18n.checks[check], + type: "script", + // TODO: img, should be addressed in https://git.f3l.de/dungeonslayers/ds4/-/issues/79 + flags: { "ds4.checkMacro": true }, + }, + { displaySheet: false }, + ); +} + /** * Executes the roll check macro for the given check. */ export async function rollCheck(check: Check): Promise { - const speaker = ChatMessage.getSpeaker(); - const actor = (getCanvas().tokens.get(speaker.token ?? "")?.actor ?? game.actors?.get(speaker.actor ?? "")) as - | DS4Actor - | null - | undefined; + const actor = getActiveActor(); if (!actor) { return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); } + return actor.rollCheck(check); } diff --git a/src/module/macros/roll-item.ts b/src/module/macros/roll-item.ts index 3dac24b1..ec85fdde 100644 --- a/src/module/macros/roll-item.ts +++ b/src/module/macros/roll-item.ts @@ -1,7 +1,6 @@ -import { DS4Actor } from "../actor/actor"; -import { getCanvas } from "../helpers"; import { DS4ItemData } from "../item/item-data"; import notifications from "../ui/notifications"; +import { getActiveActor } from "./helpers"; /** * Creates a macro from an item drop. @@ -10,34 +9,39 @@ import notifications from "../ui/notifications"; * @param slot - The hotbar slot to use */ export async function createRollItemMacro(itemData: DS4ItemData, slot: string): Promise { - const command = `game.ds4.macros.rollItem("${itemData._id}");`; - const macro = - game.macros?.entities.find((m) => m.name === itemData.name && m.data.command === command) ?? - (await Macro.create( - { - command, - name: itemData.name, - type: "script", - img: itemData.img, - flags: { "ds4.itemMacro": true }, - }, - { displaySheet: false }, - )); + const macro = await getOrCreateRollItemMacro(itemData); game.user?.assignHotbarMacro(macro, slot); } +async function getOrCreateRollItemMacro(itemData: DS4ItemData): Promise { + const command = `game.ds4.macros.rollItem("${itemData._id}");`; + + const existingMacro = game.macros?.entities.find((m) => m.name === itemData.name && m.data.command === command); + if (existingMacro) { + return existingMacro; + } + + return Macro.create( + { + command, + name: itemData.name, + type: "script", + img: itemData.img, + flags: { "ds4.itemMacro": true }, + }, + { displaySheet: false }, + ); +} + /** * Executes the roll item macro for the given itemId. */ export async function rollItem(itemId: string): Promise { - const speaker = ChatMessage.getSpeaker(); - const actor = (getCanvas().tokens.get(speaker.token ?? "")?.actor ?? game.actors?.get(speaker.actor ?? "")) as - | DS4Actor - | null - | undefined; + const actor = getActiveActor(); if (!actor) { return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro")); } + const item = actor.items?.get(itemId); if (!item) { return notifications.warn( From 23a4f7138e5b117d83812be759ca0b13e377b68d Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Mon, 29 Mar 2021 21:46:29 +0200 Subject: [PATCH 4/5] Remove unused utils --- src/module/common/utils.ts | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 src/module/common/utils.ts diff --git a/src/module/common/utils.ts b/src/module/common/utils.ts deleted file mode 100644 index df5fe8f3..00000000 --- a/src/module/common/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Partition an array into two, following a predicate. - * @param input - The Array to split. - * @param predicate - The predicate by which to split. - * @returns A tuple of two arrays, the first one containing all elements from `input` that match the predicate, the second one containing those that do not. - */ -export function partition(input: Array, predicate: (v: T) => boolean): [T[], T[]] { - return input.reduce( - (p: [Array, Array], cur: T) => { - if (predicate(cur)) { - p[0].push(cur); - } else { - p[1].push(cur); - } - return p; - }, - [[], []], - ); -} - -/** - * Zips two Arrays to an array of pairs of elements with corresponding indices. Excessive elements are dropped. - * @param a1 - First array to zip. - * @param a2 - Second array to zip. - * - * @typeParam T - Type of elements contained in `a1`. - * @typeParam U - Type of elements contained in `a2`. - * - * @returns The array of pairs that had the same index in their source array. - */ -export function zip(a1: Array, a2: Array): Array<[T, U]> { - if (a1.length <= a2.length) { - return a1.map((e1, i) => [e1, a2[i]]); - } else { - return a2.map((e2, i) => [a1[i], e2]); - } -} From d446e07abd0047637830ec683234d5ff802e25a7 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 30 Mar 2021 20:41:12 +0000 Subject: [PATCH 5/5] Apply 2 suggestion(s) to 1 file(s) --- src/lang/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/de.json b/src/lang/de.json index aa8c2685..96c654af 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -197,8 +197,8 @@ "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}", "DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.", "DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.", - "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", - "DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein ein Proben-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", + "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein Item-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", + "DS4.WarningMustControlActorToUseRollCheckMacro": "Um ein Proben-Würfel-Makro zu nutzen muss ein Aktor kontrolliert werden.", "DS4.WarningControlledActorDoesNotHaveItem": "Der kontrollierte Aktor '{actorName}' ({actorId}) hat kein Item mit der ID '{itemId}'.", "DS4.WarningItemIsNotRollable": "Für das Item '{name}' ({id}) vom Typ '{type}' kann nicht gewürfelt werden.", "DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Makros können nur für besessene Items angelegt werden.",