Merge branch '077-common-checks-macros-with-effects' into 'master'
Add functionality for common checks, which can be affected by effects and be performed as macros Closes #77 See merge request dungeonslayers/ds4!98
This commit is contained in:
commit
f2cbc67b77
10 changed files with 254 additions and 65 deletions
|
@ -197,7 +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.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.",
|
||||
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<DS4ActorData, DS4Item, DS4ActorPreparedData>
|
|||
*/
|
||||
prepareDerivedData(): void {
|
||||
this._prepareCombatValues();
|
||||
this._prepareChecks();
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of properties that are derived from others, given in dot notation.
|
||||
*/
|
||||
get derivedDataProperties(): Array<string> {
|
||||
return Object.keys(DS4.i18n.combatValues).map((combatValue) => `data.combatValues.${combatValue}.total`);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,6 +130,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
|
|||
*/
|
||||
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<DS4ActorData, DS4Item, DS4ActorPreparedData>
|
|||
* 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<DS4ActorData, DS4Item, DS4ActorPreparedData>
|
|||
.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<DS4ActorData, DS4Item, DS4ActorPreparedData>
|
|||
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<void> {
|
||||
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] }),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T>(input: Array<T>, predicate: (v: T) => boolean): [T[], T[]] {
|
||||
return input.reduce(
|
||||
(p: [Array<T>, Array<T>], 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<T, U>(a1: Array<T>, a2: Array<U>): 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]);
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
21
src/module/macros/helpers.ts
Normal file
21
src/module/macros/helpers.ts
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { rollCheck } from "./roll-check";
|
||||
import { rollItem } from "./roll-item";
|
||||
|
||||
export const macros = {
|
||||
rollItem: rollItem,
|
||||
rollCheck,
|
||||
rollItem,
|
||||
};
|
||||
|
|
49
src/module/macros/roll-check.ts
Normal file
49
src/module/macros/roll-check.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { Check } from "../actor/actor-prepared-data";
|
||||
import { DS4 } from "../config";
|
||||
import notifications from "../ui/notifications";
|
||||
import { getActiveActor } from "./helpers";
|
||||
|
||||
/**
|
||||
* 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<void> {
|
||||
const macro = await getOrCreateRollCheckMacro(check);
|
||||
game.user?.assignHotbarMacro(macro, slot);
|
||||
}
|
||||
|
||||
async function getOrCreateRollCheckMacro(check: Check): Promise<Macro | null> {
|
||||
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<void> {
|
||||
const actor = getActiveActor();
|
||||
if (!actor) {
|
||||
return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro"));
|
||||
}
|
||||
|
||||
return actor.rollCheck(check);
|
||||
}
|
|
@ -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,35 +9,39 @@ import notifications from "../ui/notifications";
|
|||
* @param slot - The hotbar slot to use
|
||||
*/
|
||||
export async function createRollItemMacro(itemData: DS4ItemData, slot: string): Promise<void> {
|
||||
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<Macro | null> {
|
||||
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.
|
||||
* @param itemId
|
||||
*/
|
||||
export async function rollItem(itemId: string): Promise<void> {
|
||||
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(
|
||||
|
|
Loading…
Reference in a new issue