From b04b18a126c04ece865f36b524a72c84649f4150 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 24 Aug 2024 13:16:17 +0200 Subject: [PATCH] Bugfixes and added so that attack traits can be toggled --- .github/workflows/deploy.yml | 2 +- BestiaryTracking.js | 85 +++++++++++++++++++--------- lang/en.json | 6 +- lang/fr.json | 4 +- module.json | 2 +- module/bestiary.js | 18 +++--- module/bestiaryAppearanceMenu.js | 2 +- pf2e-bestiary-tracking.js | 45 +++++++++++---- scripts/helpers.js | 16 ++++-- scripts/migrationHandler.js | 7 ++- styles/pf2e-bestiary-tracking.css | 1 - styles/stylesheets/bestiary.less | 1 - templates/bestiaryAppearanceMenu.hbs | 9 +++ templates/partials/monsterView.hbs | 22 ++++--- 14 files changed, 151 insertions(+), 69 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2498a7c..30222f8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,7 +30,7 @@ jobs: env: version: ${{steps.get_version.outputs.version-without-v}} url: https://github.com/${{github.repository}} - manifest: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.json + manifest: https://github.com/${{github.repository}}/releases/latest/download/module.json download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.zip # Create a zip file with all files required by the module to add to the release diff --git a/BestiaryTracking.js b/BestiaryTracking.js index 3391ec6..e94f964 100644 --- a/BestiaryTracking.js +++ b/BestiaryTracking.js @@ -5,17 +5,17 @@ const slugify = (name) => { const getCreatureSize = (size) => { switch(size){ case 'grg': - return 'Gargantuan'; + return game.i18n.localize("PF2E.ActorSizeGargantuan"); case 'huge': - return 'Huge'; + return game.i18n.localize("PF2E.ActorSizeHuge"); case 'lg': - return 'Large'; + return game.i18n.localize("PF2E.ActorSizeLarge"); case 'med': - return 'Medium'; + return game.i18n.localize("PF2E.ActorSizeMedium"); case 'sm': - return 'Small'; + return game.i18n.localize("PF2E.ActorSizeSmall"); case 'tiny': - return 'Tiny'; + return game.i18n.localize("PF2E.ActorSizeTiny"); } }; @@ -61,6 +61,10 @@ const getExpandedCreatureTypes = () => { return types; }; +const getBaseActor = (actor) => { + return actor.token ? actor.token.document ? actor.token.document.baseActor : actor.token.baseActor : actor; +}; + function handleSocketEvent({action=null, data={}}={}) { switch (action) { case socketEvent.UpdateBestiary: @@ -2394,8 +2398,8 @@ class PF2EBestiary extends HandlebarsApplicationMixin$4(ApplicationV2$4) { return acc; }, { revealed: false, values: {} }), traits: action.traits.map(trait => ({ - label: trait.label, - description: trait.description, + ...trait, + revealed: detailedInformation.attackTraits ? trait.revealed : true, })), value: `${action.totalModifier >= 0 ? '+' : '-'} ${action.totalModifier}`, ...attackParts, @@ -3197,13 +3201,13 @@ class PF2EBestiary extends HandlebarsApplicationMixin$4(ApplicationV2$4) { } static async addMonster(item){ - const monster = await PF2EBestiary.getMonsterData(item.token.document ? item.token.document.baseActor : item.token.baseActor); - if (!monster) return; + const monster = await PF2EBestiary.getMonsterData(getBaseActor(item)); + if (!monster) return null; const bestiary = await game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); // We do not currently refresh already present creatures in the Bestiary. - if(bestiary.monster[monster.uuid]) return; + if(bestiary.monster[monster.uuid]) return false; bestiary.monster[monster.uuid] = monster; @@ -3223,10 +3227,12 @@ class PF2EBestiary extends HandlebarsApplicationMixin$4(ApplicationV2$4) { }); Hooks.callAll(socketEvent.UpdateBestiary, {}); + + return true; } static async getMonsterData(item){ - if(!item || item.type !== 'npc') return null; + if(!item || item.hasPlayerOwner || item.type !== 'npc') return null; const dataObject = item.toObject(false); dataObject.uuid = item.uuid; @@ -3381,7 +3387,7 @@ class PF2EBestiary extends HandlebarsApplicationMixin$4(ApplicationV2$4) { const data = TextEditor.getDragEventData(event); const baseItem = await fromUuid(data.uuid); - if(baseItem?.type === 'character'){ + if(baseItem?.type === 'character' || baseItem.hasPlayerOwner){ ui.notifications.error(game.i18n.localize("PF2EBestiary.Bestiary.Errors.UnsupportedCharacterType")); return; } @@ -3678,7 +3684,7 @@ class BestiaryAppearanceMenu extends HandlebarsApplicationMixin$3(ApplicationV2$ useTokenArt: data.useTokenArt, contrastRevealedState: data.contrastRevealedState, optionalFields: data.optionalFields, - detailedInformation: { ...data.detailedInformation, attackTraits: false } + detailedInformation: { ...data.detailedInformation } }; this.render(); } @@ -4046,7 +4052,12 @@ const handleDataMigration = async () => { bestiary.monster[type][monsterKey].saves.fortitude.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.fortitude.value); bestiary.monster[type][monsterKey].saves.reflex.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.reflex.value); bestiary.monster[type][monsterKey].saves.will.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.will.value); - bestiary.monster[type][monsterKey].abilities.values.forEach(ability => ability.category = getCategoryLabel(attributeTable, origin.system.details.level.value, ability.mod)); + bestiary.monster[type][monsterKey].abilities.values.forEach(ability => { + // Weird error that occured here. Safety addition. + if(typeof x === 'object'){ + ability.category = getCategoryLabel(attributeTable, origin.system.details.level.value, ability.mod);} + } + ); bestiary.monster[type][monsterKey].senses.values.perception.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].senses.values.perception.value); // All spellcasting creatures should have spell data @@ -4932,14 +4943,27 @@ Hooks.once("setup", () => { }); Hooks.on("combatStart", async (encounter) => { - if(game.user.isGM){ + if (game.user.isGM) { + const added = []; + const exists = []; const automaticCombatSetting = await game.settings.get('pf2e-bestiary-tracking', 'automatic-combat-registration'); - if(automaticCombatSetting === 1){ - for(var combatant of encounter.combatants){ - await PF2EBestiary.addMonster(combatant.actor); - } + + if (automaticCombatSetting === 1) { + for (var combatant of encounter.combatants.filter(combatant => combatant?.actor?.type === 'npc')) { + const successful = await PF2EBestiary.addMonster(combatant.actor); + if (successful && combatant?.actor?.name) { + added.push(combatant.actor.name); + } + else if (successful === false && combatant?.actor?.name){ + exists.push(combatant.actor.name); + } + } + + exists?.length && ui.notifications.info(game.i18n.format('PF2EBestiary.Bestiary.Info.AlreadyExistsInBestiary', { creatures: exists.join(', ') })); + added?.length && ui.notifications.info(game.i18n.format('PF2EBestiary.Bestiary.Info.AddedToBestiary', { creatures: added.join(', ') })); } } + }); Hooks.on("updateCombatant", async (combatant, changes) => { @@ -4970,14 +4994,14 @@ Hooks.on("xdy-pf2e-workbench.tokenCreateMystification", token => { }); Hooks.on("preCreateToken", async token => { - if(!game.user.isGM) return; + if(!game.user.isGM || token.actor.type !== 'npc' || token.hasPlayerOwner) return; if(game.settings.get('pf2e-bestiary-tracking', 'hide-token-names')){ const bestiary = game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); const monster = bestiary.monster[token.baseActor.uuid]; if(monster){ - if(monster.name.custom && monster.name.revealed) { - await token.updateSource({ name: monster.name.custom }); + if(monster.name.revealed) { + await token.updateSource({ name: monster.name.custom ? monster.name.custom : monster.name.value }); return; } } @@ -5010,14 +5034,16 @@ Hooks.on("createChatMessage", async (message) => { const { automaticReveal } = game.settings.get('pf2e-bestiary-tracking', 'chat-message-handling'); if(automaticReveal){ const bestiary = game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); - const { automaticReveal } = game.settings.get('pf2e-bestiary-tracking', 'chat-message-handling'); if(message.flags.pf2e.origin){ // Attacks | Actions | Spells - const actorUuid = message.flags.pf2e.origin.actor; + const actor = await fromUuid(message.flags.pf2e.origin.actor); + if(actor.type !== 'npc' || actor.hasPlayerOwner) return; + + const actorUuid = getBaseActor(actor).uuid; const monster = bestiary.monster[actorUuid]; - const item = await fromUuid(message.flags.pf2e.origin.uuid); + const item = await fromUuid(message.flags.pf2e.origin.uuid); if(monster && item){ if(message.flags.pf2e.modifierName && automaticReveal.attacks){ const monsterItem = monster.system.actions[item.id]; @@ -5038,7 +5064,12 @@ Hooks.on("createChatMessage", async (message) => { } else { // Skills | Saving Throws - const monster = bestiary.monster[`Actor.${message.flags.pf2e.context.actor}`]; + const actor = await fromUuid(`Actor.${message.flags.pf2e.context.actor}`); + if(actor.type !== 'npc' || actor.hasPlayerOwner) return; + + const actorUuid = getBaseActor(actor).uuid; + const monster = bestiary.monster[actorUuid]; + if(monster){ if(message.flags.pf2e.context.type === 'skill-check' && automaticReveal.skills) { diff --git a/lang/en.json b/lang/en.json index 4bcac9f..9ad94ee 100644 --- a/lang/en.json +++ b/lang/en.json @@ -120,11 +120,13 @@ }, "Errors": { "UnsupportedType": "Not a supported NPC type!", - "UnsupportedCharacterType": "Player Character are not supported in the bestiary.", + "UnsupportedCharacterType": "Player Characters are not supported in the bestiary.", "DataMissing": "Some data is missing. You'll have to reimport the creature. Sorry about that!" }, "Info": { - "GMOnly": "This is a GM only function." + "GMOnly": "This is a GM only function.", + "AddedToBestiary": "[{creatures}] were added to the Bestiary", + "AlreadyExistsInBestiary": "[{creatures}] already exists in the Bestiary." } }, "Menus": { diff --git a/lang/fr.json b/lang/fr.json index 02166d4..fa6c7e2 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -123,7 +123,9 @@ "DataMissing": "Certaines données sont manquantes. Vous devrez réimporter la créature. Désolé pour cela !" }, "Info": { - "GMOnly": "Cette fonction est réservée au MJ." + "GMOnly": "Cette fonction est réservée au MJ.", + "AddedToBestiary": "Added [{creatures}] to Bestiary", + "AlreadyExistsInBestiary": "[{creatures}] already exists in the Bestiary." } }, "Menus": { diff --git a/module.json b/module.json index 0c62595..fb1c11f 100644 --- a/module.json +++ b/module.json @@ -3,7 +3,7 @@ "name": "pf2e-bestiary-tracking", "title": "PF2E Bestiary Tracking", "description": "A module to track known information about enemies for players in a bestiary.", - "version": "0.8.9.5.1", + "version": "0.8.9.6", "authors": [ { "name": "HarryBoy" diff --git a/module/bestiary.js b/module/bestiary.js index 72856bf..4dd776b 100644 --- a/module/bestiary.js +++ b/module/bestiary.js @@ -1,5 +1,5 @@ -import { getCreatureSize, getCreaturesTypes, getExpandedCreatureTypes, getIWRString, getMultiplesString, slugify } from "../scripts/helpers.js"; +import { getBaseActor, getCreatureSize, getCreaturesTypes, getExpandedCreatureTypes, getIWRString, getMultiplesString, slugify } from "../scripts/helpers.js"; import { socketEvent } from "../scripts/socket.js"; import { acTable, attackTable, attributeTable, damageTable, hpTable, savingThrowPerceptionTable, skillTable, spellAttackTable, spellDCTable } from "../scripts/statisticsData.js"; import { getCategoryFromIntervals, getCategoryLabel, getCategoryRange, getMixedCategoryLabel, getRollAverage, getWeaknessCategoryClass } from "../scripts/statisticsHelper.js"; @@ -405,8 +405,8 @@ export default class PF2EBestiary extends HandlebarsApplicationMixin(Application return acc; }, { revealed: false, values: {} }), traits: action.traits.map(trait => ({ - label: trait.label, - description: trait.description, + ...trait, + revealed: detailedInformation.attackTraits ? trait.revealed : true, })), value: `${action.totalModifier >= 0 ? '+' : '-'} ${action.totalModifier}`, ...attackParts, @@ -1209,13 +1209,13 @@ export default class PF2EBestiary extends HandlebarsApplicationMixin(Application } static async addMonster(item){ - const monster = await PF2EBestiary.getMonsterData(item.token.document ? item.token.document.baseActor : item.token.baseActor); - if (!monster) return; + const monster = await PF2EBestiary.getMonsterData(getBaseActor(item)); + if (!monster) return null; const bestiary = await game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); // We do not currently refresh already present creatures in the Bestiary. - if(bestiary.monster[monster.uuid]) return; + if(bestiary.monster[monster.uuid]) return false; bestiary.monster[monster.uuid] = monster; @@ -1235,10 +1235,12 @@ export default class PF2EBestiary extends HandlebarsApplicationMixin(Application }); Hooks.callAll(socketEvent.UpdateBestiary, {}); + + return true; } static async getMonsterData(item){ - if(!item || item.type !== 'npc') return null; + if(!item || item.hasPlayerOwner || item.type !== 'npc') return null; const dataObject = item.toObject(false); dataObject.uuid = item.uuid; @@ -1393,7 +1395,7 @@ export default class PF2EBestiary extends HandlebarsApplicationMixin(Application const data = TextEditor.getDragEventData(event); const baseItem = await fromUuid(data.uuid); - if(baseItem?.type === 'character'){ + if(baseItem?.type === 'character' || baseItem.hasPlayerOwner){ ui.notifications.error(game.i18n.localize("PF2EBestiary.Bestiary.Errors.UnsupportedCharacterType")); return; } diff --git a/module/bestiaryAppearanceMenu.js b/module/bestiaryAppearanceMenu.js index f3640ea..a7e5105 100644 --- a/module/bestiaryAppearanceMenu.js +++ b/module/bestiaryAppearanceMenu.js @@ -86,7 +86,7 @@ export default class BestiaryAppearanceMenu extends HandlebarsApplicationMixin(A useTokenArt: data.useTokenArt, contrastRevealedState: data.contrastRevealedState, optionalFields: data.optionalFields, - detailedInformation: { ...data.detailedInformation, attackTraits: false } + detailedInformation: { ...data.detailedInformation } }; this.render(); } diff --git a/pf2e-bestiary-tracking.js b/pf2e-bestiary-tracking.js index 8568d2b..30f21d0 100644 --- a/pf2e-bestiary-tracking.js +++ b/pf2e-bestiary-tracking.js @@ -4,6 +4,7 @@ import { registerGameSettings } from "./scripts/setup.js"; import { handleSocketEvent, socketEvent } from "./scripts/socket.js"; import * as macros from "./scripts/macros.js"; import { handleDataMigration } from "./scripts/migrationHandler.js"; +import { getBaseActor } from "./scripts/helpers.js"; Hooks.once('init', () => { registerGameSettings(); @@ -56,14 +57,27 @@ Hooks.once("setup", () => { }); Hooks.on("combatStart", async (encounter) => { - if(game.user.isGM){ + if (game.user.isGM) { + const added = []; + const exists = []; const automaticCombatSetting = await game.settings.get('pf2e-bestiary-tracking', 'automatic-combat-registration'); - if(automaticCombatSetting === 1){ - for(var combatant of encounter.combatants){ - await PF2EBestiary.addMonster(combatant.actor); - } + + if (automaticCombatSetting === 1) { + for (var combatant of encounter.combatants.filter(combatant => combatant?.actor?.type === 'npc')) { + const successful = await PF2EBestiary.addMonster(combatant.actor); + if (successful && combatant?.actor?.name) { + added.push(combatant.actor.name); + } + else if (successful === false && combatant?.actor?.name){ + exists.push(combatant.actor.name); + } + } + + exists?.length && ui.notifications.info(game.i18n.format('PF2EBestiary.Bestiary.Info.AlreadyExistsInBestiary', { creatures: exists.join(', ') })); + added?.length && ui.notifications.info(game.i18n.format('PF2EBestiary.Bestiary.Info.AddedToBestiary', { creatures: added.join(', ') })); } } + }); Hooks.on("updateCombatant", async (combatant, changes) => { @@ -94,14 +108,14 @@ Hooks.on("xdy-pf2e-workbench.tokenCreateMystification", token => { }); Hooks.on("preCreateToken", async token => { - if(!game.user.isGM) return; + if(!game.user.isGM || token.actor.type !== 'npc' || token.hasPlayerOwner) return; if(game.settings.get('pf2e-bestiary-tracking', 'hide-token-names')){ const bestiary = game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); const monster = bestiary.monster[token.baseActor.uuid]; if(monster){ - if(monster.name.custom && monster.name.revealed) { - await token.updateSource({ name: monster.name.custom }); + if(monster.name.revealed) { + await token.updateSource({ name: monster.name.custom ? monster.name.custom : monster.name.value }); return; } } @@ -134,14 +148,16 @@ Hooks.on("createChatMessage", async (message) => { const { automaticReveal } = game.settings.get('pf2e-bestiary-tracking', 'chat-message-handling'); if(automaticReveal){ const bestiary = game.settings.get('pf2e-bestiary-tracking', 'bestiary-tracking'); - const { automaticReveal } = game.settings.get('pf2e-bestiary-tracking', 'chat-message-handling'); if(message.flags.pf2e.origin){ // Attacks | Actions | Spells - const actorUuid = message.flags.pf2e.origin.actor; + const actor = await fromUuid(message.flags.pf2e.origin.actor); + if(actor.type !== 'npc' || actor.hasPlayerOwner) return; + + const actorUuid = getBaseActor(actor).uuid; const monster = bestiary.monster[actorUuid]; - const item = await fromUuid(message.flags.pf2e.origin.uuid); + const item = await fromUuid(message.flags.pf2e.origin.uuid); if(monster && item){ if(message.flags.pf2e.modifierName && automaticReveal.attacks){ const monsterItem = monster.system.actions[item.id]; @@ -162,7 +178,12 @@ Hooks.on("createChatMessage", async (message) => { } else { // Skills | Saving Throws - const monster = bestiary.monster[`Actor.${message.flags.pf2e.context.actor}`]; + const actor = await fromUuid(`Actor.${message.flags.pf2e.context.actor}`); + if(actor.type !== 'npc' || actor.hasPlayerOwner) return; + + const actorUuid = getBaseActor(actor).uuid; + const monster = bestiary.monster[actorUuid]; + if(monster){ if(message.flags.pf2e.context.type === 'skill-check' && automaticReveal.skills) { diff --git a/scripts/helpers.js b/scripts/helpers.js index e269d22..4af4c52 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -5,17 +5,17 @@ export const slugify = (name) => { export const getCreatureSize = (size) => { switch(size){ case 'grg': - return 'Gargantuan'; + return game.i18n.localize("PF2E.ActorSizeGargantuan"); case 'huge': - return 'Huge'; + return game.i18n.localize("PF2E.ActorSizeHuge"); case 'lg': - return 'Large'; + return game.i18n.localize("PF2E.ActorSizeLarge"); case 'med': - return 'Medium'; + return game.i18n.localize("PF2E.ActorSizeMedium"); case 'sm': - return 'Small'; + return game.i18n.localize("PF2E.ActorSizeSmall"); case 'tiny': - return 'Tiny'; + return game.i18n.localize("PF2E.ActorSizeTiny"); } }; @@ -59,4 +59,8 @@ export const getExpandedCreatureTypes = () => { return types; +}; + +export const getBaseActor = (actor) => { + return actor.token ? actor.token.document ? actor.token.document.baseActor : actor.token.baseActor : actor; }; \ No newline at end of file diff --git a/scripts/migrationHandler.js b/scripts/migrationHandler.js index e091621..5cbba56 100644 --- a/scripts/migrationHandler.js +++ b/scripts/migrationHandler.js @@ -112,7 +112,12 @@ export const handleDataMigration = async () => { bestiary.monster[type][monsterKey].saves.fortitude.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.fortitude.value); bestiary.monster[type][monsterKey].saves.reflex.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.reflex.value); bestiary.monster[type][monsterKey].saves.will.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].saves.will.value); - bestiary.monster[type][monsterKey].abilities.values.forEach(ability => ability.category = getCategoryLabel(attributeTable, origin.system.details.level.value, ability.mod)); + bestiary.monster[type][monsterKey].abilities.values.forEach(ability => { + // Weird error that occured here. Safety addition. + if(typeof x === 'object'){ + ability.category = getCategoryLabel(attributeTable, origin.system.details.level.value, ability.mod)} + } + ); bestiary.monster[type][monsterKey].senses.values.perception.category = getCategoryLabel(savingThrowPerceptionTable, origin.system.details.level.value, bestiary.monster[type][monsterKey].senses.values.perception.value); // All spellcasting creatures should have spell data diff --git a/styles/pf2e-bestiary-tracking.css b/styles/pf2e-bestiary-tracking.css index d4e177e..dc88664 100644 --- a/styles/pf2e-bestiary-tracking.css +++ b/styles/pf2e-bestiary-tracking.css @@ -638,7 +638,6 @@ flex: 0; } .pf2e-bestiary-tracking.bestiary .monster-container .right-monster-container .attack-container .attack-header .attack-traits-container .attack-trait-label { - text-decoration: underline; white-space: nowrap; } .pf2e-bestiary-tracking.bestiary .monster-container .right-monster-container .attack-container .attack-body { diff --git a/styles/stylesheets/bestiary.less b/styles/stylesheets/bestiary.less index 271d424..8924572 100644 --- a/styles/stylesheets/bestiary.less +++ b/styles/stylesheets/bestiary.less @@ -671,7 +671,6 @@ } .attack-trait-label { - text-decoration: underline; white-space: nowrap; } } diff --git a/templates/bestiaryAppearanceMenu.hbs b/templates/bestiaryAppearanceMenu.hbs index 0b640d4..7bce8cd 100644 --- a/templates/bestiaryAppearanceMenu.hbs +++ b/templates/bestiaryAppearanceMenu.hbs @@ -47,6 +47,15 @@ + +
+ +
+
+ +
+
+