diff --git a/daggerheart.mjs b/daggerheart.mjs index d7aba401..e079703a 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -8,10 +8,8 @@ import * as fields from './module/data/fields/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; -import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs'; import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll } from './module/dice/_module.mjs'; import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs'; -import { registerCountdownHooks } from './module/data/countdowns.mjs'; import { handlebarsRegistration, runMigrations, @@ -140,6 +138,7 @@ Hooks.once('init', () => { CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.ui.resources = applications.ui.DhFearTracker; + CONFIG.ui.countdowns = applications.ui.DhCountdowns; CONFIG.ux.ContextMenu = applications.ux.DHContextMenu; CONFIG.ux.TooltipManager = documents.DhTooltipManager; @@ -166,10 +165,12 @@ Hooks.on('ready', async () => { if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide') ui.resources.render({ force: true }); + ui.countdowns = new CONFIG.ui.countdowns(); + ui.countdowns.render({ force: true }); + if (!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser)) ui.compendiumBrowser = new applications.ui.ItemBrowser(); - registerCountdownHooks(); socketRegistration.registerSocketHooks(); registerRollDiceHooks(); socketRegistration.registerUserQueries(); @@ -242,29 +243,6 @@ Hooks.on('chatMessage', (_, message) => { } }); -Hooks.on('renderJournalDirectory', async (tab, html, _, options) => { - if (tab.id === 'journal') { - if (options.parts && !options.parts.includes('footer')) return; - - const buttons = tab.element.querySelector('.directory-footer.action-buttons'); - const title = game.i18n.format('DAGGERHEART.APPLICATIONS.Countdown.title', { - type: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.types.narrative') - }); - buttons.insertAdjacentHTML( - 'afterbegin', - ` - ` - ); - - buttons.querySelector('#narrative-countdown-button').onclick = async () => { - new NarrativeCountdowns().open(); - }; - } -}); - Hooks.on('moveToken', async (movedToken, data) => { const effectsAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).effects; if (!effectsAutomation.rangeDependent) return; diff --git a/lang/en.json b/lang/en.json index a978cd16..9e48e3c5 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2470,6 +2470,10 @@ "playerMessage": "{user} rerolled their {name}" } }, + "Countdowns": { + "title": "Countdowns", + "minimize": "Minimize" + }, "ItemBrowser": { "title": "Daggerheart Compendium Browser", "hint": "Select a Folder in sidebar to start browsing through the compendium", diff --git a/module/applications/ui/_module.mjs b/module/applications/ui/_module.mjs index 7645219e..35a58566 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -1,7 +1,7 @@ export { default as CountdownEdit } from './countdownEdit.mjs'; +export { default as DhCountdowns } from './countdowns.mjs'; export { default as DhChatLog } from './chatLog.mjs'; export { default as DhCombatTracker } from './combatTracker.mjs'; -export * as DhCountdowns from './countdowns.mjs'; export { default as DhFearTracker } from './fearTracker.mjs'; export { default as DhHotbar } from './hotbar.mjs'; export { ItemBrowser } from './itemBrowser.mjs'; diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 85c77cd3..1d42e186 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -1,5 +1,3 @@ -import { EncounterCountdowns } from '../ui/countdowns.mjs'; - export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker { static DEFAULT_OPTIONS = { actions: { @@ -168,8 +166,4 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C await combatant.update({ 'system.actionTokens': newIndex }); this.render(); } - - static openCountdowns() { - new EncounterCountdowns().open(); - } } diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 5f80ae10..754c5071 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -1,4 +1,5 @@ import { DhCountdown } from '../../data/countdowns.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -62,7 +63,10 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio async updateSetting(update) { await this.data.updateSource(update); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, this.data); + await emitAsGM(GMUpdateEvent.UpdateCountdowns, this.gmSetSetting.bind(this.data), this.data, null, { + refreshType: RefreshType.Countdown + }); + this.render(); } @@ -70,6 +74,15 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio this.updateSetting(foundry.utils.expandObject(formData.object)); } + async gmSetSetting(data) { + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data), + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); + } + static #addCountdown() { this.updateSetting({ [`countdowns.${foundry.utils.randomID()}`]: DhCountdown.defaultCountdown() diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 5e3ad1ab..7645526f 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -1,355 +1,139 @@ -import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; -import constructHTMLButton from '../../helpers/utils.mjs'; -import OwnershipSelection from '../dialogs/ownershipSelection.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; -class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(basePath) { - super({}); +/** + * A UI element which displays the countdowns in this world. + * + * @extends ApplicationV2 + * @mixes HandlebarsApplication + */ - this.basePath = basePath; - } - - get title() { - return game.i18n.format('DAGGERHEART.APPLICATIONS.Countdown.title', { - type: game.i18n.localize(`DAGGERHEART.APPLICATIONS.Countdown.types.${this.basePath}`) - }); +export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(options = {}) { + super(options); + + this.sidebarCollapsed = true; + this.setupHooks(); } + /** @inheritDoc */ static DEFAULT_OPTIONS = { - classes: ['daggerheart', 'dh-style', 'countdown'], - tag: 'form', - position: { width: 740, height: 700 }, + id: 'countdowns', + tag: 'div', + classes: ['daggerheart', 'dh-style', 'countdowns'], window: { + icon: 'fa-solid fa-clock-rotate-left', frame: true, - title: 'Countdowns', - resizable: true, + title: 'Fear', + positioned: false, + resizable: false, minimizable: false }, actions: { - addCountdown: this.addCountdown, - removeCountdown: this.removeCountdown, - editImage: this.onEditImage, - openOwnership: this.openOwnership, - openCountdownOwnership: this.openCountdownOwnership, - toggleSimpleView: this.toggleSimpleView + decreaseCountdown: (_, target) => this.editCountdown(false, target), + increaseCountdown: (_, target) => this.editCountdown(true, target) }, - form: { handler: this.updateData, submitOnChange: true } - }; - - static PARTS = { - countdowns: { - template: 'systems/daggerheart/templates/ui/countdowns.hbs', - scrollable: ['.expanded-view'] + position: { + width: 400, + height: 222, + top: 50 } }; - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); + /** @override */ + static PARTS = { + resources: { + root: true, + template: 'systems/daggerheart/templates/ui/countdowns.hbs' + } + }; - htmlElement.querySelectorAll('.mini-countdown-container').forEach(element => { - element.addEventListener('click', event => this.updateCountdownValue.bind(this)(event, false)); - element.addEventListener('contextmenu', event => this.updateCountdownValue.bind(this)(event, true)); - }); + get title() { + return game.i18n.localize('DAGGERHEART.UI.Countdowns.title'); } - async _preFirstRender(context, options) { - options.position = - game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.basePath}Countdown`].position) ?? - Countdowns.DEFAULT_OPTIONS.position; - - const viewSetting = - game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.basePath}Countdown`].simple) ?? !game.user.isGM; - this.simpleView = - game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true; - context.simple = this.simpleView; - } - - _onPosition(position) { - game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.basePath}Countdown`].position, position); + get element() { + return document.body.querySelector('.daggerheart.dh-style.countdowns'); } + /**@inheritdoc */ async _renderFrame(options) { const frame = await super._renderFrame(options); + const header = frame.querySelector('.window-header'); + header.querySelector('button[data-action="close"]').remove(); - if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) { - const button = constructHTMLButton({ - label: '', - classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'], - dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.APPLICATIONS.Countdown.toggleSimple' } - }); - this.window.controls.after(button); - } + const minimizeTooltip = game.i18n.localize('DAGGERHEART.UI.Countdowns.minimize'); + const minimizeButton = ``; + header.insertAdjacentHTML('beforeEnd', minimizeButton); return frame; } - testUserPermission(level, exact, altSettings) { - if (game.user.isGM) return true; + /** @override */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.sidebarCollapsed = this.sidebarCollapsed; - const settings = - altSettings ?? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[this.basePath]; - const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level; - const userAllowed = exact - ? settings.playerOwnership[game.user.id]?.value === level - : settings.playerOwnership[game.user.id]?.value >= level; - return defaultAllowed || userAllowed; - } + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + context.countdowns = Object.keys(setting.countdowns).reduce((acc, key) => { + const countdown = setting.countdowns[key]; + const playerOwnership = countdown.ownership[game.user.id]; + const ownership = + playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT + ? setting.defaultOwnership + : playerOwnership; + if (ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) return acc; - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - const countdownData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[ - this.basePath - ]; - - context.isGM = game.user.isGM; - context.base = this.basePath; - - context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true); - context.source = { - ...countdownData, - countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { - const countdown = countdownData.countdowns[key]; - - if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) { - acc[key] = { - ...countdown, - canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown) - }; - } - - return acc; - }, {}) - }; - context.systemFields = countdownData.schema.fields; - context.countdownFields = context.systemFields.countdowns.element.fields; - context.simple = this.simpleView; + acc[key] = { + ...countdown, + editable: game.user.isGM || ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER + }; + return acc; + }, {}); return context; } - static async updateData(event, _, formData) { - const data = foundry.utils.expandObject(formData.object); - const newSetting = foundry.utils.mergeObject( - game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).toObject(), - data - ); + toggleCollapsedPosition = async (_, collapsed) => { + this.sidebarCollapsed = collapsed; + if (!collapsed) this.element.classList.add('expanded'); + else this.element.classList.remove('expanded'); + }; - if (game.user.isGM) { - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, newSetting); - this.render(); - } else { - await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.GMUpdate, - data: { - action: GMUpdateEvent.UpdateSetting, - uuid: CONFIG.DH.SETTINGS.gameSettings.Countdowns, - update: newSetting - } - }); - } + cooldownRefresh = ({ refreshType }) => { + if (refreshType === RefreshType.Countdown) this.render(); + }; + + static async editCountdown(increase, target) { + const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + const countdown = settings.countdowns[target.id]; + const newCurrent = increase + ? Math.min(countdown.progress.current + 1, countdown.progress.max) + : Math.max(countdown.progress.current - 1, 0); + await settings.updateSource({ [`countdowns.${target.id}.progress.current`]: newCurrent }); + await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { + refreshType: RefreshType.Countdown + }); } - async updateSetting(update) { - if (game.user.isGM) { - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, update); - await game.socket.emit(`system.${CONFIG.DH.id}`, { + static async gmSetSetting(data) { + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data), + game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.Refresh, - data: { - refreshType: RefreshType.Countdown, - application: `${this.basePath}-countdowns` - } + data: { refreshType: RefreshType.Countdown } }); - - this.render(); - } else { - await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.GMUpdate, - data: { - action: GMUpdateEvent.UpdateSetting, - uuid: CONFIG.DH.SETTINGS.gameSettings.Countdowns, - update: update, - refresh: { refreshType: RefreshType.Countdown, application: `${this.basePath}-countdowns` } - } - }); - } + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); } - static onEditImage(_, target) { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[this.basePath]; - const current = setting.countdowns[target.dataset.countdown].img; - const fp = new foundry.applications.apps.FilePicker.implementation({ - current, - type: 'image', - callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown), - top: this.position.top + 40, - left: this.position.left + 10 - }); - return fp.browse(); + setupHooks() { + Hooks.on('collapseSidebar', this.toggleCollapsedPosition.bind()); + Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind()); } - async updateImage(path, countdown) { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - await setting.updateSource({ - [`${this.basePath}.countdowns.${countdown}.img`]: path - }); - - await this.updateSetting(setting); - } - - static openOwnership(_, target) { - new Promise((resolve, reject) => { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[this.basePath]; - const ownership = { default: setting.ownership.default, players: setting.playerOwnership }; - new OwnershipSelection(resolve, reject, this.title, ownership).render(true); - }).then(async ownership => { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - await setting.updateSource({ - [`${this.basePath}.ownership`]: ownership - }); - - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, setting.toObject()); - this.render(); - }); - } - - static openCountdownOwnership(_, target) { - const countdownId = target.dataset.countdown; - new Promise((resolve, reject) => { - const countdown = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[this.basePath] - .countdowns[countdownId]; - const ownership = { default: countdown.ownership.default, players: countdown.playerOwnership }; - new OwnershipSelection(resolve, reject, countdown.name, ownership).render(true); - }).then(async ownership => { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - await setting.updateSource({ - [`${this.basePath}.countdowns.${countdownId}.ownership`]: ownership - }); - - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, setting); - this.render(); - }); - } - - static async toggleSimpleView() { - this.simpleView = !this.simpleView; - await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS[`${this.basePath}Countdown`].simple, this.simpleView); - this.render(); - } - - async updateCountdownValue(event, increase) { - const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown]; - - if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) { - return; - } - - const currentValue = countdown.progress.current; - - if (increase && currentValue === countdown.progress.max) return; - if (!increase && currentValue === 0) return; - - await countdownSetting.updateSource({ - [`${this.basePath}.countdowns.${event.currentTarget.dataset.countdown}.progress.current`]: increase - ? currentValue + 1 - : currentValue - 1 - }); - - await this.updateSetting(countdownSetting.toObject()); - } - - static async addCountdown() { - const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - await countdownSetting.updateSource({ - [`${this.basePath}.countdowns.${foundry.utils.randomID()}`]: { - name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.newCountdown'), - ownership: game.user.isGM - ? {} - : { - players: { - [game.user.id]: { type: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER } - } - } - } - }); - - await this.updateSetting(countdownSetting.toObject()); - } - - static async removeCountdown(_, target) { - const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdownName = countdownSetting[this.basePath].countdowns[target.dataset.countdown].name; - - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.removeCountdownTitle') - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.Countdown.removeCountdownText', { name: countdownName }) - }); - if (!confirmed) return; - - await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null }); - - await this.updateSetting(countdownSetting.toObject()); - } - - async open() { - await this.render(true); - if ( - Object.keys( - game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns)[this.basePath].countdowns - ).length > 0 - ) { - this.minimize(); - } + close(options) { + Hooks.off('collapseSidebar', this.toggleCollapsedPosition); + Hooks.off(socketEvent.Refresh, this.cooldownRefresh); + super.close(options); } } - -export class NarrativeCountdowns extends Countdowns { - constructor() { - super('narrative'); - } - - static DEFAULT_OPTIONS = { - id: 'narrative-countdowns' - }; -} - -export class EncounterCountdowns extends Countdowns { - constructor() { - super('encounter'); - } - - static DEFAULT_OPTIONS = { - id: 'encounter-countdowns' - }; -} - -export async function updateCountdowns(progressType) { - const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const update = Object.keys(countdownSetting).reduce((update, typeKey) => { - return foundry.utils.mergeObject( - update, - Object.keys(countdownSetting[typeKey].countdowns).reduce((acc, countdownKey) => { - const countdown = countdownSetting[typeKey].countdowns[countdownKey]; - if (countdown.progress.current > 0 && countdown.progress.type.value === progressType) { - acc[`${typeKey}.countdowns.${countdownKey}.progress.current`] = countdown.progress.current - 1; - } - - return acc; - }, {}) - ); - }, {}); - - await countdownSetting.updateSource(update); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSetting); - - const data = { refreshType: RefreshType.Countdown }; - await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data - }); - Hooks.callAll(socketEvent.Refresh, data); -} diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index 397e87c1..1bfc2b18 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -1,5 +1,3 @@ -import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs'; - export default class DhCountdowns extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -136,18 +134,3 @@ export class DhCountdown extends foundry.abstract.DataModel { }, {}); } } - -export const registerCountdownHooks = () => { - Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => { - if (refreshType === RefreshType.Countdown) { - if (application) { - foundry.applications.instances.get(application)?.render(); - } else { - foundry.applications.instances.get('narrative-countdowns')?.render(); - foundry.applications.instances.get('encounter-countdowns')?.render(); - } - - return false; - } - }); -}; diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index f75c7b36..14b4cec1 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -25,6 +25,7 @@ export const GMUpdateEvent = { UpdateEffect: 'DhGMUpdateEffect', UpdateSetting: 'DhGMUpdateSetting', UpdateFear: 'DhGMUpdateFear', + UpdateCountdowns: 'DhGMUpdateCountdowns', UpdateSaveMessage: 'DhGMUpdateSaveMessage' }; @@ -60,6 +61,10 @@ export const registerSocketHooks = () => { ) ); break; + case GMUpdateEvent.UpdateCountdowns: + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data.update); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); + break; case GMUpdateEvent.UpdateSaveMessage: const action = await fromUuid(data.update.action), message = game.messages.get(data.update.message); @@ -84,14 +89,15 @@ export const registerUserQueries = () => { CONFIG.queries.reactionRoll = game.system.api.fields.ActionFields.SaveField.rollSaveQuery; }; -export const emitAsGM = async (eventName, callback, update, uuid = null) => { +export const emitAsGM = async (eventName, callback, update, uuid = null, refresh = null) => { if (!game.user.isGM) { return await game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.GMUpdate, data: { action: eventName, uuid, - update + update, + refresh } }); } else return callback(update); diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 5e46989a..6f21a000 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -1,60 +1,70 @@ -@import '../../utils/colors.less'; -@import '../../utils/fonts.less'; +.theme-dark { + .daggerheart.dh-style.countdowns { + background-image: url(../assets/parchments/dh-parchment-dark.png); -.daggerheart.dh-style.countdown { - fieldset { - align-items: center; - margin-top: 5px; - border-radius: 6px; - border-color: light-dark(@dark-blue, @golden); - - legend { - font-weight: bold; - color: light-dark(@dark-blue, @golden); - - a { - text-shadow: none; - } + .window-header { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + } +} + +.daggerheart.dh-style.countdowns { + z-index: var(--z-index-ui) !important; + border: 0; + border-radius: 4px; + box-shadow: none; + width: 300px; + top: 16px; + right: 64px; + transition: right ease 250ms; + + &.expanded { + right: 364px; + } + + .window-header { + cursor: default; + border-bottom: 0; + } + + .window-content { + padding-top: 4px; + padding-bottom: 16px; + + .countdowns-container { + display: flex; + flex-direction: column; + gap: 8px; + + .countdown-container { + display: flex; + gap: 16px; + + img { + width: 44px; + height: 44px; + } + + .countdown-content { + height: 44px; + display: flex; + flex-direction: column; + justify-content: space-between; + + .countdown-tools { + display: flex; + align-items: center; + gap: 16px; + + .progress-tag { + border: 1px solid; + border-radius: 4px; + padding: 2px 4px; + background-color: light-dark(@beige, @dark-blue); + } + } + } + } } } - - .minimized-view { - display: flex; - gap: 8px; - flex-wrap: wrap; - - .mini-countdown-container { - width: fit-content; - display: flex; - align-items: center; - gap: 8px; - border: 2px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - padding: 0 4px 0 0; - background-image: url('../assets/parchments/dh-parchment-light.png'); - color: light-dark(@beige, @dark); - cursor: pointer; - - &.disabled { - cursor: initial; - } - - img { - width: 30px; - height: 30px; - border-radius: 6px 0 0 6px; - } - - .mini-countdown-name { - white-space: nowrap; - } - - .mini-countdown-value { - } - } - } - - .hidden { - display: none; - } } diff --git a/templates/ui/combatTracker/combatTrackerHeader.hbs b/templates/ui/combatTracker/combatTrackerHeader.hbs index 9ecff9e6..fe7d33a0 100644 --- a/templates/ui/combatTracker/combatTrackerHeader.hbs +++ b/templates/ui/combatTracker/combatTrackerHeader.hbs @@ -67,7 +67,6 @@ {{!-- Combat Controls --}}
{{#if hasCombat}} -
{{/if}} - {{#if isGM}}{{/if}} -
- -
- {{#each source.countdowns}} -
- - {{this.name}} - {{#if this.canEdit}}{{/if}} - {{#if @root.isGM}}{{/if}} - - - -
- -
- {{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}} -
- {{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}} - {{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}} -
- {{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}} -
+
+ {{#each countdowns as | countdown id |}} +
+ +
+ +
+ {{#if countdown.editable}}{{/if}} +
+ {{countdown.progress.current}}/{{countdown.progress.max}}
-
- {{/each}} + {{#if countdown.editable}}{{/if}} +
+
- - {{/if}} + {{/each}} + \ No newline at end of file