diff --git a/lang/en.json b/lang/en.json index a78ed588..c5f42ed6 100755 --- a/lang/en.json +++ b/lang/en.json @@ -366,7 +366,8 @@ "toggleSimple": "Toggle Simple View", "types": { "narrative": "Narrative", - "encounter": "Encounter" + "encounter": "Encounter", + "longterm": "Long Term" } }, "CountdownEdit": { @@ -2661,7 +2662,8 @@ "noPlayerAccess": "This countdown isn't visible to any players", "loop": "Looping", "decreasingLoop": "Decreasing Looping", - "increasingLoop": "Increasing Looping" + "increasingLoop": "Increasing Looping", + "longTermIndicator": "Long Term Countdowns" }, "EffectsDisplay": { "removeThing": "[Right Click] Remove {thing}", diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 42920a4a..204ef150 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -15,6 +15,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application super(options); this.setupHooks(); + this._isFocused = false; } /** @inheritDoc */ @@ -90,7 +91,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application countdown, ownership: DhCountdowns.#getPlayerOwnership(game.user, setting, countdown) })); - return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE); + return values.filter(v => + v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE + ); } /** @override */ @@ -102,14 +105,18 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === CONFIG.DH.GENERAL.countdownAppMode.iconOnly; const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - context.countdowns = this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { - const playersWithAccess = game.users.reduce((acc, user) => { - const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown); - if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { - acc.push(user); - } - return acc; - }, []); + + const allCountdowns = this.#getCountdowns(); + + const { longTermCountdowns, otherCountdowns } = allCountdowns.reduce((acc, { key, countdown, ownership }) => { + const playersWithAccess = game.users.reduce((acc, user) => { + const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown); + if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { + acc.push(user); + } + return acc; + }, []); + const nonGmPlayers = game.users.filter(x => !x.isGM); const countdownEditable = game.user.isGM || ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; @@ -125,7 +132,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application !countdownEditable || (isLooping && (countdown.progress.current > 0 || countdown.progress.start === '0')); - acc[key] = { + const countdownData = { ...countdown, editable: countdownEditable, noPlayerAccess: nonGmPlayers.length && playersWithAccess.length === 0, @@ -133,12 +140,37 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application loopDisabled: isLooping ? loopDisabled : null, loopTooltip: isLooping && game.i18n.localize(loopTooltip) }; + if (countdown.type == CONFIG.DH.GENERAL.countdownBaseTypes.longterm.id){ + if (this._isFocused){ + acc.longTermCountdowns.push([key, countdownData]); + } + } else { + acc.otherCountdowns.push([key, countdownData]) + } + return acc; + }, {longTermCountdowns: [], otherCountdowns: []}); + + // Combine: regular countdowns first, then long-term + context.countdowns = [...otherCountdowns, ...longTermCountdowns].reduce((acc, [key, countdown]) => { + acc[key]=countdown; return acc; }, {}); - + context.hasHiddenLongTerm = !this._isFocused && allCountdowns.some( + ({countdown}) => countdown.type === CONFIG.DH.GENERAL.countdownBaseTypes.longterm.id + ); return context; } + /**Filter countdowns based on focus state */ + #shouldShowCountdown(countdown){ + // Always show narrative and encounter countdowns + if (countdown.type !== CONFIG.DH.GENERAL.countdownBaseTypes.longterm.id){ + return true; + } + // Only show longterm countdowns when focused/hovered + return this._isFocused; + } + static #getPlayerOwnership(user, setting, countdown) { const playerOwnership = countdown.ownership[user.id]; return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT @@ -236,6 +268,19 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application setupHooks() { Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind()); } + #onFocus() { + if (!this._isFocused){ + this._isFocused = true; + this.render() + } + } + + #onBlur() { + if (this._isFocused){ + this._isFocused = false; + this.render() + } + } async close(options) { /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */ @@ -291,5 +336,10 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application if (options?.force) { document.getElementById('ui-right-column-1')?.appendChild(this.element); } + // Hover/focus listeners + this.element.addEventListener('mouseenter', this.#onFocus.bind(this)); + this.element.addEventListener('mouseleave', this.#onBlur.bind(this)); + this.element.addEventListener('focusin', this.#onFocus.bind(this)); + this.element.addEventListener('focusout', this.#onBlur.bind(this)); } } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 3f49f7aa..d8d3a633 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -690,6 +690,10 @@ export const countdownBaseTypes = { encounter: { id: 'encounter', label: 'DAGGERHEART.APPLICATIONS.Countdown.types.encounter' + }, + longterm: { + id: 'longterm', + label: 'DAGGERHEART.APPLICATIONS.Countdown.types.longterm' } }; diff --git a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json index 5320a0ed..aaf6babd 100644 --- a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json +++ b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json @@ -346,15 +346,15 @@ "countdown": [ { "name": "Casus Belli", - "type": "narrative", + "type": "longterm", "defaultOwnership": -1, "img": "icons/sundries/scrolls/scroll-bound-sealed-red-tan.webp", "progress": { "looping": "noLooping", "type": "custom", - "start": 1, "startFormula": "8", - "current": 1 + "current": 1, + "start": 1 }, "ownership": {} } diff --git a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json index aacf87e9..f34d90b7 100644 --- a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json +++ b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json @@ -180,15 +180,15 @@ "countdown": [ { "name": "Final Preparations", - "type": "encounter", + "type": "longterm", "defaultOwnership": -1, "img": "icons/magic/unholy/hands-circle-light-green.webp", "progress": { "looping": "noLooping", "type": "custom", - "start": 1, "startFormula": "8", - "current": 1 + "current": 1, + "start": 1 }, "ownership": {} } diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 47f06eb7..ca17ee86 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -153,4 +153,20 @@ } } } + .longterm-indicator { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px; + margin-top: 4px; + border-top: 1px solid light-dark(@dark-blue-40, @beige-40); + font-size: var(--font-size-12); + color: light-dark(@dark-80, @beige-80); + cursor: default; + + i { + font-size: var(--font-size-10); + } + } } diff --git a/templates/ui/countdowns.hbs b/templates/ui/countdowns.hbs index 18694e49..a7885488 100644 --- a/templates/ui/countdowns.hbs +++ b/templates/ui/countdowns.hbs @@ -38,5 +38,11 @@ {{/each}} + {{#if hasHiddenLongTerm}} +