From 1fe1ec3dcbc3deae90ca19575f5f3a5e2a34d019 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 28 Mar 2026 01:34:21 +0100 Subject: [PATCH] A token having a turnmarker is now determined by a global setting, and not wether its a combatants turn --- lang/en.json | 3 +- module/applications/ui/combatTracker.mjs | 13 +++++- module/canvas/placeables/token.mjs | 28 ++++++++++++ module/config/settingsConfig.mjs | 3 +- module/data/_module.mjs | 1 + module/data/spotlightTracker.mjs | 9 ++++ module/macros/spotlightCombatant.mjs | 55 +++++++++++++++++------- module/systemRegistration/settings.mjs | 14 +++++- 8 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 module/data/spotlightTracker.mjs diff --git a/lang/en.json b/lang/en.json index d341b4bf..cb020a93 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2555,8 +2555,7 @@ "MACROS": { "Spotlight": { "errors": { - "noActiveCombat": "There is no active encounter", - "noCombatantSelected": "A combatant token must be either selected or hovered to spotlight it" + "noTokenSelected": "A token on the canvas must either be selected or hovered to spotlight it" } } }, diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 345c6fcd..38e6802c 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -1,5 +1,6 @@ import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs'; import { expireActiveEffects } from '../../helpers/utils.mjs'; +import { spotlightToken } from '../../macros/spotlightCombatant.mjs'; export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker { static DEFAULT_OPTIONS = { @@ -150,13 +151,13 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C } async setCombatantSpotlight(combatantId) { + const combatant = this.viewed.combatants.get(combatantId); const update = { system: { 'spotlight.requesting': false, 'spotlight.requestOrderIndex': 0 } }; - const combatant = this.viewed.combatants.get(combatantId); const toggleTurn = this.viewed.combatants.contents .sort(this.viewed._sortCombatants) @@ -187,6 +188,16 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C round: this.viewed.round + 1 }); await combatant.update(update); + + if (!combatant.token) return; + spotlightToken(combatant.token); + } + + async clearTurn() { + await this.viewed.update({ + turn: null, + round: this.viewed.round + 1 + }); } static async requestSpotlight(_, target) { diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 35d34f83..2af2bf6f 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -10,6 +10,34 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { this.previewHelp ||= this.addChild(this.#drawPreviewHelp()); } + /**@inheritdoc */ + _refreshTurnMarker() { + // Should a Turn Marker be active? + const { turnMarker } = this.document; + const markersEnabled = + CONFIG.Combat.settings.turnMarker.enabled && turnMarker.mode !== CONST.TOKEN_TURN_MARKER_MODES.DISABLED; + const spotlighted = game.settings + .get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker) + .spotlightedTokens.has(this.document.uuid); + + const markerActive = markersEnabled && spotlighted; + + // Activate a Turn Marker + if (markerActive) { + if (!this.turnMarker) + this.turnMarker = this.addChildAt(new foundry.canvas.placeables.tokens.TokenTurnMarker(this), 0); + canvas.tokens.turnMarkers.add(this); + this.turnMarker.draw(); + } + + // Remove a Turn Marker + else if (this.turnMarker) { + canvas.tokens.turnMarkers.delete(this); + this.turnMarker.destroy(); + this.turnMarker = null; + } + } + /** @inheritDoc */ async _drawEffects() { this.effects.renderable = false; diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 36892731..de4d96be 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -39,7 +39,8 @@ export const gameSettings = { Countdowns: 'Countdowns', LastMigrationVersion: 'LastMigrationVersion', SpotlightRequestQueue: 'SpotlightRequestQueue', - CompendiumBrowserSettings: 'CompendiumBrowserSettings' + CompendiumBrowserSettings: 'CompendiumBrowserSettings', + SpotlightTracker: 'SpotlightTracker' }; export const actionAutomationChoices = { diff --git a/module/data/_module.mjs b/module/data/_module.mjs index 43ff7807..0e7e295e 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -4,6 +4,7 @@ export { default as DhRollTable } from './rollTable.mjs'; export { default as RegisteredTriggers } from './registeredTriggers.mjs'; export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs'; export { default as TagTeamData } from './tagTeamData.mjs'; +export { default as SpotlightTracker } from './spotlightTracker.mjs'; export * as countdowns from './countdowns.mjs'; export * as actions from './action/_module.mjs'; diff --git a/module/data/spotlightTracker.mjs b/module/data/spotlightTracker.mjs new file mode 100644 index 00000000..57f54e16 --- /dev/null +++ b/module/data/spotlightTracker.mjs @@ -0,0 +1,9 @@ +export default class SpotlightTracker extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + spotlightedTokens: new fields.SetField(new fields.DocumentUUIDField()) + }; + } +} diff --git a/module/macros/spotlightCombatant.mjs b/module/macros/spotlightCombatant.mjs index 68a26ff9..6d4c63fc 100644 --- a/module/macros/spotlightCombatant.mjs +++ b/module/macros/spotlightCombatant.mjs @@ -1,21 +1,44 @@ /** - * Spotlights a combatant. - * The combatant can be selected in a number of ways. If many are applied at the same time, the following order is used: - * 1) SelectedCombatant - * 2) HoveredCombatant + * Spotlight a token on the canvas. If it is a combatant, run it through combatTracker's spotlight logic. + * @param {TokenDocument} token - The token to spotlight + * @returns {void} */ -const spotlightCombatant = () => { - if (!game.combat) - return ui.notifications.error(game.i18n.localize('DAGGERHEART.MACROS.Spotlight.errors.noActiveCombat')); +const spotlightCombatantMacro = token => { + if (!token) + return ui.notifications.error(game.i18n.localize('DAGGERHEART.MACROS.Spotlight.errors.noTokenSelected')); - const selectedCombatant = canvas.tokens.controlled.length > 0 ? canvas.tokens.controlled[0].combatant : null; - const hoveredCombatant = game.canvas.tokens.hover?.combatant; - - const combatant = selectedCombatant ?? hoveredCombatant; - if (!combatant) - return ui.notifications.error(game.i18n.localize('DAGGERHEART.MACROS.Spotlight.errors.noCombatantSelected')); - - ui.combat.setCombatantSpotlight(combatant.id); + if (game.combat && token.combatant) { + ui.combat.setCombatantSpotlight(token.combatant.id); + } else { + if (game.combat) ui.combat.clearTurn(); + spotlightToken(token.document); + } }; -export default spotlightCombatant; +/** + * Spotlight a token on the canvas. + * @param {TokenDocument} token - The token to spotlight + * @returns {void} + */ +export const spotlightToken = async token => { + const spotlightTracker = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker); + let previouslySpotlightedUuid = null; + const isSpotlighted = spotlightTracker.spotlightedTokens.has(token.uuid); + if (!isSpotlighted && spotlightTracker.spotlightedTokens.size > 0) { + previouslySpotlightedUuid = spotlightTracker.spotlightedTokens.first(); + } + + spotlightTracker.updateSource({ + spotlightedTokens: isSpotlighted ? [] : [token.uuid] + }); + + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker, spotlightTracker); + token.object.renderFlags.set({ refreshTurnMarker: true }); + + if (previouslySpotlightedUuid) { + const previousToken = await foundry.utils.fromUuid(previouslySpotlightedUuid); + previousToken.object.renderFlags.set({ refreshTurnMarker: true }); + } +}; + +export default spotlightCombatantMacro; diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index 5eb9dad1..63611cda 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -16,6 +16,7 @@ import { DhVariantRuleSettings } from '../applications/settings/_module.mjs'; import { CompendiumBrowserSettings } from '../data/_module.mjs'; +import SpotlightTracker from '../data/spotlightTracker.mjs'; export const registerDHSettings = () => { registerKeyBindings(); @@ -40,7 +41,12 @@ export const registerKeyBindings = () => { hint: game.i18n.localize('DAGGERHEART.SETTINGS.Keybindings.spotlight.hint'), uneditable: [], editable: [], - onDown: game.system.api.macros.spotlightCombatant, + onDown: () => { + const selectedTokens = canvas.tokens.controlled.length > 0 ? canvas.tokens.controlled[0] : null; + const hoveredTokens = game.canvas.tokens.hover ? game.canvas.tokens.hover : null; + const tokens = selectedTokens ?? hoveredTokens; + game.system.api.macros.spotlightCombatant(tokens); + }, onUp: () => {}, restricted: true, reservedModifiers: [], @@ -177,4 +183,10 @@ const registerNonConfigSettings = () => { config: false, type: CompendiumBrowserSettings }); + + game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightTracker, { + scope: 'world', + config: false, + type: SpotlightTracker + }); };