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

This commit is contained in:
Johannes Loher 2021-09-19 20:12:01 +02:00
parent 3b2a7857e1
commit 0fa9d838e2
13 changed files with 57 additions and 61 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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<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], {
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<void> {
async rollGenericCheck(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
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,
});
}

View file

@ -44,7 +44,7 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
}
/** @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);
if (position) {
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.
* @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) {
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<void> {
protected async rollWeapon(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
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<void> {
protected async rollSpell(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise<void> {
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,
});
}

View file

@ -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 };
}

View file

@ -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<Macro | undefine
* Executes the roll check macro for the given check.
*/
export async function rollCheck(check: Check): Promise<void> {
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 }));
}

View file

@ -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<void> {
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 }));
}

View file

@ -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<void> {
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<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 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<string, string | number | null>;
speaker?: ReturnType<typeof ChatMessage.getSpeaker>;
}

View file

@ -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"],

View file

@ -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<compat/typescript>":
version: 4.4.3
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
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":
version: 3.14.1
resolution: "uglify-js@npm:3.14.1"