From d4d13738374ace2e10b7bbece684214657b3a8af Mon Sep 17 00:00:00 2001 From: wrycu Date: Thu, 22 Feb 2024 15:11:13 -0800 Subject: [PATCH] Handle hidden actors on combat tracker Handle hidden actors. Hidden actors have their initiative slots shown but names and images hidden from players. GM view: ![image](https://github.com/StarWarsFoundryVTT/StarWarsFFG/assets/4709746/9d4f28fd-ead8-40cc-af91-a8767c7cb5f3) Player view of the same combat at the same time: ![image](https://github.com/StarWarsFoundryVTT/StarWarsFFG/assets/4709746/6b0882a1-bc9b-429b-aef7-413c575237df) --- images/combat/hidden.png | Bin 0 -> 9580 bytes lang/en.json | 1 + modules/combat-ffg.js | 132 ++++++++++++++++++++++++-- modules/swffg-main.js | 15 ++- templates/dialogs/combat-tracker.html | 50 +++++++++- 5 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 images/combat/hidden.png diff --git a/images/combat/hidden.png b/images/combat/hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..4b56861570a2c9c116f1d28080cafc958a1a4f09 GIT binary patch literal 9580 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ç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 P`V`Pm)ln%^vJU+p-w}ju literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json index 198da7e6..8e3cc1d6 100644 --- a/lang/en.json +++ b/lang/en.json @@ -621,6 +621,7 @@ "SWFFG.Combats.Actors.Friendly": "Friendly Actors", "SWFFG.Combats.Actors.Enemy": "Enemy Actors", "SWFFG.Combats.Actors.Neutral": "Neutral Actors", + "SWFFG.Combats.Actors.Hidden": "Hidden Actor", "SWFFG.Settings.UseGenericSlots.Name": "Use Generic Slots", "SWFFG.Settings.UseGenericSlots.Hint": "Replaces named slots in the combat tracker with generic, claimable slots", "SWFFG.Settings.showMinionCount.Name": "Show Minion Count", diff --git a/modules/combat-ffg.js b/modules/combat-ffg.js index 4fe08756..130d6ea2 100644 --- a/modules/combat-ffg.js +++ b/modules/combat-ffg.js @@ -225,6 +225,40 @@ export class CombatFFG extends Combat { return []; } + /** + * Check if a given combatant has any claims in the current combat round + * @param combatantId - STRING - the combatant ID (NOT token ID, NOT actor ID) + * @returns {boolean|string} - false if no claims, otherwise the round of the claimant + */ + hasClaims(combatantId) { + const claims = this.getClaims(this.round); + if (!claims) { + return false; + } + if (Object.values(claims).includes(combatantId)) { + return Object.keys(claims).find(key => claims[key] === combatantId); + } else { + return false; + } + } + + async handleCombatantRemoval(combatant, options, combatantId) { + CONFIG.logger.debug(`Handling combatant removal of ${combatant?.name}`); + const claims = this.hasClaims(combatant.id); + if (!claims) { + CONFIG.logger.debug("No claimed slots found, nothing to do!"); + return; + } + CONFIG.logger.debug("Claimed slots found, unclaiming..."); + await this.unclaimSlot(this.round, claims); + CONFIG.logger.debug("...Done!"); + } + + async handleCombatantAddition(combatant, context, options, combatantI) { + // there may be cases when this is needed, but for now, we don't need to do anything + // (leaving as a placeholder until we know for sure) + } + /** * Claim a slot for a given combatant * @param round - INT - the round @@ -359,12 +393,18 @@ export class CombatTrackerFFG extends CombatTracker { /** @override */ async getData(options) { - const data = await super.getData(options); const combat = this.viewed; + if (!combat) { + return await super.getData(options); + } - if (!combat) { - return data; + // create a copy of the turn data, then set hidden to false so non-GMs can view all turns, then set the data back + const tempData = foundry.utils.deepClone(this.viewed.turns); + for (const turn of this.viewed.turns) { + turn.hidden = false; } + const data = await super.getData(options); + this.viewed.turns = tempData; const initiatives = combat.combatants.reduce((accumulator, combatant) => { accumulator[combatant.id] = [{activationId: -1, initiative: combatant.initiative}]; @@ -390,6 +430,7 @@ export class CombatTrackerFFG extends CombatTracker { let claim = {}; if (combat.started && claimant) { + CONFIG.logger.debug(`slot ${index} has been claimed by ${claimant.name}`); let defeated = claimant.isDefeated; const effects = new Set(); @@ -410,8 +451,11 @@ export class CombatTrackerFFG extends CombatTracker { } } - // propagate this to the overall turn data, so we can gray out claimed slots - data.turns.find(i => i.id === claimant.id).claimed = true; + const hidden = this._getTokenHidden(claimant.tokenId); + + if (!hidden && turn.css) { + turn.css = turn?.css?.replace('hidden', ''); + } claim = { id: claimant.id, @@ -419,10 +463,29 @@ export class CombatTrackerFFG extends CombatTracker { img: claimant.img ?? CONST.DEFAULT_TOKEN, owner: claimant.owner, defeated, - hidden: claimant.hidden, + hidden: hidden, canPing: claimant.sceneId === canvas.scene?.id && game.user.hasPermission("PING_CANVAS"), effects, }; + turn.hidden = hidden; + turn.tokenId = claimant.tokenId; + } else { + CONFIG.logger.debug(`slot ${index} is unclaimed`); + combatant.hidden = this._getTokenHidden(combatant.tokenId); + turn.tokenId = combatant.tokenId; + // sync the turn state to the token state + turn.hidden = combatant.hidden; + } + + if (combatant.css === undefined) { + combatant.css = ""; + } + if (combat.turn === index) { + combatant.active = true; + combatant.css += " active"; + } else { + combatant.active = false; + combatant.css = ""; } const disposition = combatant.token?.disposition ?? combatant.actor?.token.disposition ?? 0; let slotType; @@ -464,6 +527,30 @@ export class CombatTrackerFFG extends CombatTracker { Neutral: data.turns.filter(i => combat.combatants.get(i.id).token.disposition === CONST.TOKEN_DISPOSITIONS.NEUTRAL), }; + // update visibility state for each token + for (const turn of turnData['Friendly']) { + turn.hidden = this._getTokenHidden(turn.tokenId); + turn.claimed = combat.hasClaims(combat.combatants.find(i => i.tokenId === turn.tokenId).id); + } + + for (const turn of turnData['Enemy']) { + const combatant = combat.combatants.get(turn.id); + const claimantId = combat.hasClaims(combat.combatants.find(i => i.tokenId === turn.tokenId).id); + const claimant = claimantId ? (combat.combatants.get(claimantId)) : undefined; + if (combat.started && claimant) { + turn.hidden = this._getTokenHidden(claimant.tokenId); + turn.claimed = combat.hasClaims(combat.combatants.find(i => i.tokenId === claimant.tokenId).id ); + } else { + turn.hidden = this._getTokenHidden(combatant.tokenId); + turn.claimed = combat.hasClaims(combat.combatants.find(i => i.tokenId === combatant.tokenId).id); + } + } + + for (const turn of turnData['Neutral']) { + turn.hidden = this._getTokenHidden(turn.tokenId); + turn.claimed = combat.hasClaims(combat.combatants.find(i => i.tokenId === turn.tokenId).id); + } + return { ...data, turns, @@ -487,7 +574,7 @@ export class CombatTrackerFFG extends CombatTracker { return rawAlive.length + defeated.filter(i => i.id && claimed.includes(i.id)).length; } - /* @override */ + /** @override */ _getEntryContextOptions() { const baseEntries = super._getEntryContextOptions(); @@ -506,7 +593,7 @@ export class CombatTrackerFFG extends CombatTracker { return [...baseEntries, unClaimSlot]; } - /* @override */ + /** @override */ async _onCombatantHoverIn(event) { event.preventDefault(); @@ -516,7 +603,7 @@ export class CombatTrackerFFG extends CombatTracker { return super._onCombatantHoverIn(event); } - /* @override */ + /** @override */ async _onCombatantMouseDown(event) { event.preventDefault(); @@ -525,4 +612,31 @@ export class CombatTrackerFFG extends CombatTracker { } return super._onCombatantMouseDown(event); } + + /** + * Determine the hidden status of a token, since the state in the combat tracker seems to lag + * @param tokenId + * @returns {boolean} + * @private + */ + _getTokenHidden(tokenId) { + let hidden = true; + const scene = game.scenes.get(this.viewed.scene.id); + const token = scene.tokens.get(tokenId); + if (token) { + hidden = token.hidden; + } + CONFIG.logger.debug(`looking up hidden state for ${token?.name}/${tokenId} on scene ${scene.id}: ${hidden}`); + return hidden; + } +} + +/** + * Force the combat tracker to re-render, which picks up "hidden" state changes of tokens + */ +export function updateCombatTracker() { + // Used to force the tracker to re-render based on updated visibility state + if (game.combat && game.settings.get("starwarsffg", "useGenericSlots")) { + ui.combat.render(true); + } } diff --git a/modules/swffg-main.js b/modules/swffg-main.js index 1f15615b..ee1a207e 100644 --- a/modules/swffg-main.js +++ b/modules/swffg-main.js @@ -7,7 +7,7 @@ // Import Modules import { FFG } from "./swffg-config.js"; import { ActorFFG } from "./actors/actor-ffg.js"; -import {CombatFFG, CombatTrackerFFG} from "./combat-ffg.js"; +import {CombatFFG, CombatTrackerFFG, updateCombatTracker} from "./combat-ffg.js"; import { ItemFFG } from "./items/item-ffg.js"; import { ItemSheetFFG } from "./items/item-sheet-ffg.js"; import { ItemSheetFFGV2 } from "./items/item-sheet-ffg-v2.js"; @@ -314,6 +314,19 @@ Hooks.once("init", async function () { } } }); + + Hooks.on("updateToken", async (tokenDocument, options, diffData, tokenId) => { + if (Object.keys(options).includes('hidden')) { + updateCombatTracker(); + } + }); + + Hooks.on("preCreateCombatant", async (combatant, context, options, combatantId) => { + await game.combat.handleCombatantAddition(combatant, context, options, combatantId); + }); + Hooks.on("preDeleteCombatant", async (combatant, options, unknownId) => { + await game.combat.handleCombatantRemoval(combatant, options, unknownId); + }); } await gameSkillsList(); diff --git a/templates/dialogs/combat-tracker.html b/templates/dialogs/combat-tracker.html index 5d1db91e..574e8438 100644 --- a/templates/dialogs/combat-tracker.html +++ b/templates/dialogs/combat-tracker.html @@ -67,7 +67,15 @@

{{localize "COMBAT.None"}}

{{#each turnData.Friendly }} - {{name}} + {{#if ../user.isGM }} + {{name}} + {{else}} + {{#if hidden}} + {{localize + {{else}} + {{name}} + {{/if}} + {{/if}} {{/each}}
{{/if}} @@ -77,7 +85,15 @@

{{localize "COMBAT.None"}}

{{#each turnData.Enemy }} - {{name}} + {{#if ../user.isGM }} + {{name}} + {{else}} + {{#if hidden}} + {{localize + {{else}} + {{name}} + {{/if}} + {{/if}} {{/each}}
{{/if}} @@ -87,16 +103,40 @@

{{localize "COMBAT.None"}}

{{#each turnData.Neutral }} - {{name}} + {{#if ../user.isGM }} + {{name}} + {{else}} + {{#if hidden}} + {{localize + {{else}} + {{name}} + {{/if}} + {{/if}} {{/each}}
{{/if}} {{#each turns}} {{#if claimed}} -
  • - {{name}} +
  • + {{#if ../user.isGM}} + {{name}} + {{else}} + {{#if hidden }} + {{localize + {{else}} + {{name}} + {{/if}} + {{/if}}
    + {{#if ../user.isGM}}

    {{name}}

    + {{else}} + {{#if hidden }} +

    {{localize "SWFFG.Combats.Actors.Hidden"}}

    + {{else}} +

    {{name}}

    + {{/if}} + {{/if}}
    {{#if ../user.isGM}} {{#if (not hasRolled)}}