Merge branch 'use-correct-speaker-for-rolls' into 'master'

feat: use the selected token as speaker when performing a roll

See merge request dungeonslayers/ds4!144
This commit is contained in:
Johannes Loher 2021-09-19 18:28:06 +00:00
commit 5bee77c902
13 changed files with 57 additions and 61 deletions

View file

@ -56,7 +56,7 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "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/fs-extra": "9.0.12",
"@types/jest": "27.0.1", "@types/jest": "27.0.1",
"@typescript-eslint/eslint-plugin": "4.31.1", "@typescript-eslint/eslint-plugin": "4.31.1",

View file

@ -274,6 +274,7 @@
"DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben.", "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben.",
"DS4.SettingShowSlayerPointsName": "Slayerpunkte", "DS4.SettingShowSlayerPointsName": "Slayerpunkte",
"DS4.SettingShowSlayerPointsHint": "Zeige Slayerpunkte im Charakterbogen an.", "DS4.SettingShowSlayerPointsHint": "Zeige Slayerpunkte im Charakterbogen an.",
"DS4.Checks": "Proben",
"DS4.ChecksAppraise": "Schätzen", "DS4.ChecksAppraise": "Schätzen",
"DS4.ChecksChangeSpell": "Zauber Wechseln", "DS4.ChecksChangeSpell": "Zauber Wechseln",
"DS4.ChecksClimb": "Klettern", "DS4.ChecksClimb": "Klettern",

View file

@ -274,6 +274,7 @@
"DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks.", "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks.",
"DS4.SettingShowSlayerPointsName": "Slayer Points", "DS4.SettingShowSlayerPointsName": "Slayer Points",
"DS4.SettingShowSlayerPointsHint": "Show Slayer Points in the character sheet.", "DS4.SettingShowSlayerPointsHint": "Show Slayer Points in the character sheet.",
"DS4.Checks": "Checks",
"DS4.ChecksAppraise": "Appraise", "DS4.ChecksAppraise": "Appraise",
"DS4.ChecksChangeSpell": "Change Spell", "DS4.ChecksChangeSpell": "Change Spell",
"DS4.ChecksClimb": "Climb", "DS4.ChecksClimb": "Climb",

View file

