From 0fa9d838e2dfa6ed31f30615f05863da1fad8bef Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Sun, 19 Sep 2021 20:12:01 +0200 Subject: [PATCH] feat: use the selected token as speaker when performing a roll --- package.json | 2 +- src/lang/de.json | 1 + src/lang/en.json | 1 + src/module/actor/actor.ts | 22 +++++++++++++----- src/module/item/item-sheet.ts | 2 +- src/module/item/item.ts | 19 +++++++++------ src/module/macros/helpers.ts | 16 ++++++------- src/module/macros/roll-check.ts | 6 ++--- src/module/macros/roll-generic-check.ts | 6 ++--- src/module/macros/roll-item.ts | 6 ++--- src/module/rolls/check-factory.ts | 4 +++- src/system.json | 2 +- yarn.lock | 31 ++++--------------------- 13 files changed, 57 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index c5d8bffb..4ce10749 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@commitlint/cli": "13.1.0", "@commitlint/config-conventional": "13.1.0", - "@league-of-foundry-developers/foundry-vtt-types": "0.8.8-9", + "@league-of-foundry-developers/foundry-vtt-types": "0.8.9-3", "@types/fs-extra": "9.0.12", "@types/jest": "27.0.1", "@typescript-eslint/eslint-plugin": "4.31.1", diff --git a/src/lang/de.json b/src/lang/de.json index 374e5587..067f0d4f 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -274,6 +274,7 @@ "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben.", "DS4.SettingShowSlayerPointsName": "Slayerpunkte", "DS4.SettingShowSlayerPointsHint": "Zeige Slayerpunkte im Charakterbogen an.", + "DS4.Checks": "Proben", "DS4.ChecksAppraise": "Schätzen", "DS4.ChecksChangeSpell": "Zauber Wechseln", "DS4.ChecksClimb": "Klettern", diff --git a/src/lang/en.json b/src/lang/en.json index cc0364d2..2b9bb069 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -274,6 +274,7 @@ "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks.", "DS4.SettingShowSlayerPointsName": "Slayer Points", "DS4.SettingShowSlayerPointsHint": "Show Slayer Points in the character sheet.", + "DS4.Checks": "Checks", "DS4.ChecksAppraise": "Appraise", "DS4.ChecksChangeSpell": "Change Spell", "DS4.ChecksClimb": "Climb", diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index e361f391..f0abfaf8 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -294,33 +294,43 @@ export class DS4Actor extends Actor { /** * Roll for a given check. * @param check - The check to perform + * @param options - Additional options to customize the roll */ - async rollCheck(check: Check): Promise { + async rollCheck( + check: Check, + options: { speaker?: { token?: TokenDocument; alias?: string } } = {}, + ): Promise { + const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker }); await createCheckRoll(this.data.data.checks[check], { rollMode: getGame().settings.get("core", "rollMode"), maximumCoupResult: this.data.data.rolling.maximumCoupResult, minimumFumbleResult: this.data.data.rolling.minimumFumbleResult, flavor: "DS4.ActorCheckFlavor", - flavorData: { actor: this.name, check: DS4.i18nKeys.checks[check] }, + flavorData: { actor: speaker.alias ?? this.name, check: DS4.i18nKeys.checks[check] }, + speaker, }); } /** * Roll a generic check. A dialog is presented to select the combination of * Attribute and Trait to perform the check against. + * @param options - Additional options to customize the roll */ - async rollGenericCheck(): Promise { + async rollGenericCheck(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { const { attribute, trait } = await this.selectAttributeAndTrait(); const checkTargetNumber = this.data.data.attributes[attribute].total + this.data.data.traits[trait].total; + const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker }); await createCheckRoll(checkTargetNumber, { rollMode: getGame().settings.get("core", "rollMode"), maximumCoupResult: this.data.data.rolling.maximumCoupResult, minimumFumbleResult: this.data.data.rolling.minimumFumbleResult, - flavor: getGame().i18n.format("DS4.ActorGenericCheckFlavor", { - actor: this.name, + flavor: "DS4.ActorGenericCheckFlavor", + flavorData: { + actor: speaker.alias ?? this.name, attribute: DS4.i18n.attributes[attribute], trait: DS4.i18n.traits[trait], - }), + }, + speaker, }); } diff --git a/src/module/item/item-sheet.ts b/src/module/item/item-sheet.ts index ee8b1346..943ac085 100644 --- a/src/module/item/item-sheet.ts +++ b/src/module/item/item-sheet.ts @@ -44,7 +44,7 @@ export class DS4ItemSheet extends ItemSheet } /** @override */ - setPosition(options: Partial = {}): (Application.Position & { height: number }) | undefined { + setPosition(options: Partial = {}): (Application.Position & { height: number }) | void { const position = super.setPosition(options); if (position) { const sheetBody = this.element.find(".sheet-body"); diff --git a/src/module/item/item.ts b/src/module/item/item.ts index caf5a6d9..822a68e7 100644 --- a/src/module/item/item.ts +++ b/src/module/item/item.ts @@ -64,13 +64,14 @@ export class DS4Item extends Item { /** * Roll a check for an action with this item. + * @param options - Additional options to customize the roll */ - async roll(): Promise { + async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { switch (this.data.type) { case "weapon": - return this.rollWeapon(); + return this.rollWeapon(options); case "spell": - return this.rollSpell(); + return this.rollSpell(options); default: throw new Error( getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type }), @@ -78,7 +79,7 @@ export class DS4Item extends Item { } } - protected async rollWeapon(): Promise { + protected async rollWeapon(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { if (!(this.data.type === "weapon")) { throw new Error( getGame().i18n.format("DS4.ErrorWrongItemType", { @@ -109,16 +110,18 @@ export class DS4Item extends Item { const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus; + const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); await createCheckRoll(checkTargetNumber, { rollMode: getGame().settings.get("core", "rollMode"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, flavor: "DS4.ItemWeaponCheckFlavor", - flavorData: { actor: this.actor.name, weapon: this.name }, + flavorData: { actor: speaker.alias ?? this.actor.name, weapon: this.name }, + speaker, }); } - protected async rollSpell(): Promise { + protected async rollSpell(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { if (!(this.data.type === "spell")) { throw new Error( getGame().i18n.format("DS4.ErrorWrongItemType", { @@ -157,12 +160,14 @@ export class DS4Item extends Item { const spellType = this.data.data.spellType; const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0); + const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); await createCheckRoll(checkTargetNumber, { rollMode: getGame().settings.get("core", "rollMode"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, flavor: "DS4.ItemSpellCheckFlavor", - flavorData: { actor: this.actor.name, spell: this.name }, + flavorData: { actor: speaker.alias ?? this.actor.name, spell: this.name }, + speaker, }); } diff --git a/src/module/macros/helpers.ts b/src/module/macros/helpers.ts index 1aa42890..941ee947 100644 --- a/src/module/macros/helpers.ts +++ b/src/module/macros/helpers.ts @@ -6,20 +6,18 @@ import { DS4Actor } from "../actor/actor"; import { getCanvas, getGame } 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. + * Gets the currently active actor and token based on how {@link ChatMessage} + * determines the current speaker. + * @returns The currently active {@link DS4Actor} and {@link TokenDocument}. */ -export function getActiveActor(): DS4Actor | undefined { +export function getActiveActorAndToken(): { actor?: DS4Actor; token?: TokenDocument } { const speaker = ChatMessage.getSpeaker(); - const speakerToken = speaker.token ? getCanvas().tokens?.get(speaker.token) : undefined; + const speakerToken = speaker.token ? getCanvas().tokens?.get(speaker.token)?.document : undefined; if (speakerToken) { - return speakerToken.actor ?? undefined; + return { actor: speakerToken.actor ?? undefined, token: speakerToken }; } const speakerActor = speaker.actor ? getGame().actors?.get(speaker.actor) : undefined; - if (speakerActor) { - return speakerActor; - } + return { actor: speakerActor }; } diff --git a/src/module/macros/roll-check.ts b/src/module/macros/roll-check.ts index cd5f34e8..327541af 100644 --- a/src/module/macros/roll-check.ts +++ b/src/module/macros/roll-check.ts @@ -6,7 +6,7 @@ import { Check } from "../actor/actor-data-properties"; import { DS4 } from "../config"; import { getGame } from "../helpers"; import notifications from "../ui/notifications"; -import { getActiveActor } from "./helpers"; +import { getActiveActorAndToken } from "./helpers"; /** * Creates a macro from a check drop. @@ -45,10 +45,10 @@ async function getOrCreateRollCheckMacro(check: Check): Promise { - const actor = getActiveActor(); + const { actor, token } = getActiveActorAndToken(); if (!actor) { return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); } - return actor.rollCheck(check).catch((e) => notifications.error(e, { log: true })); + return actor.rollCheck(check, { speaker: { token } }).catch((e) => notifications.error(e, { log: true })); } diff --git a/src/module/macros/roll-generic-check.ts b/src/module/macros/roll-generic-check.ts index 7584f0a7..a0108267 100644 --- a/src/module/macros/roll-generic-check.ts +++ b/src/module/macros/roll-generic-check.ts @@ -4,16 +4,16 @@ import { getGame } from "../helpers"; import notifications from "../ui/notifications"; -import { getActiveActor } from "./helpers"; +import { getActiveActorAndToken } from "./helpers"; /** * Executes the roll generic check macro. */ export async function rollGenericCheck(): Promise { - const actor = getActiveActor(); + const { actor, token } = getActiveActorAndToken(); if (!actor) { return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); } - return actor.rollGenericCheck().catch((e) => notifications.error(e, { log: true })); + return actor.rollGenericCheck({ speaker: { token } }).catch((e) => notifications.error(e, { log: true })); } diff --git a/src/module/macros/roll-item.ts b/src/module/macros/roll-item.ts index b0a2b84b..b78c908a 100644 --- a/src/module/macros/roll-item.ts +++ b/src/module/macros/roll-item.ts @@ -4,7 +4,7 @@ import { getGame } from "../helpers"; import notifications from "../ui/notifications"; -import { getActiveActor } from "./helpers"; +import { getActiveActorAndToken } from "./helpers"; /** * Creates a macro from an item drop. @@ -41,7 +41,7 @@ async function getOrCreateRollItemMacro(itemData: foundry.data.ItemData["_source * Executes the roll item macro for the item associated to the given `itemId`. */ export async function rollItem(itemId: string): Promise { - const actor = getActiveActor(); + const { actor, token } = getActiveActorAndToken(); if (!actor) { return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro")); } @@ -57,5 +57,5 @@ export async function rollItem(itemId: string): Promise { ); } - return item.roll().catch((e) => notifications.error(e, { log: true })); + return item.roll({ speaker: { token } }).catch((e) => notifications.error(e, { log: true })); } diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts index 48dacc94..4b0e5c5a 100644 --- a/src/module/rolls/check-factory.ts +++ b/src/module/rolls/check-factory.ts @@ -45,7 +45,7 @@ class CheckFactory { ); const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const roll = Roll.create(formula); - const speaker = ChatMessage.getSpeaker(); + const speaker = this.options.speaker ?? ChatMessage.getSpeaker(); return roll.toMessage( { @@ -96,6 +96,7 @@ export async function createCheckRoll( rollMode: gmModifierData.rollMode ?? options.rollMode, flavor: options.flavor, flavorData: options.flavorData, + speaker: options.speaker, }; // Create Factory @@ -230,4 +231,5 @@ export interface DS4CheckFactoryOptions { rollMode: foundry.CONST.DiceRollMode; flavor?: string; flavorData?: Record; + speaker?: ReturnType; } diff --git a/src/system.json b/src/system.json index a6c7e4f3..ab41631c 100644 --- a/src/system.json +++ b/src/system.json @@ -33,7 +33,7 @@ "bugs": "https://git.f3l.de/dungeonslayers/ds4/-/issues", "changelog": "https://git.f3l.de/dungeonslayers/ds4/-/releases/1.5.0", "version": "1.5.0", - "minimumCoreVersion": "0.8.8", + "minimumCoreVersion": "0.8.9", "compatibleCoreVersion": "0.8.9", "esmodules": ["module/ds4.js"], "styles": ["css/ds4.css"], diff --git a/yarn.lock b/yarn.lock index 08c9051d..2278ab5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -868,9 +868,9 @@ __metadata: languageName: node linkType: hard -"@league-of-foundry-developers/foundry-vtt-types@npm:0.8.8-9": - version: 0.8.8-9 - resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.8.8-9" +"@league-of-foundry-developers/foundry-vtt-types@npm:0.8.9-3": + version: 0.8.9-3 + resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.8.9-3" dependencies: "@types/jquery": ~3.5.6 "@types/simple-peer": ~9.11.1 @@ -879,8 +879,7 @@ __metadata: pixi.js: 5.3.4 socket.io-client: 4.1.2 tinymce: 5.8.1 - typescript: ^4.3.5 - checksum: 0c217a2bb2a1f9ae87548511d5e835648aee03522e0ea7c374bbc3c9152ad8a6568cc4f421fbd14c6b81434df9057eae2e6217371accd417a7455a993a7c4108 + checksum: 01f4bd4ea4b01a51a1f0572a683817ad63dc4daa46bbf0ed5fa6854bc88eb57269d0940575efbf58e984f0b6a16c78d166dbd19c7d0719a9750e7bbe1c2b66ac languageName: node linkType: hard @@ -3366,7 +3365,7 @@ __metadata: dependencies: "@commitlint/cli": 13.1.0 "@commitlint/config-conventional": 13.1.0 - "@league-of-foundry-developers/foundry-vtt-types": 0.8.8-9 + "@league-of-foundry-developers/foundry-vtt-types": 0.8.9-3 "@types/fs-extra": 9.0.12 "@types/jest": 27.0.1 "@typescript-eslint/eslint-plugin": 4.31.1 @@ -9160,16 +9159,6 @@ typescript@4.4.3: languageName: node linkType: hard -typescript@^4.3.5: - version: 4.3.5 - resolution: "typescript@npm:4.3.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: bab033b5e2b0790dd35b77fd005df976ef80b8d84fd2c6e63cc31808151875beae9216e5a315fe7068e8499905c3c354248fe83272cdfc13b7705635f0c66c97 - languageName: node - linkType: hard - "typescript@patch:typescript@4.4.3#~builtin": version: 4.4.3 resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin::version=4.4.3&hash=32657b" @@ -9180,16 +9169,6 @@ typescript@^4.3.5: languageName: node linkType: hard -"typescript@patch:typescript@^4.3.5#~builtin": - version: 4.3.5 - resolution: "typescript@patch:typescript@npm%3A4.3.5#~builtin::version=4.3.5&hash=32657b" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 4e2edec3c9b7b9a22ad9cbf9371339d9e13bfe44731f0e2d6a5287f991c3b0e3e0ac0c0ebbf9bf6fc2051e30559c789bfc9cf572cd6b836a0222ae141fac1dba - languageName: node - linkType: hard - "uglify-js@npm:^3.1.4": version: 3.14.1 resolution: "uglify-js@npm:3.14.1"