From 212295069fdea37edfa739acf7adceb97e5246c7 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Sun, 14 Mar 2021 19:04:28 +0100 Subject: [PATCH 1/9] Add functionality to create item macros via drag & drop --- src/lang/de.json | 7 ++++- src/lang/en.json | 9 ++++-- src/module/ds4.ts | 5 +++ src/module/helpers.ts | 6 ++++ src/module/hooks/hooks.ts | 5 +++ src/module/hooks/hotbar-drop.ts | 28 +++++++++++++++++ src/module/item/item.ts | 18 +++++++---- src/module/macros/item-macro.ts | 54 +++++++++++++++++++++++++++++++++ src/module/macros/macros.ts | 5 +++ 9 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 src/module/helpers.ts create mode 100644 src/module/hooks/hooks.ts create mode 100644 src/module/hooks/hotbar-drop.ts create mode 100644 src/module/macros/item-macro.ts create mode 100644 src/module/macros/macros.ts diff --git a/src/lang/de.json b/src/lang/de.json index 3e2c0ae9..3c65708d 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -192,7 +192,12 @@ "DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.", "DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}", - "DS4.ErrorItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.", + "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.WarningMustControlActorToRollItemMacro": "Um ein Item 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.", "DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' musss manuell angegeben werden.", "DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Migrationsversion {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.", "DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!", diff --git a/src/lang/en.json b/src/lang/en.json index 056091c6..7a2e5736 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -192,8 +192,13 @@ "DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.", "DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}", - "DS4.ErrorItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.", - "DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bons '{spellBonus}' of the spell '{name}' needs to be entered by manually.", + "DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.", + "DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.", + "DS4.WarningMustControlActorToRollItemMacro": "You must control an actor to be able to use an item 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.", + "DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bonus '{spellBonus}' of the spell '{name}' needs to be entered by manually.", "DS4.InfoSystemUpdateStart": "Migrating DS4 system from migration version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.", "DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!", "DS4.UnitRounds": "Rounds", diff --git a/src/module/ds4.ts b/src/module/ds4.ts index 67d03724..710bbd68 100644 --- a/src/module/ds4.ts +++ b/src/module/ds4.ts @@ -10,6 +10,8 @@ import { registerSystemSettings } from "./settings"; import { migration } from "./migrations"; import registerHandlebarsHelpers from "./handlebars/handlebars-helpers"; import registerHandlebarsPartials from "./handlebars/handlebars-partials"; +import { macros } from "./macros/macros"; +import registerHooks from "./hooks/hooks"; Hooks.once("init", async () => { console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`); @@ -20,6 +22,7 @@ Hooks.once("init", async () => { DS4, createCheckRoll, migration, + macros, }; CONFIG.DS4 = DS4; @@ -43,6 +46,8 @@ Hooks.once("init", async () => { await registerHandlebarsPartials(); registerHandlebarsHelpers(); + + registerHooks(); }); /** diff --git a/src/module/helpers.ts b/src/module/helpers.ts new file mode 100644 index 00000000..21a8c7db --- /dev/null +++ b/src/module/helpers.ts @@ -0,0 +1,6 @@ +export function getCanvas(): Canvas { + if (!(canvas instanceof Canvas) || !canvas.ready) { + throw new Error(game.i18n.localize("DS4.ErrorCanvasIsNotInitialized")); + } + return canvas; +} diff --git a/src/module/hooks/hooks.ts b/src/module/hooks/hooks.ts new file mode 100644 index 00000000..3161b8f0 --- /dev/null +++ b/src/module/hooks/hooks.ts @@ -0,0 +1,5 @@ +import registerHotbarHook from "./hotbar-drop"; + +export default function registerHooks(): void { + registerHotbarHook(); +} diff --git a/src/module/hooks/hotbar-drop.ts b/src/module/hooks/hotbar-drop.ts new file mode 100644 index 00000000..9cd7b544 --- /dev/null +++ b/src/module/hooks/hotbar-drop.ts @@ -0,0 +1,28 @@ +import { DS4Item } from "../item/item"; +import { DS4ItemData } from "../item/item-data"; +import { createItemMacro } from "../macros/item-macro"; +import notifications from "../ui/notifications"; + +export default function registerHotbarHook(): void { + Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: { type: string } & Record, slot: string) => { + switch (data.type) { + case "Item": { + if (!("data" in data)) { + return notifications.warn(game.i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems")); + } + const itemData = data.data as DS4ItemData; + + if (!DS4Item.rollableItemTypes.includes(itemData.type)) { + return notifications.warn( + game.i18n.format("DS4.WarningItemIsNotRollable", { + name: itemData.name, + id: itemData._id, + type: itemData.type, + }), + ); + } + await createItemMacro(itemData, slot); + } + } + }); +} diff --git a/src/module/item/item.ts b/src/module/item/item.ts index ac9b90cd..1374f9fd 100644 --- a/src/module/item/item.ts +++ b/src/module/item/item.ts @@ -2,7 +2,7 @@ import { DS4Actor } from "../actor/actor"; import { DS4 } from "../config"; import { createCheckRoll } from "../rolls/check-factory"; import notifications from "../ui/notifications"; -import { AttackType, DS4ItemData } from "./item-data"; +import { AttackType, DS4ItemData, ItemType } from "./item-data"; /** * The Item class for DS4 @@ -40,6 +40,13 @@ export class DS4Item extends Item { return 1; } + /** + * The list of item types that are rollable. + */ + static get rollableItemTypes(): ItemType[] { + return ["weapon", "spell"]; + } + /** * Roll a check for a action with this item. */ @@ -71,8 +78,8 @@ export class DS4Item extends Item { } if (!this.data.data.equipped) { - throw new Error( - game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", { + return notifications.warn( + game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { name: this.name, id: this.id, type: this.data.type, @@ -104,8 +111,8 @@ export class DS4Item extends Item { } if (!this.data.data.equipped) { - throw new Error( - game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", { + return notifications.warn( + game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { name: this.name, id: this.id, type: this.data.type, @@ -158,7 +165,6 @@ export class DS4Item extends Item { } return `${selectedAttackType}Attack` as const; }, - render: () => undefined, // TODO(types): This is actually optional, remove when types are updated ) options: { jQuery: true }, }); return answer; diff --git a/src/module/macros/item-macro.ts b/src/module/macros/item-macro.ts new file mode 100644 index 00000000..d6ed404b --- /dev/null +++ b/src/module/macros/item-macro.ts @@ -0,0 +1,54 @@ +import { DS4Actor } from "../actor/actor"; +import { getCanvas } from "../helpers"; +import { DS4ItemData } from "../item/item-data"; +import notifications from "../ui/notifications"; + +/** + * Create a Macro from an item drop. + * Get an existing item macro if one exists, otherwise create a new one. + * @param itemData - The item data + * @param slot - The hotbar slot to use + */ +export async function createItemMacro(itemData: DS4ItemData, slot: string): Promise { + const command = `game.ds4.macros.rollItemMacro("${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 }, + )); + game.user?.assignHotbarMacro(macro, (slot as unknown) as number); // TODO(types): adjust so that cast is not needed, slot should be an optional string +} + +/** + * Execute the item macro for the given itemId. + * @param itemId + */ +export async function rollItemMacro(itemId: string): 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.WarningMustControlActorToRollItemMacro")); + } + const item = actor.items?.get(itemId); + if (!item) { + return notifications.warn( + game.i18n.format("DS4.WarningControlledActorDoesNotHaveItem", { + actorName: actor.name, + actorId: actor.id, + itemId, + }), + ); + } + + return item.roll(); +} diff --git a/src/module/macros/macros.ts b/src/module/macros/macros.ts new file mode 100644 index 00000000..c4a13e3f --- /dev/null +++ b/src/module/macros/macros.ts @@ -0,0 +1,5 @@ +import { rollItemMacro } from "./item-macro"; + +export const macros = { + rollItemMacro, +}; From 67d0253f66b5d8914f4c154c071d4cb880d612ea Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 07:11:12 +0100 Subject: [PATCH 2/9] Update foundry-vtt-types to remove some type workarounds --- package.json | 2 +- src/module/macros/item-macro.ts | 2 +- src/module/rolls/roll.ts | 4 ++-- src/module/rolls/slaying-dice-modifier.ts | 3 --- yarn.lock | 10 +++++----- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index a27be464..440ce47c 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "postinstall": "husky install" }, "devDependencies": { - "@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-1", + "@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-3", "@types/fs-extra": "^9.0.8", "@types/jest": "^26.0.20", "@typescript-eslint/eslint-plugin": "^4.16.1", diff --git a/src/module/macros/item-macro.ts b/src/module/macros/item-macro.ts index d6ed404b..0abc24f4 100644 --- a/src/module/macros/item-macro.ts +++ b/src/module/macros/item-macro.ts @@ -23,7 +23,7 @@ export async function createItemMacro(itemData: DS4ItemData, slot: string): Prom }, { displaySheet: false }, )); - game.user?.assignHotbarMacro(macro, (slot as unknown) as number); // TODO(types): adjust so that cast is not needed, slot should be an optional string + game.user?.assignHotbarMacro(macro, slot); } /** diff --git a/src/module/rolls/roll.ts b/src/module/rolls/roll.ts index 22ec0cd5..e0720a3c 100644 --- a/src/module/rolls/roll.ts +++ b/src/module/rolls/roll.ts @@ -8,7 +8,7 @@ export class DS4Roll = Record * template if the first dice term is a ds4 check. * @override */ - async render(chatOptions: Roll.ChatOptions = {}): Promise { + async render(chatOptions: Roll.ChatOptions = {}): Promise { chatOptions = mergeObject( { user: game.user?._id, @@ -39,6 +39,6 @@ export class DS4Roll = Record }; // Render the roll display template - return (renderTemplate(chatOptions.template ?? "", chatData) as unknown) as Promise; // TODO(types): Make this cast unnecessary by fixing upstream + return renderTemplate(chatOptions.template ?? "", chatData); } } diff --git a/src/module/rolls/slaying-dice-modifier.ts b/src/module/rolls/slaying-dice-modifier.ts index c92242ac..0af9d6b9 100644 --- a/src/module/rolls/slaying-dice-modifier.ts +++ b/src/module/rolls/slaying-dice-modifier.ts @@ -1,9 +1,6 @@ import { DS4Check } from "./check"; export default function registerSlayingDiceModifier(): void { - // TODO(types): Adjust types to allow extension of DiceTerm.MODIFIERS (see https://github.com/League-of-Foundry-Developers/foundry-vtt-types/pull/573) - // eslint-disable-next-line - // @ts-ignore DicePool.MODIFIERS.x = slay; DicePool.POOL_REGEX = /^{([^}]+)}([A-z]([A-z0-9<=>]+)?)?$/; } diff --git a/yarn.lock b/yarn.lock index 42ba48ab..2c6ae598 100644 --- a/yarn.lock +++ b/yarn.lock @@ -647,9 +647,9 @@ __metadata: languageName: node linkType: hard -"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-1": - version: 0.7.9-1 - resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-1" +"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-3": + version: 0.7.9-3 + resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-3" dependencies: "@types/howler": 2.2.1 "@types/jquery": 3.5.1 @@ -659,7 +659,7 @@ __metadata: pixi.js: 5.3.4 tinymce: 5.6.2 typescript: ^4.1.4 - checksum: 1b2e311e5ab2db9da4d67fee7b5ab038b228b76ff78f90830f770ae4cee7191f652a18dec0161cc7e2e9696fc06969e23b33a0402249bc4251cc557fa5efc430 + checksum: 75524c7aa78ddb77cad1a9d041af30ae5bbd708f5b26568dabbb3d913a4643aefcc6f2ed80e1e76b3c17050579665eab155f035f840db6397691cf68eeee9b3f languageName: node linkType: hard @@ -2820,7 +2820,7 @@ __metadata: version: 0.0.0-use.local resolution: "dungeonslayers4@workspace:." dependencies: - "@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-1 + "@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-3 "@types/fs-extra": ^9.0.8 "@types/jest": ^26.0.20 "@typescript-eslint/eslint-plugin": ^4.16.1 From 32fac517ad3c17d3e753c4ce6538c1230096e7a7 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 07:42:00 +0100 Subject: [PATCH 3/9] Rename a few things --- src/lang/de.json | 2 +- src/lang/en.json | 2 +- src/module/ds4.ts | 4 ++-- src/module/hooks/hooks.ts | 6 +++--- src/module/hooks/hotbar-drop.ts | 6 +++--- src/module/macros/macros.ts | 4 ++-- src/module/macros/{item-macro.ts => roll-item-macro.ts} | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) rename src/module/macros/{item-macro.ts => roll-item-macro.ts} (85%) diff --git a/src/lang/de.json b/src/lang/de.json index 37e0236f..bf89e0fb 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -195,7 +195,7 @@ "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.WarningMustControlActorToRollItemMacro": "Um ein Item Makro zu nutzen muss ein Aktor kontolliert werden.", + "DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item 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.", diff --git a/src/lang/en.json b/src/lang/en.json index 7c9a511b..93011357 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -195,7 +195,7 @@ "DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}", "DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.", "DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.", - "DS4.WarningMustControlActorToRollItemMacro": "You must control an actor to be able to use an item macro.", + "DS4.WarningMustControlActorToUseRollItemMacro": "You must control an actor to be able to use a roll item 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.", diff --git a/src/module/ds4.ts b/src/module/ds4.ts index 45619fc8..59904351 100644 --- a/src/module/ds4.ts +++ b/src/module/ds4.ts @@ -4,7 +4,7 @@ import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet"; import { DS4 } from "./config"; import registerHandlebarsHelpers from "./handlebars/handlebars-helpers"; import registerHandlebarsPartials from "./handlebars/handlebars-partials"; -import registerHooks from "./hooks/hooks"; +import registerForHooks from "./hooks/hooks"; import { DS4Item } from "./item/item"; import { DS4ItemSheet } from "./item/item-sheet"; import { macros } from "./macros/macros"; @@ -53,7 +53,7 @@ Hooks.once("init", async () => { await registerHandlebarsPartials(); registerHandlebarsHelpers(); - registerHooks(); + registerForHooks(); }); /** diff --git a/src/module/hooks/hooks.ts b/src/module/hooks/hooks.ts index 3161b8f0..5b0da07c 100644 --- a/src/module/hooks/hooks.ts +++ b/src/module/hooks/hooks.ts @@ -1,5 +1,5 @@ -import registerHotbarHook from "./hotbar-drop"; +import registerForHotbarDropHook from "./hotbar-drop"; -export default function registerHooks(): void { - registerHotbarHook(); +export default function registerForHooks(): void { + registerForHotbarDropHook(); } diff --git a/src/module/hooks/hotbar-drop.ts b/src/module/hooks/hotbar-drop.ts index 9cd7b544..08657408 100644 --- a/src/module/hooks/hotbar-drop.ts +++ b/src/module/hooks/hotbar-drop.ts @@ -1,9 +1,9 @@ import { DS4Item } from "../item/item"; import { DS4ItemData } from "../item/item-data"; -import { createItemMacro } from "../macros/item-macro"; +import { createRollItemMacro } from "../macros/roll-item-macro"; import notifications from "../ui/notifications"; -export default function registerHotbarHook(): void { +export default function registerForHotbarDropHook(): void { Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: { type: string } & Record, slot: string) => { switch (data.type) { case "Item": { @@ -21,7 +21,7 @@ export default function registerHotbarHook(): void { }), ); } - await createItemMacro(itemData, slot); + await createRollItemMacro(itemData, slot); } } }); diff --git a/src/module/macros/macros.ts b/src/module/macros/macros.ts index c4a13e3f..3b50c927 100644 --- a/src/module/macros/macros.ts +++ b/src/module/macros/macros.ts @@ -1,5 +1,5 @@ -import { rollItemMacro } from "./item-macro"; +import { rollItem } from "./roll-item-macro"; export const macros = { - rollItemMacro, + rollItem: rollItem, }; diff --git a/src/module/macros/item-macro.ts b/src/module/macros/roll-item-macro.ts similarity index 85% rename from src/module/macros/item-macro.ts rename to src/module/macros/roll-item-macro.ts index 0abc24f4..d321d45b 100644 --- a/src/module/macros/item-macro.ts +++ b/src/module/macros/roll-item-macro.ts @@ -9,8 +9,8 @@ import notifications from "../ui/notifications"; * @param itemData - The item data * @param slot - The hotbar slot to use */ -export async function createItemMacro(itemData: DS4ItemData, slot: string): Promise { - const command = `game.ds4.macros.rollItemMacro("${itemData._id}");`; +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( @@ -30,14 +30,14 @@ export async function createItemMacro(itemData: DS4ItemData, slot: string): Prom * Execute the item macro for the given itemId. * @param itemId */ -export async function rollItemMacro(itemId: string): Promise { +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; if (!actor) { - return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToRollItemMacro")); + return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro")); } const item = actor.items?.get(itemId); if (!item) { From b439915c7c867bf16fa243d7af80fb1f35da41d7 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 07:42:49 +0100 Subject: [PATCH 4/9] Fix color of coup / fumble dice totals in firefox --- src/scss/components/_dice_total.scss | 10 ++++++---- src/scss/utils/_colors.scss | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/scss/components/_dice_total.scss b/src/scss/components/_dice_total.scss index c19a0572..a9082cc1 100644 --- a/src/scss/components/_dice_total.scss +++ b/src/scss/components/_dice_total.scss @@ -1,18 +1,20 @@ @use "../utils/colors"; -.ds4-dice-total { +// Needs to be nested in .dice-roll to win against foundry's style.css with respect to specificity +.dice-roll .ds4-dice-total { @mixin color-filter($rotation) { filter: sepia(0.5) hue-rotate($rotation); - backdrop-filter: sepia(0) hue-rotate($rotation); } &--coup { - color: colors.$c-coup; @include color-filter(60deg); + background-color: colors.$c-coup-bg; + color: colors.$c-coup; } &--fumble { - color: colors.$c-fumble; @include color-filter(-60deg); + background-color: colors.$c-fumble-bg; + color: colors.$c-fumble; } } diff --git a/src/scss/utils/_colors.scss b/src/scss/utils/_colors.scss index 91844386..b3a864a2 100644 --- a/src/scss/utils/_colors.scss +++ b/src/scss/utils/_colors.scss @@ -4,4 +4,6 @@ $c-light-grey: #777; $c-border-groove: #eeede0; $c-invalid-input: rgba(lightcoral, 50%); $c-coup: #18520b; +$c-coup-bg: #acc2a7; $c-fumble: #aa0200; +$c-fumble-bg: #d8b5ba; From 3c54a6126bae722167c5d78b9d6981ccb1120824 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 07:48:47 +0100 Subject: [PATCH 5/9] Small documentation adjustment --- src/module/macros/roll-item-macro.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module/macros/roll-item-macro.ts b/src/module/macros/roll-item-macro.ts index d321d45b..0ceabcf5 100644 --- a/src/module/macros/roll-item-macro.ts +++ b/src/module/macros/roll-item-macro.ts @@ -27,7 +27,7 @@ export async function createRollItemMacro(itemData: DS4ItemData, slot: string): } /** - * Execute the item macro for the given itemId. + * Execute the roll item macro for the given itemId. * @param itemId */ export async function rollItem(itemId: string): Promise { From a151e62a4c904de259f9840fd38256032f769e42 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 08:22:27 +0100 Subject: [PATCH 6/9] Cleanup hooks --- src/module/ds4.ts | 106 +-------------------------- src/module/hooks/hooks.ts | 8 ++ src/module/hooks/init.ts | 58 +++++++++++++++ src/module/hooks/ready.ts | 7 ++ src/module/hooks/render.ts | 23 ++++++ src/module/hooks/setup.ts | 32 ++++++++ src/module/macros/roll-item-macro.ts | 4 +- 7 files changed, 131 insertions(+), 107 deletions(-) create mode 100644 src/module/hooks/init.ts create mode 100644 src/module/hooks/ready.ts create mode 100644 src/module/hooks/render.ts create mode 100644 src/module/hooks/setup.ts diff --git a/src/module/ds4.ts b/src/module/ds4.ts index 59904351..337bb604 100644 --- a/src/module/ds4.ts +++ b/src/module/ds4.ts @@ -1,107 +1,3 @@ -import { DS4Actor } from "./actor/actor"; -import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet"; -import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet"; -import { DS4 } from "./config"; -import registerHandlebarsHelpers from "./handlebars/handlebars-helpers"; -import registerHandlebarsPartials from "./handlebars/handlebars-partials"; import registerForHooks from "./hooks/hooks"; -import { DS4Item } from "./item/item"; -import { DS4ItemSheet } from "./item/item-sheet"; -import { macros } from "./macros/macros"; -import { migration } from "./migrations"; -import { DS4Check } from "./rolls/check"; -import { createCheckRoll } from "./rolls/check-factory"; -import { DS4Roll } from "./rolls/roll"; -import registerSlayingDiceModifier from "./rolls/slaying-dice-modifier"; -import { registerSystemSettings } from "./settings"; -Hooks.once("init", async () => { - console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`); - - game.ds4 = { - DS4Actor, - DS4Item, - DS4, - createCheckRoll, - migration, - macros, - }; - - CONFIG.DS4 = DS4; - - CONFIG.Actor.entityClass = DS4Actor; - CONFIG.Item.entityClass = DS4Item; - - CONFIG.Actor.typeLabels = DS4.i18n.actorTypes; - CONFIG.Item.typeLabels = DS4.i18n.itemTypes; - - CONFIG.Dice.types.push(DS4Check); - CONFIG.Dice.terms.s = DS4Check; - - CONFIG.Dice.rolls.unshift(DS4Roll); - - registerSlayingDiceModifier(); - - registerSystemSettings(); - - Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); - Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true }); - Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true }); - - await registerHandlebarsPartials(); - registerHandlebarsHelpers(); - - registerForHooks(); -}); - -/** - * This function runs after game data has been requested and loaded from the servers, so entities exist - */ -Hooks.once("setup", () => { - localizeAndSortConfigObjects(); -}); - -Hooks.once("ready", () => { - migration.migrate(); -}); - -/** - * Select the text of input elements in given sheets via onfocus listener. - * The hook names are of the form "render"+sheet_superclassname and are called within - * the render() method of the foundry Application class. - * Note: The render hooks of all classes in the class hierarchy are called, - * so e.g. for a Dialog, both "renderDialog" and "renderApplication" are called - * (in this order). - */ -["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hookName: string) => { - Hooks.on(hookName, (app: Dialog, html: JQueryStatic) => { - $(html) - .find("input") - .on("focus", (ev: JQuery.FocusEvent) => { - ev.currentTarget.select(); - }); - }); -}); - -/** - * Localizes all objects in {@link DS4.i18n} and sorts them unless they are explicitly excluded. - */ -function localizeAndSortConfigObjects() { - const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"]; - - const localizeObject = (obj: T, sort = true): T => { - const localized = Object.entries(obj).map(([key, value]) => { - return [key, game.i18n.localize(value)]; - }); - if (sort) localized.sort((a, b) => a[1].localeCompare(b[1])); - return Object.fromEntries(localized); - }; - - DS4.i18n = Object.fromEntries( - Object.entries(DS4.i18n).map(([key, value]) => { - return [key, localizeObject(value, !noSort.includes(key))]; - }), - ) as typeof DS4.i18n; -} +registerForHooks(); diff --git a/src/module/hooks/hooks.ts b/src/module/hooks/hooks.ts index 5b0da07c..72509895 100644 --- a/src/module/hooks/hooks.ts +++ b/src/module/hooks/hooks.ts @@ -1,5 +1,13 @@ import registerForHotbarDropHook from "./hotbar-drop"; +import registerForInitHook from "./init"; +import registerForReadyHook from "./ready"; +import registerForRenderHooks from "./render"; +import registerForSetupHook from "./setup"; export default function registerForHooks(): void { registerForHotbarDropHook(); + registerForInitHook(); + registerForReadyHook(); + registerForRenderHooks(); + registerForSetupHook(); } diff --git a/src/module/hooks/init.ts b/src/module/hooks/init.ts new file mode 100644 index 00000000..4722da94 --- /dev/null +++ b/src/module/hooks/init.ts @@ -0,0 +1,58 @@ +import { DS4Actor } from "../actor/actor"; +import { DS4CharacterActorSheet } from "../actor/sheets/character-sheet"; +import { DS4CreatureActorSheet } from "../actor/sheets/creature-sheet"; +import { DS4 } from "../config"; +import registerHandlebarsHelpers from "../handlebars/handlebars-helpers"; +import registerHandlebarsPartials from "../handlebars/handlebars-partials"; +import { DS4Item } from "../item/item"; +import { DS4ItemSheet } from "../item/item-sheet"; +import { macros } from "../macros/macros"; +import { migration } from "../migrations"; +import { DS4Check } from "../rolls/check"; +import { createCheckRoll } from "../rolls/check-factory"; +import { DS4Roll } from "../rolls/roll"; +import registerSlayingDiceModifier from "../rolls/slaying-dice-modifier"; +import { registerSystemSettings } from "../settings"; + +export default function registerForInitHook(): void { + Hooks.once("init", init); +} + +async function init() { + console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`); + + game.ds4 = { + DS4Actor, + DS4Item, + DS4, + createCheckRoll, + migration, + macros, + }; + + CONFIG.DS4 = DS4; + + CONFIG.Actor.entityClass = DS4Actor; + CONFIG.Item.entityClass = DS4Item; + + CONFIG.Actor.typeLabels = DS4.i18n.actorTypes; + CONFIG.Item.typeLabels = DS4.i18n.itemTypes; + + CONFIG.Dice.types.push(DS4Check); + CONFIG.Dice.terms.s = DS4Check; + + CONFIG.Dice.rolls.unshift(DS4Roll); + + registerSlayingDiceModifier(); + + registerSystemSettings(); + + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); + Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true }); + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true }); + + await registerHandlebarsPartials(); + registerHandlebarsHelpers(); +} diff --git a/src/module/hooks/ready.ts b/src/module/hooks/ready.ts new file mode 100644 index 00000000..07cbb899 --- /dev/null +++ b/src/module/hooks/ready.ts @@ -0,0 +1,7 @@ +import { migration } from "../migrations"; + +export default function registerForReadyHook(): void { + Hooks.once("ready", () => { + migration.migrate(); + }); +} diff --git a/src/module/hooks/render.ts b/src/module/hooks/render.ts new file mode 100644 index 00000000..b1e0fc2e --- /dev/null +++ b/src/module/hooks/render.ts @@ -0,0 +1,23 @@ +/** + * @remarks The render hooks of all classes in the class hierarchy are called, so e.g. for a {@link Dialog}, both the + * "renderDialog" hook and the "renderApplication" hook are called (in this order). + */ +export default function registerForRenderHooks(): void { + ["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hook) => { + Hooks.on(hook, selectTargetInputOnFocus); + }); +} + +/** + * Select the text of input elements in given application when focused via an on focus listener. + * + * @param app - The application in which to activate the listener. + * @param html - The {@link JQuery} representing the HTML of the application. + */ +function selectTargetInputOnFocus(app: Application, html: JQuery) { + $(html) + .find("input") + .on("focus", (ev: JQuery.FocusEvent) => { + ev.currentTarget.select(); + }); +} diff --git a/src/module/hooks/setup.ts b/src/module/hooks/setup.ts new file mode 100644 index 00000000..78bce9b3 --- /dev/null +++ b/src/module/hooks/setup.ts @@ -0,0 +1,32 @@ +import { DS4 } from "../config"; + +/** + * @remarks The "setup" hook is called after game data has been requested and loaded from the server, so entities + * already exist. + */ +export default function registerForSetupHooks(): void { + Hooks.once("setup", () => { + localizeAndSortConfigObjects(); + }); +} + +/** + * Localizes all objects in {@link DS4.i18n} and sorts them unless they are explicitly excluded. + */ +function localizeAndSortConfigObjects() { + const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"]; + + const localizeObject = (obj: T, sort = true): T => { + const localized = Object.entries(obj).map(([key, value]) => { + return [key, game.i18n.localize(value)]; + }); + if (sort) localized.sort((a, b) => a[1].localeCompare(b[1])); + return Object.fromEntries(localized); + }; + + DS4.i18n = Object.fromEntries( + Object.entries(DS4.i18n).map(([key, value]) => { + return [key, localizeObject(value, !noSort.includes(key))]; + }), + ) as typeof DS4.i18n; +} diff --git a/src/module/macros/roll-item-macro.ts b/src/module/macros/roll-item-macro.ts index 0ceabcf5..2bf36588 100644 --- a/src/module/macros/roll-item-macro.ts +++ b/src/module/macros/roll-item-macro.ts @@ -4,7 +4,7 @@ import { DS4ItemData } from "../item/item-data"; import notifications from "../ui/notifications"; /** - * Create a Macro from an item drop. + * Creates a macro from an item drop. * Get an existing item macro if one exists, otherwise create a new one. * @param itemData - The item data * @param slot - The hotbar slot to use @@ -27,7 +27,7 @@ export async function createRollItemMacro(itemData: DS4ItemData, slot: string): } /** - * Execute the roll item macro for the given itemId. + * Executes the roll item macro for the given itemId. * @param itemId */ export async function rollItem(itemId: string): Promise { From 48a1cc652a3ccde26226017f2da319fbad190e02 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 08:25:56 +0100 Subject: [PATCH 7/9] Small renaming --- src/module/hooks/hotbar-drop.ts | 2 +- src/module/macros/macros.ts | 2 +- src/module/macros/{roll-item-macro.ts => roll-item.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/module/macros/{roll-item-macro.ts => roll-item.ts} (100%) diff --git a/src/module/hooks/hotbar-drop.ts b/src/module/hooks/hotbar-drop.ts index 08657408..8263181f 100644 --- a/src/module/hooks/hotbar-drop.ts +++ b/src/module/hooks/hotbar-drop.ts @@ -1,6 +1,6 @@ import { DS4Item } from "../item/item"; import { DS4ItemData } from "../item/item-data"; -import { createRollItemMacro } from "../macros/roll-item-macro"; +import { createRollItemMacro } from "../macros/roll-item"; import notifications from "../ui/notifications"; export default function registerForHotbarDropHook(): void { diff --git a/src/module/macros/macros.ts b/src/module/macros/macros.ts index 3b50c927..95b47854 100644 --- a/src/module/macros/macros.ts +++ b/src/module/macros/macros.ts @@ -1,4 +1,4 @@ -import { rollItem } from "./roll-item-macro"; +import { rollItem } from "./roll-item"; export const macros = { rollItem: rollItem, diff --git a/src/module/macros/roll-item-macro.ts b/src/module/macros/roll-item.ts similarity index 100% rename from src/module/macros/roll-item-macro.ts rename to src/module/macros/roll-item.ts From 196ddb7eb28119b49785f71a81f0b4a231102e28 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 08:42:36 +0100 Subject: [PATCH 8/9] Small documentation fix --- src/module/macros/roll-item.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module/macros/roll-item.ts b/src/module/macros/roll-item.ts index 2bf36588..9d8af10e 100644 --- a/src/module/macros/roll-item.ts +++ b/src/module/macros/roll-item.ts @@ -5,7 +5,7 @@ import notifications from "../ui/notifications"; /** * Creates a macro from an item drop. - * Get an existing item macro if one exists, otherwise create a new one. + * Get an existing roll item macro if one exists, otherwise create a new one. * @param itemData - The item data * @param slot - The hotbar slot to use */ From 0b2be5d9fc6e5f0634a1c1d71c98adaa1dd02333 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 16 Mar 2021 08:55:10 +0100 Subject: [PATCH 9/9] Remove redundant comment --- src/module/hooks/setup.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/module/hooks/setup.ts b/src/module/hooks/setup.ts index 78bce9b3..e87272ca 100644 --- a/src/module/hooks/setup.ts +++ b/src/module/hooks/setup.ts @@ -1,9 +1,5 @@ import { DS4 } from "../config"; -/** - * @remarks The "setup" hook is called after game data has been requested and loaded from the server, so entities - * already exist. - */ export default function registerForSetupHooks(): void { Hooks.once("setup", () => { localizeAndSortConfigObjects();