@ -294,33 +294,43 @@ export class DS4Actor extends Actor {
/** /**
* Roll for a given check. * Roll for a given check.
* @param check - The check to perform * @param check - The check to perform
* @param options - Additional options to customize the roll
*/ */
async rollCheck(check: Check): Promise<void> { async rollCheck(
check: Check,
options: { speaker?: { token?: TokenDocument; alias?: string } } = {},
): Promise<void> {
const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker });
await createCheckRoll(this.data.data.checks[check], { await createCheckRoll(this.data.data.checks[check], {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get("core", "rollMode"),
maximumCoupResult: this.data.data.rolling.maximumCoupResult, maximumCoupResult: this.data.data.rolling.maximumCoupResult,
minimumFumbleResult: this.data.data.rolling.minimumFumbleResult, minimumFumbleResult: this.data.data.rolling.minimumFumbleResult,
flavor: "DS4.ActorCheckFlavor", 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 * Roll a generic check. A dialog is presented to select the combination of
* Attribute and Trait to perform the check against. * Attribute and Trait to perform the check against.
* @param options - Additional options to customize the roll
*/ */
async rollGenericCheck(): Promise<void> { async rollGenericCheck(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
const { attribute, trait } = await this.selectAttributeAndTrait(); const { attribute, trait } = await this.selectAttributeAndTrait();
const checkTargetNumber = this.data.data.attributes[attribute].total + this.data.data.traits[trait].total; 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, { await createCheckRoll(checkTargetNumber, {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get("core", "rollMode"),
maximumCoupResult: this.data.data.rolling.maximumCoupResult, maximumCoupResult: this.data.data.rolling.maximumCoupResult,
minimumFumbleResult: this.data.data.rolling.minimumFumbleResult, minimumFumbleResult: this.data.data.rolling.minimumFumbleResult,
flavor: getGame().i18n.format("DS4.ActorGenericCheckFlavor", { flavor: "DS4.ActorGenericCheckFlavor",
actor: this.name, flavorData: {
actor: speaker.alias ?? this.name,
attribute: DS4.i18n.attributes[attribute], attribute: DS4.i18n.attributes[attribute],
trait: DS4.i18n.traits[trait], trait: DS4.i18n.traits[trait],
}), },
speaker,
}); });
} }

View file

@ -44,7 +44,7 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
} }
/** @override */ /** @override */
setPosition(options: Partial<Application.Position> = {}): (Application.Position & { height: number }) | undefined { setPosition(options: Partial<Application.Position> = {}): (Application.Position & { height: number }) | void {
const position = super.setPosition(options); const position = super.setPosition(options);
if (position) { if (position) {
const sheetBody = this.element.find(".sheet-body"); const sheetBody = this.element.find(".sheet-body");

View file

@ -64,13 +64,14 @@ export class DS4Item extends Item {
/** /**
* Roll a check for an action with this item. * Roll a check for an action with this item.
* @param options - Additional options to customize the roll
*/ */
async roll(): Promise<void> { async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
switch (this.data.type) { switch (this.data.type) {
case "weapon": case "weapon":
return this.rollWeapon(); return this.rollWeapon(options);
case "spell": case "spell":
return this.rollSpell(); return this.rollSpell(options);
default: default:
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type }), getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type }),
@ -78,7 +79,7 @@ export class DS4Item extends Item {
} }
} }
protected async rollWeapon(): Promise<void> { protected async rollWeapon(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
if (!(this.data.type === "weapon")) { if (!(this.data.type === "weapon")) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorWrongItemType", { getGame().i18n.format("DS4.ErrorWrongItemType", {
@ -109,16 +110,18 @@ export class DS4Item extends Item {
const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType);
const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus; const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus;
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: "DS4.ItemWeaponCheckFlavor", 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<void> { protected async rollSpell(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
if (!(this.data.type === "spell")) { if (!(this.data.type === "spell")) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorWrongItemType", { getGame().i18n.format("DS4.ErrorWrongItemType", {
@ -157,12 +160,14 @@ export class DS4Item extends Item {
const spellType = this.data.data.spellType; const spellType = this.data.data.spellType;
const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0); const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0);
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: "DS4.ItemSpellCheckFlavor", flavor: "DS4.ItemSpellCheckFlavor",
flavorData: { actor: this.actor.name, spell: this.name }, flavorData: { actor: speaker.alias ?? this.actor.name, spell: this.name },
speaker,
}); });
} }

View file

@ -6,20 +6,18 @@ import { DS4Actor } from "../actor/actor";
import { getCanvas, getGame } from "../helpers"; import { getCanvas, getGame } from "../helpers";
/** /**
* Gets the currently active actor based on how {@link ChatMessage} determines * Gets the currently active actor and token based on how {@link ChatMessage}
* the current speaker. * determines the current speaker.
* @returns The currently active {@link DS4Actor} if any, and `undefined` otherwise. * @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 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) { if (speakerToken) {
return speakerToken.actor ?? undefined; return { actor: speakerToken.actor ?? undefined, token: speakerToken };
} }
const speakerActor = speaker.actor ? getGame().actors?.get(speaker.actor) : undefined; const speakerActor = speaker.actor ? getGame().actors?.get(speaker.actor) : undefined;
if (speakerActor) { return { actor: speakerActor };
return speakerActor;
}
} }

View file

@ -6,7 +6,7 @@ import { Check } from "../actor/actor-data-properties";
import { DS4 } from "../config"; import { DS4 } from "../config";
import { getGame } from "../helpers"; import { getGame } from "../helpers";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { getActiveActor } from "./helpers"; import { getActiveActorAndToken } from "./helpers";
/** /**
* Creates a macro from a check drop. * Creates a macro from a check drop.
@ -45,10 +45,10 @@ async function getOrCreateRollCheckMacro(check: Check): Promise<Macro | undefine
* Executes the roll check macro for the given check. * Executes the roll check macro for the given check.
*/ */
export async function rollCheck(check: Check): Promise<void> { export async function rollCheck(check: Check): Promise<void> {
const actor = getActiveActor(); const { actor, token } = getActiveActorAndToken();
if (!actor) { if (!actor) {
return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); 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 }));
} }

View file

@ -4,16 +4,16 @@
import { getGame } from "../helpers"; import { getGame } from "../helpers";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { getActiveActor } from "./helpers"; import { getActiveActorAndToken } from "./helpers";
/** /**
* Executes the roll generic check macro. * Executes the roll generic check macro.
*/ */
export async function rollGenericCheck(): Promise<void> { export async function rollGenericCheck(): Promise<void> {
const actor = getActiveActor(); const { actor, token } = getActiveActorAndToken();
if (!actor) { if (!actor) {
return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); 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 }));
} }

View file

@ -4,7 +4,7 @@
import { getGame } from "../helpers"; import { getGame } from "../helpers";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { getActiveActor } from "./helpers"; import { getActiveActorAndToken } from "./helpers";
/** /**
* Creates a macro from an item drop. * 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`. * Executes the roll item macro for the item associated to the given `itemId`.
*/ */
export async function rollItem(itemId: string): Promise<void> { export async function rollItem(itemId: string): Promise<void> {
const actor = getActiveActor(); const { actor, token } = getActiveActorAndToken();
if (!actor) { if (!actor) {
return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro")); return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro"));
} }
@ -57,5 +57,5 @@ export async function rollItem(itemId: string): Promise<void> {
); );
} }
return item.roll().catch((e) => notifications.error(e, { log: true })); return item.roll({ speaker: { token } }).catch((e) => notifications.error(e, { log: true }));
} }

View file

@ -45,7 +45,7 @@ class CheckFactory {
); );
const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula;
const roll = Roll.create(formula); const roll = Roll.create(formula);
const speaker = ChatMessage.getSpeaker(); const speaker = this.options.speaker ?? ChatMessage.getSpeaker();
return roll.toMessage( return roll.toMessage(
{ {
@ -96,6 +96,7 @@ export async function createCheckRoll(
rollMode: gmModifierData.rollMode ?? options.rollMode, rollMode: gmModifierData.rollMode ?? options.rollMode,
flavor: options.flavor, flavor: options.flavor,
flavorData: options.flavorData, flavorData: options.flavorData,
speaker: options.speaker,
}; };
// Create Factory // Create Factory
@ -230,4 +231,5 @@ export interface DS4CheckFactoryOptions {
rollMode: foundry.CONST.DiceRollMode; rollMode: foundry.CONST.DiceRollMode;
flavor?: string; flavor?: string;
flavorData?: Record<string, string | number | null>; flavorData?: Record<string, string | number | null>;
speaker?: ReturnType<typeof ChatMessage.getSpeaker>;
} }

View file

@ -33,7 +33,7 @@
"bugs": "https://git.f3l.de/dungeonslayers/ds4/-/issues", "bugs": "https://git.f3l.de/dungeonslayers/ds4/-/issues",
"changelog": "https://git.f3l.de/dungeonslayers/ds4/-/releases/1.5.0", "changelog": "https://git.f3l.de/dungeonslayers/ds4/-/releases/1.5.0",
"version": "1.5.0", "version": "1.5.0",
"minimumCoreVersion": "0.8.8", "minimumCoreVersion": "0.8.9",
"compatibleCoreVersion": "0.8.9", "compatibleCoreVersion": "0.8.9",
"esmodules": ["module/ds4.js"], "esmodules": ["module/ds4.js"],
"styles": ["css/ds4.css"], "styles": ["css/ds4.css"],

View file

@ -868,9 +868,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@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.8-9 version: 0.8.9-3
resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.8.8-9" resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.8.9-3"
dependencies: dependencies:
"@types/jquery": ~3.5.6 "@types/jquery": ~3.5.6
"@types/simple-peer": ~9.11.1 "@types/simple-peer": ~9.11.1
@ -879,8 +879,7 @@ __metadata:
pixi.js: 5.3.4 pixi.js: 5.3.4
socket.io-client: 4.1.2 socket.io-client: 4.1.2
tinymce: 5.8.1 tinymce: 5.8.1
typescript: ^4.3.5 checksum: 01f4bd4ea4b01a51a1f0572a683817ad63dc4daa46bbf0ed5fa6854bc88eb57269d0940575efbf58e984f0b6a16c78d166dbd19c7d0719a9750e7bbe1c2b66ac
checksum: 0c217a2bb2a1f9ae87548511d5e835648aee03522e0ea7c374bbc3c9152ad8a6568cc4f421fbd14c6b81434df9057eae2e6217371accd417a7455a993a7c4108
languageName: node languageName: node
linkType: hard linkType: hard
@ -3366,7 +3365,7 @@ __metadata:
dependencies: dependencies:
"@commitlint/cli": 13.1.0 "@commitlint/cli": 13.1.0
"@commitlint/config-conventional": 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/fs-extra": 9.0.12
"@types/jest": 27.0.1 "@types/jest": 27.0.1
"@typescript-eslint/eslint-plugin": 4.31.1 "@typescript-eslint/eslint-plugin": 4.31.1
@ -9160,16 +9159,6 @@ typescript@4.4.3:
languageName: node languageName: node
linkType: hard 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<compat/typescript>": "typescript@patch:typescript@4.4.3#~builtin<compat/typescript>":
version: 4.4.3 version: 4.4.3
resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin<compat/typescript>::version=4.4.3&hash=32657b" resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin<compat/typescript>::version=4.4.3&hash=32657b"
@ -9180,16 +9169,6 @@ typescript@^4.3.5:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@^4.3.5#~builtin<compat/typescript>":
version: 4.3.5
resolution: "typescript@patch:typescript@npm%3A4.3.5#~builtin<compat/typescript>::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": "uglify-js@npm:^3.1.4":
version: 3.14.1 version: 3.14.1
resolution: "uglify-js@npm:3.14.1" resolution: "uglify-js@npm:3.14.1"