From 1bc9e07098bc052e902770e9df4c3ef76bfb13dc Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 30 Jan 2026 09:58:21 -0500 Subject: [PATCH 1/8] [Feature] Show token distance on hover (#1607) * Show token distance on hover * Do not show distance hover when ranges variant rule is disabled * Use range labels function for distance hover * Fix very far and support feet * . --------- Co-authored-by: WBHarry --- lang/en.json | 8 +++ module/canvas/placeables/measuredTemplate.mjs | 31 ++-------- module/canvas/placeables/token.mjs | 56 +++++++++++++++++++ module/data/settings/Appearance.mjs | 19 +++++++ .../settings/appearance-settings/main.hbs | 4 ++ 5 files changed, 92 insertions(+), 26 deletions(-) diff --git a/lang/en.json b/lang/en.json index e358977f..0186ae3e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2405,6 +2405,14 @@ "hideAttribution": { "label": "Hide Attribution" }, + "showTokenDistance": { + "label": "Show Token Distance on Hover", + "choices": { + "always": "Always", + "encounters": "Encounters", + "never": "Never" + } + }, "expandedTitle": "Auto-expand Descriptions", "extendCharacterDescriptions": { "label": "Characters" diff --git a/module/canvas/placeables/measuredTemplate.mjs b/module/canvas/placeables/measuredTemplate.mjs index ef7d284b..e1ea79f5 100644 --- a/module/canvas/placeables/measuredTemplate.mjs +++ b/module/canvas/placeables/measuredTemplate.mjs @@ -18,8 +18,9 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur static getRangeLabels(distanceValue, settings) { let result = { distance: distanceValue, units: '' }; - const sceneRangeMeasurement = canvas.scene.flags.daggerheart?.rangeMeasurement; + if (!settings.enabled) return result; + const sceneRangeMeasurement = canvas.scene.flags.daggerheart?.rangeMeasurement; const { disable, custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting; if (sceneRangeMeasurement?.setting === disable.id) { result.distance = distanceValue; @@ -27,31 +28,9 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur return result; } - const melee = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.melee : settings.melee; - const veryClose = - sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.veryClose : settings.veryClose; - const close = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.close : settings.close; - const far = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement.far : settings.far; - if (distanceValue <= melee) { - result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.melee.name'); - return result; - } - if (distanceValue <= veryClose) { - result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryClose.name'); - return result; - } - if (distanceValue <= close) { - result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.close.name'); - return result; - } - if (distanceValue <= far) { - result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name'); - return result; - } - if (distanceValue > far) { - result.distance = game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name'); - } - + const ranges = sceneRangeMeasurement?.setting === custom.id ? sceneRangeMeasurement : settings; + const distanceKey = ['melee', 'veryClose', 'close', 'far'].find(r => ranges[r] >= distanceValue); + result.distance = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${distanceKey ?? 'veryFar'}.name`); return result; } } diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 2266d0da..bc5c2a01 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -1,3 +1,5 @@ +import DhMeasuredTemplate from "./measuredTemplate.mjs"; + export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { /** @inheritdoc */ async _draw(options) { @@ -78,6 +80,60 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance; } + _onHoverIn(event, options) { + super._onHoverIn(event, options); + + // Check if the setting is enabled + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).showTokenDistance; + if (setting === "never" || (setting === "encounters" && !game.combat?.started)) return; + + // Check if this token isn't invisible and is actually being hovered + const isTokenValid = + this.visible && + this.hover && + !this.isPreview && + !this.document.isSecret && + !this.controlled && + !this.animation; + if (!isTokenValid) return; + + // Ensure we have a single controlled token + const originToken = canvas.tokens.controlled[0]; + if (!originToken || canvas.tokens.controlled.length > 1) return; + + // Determine the actual range + const ranges = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; + const distanceNum = originToken.distanceTo(this); + const distanceResult = DhMeasuredTemplate.getRangeLabels(distanceNum, ranges); + const distanceLabel = `${distanceResult.distance} ${distanceResult.units}`.trim(); + + // Create the element + const element = document.createElement('div'); + element.id = 'token-hover-distance'; + element.classList.add('waypoint-label', 'last'); + const ruler = document.createElement('i'); + ruler.classList.add('fa-solid', 'fa-ruler'); + element.appendChild(ruler); + const labelEl = document.createElement('span'); + labelEl.classList.add('total-measurement'); + labelEl.textContent = distanceLabel; + element.appendChild(labelEl); + + // Position the element and add to the DOM + const center = this.getCenterPoint(); + element.style.setProperty('--transformY', 'calc(-100% - 10px)'); + element.style.setProperty('--position-y', `${this.y}px`); + element.style.setProperty('--position-x', `${center.x}px`); + element.style.setProperty('--ui-scale', String(canvas.dimensions.uiScale)); + document.querySelector('#token-hover-distance')?.remove(); + document.querySelector('#measurement').appendChild(element); + } + + _onHoverOut(...args) { + super._onHoverOut(...args); + document.querySelector('#token-hover-distance')?.remove(); + } + /** Returns the point at which a line starting at origin and ending at destination intersects the edge of the bounds */ #getEdgeBoundary(bounds, originPoint, destinationPoint) { const points = [ diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index 2b8d3b27..d7a638d7 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -42,6 +42,25 @@ export default class DhAppearance extends foundry.abstract.DataModel { damage: new BooleanField(), target: new BooleanField() }), + showTokenDistance: new StringField({ + required: true, + choices: { + always: { + value: 'always', + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showTokenDistance.choices.always' + }, + encounters: { + value: 'encounters', + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showTokenDistance.choices.encounters' + }, + never: { + value: 'never', + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showTokenDistance.choices.never' + } + }, + nullable: false, + initial: 'always' + }), hideAttribution: new BooleanField(), showGenericStatusEffects: new BooleanField({ initial: true }) }; diff --git a/templates/settings/appearance-settings/main.hbs b/templates/settings/appearance-settings/main.hbs index 75a7e634..32dd9e63 100644 --- a/templates/settings/appearance-settings/main.hbs +++ b/templates/settings/appearance-settings/main.hbs @@ -16,6 +16,10 @@ value=setting.showGenericStatusEffects localize=true}} {{formGroup + fields.showTokenDistance + value=setting.showTokenDistance + localize=true}} + {{formGroup fields.hideAttribution value=setting.hideAttribution localize=true}} From 94efbeada3e89155d434fc50d09445f9dd221b73 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:05:11 +0100 Subject: [PATCH 2/8] Fixed a silly error with Hexgrid (#1608) --- module/documents/token.mjs | 2 +- system.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/documents/token.mjs b/module/documents/token.mjs index 317f3acf..b9507c2f 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -269,7 +269,7 @@ export default class DHToken extends CONFIG.Token.documentClass { // Hexagon symmetry if (columns) { - const rowData = BaseToken.#getHexagonalShape(height, width, shape, false); + const rowData = DHToken.#getHexagonalShape(height, width, shape, false); if (!rowData) return null; // Transpose the offsets/points of the shape in row orientation diff --git a/system.json b/system.json index 4a5eee0f..90ebf11d 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.6.1", + "version": "1.6.2", "compatibility": { "minimum": "13.346", "verified": "13.351", From 4c51bb589996b4b643c5b9aac11781a26946e780 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 31 Jan 2026 15:08:07 +0100 Subject: [PATCH 3/8] 1613-Rolltable-delete-formula-buttons --- module/applications/sheets/rollTables/rollTable.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/module/applications/sheets/rollTables/rollTable.mjs b/module/applications/sheets/rollTables/rollTable.mjs index 9ead6814..edb0a734 100644 --- a/module/applications/sheets/rollTables/rollTable.mjs +++ b/module/applications/sheets/rollTables/rollTable.mjs @@ -108,14 +108,16 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa getSystemFlagUpdate() { const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce( (acc, formulaKey) => { - if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[`-=${formulaKey}`] = null; + if (!this.daggerheartFlag.altFormula[formulaKey]) + acc.altFormula[formulaKey] = foundry.data.operators.ForcedDeletion.create(); return acc; }, { altFormula: {} } ); - return { ['flags.daggerheart']: foundry.utils.mergeObject(this.daggerheartFlag.toObject(), deleteUpdate) }; + const flagData = this.daggerheartFlag.toObject(); + return { ...flagData, altFormula: { ...flagData.altFormula, ...deleteUpdate.altFormula } }; } static async #addFormula() { @@ -127,7 +129,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa static async #removeFormula(_event, target) { await this.daggerheartFlag.updateSource({ - [`altFormula.-=${target.dataset.key}`]: null + [`altFormula.${target.dataset.key}`]: foundry.data.operators.ForcedDeletion.create() }); this.render({ internalRefresh: true }); } From 9553f3387f3987cca55a16c9f1b8b4f78e89a358 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 31 Jan 2026 15:09:07 +0100 Subject: [PATCH 4/8] Fixed sceneConfig, sceneNavigation and SceneEnvironments (#1616) --- .../scene/sceneConfigSettings.mjs | 8 +++ module/applications/ui/sceneNavigation.mjs | 5 +- styles/less/ui/scene-config/scene-config.less | 2 + .../ui/sceneNavigation/scene-navigation.hbs | 52 ++++++++++++++++++- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index 98e18f09..dda4330a 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -62,7 +62,15 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S } async _onDrop(event) { + event.stopPropagation(); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + if (data.type === 'Level') { + const level = await foundry.documents.Level.fromDropData(data); + if (level?.parent === this.document) return this._onSortLevel(event, level); + + return; + } + const item = await foundry.utils.fromUuid(data.uuid); if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') { let sceneUuid = data.uuid; diff --git a/module/applications/ui/sceneNavigation.mjs b/module/applications/ui/sceneNavigation.mjs index 0a3e08a5..bc906dac 100644 --- a/module/applications/ui/sceneNavigation.mjs +++ b/module/applications/ui/sceneNavigation.mjs @@ -31,7 +31,7 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi const environments = daggerheartInfo.sceneEnvironments.filter( x => x && x.testUserPermission(game.user, 'LIMITED') ); - const hasEnvironments = environments.length > 0 && x.isView; + const hasEnvironments = environments.length > 0 && x.active; return { ...x, hasEnvironments, @@ -39,9 +39,10 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi environments: environments }; }); + context.scenes.active = extendScenes(context.scenes.active); context.scenes.inactive = extendScenes(context.scenes.inactive); - + context.scenes.viewed = context.scenes.viewed ? extendScenes([context.scenes.viewed])[0] : null; return context; } diff --git a/styles/less/ui/scene-config/scene-config.less b/styles/less/ui/scene-config/scene-config.less index 664e7526..ba1afb0b 100644 --- a/styles/less/ui/scene-config/scene-config.less +++ b/styles/less/ui/scene-config/scene-config.less @@ -13,6 +13,8 @@ .application.sheet.scene-config { .sheet-tabs.tabs { + font-size: 12px; + a[data-tab='dh'] { display: flex; align-items: center; diff --git a/templates/ui/sceneNavigation/scene-navigation.hbs b/templates/ui/sceneNavigation/scene-navigation.hbs index 41e9e3e8..933d2074 100644 --- a/templates/ui/sceneNavigation/scene-navigation.hbs +++ b/templates/ui/sceneNavigation/scene-navigation.hbs @@ -1,10 +1,40 @@ + + + +{{#*inline ".scene"}} +
  • +
    + {{ name }} + {{#if users}} +
      + {{#each users}} +
    • {{ letter }}
    • + {{/each}} +
    + {{/if}} +
    + {{#if hasEnvironments}} + + {{/if}} +
  • +{{/inline}} + From cd52aa8f9ceab205263a1f5037b6342609c65e6c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 31 Jan 2026 18:31:10 +0100 Subject: [PATCH 5/8] Updated from special database update syntax to DataFieldOperators --- .../characterCreation/characterCreation.mjs | 2 +- module/applications/dialogs/characterResetDialog.mjs | 6 +++--- module/applications/dialogs/tagTeamDialog.mjs | 2 +- module/applications/levelup/levelup.mjs | 6 +++--- module/applications/scene/sceneConfigSettings.mjs | 2 +- module/applications/settings/homebrewSettings.mjs | 12 ++++++------ .../sheets-configs/adversary-settings.mjs | 2 +- .../sheets-configs/character-settings.mjs | 6 +++--- .../sheets-configs/companion-settings.mjs | 2 +- .../sheets-configs/environment-settings.mjs | 2 +- .../sheets-configs/setting-feature-config.mjs | 2 +- .../sheets-configs/token-config-mixin.mjs | 2 +- module/applications/sheets/items/beastform.mjs | 2 +- module/applications/sheets/rollTables/rollTable.mjs | 5 ++--- module/applications/ui/countdownEdit.mjs | 2 +- module/data/activeEffect/beastformEffect.mjs | 2 +- module/data/actor/base.mjs | 4 +--- module/data/actor/party.mjs | 2 +- module/data/fields/actionField.mjs | 4 ++-- module/data/item/armor.mjs | 2 +- module/data/item/base.mjs | 6 +++--- module/data/item/weapon.mjs | 2 +- module/documents/actor.mjs | 4 ++-- module/helpers/utils.mjs | 4 ++-- module/systemRegistration/migrations.mjs | 2 +- 25 files changed, 42 insertions(+), 45 deletions(-) diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index aa764c56..e6c0f299 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -554,7 +554,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl experiences: { ...this.setup.experiences, ...Object.keys(this.character.system.experiences).reduce((acc, key) => { - acc[`-=${key}`] = null; + acc[`${key}`] = _del; return acc; }, {}) } diff --git a/module/applications/dialogs/characterResetDialog.mjs b/module/applications/dialogs/characterResetDialog.mjs index 0836af9c..aecebc7c 100644 --- a/module/applications/dialogs/characterResetDialog.mjs +++ b/module/applications/dialogs/characterResetDialog.mjs @@ -77,8 +77,8 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App if (!this.data.optional.portrait.keep) { foundry.utils.setProperty(update, 'img', this.actor.schema.fields.img.initial(this.actor)); - foundry.utils.setProperty(update, 'prototypeToken.==texture', {}); - foundry.utils.setProperty(update, 'prototypeToken.==ring', {}); + foundry.utils.setProperty(update, 'prototypeToken.texture', _replace({})); + foundry.utils.setProperty(update, 'prototypeToken.ring', _replace({})); } if (this.data.optional.biography.keep) @@ -89,7 +89,7 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App const { system, ...rest } = update; await this.actor.update({ ...rest, - '==system': system ?? {} + system: _replace(system ?? {}) }); const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot']; diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index d1a1e123..c28b773c 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -168,7 +168,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } static async #removeMember(_, button) { - const update = { [`members.-=${button.dataset.characterId}`]: null }; + const update = { [`members.${button.dataset.characterId}`]: _del }; if (this.data.initiator.id === button.dataset.characterId) { update.iniator = { id: null }; } diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index ba6110cc..c4616d9a 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -477,7 +477,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const secondaryData = Object.keys( foundry.utils.getProperty(this.levelup, `${target.dataset.path}.secondaryData`) ).reduce((acc, key) => { - acc[`-=${key}`] = null; + acc[key] = _del; return acc; }, {}); await this.levelup.updateSource({ @@ -511,9 +511,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const current = foundry.utils.getProperty(this.levelup, `${basePath}.${button.dataset.option}`); if (Number(button.dataset.cost) > 1 || Object.keys(current).length === 1) { // Simple handling that doesn't cover potential Custom LevelTiers. - update[`${basePath}.-=${button.dataset.option}`] = null; + update[`${basePath}.${button.dataset.option}`] = _del; } else { - update[`${basePath}.${button.dataset.option}.-=${button.dataset.checkboxNr}`] = null; + update[`${basePath}.${button.dataset.option}.${button.dataset.checkboxNr}`] = _del; } } else { if (this.levelup.levels[this.levelup.currentLevel].nrSelections.available < Number(button.dataset.cost)) { diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index dda4330a..ceb403cf 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -118,7 +118,7 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) { if (!submitData.flags.daggerheart.sceneEnvironments[key]) { - submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null; + submitData.flags.daggerheart.sceneEnvironments[key] = _del; } } diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 6e2e665d..cecd12d7 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -228,7 +228,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli const isDowntime = ['shortRest', 'longRest'].includes(type); const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`; await this.settings.updateSource({ - [`${path}.-=${id}`]: null + [`${path}.${id}`]: _del }); this.render(); } @@ -250,7 +250,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli const fields = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).schema.fields; const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => { - acc[`-=${key}`] = null; + acc[key] = _del; return acc; }, {}); @@ -310,7 +310,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli [`itemFeatures.${target.dataset.type}`]: Object.keys( this.settings.itemFeatures[target.dataset.type] ).reduce((acc, key) => { - acc[`-=${key}`] = null; + acc[key] = _del; return acc; }, {}) @@ -383,12 +383,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli if (!confirmed) return; await this.settings.updateSource({ - [`domains.-=${this.selected.domain}`]: null + [`domains.${this.selected.domain}`]: _del }); const currentSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew); if (currentSettings.domains[this.selected.domain]) { - await currentSettings.updateSource({ [`domains.-=${this.selected.domain}`]: null }); + await currentSettings.updateSource({ [`domains.${this.selected.domain}`]: _del }); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, currentSettings); } @@ -435,7 +435,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli static async deleteAdversaryType(_, target) { const { key } = target.dataset; - await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null }); + await this.settings.updateSource({ [`adversaryTypes.${key}`]: _del }); this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType; this.render(); diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index d3d215be..6593f23d 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -95,7 +95,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { }); if (!confirmed) return; - await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); + await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); } async _onDragStart(event) { diff --git a/module/applications/sheets-configs/character-settings.mjs b/module/applications/sheets-configs/character-settings.mjs index 20a09cfc..c655b23f 100644 --- a/module/applications/sheets-configs/character-settings.mjs +++ b/module/applications/sheets-configs/character-settings.mjs @@ -101,8 +101,8 @@ export default class DHCharacterSettings extends DHBaseActorSettings { if (relinkAchievementData.length > 0) { relinkAchievementData.forEach(data => { - updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.-=${data.experience}`] = - null; + updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.${data.experience}`] = + _del; }); } else if (relinkSelectionData.length > 0) { relinkSelectionData.forEach(data => { @@ -137,7 +137,7 @@ export default class DHCharacterSettings extends DHBaseActorSettings { await this.actor.update({ ...updates, - [`system.experiences.-=${target.dataset.experience}`]: null + [`system.experiences.${target.dataset.experience}`]: _del }); } } diff --git a/module/applications/sheets-configs/companion-settings.mjs b/module/applications/sheets-configs/companion-settings.mjs index 8aa21479..6c1265af 100644 --- a/module/applications/sheets-configs/companion-settings.mjs +++ b/module/applications/sheets-configs/companion-settings.mjs @@ -117,6 +117,6 @@ export default class DHCompanionSettings extends DHBaseActorSettings { }); if (!confirmed) return; - await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); + await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); } } diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 15f5701d..5c039f85 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -79,7 +79,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { * @type {ApplicationClickAction} */ static async #removeCategory(_, target) { - await this.actor.update({ [`system.potentialAdversaries.-=${target.dataset.categoryId}`]: null }); + await this.actor.update({ [`system.potentialAdversaries.${target.dataset.categoryId}`]: _del }); } /** diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index e8bf6109..e8ff7818 100644 --- a/module/applications/sheets-configs/setting-feature-config.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -206,7 +206,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App } }); } else { - await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); + await this.settings.updateSource({ [`${this.actionsPath}.${target.dataset.id}`]: _del }); } this.move = foundry.utils.getProperty(this.settings, this.movePath); diff --git a/module/applications/sheets-configs/token-config-mixin.mjs b/module/applications/sheets-configs/token-config-mixin.mjs index c29b54ff..a23aa7f4 100644 --- a/module/applications/sheets-configs/token-config-mixin.mjs +++ b/module/applications/sheets-configs/token-config-mixin.mjs @@ -67,7 +67,7 @@ export default function DHTokenConfigMixin(Base) { changes.height = tokenSize; } - const deletions = { '-=actorId': null, '-=actorLink': null }; + const deletions = { actorId: _del, actorLink: _del }; const mergeOptions = { inplace: false, performDeletions: true }; this._preview.updateSource(mergeObject(changes, deletions, mergeOptions)); diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs index 880c0796..0c9991c4 100644 --- a/module/applications/sheets/items/beastform.mjs +++ b/module/applications/sheets/items/beastform.mjs @@ -102,7 +102,7 @@ export default class BeastformSheet extends DHBaseItemSheet { async advantageOnRemove(event) { await this.document.update({ - [`system.advantageOn.-=${event.detail.data.value}`]: null + [`system.advantageOn.${event.detail.data.value}`]: _del }); } } diff --git a/module/applications/sheets/rollTables/rollTable.mjs b/module/applications/sheets/rollTables/rollTable.mjs index edb0a734..d7498e80 100644 --- a/module/applications/sheets/rollTables/rollTable.mjs +++ b/module/applications/sheets/rollTables/rollTable.mjs @@ -108,8 +108,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa getSystemFlagUpdate() { const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce( (acc, formulaKey) => { - if (!this.daggerheartFlag.altFormula[formulaKey]) - acc.altFormula[formulaKey] = foundry.data.operators.ForcedDeletion.create(); + if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[formulaKey] = _del; return acc; }, @@ -129,7 +128,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa static async #removeFormula(_event, target) { await this.daggerheartFlag.updateSource({ - [`altFormula.${target.dataset.key}`]: foundry.data.operators.ForcedDeletion.create() + [`altFormula.${target.dataset.key}`]: _del }); this.render({ internalRefresh: true }); } diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 7f1deea3..8bb9fc1d 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -233,6 +233,6 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio } if (this.editingCountdowns.has(countdownId)) this.editingCountdowns.delete(countdownId); - this.updateSetting({ [`countdowns.-=${countdownId}`]: null }); + this.updateSetting({ [`countdowns.${countdownId}`]: _del }); } } diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 53430ba3..47e28b4c 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -100,7 +100,7 @@ export default class BeastformEffect extends BaseEffect { token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg } }, - 'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null } + 'flags.daggerheart': { beastformTokenImg: _del, beastformSubjectTexture: _del } }; }; diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 08308eab..833f1222 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -169,9 +169,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { const tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); await tagTeam.updateSource({ initiator: this.parent.id === tagTeam.initiator ? null : tagTeam.initiator, - members: Object.keys(tagTeam.members).find(x => x === this.parent.id) - ? { [`-=${this.parent.id}`]: null } - : {} + members: Object.keys(tagTeam.members).find(x => x === this.parent.id) ? { [this.parent.id]: _del } : {} }); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll, tagTeam); } diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index 236d65db..3eddf235 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -48,7 +48,7 @@ export default class DhParty extends BaseDataActor { initiator: this.partyMembers.some(x => x.id === tagTeam.initiator) ? null : tagTeam.initiator, members: Object.keys(tagTeam.members).reduce((acc, key) => { if (this.partyMembers.find(x => x.id === key)) { - acc[`-=${key}`] = null; + acc[key] = _del; } return acc; diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index a6d0abbe..1305a6d8 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -243,11 +243,11 @@ export function ActionMixin(Base) { : foundry.utils.getProperty(result, basePath); } - delete() { + async delete() { if (!this.inCollection) return this.item; const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id); if (!action) return this.item; - this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null }); + await this.item.update({ [`system.${this.systemPath}.${this.id}`]: _del }); // Does not work. Unsure why. It worked in v13 <_<' this.constructor._sheets.get(this.uuid)?.close(); } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 3d4a62fa..050b66d4 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -87,7 +87,7 @@ export default class DHArmor extends AttachableItem { } await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds); changes.system.actions = actionIds.reduce((acc, id) => { - acc[`-=${id}`] = null; + acc[id] = _del; return acc; }, {}); diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 84f39103..447da3bf 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -230,9 +230,9 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { if (changed.system?.actions) { const triggersToRemove = Object.keys(changed.system.actions).reduce((acc, key) => { - if (!changed.system.actions[key]) { - const strippedKey = key.replace('-=', ''); - acc.push(...this.actions.get(strippedKey).triggers.map(x => x.trigger)); + const action = changed.system.actions[key]; + if (action && Object.keys(action).length === 0) { + acc.push(...this.actions.get(key).triggers.map(x => x.trigger)); } return acc; diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index f333e5f3..85849a83 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -147,7 +147,7 @@ export default class DHWeapon extends AttachableItem { await this.parent.deleteEmbeddedDocuments('ActiveEffect', removedEffectsUpdate); changes.system.actions = removedActionsUpdate.reduce((acc, id) => { - acc[`-=${id}`] = null; + acc[id] = _del; return acc; }, {}); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index e8bea0bf..cb51a255 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -142,7 +142,7 @@ export default class DhpActor extends Actor { } const updatedLevelups = Object.keys(this.system.levelData.levelups).reduce((acc, level) => { - if (Number(level) > usedLevel) acc[`-=${level}`] = null; + if (Number(level) > usedLevel) acc[level] = _del; return acc; }, {}); @@ -187,7 +187,7 @@ export default class DhpActor extends Actor { if (experiences.length > 0) { const getUpdate = () => ({ 'system.experiences': experiences.reduce((acc, key) => { - acc[`-=${key}`] = null; + acc[key] = _del; return acc; }, {}) }); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index bb9a0cfa..7ca2c41c 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -171,10 +171,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue [innerProperty]: innerPropertyDefaultValue }; } else { - acc[`${key}.-=${innerProperty}`] = null; + acc[`${key}.${innerProperty}`] = _del; } } else { - acc[`-=${key}`] = null; + acc[`${key}`] = _del; } return acc; diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 743d42a4..4216c38f 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -197,7 +197,7 @@ export async function runMigrations() { const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { if (!game.actors.some(actor => actor.id === id)) { - acc[`-=${id}`] = null; + acc[id] = _del; } return acc; }, {}); From ae91d6786f85686420a8c5f828de67f2800cd3cc Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 31 Jan 2026 19:32:51 +0100 Subject: [PATCH 6/8] Fixed actions not being delete:able --- module/data/fields/_module.mjs | 1 - module/data/fields/actionField.mjs | 11 ++- module/data/fields/mappingField.mjs | 128 ---------------------------- 3 files changed, 5 insertions(+), 135 deletions(-) delete mode 100644 module/data/fields/mappingField.mjs diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 2a8ba454..58423979 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -3,5 +3,4 @@ export { default as FormulaField } from './formulaField.mjs'; export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs'; export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs'; export { default as TriggerField } from './triggerField.mjs'; -export { default as MappingField } from './mappingField.mjs'; export * as ActionFields from './action/_module.mjs'; diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 1305a6d8..d0cc808b 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -1,6 +1,5 @@ import DHActionConfig from '../../applications/sheets-configs/action-config.mjs'; import { itemAbleRollParse } from '../../helpers/utils.mjs'; -import MappingField from './mappingField.mjs'; /** * Specialized collection type for stored actions. @@ -11,9 +10,9 @@ export class ActionCollection extends Collection { constructor(model, entries) { super(); this.#model = model; - for (const entry of entries) { - if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue; - this.set(entry._id, entry); + for (const [key, value] of entries) { + if (!(value instanceof game.system.api.models.actions.actionsTypes.base)) continue; + this.set(key, value); } } @@ -61,7 +60,7 @@ export class ActionCollection extends Collection { /** * Field that stores actions. */ -export class ActionsField extends MappingField { +export class ActionsField extends foundry.data.fields.TypedObjectField { constructor(options) { super(new ActionField(), options); } @@ -70,7 +69,7 @@ export class ActionsField extends MappingField { /** @inheritDoc */ initialize(value, model, options) { - const actions = Object.values(super.initialize(value, model, options)); + const actions = Object.entries(super.initialize(value, model, options)); return new ActionCollection(model, actions); } } diff --git a/module/data/fields/mappingField.mjs b/module/data/fields/mappingField.mjs deleted file mode 100644 index 31d91c76..00000000 --- a/module/data/fields/mappingField.mjs +++ /dev/null @@ -1,128 +0,0 @@ -/** - * A subclass of ObjectField that represents a mapping of keys to the provided DataField type. - * - * @param {DataField} model The class of DataField which should be embedded in this field. - * @param {MappingFieldOptions} [options={}] Options which configure the behavior of the field. - * @property {string[]} [initialKeys] Keys that will be created if no data is provided. - * @property {MappingFieldInitialValueBuilder} [initialValue] Function to calculate the initial value for a key. - * @property {boolean} [initialKeysOnly=false] Should the keys in the initialized data be limited to the keys provided - * by `options.initialKeys`? - */ -export default class MappingField extends foundry.data.fields.ObjectField { - constructor(model, options) { - if (!(model instanceof foundry.data.fields.DataField)) { - throw new Error('MappingField must have a DataField as its contained element'); - } - super(options); - - /** - * The embedded DataField definition which is contained in this field. - * @type {DataField} - */ - this.model = model; - model.parent = this; - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - static get _defaults() { - return foundry.utils.mergeObject(super._defaults, { - initialKeys: null, - initialValue: null, - initialKeysOnly: false - }); - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - _cleanType(value, options) { - Object.entries(value).forEach(([k, v]) => { - if (k.startsWith('-=')) return; - value[k] = this.model.clean(v, options); - }); - return value; - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - getInitialValue(data) { - let keys = this.initialKeys; - const initial = super.getInitialValue(data); - if (!keys || !foundry.utils.isEmpty(initial)) return initial; - if (!(keys instanceof Array)) keys = Object.keys(keys); - for (const key of keys) initial[key] = this._getInitialValueForKey(key); - return initial; - } - - /* -------------------------------------------- */ - - /** - * Get the initial value for the provided key. - * @param {string} key Key within the object being built. - * @param {object} [object] Any existing mapping data. - * @returns {*} Initial value based on provided field type. - */ - _getInitialValueForKey(key, object) { - const initial = this.model.getInitialValue(); - return this.initialValue?.(key, initial, object) ?? initial; - } - - /* -------------------------------------------- */ - - /** @override */ - _validateType(value, options = {}) { - if (foundry.utils.getType(value) !== 'Object') throw new Error('must be an Object'); - const errors = this._validateValues(value, options); - if (!foundry.utils.isEmpty(errors)) { - const failure = new foundry.data.validation.DataModelValidationFailure(); - failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure })); - throw failure.asError(); - } - } - - /* -------------------------------------------- */ - - /** - * Validate each value of the object. - * @param {object} value The object to validate. - * @param {object} options Validation options. - * @returns {Record} An object of value-specific errors by key. - */ - _validateValues(value, options) { - const errors = {}; - for (const [k, v] of Object.entries(value)) { - if (k.startsWith('-=')) continue; - const error = this.model.validate(v, options); - if (error) errors[k] = error; - } - return errors; - } - - /* -------------------------------------------- */ - - /** @override */ - initialize(value, model, options = {}) { - if (!value) return value; - const obj = {}; - const initialKeys = this.initialKeys instanceof Array ? this.initialKeys : Object.keys(this.initialKeys ?? {}); - const keys = this.initialKeysOnly ? initialKeys : Object.keys(value); - for (const key of keys) { - const data = value[key] ?? this._getInitialValueForKey(key, value); - obj[key] = this.model.initialize(data, model, options); - } - return obj; - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - _getField(path) { - if (path.length === 0) return this; - else if (path.length === 1) return this.model; - path.shift(); - return this.model._getField(path); - } -} From 22d446f360ec2e9094b13a0cb00f8570578c744a Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 31 Jan 2026 19:34:37 +0100 Subject: [PATCH 7/8] Fixed so that homebrew downtime move actions can have effects (#1615) --- .../settings/homebrewSettings.mjs | 3 +- module/data/action/baseAction.mjs | 17 ++++++- module/data/fields/action/effectsField.mjs | 6 +-- module/data/fields/actionField.mjs | 1 + module/data/settings/Homebrew.mjs | 44 +++++++------------ 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 6e2e665d..add3c9e1 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -165,7 +165,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newDowntimeMove'), img: 'icons/magic/life/cross-worn-green.webp', description: '', - actions: [] + actions: [], + effects: [] } }); } else if (['armorFeatures', 'weaponFeatures'].includes(type)) { diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 115e6463..e843027b 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -114,9 +114,24 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * Return Item the action is attached too. */ get item() { + if (!this.parent.parent && this.systemPath) + return foundry.utils.getProperty(this.parent, this.systemPath).get(this.id); + return this.parent.parent; } + get applyEffects() { + if (this.item.systemPath) { + const itemEffectIds = this.item.effects.map(x => x._id); + const movePathSplit = this.item.systemPath.split('.'); + movePathSplit.pop(); + const move = foundry.utils.getProperty(this.parent, movePathSplit.join('.')); + return new Collection(itemEffectIds.map(id => [id, move.effects.find(x => x.id === id)])); + } + + return this.item.effects; + } + /** * Return the first Actor parent found. */ @@ -125,7 +140,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel ? this.item : this.item?.parent instanceof DhpActor ? this.item.parent - : this.item?.actor; + : null; } static getRollType(parent) { diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 2233a383..6afd470b 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -73,7 +73,7 @@ export default class EffectsField extends fields.ArrayField { }); effects.forEach(async e => { - const effect = this.item.effects.get(e._id); + const effect = (this.item.applyEffects ?? this.item.effects).get(e._id); if (!token.actor || !effect) return; await EffectsField.applyEffect(effect, token.actor); }); @@ -96,7 +96,7 @@ export default class EffectsField extends fields.ArrayField { content: await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/ui/chat/effectSummary.hbs', { - effects: this.effects.map(e => this.item.effects.get(e._id)), + effects: this.effects.map(e => (this.item.applyEffects ?? this.item.effects).get(e._id)), targets: messageTargets } ) @@ -123,7 +123,7 @@ export default class EffectsField extends fields.ArrayField { // Otherwise, create a new effect on the target const effectData = foundry.utils.mergeObject({ - ...effect.toObject(), + ...(effect.toObject?.() ?? effect), disabled: false, transfer: false, origin: effect.uuid diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 0d71ab86..9c006beb 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -152,6 +152,7 @@ export function ActionMixin(Base) { } get uuid() { + if (!(this.item instanceof game.system.api.documents.DHItem)) return null; return `${this.item.uuid}.${this.documentName}.${this.id}`; } diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 0138713c..b8804fa7 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -12,6 +12,20 @@ const currencyField = (initial, label, icon) => icon: new foundry.data.fields.StringField({ required: true, nullable: false, blank: true, initial: icon }) }); +const restMoveField = () => + new foundry.data.fields.SchemaField({ + name: new foundry.data.fields.StringField({ required: true }), + icon: new foundry.data.fields.StringField({ required: true }), + img: new foundry.data.fields.FilePathField({ + initial: 'icons/magic/life/cross-worn-green.webp', + categories: ['IMAGE'], + base64: false + }), + description: new foundry.data.fields.HTMLField(), + actions: new ActionsField(), + effects: new foundry.data.fields.ArrayField(new foundry.data.fields.ObjectField()) + }); + export default class DhHomebrew extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -105,37 +119,11 @@ export default class DhHomebrew extends foundry.abstract.DataModel { restMoves: new fields.SchemaField({ longRest: new fields.SchemaField({ nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), - moves: new fields.TypedObjectField( - new fields.SchemaField({ - name: new fields.StringField({ required: true }), - icon: new fields.StringField({ required: true }), - img: new fields.FilePathField({ - initial: 'icons/magic/life/cross-worn-green.webp', - categories: ['IMAGE'], - base64: false - }), - description: new fields.HTMLField(), - actions: new ActionsField() - }), - { initial: defaultRestOptions.longRest() } - ) + moves: new fields.TypedObjectField(restMoveField(), { initial: defaultRestOptions.longRest() }) }), shortRest: new fields.SchemaField({ nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), - moves: new fields.TypedObjectField( - new fields.SchemaField({ - name: new fields.StringField({ required: true }), - icon: new fields.StringField({ required: true }), - img: new fields.FilePathField({ - initial: 'icons/magic/life/cross-worn-green.webp', - categories: ['IMAGE'], - base64: false - }), - description: new fields.HTMLField(), - actions: new ActionsField() - }), - { initial: defaultRestOptions.shortRest() } - ) + moves: new fields.TypedObjectField(restMoveField(), { initial: defaultRestOptions.shortRest() }) }) }), domains: new fields.TypedObjectField( From 483caa106249a399733bb55a20b079895369282e Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 31 Jan 2026 19:46:21 +0100 Subject: [PATCH 8/8] Corrected default DowntimeMoves to have effects defined --- module/config/generalConfig.mjs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index be1dfce1..ae49c73e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -252,7 +252,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, clearStress: { id: 'clearStress', @@ -285,7 +286,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, repairArmor: { id: 'repairArmor', @@ -318,7 +320,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, prepare: { id: 'prepare', @@ -326,7 +329,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'), - actions: {} + actions: {}, + effects: [] } }), longRest: () => ({ @@ -361,7 +365,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, clearStress: { id: 'clearStress', @@ -394,7 +399,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, repairArmor: { id: 'repairArmor', @@ -427,7 +433,8 @@ export const defaultRestOptions = { ] } } - } + }, + effects: [] }, prepare: { id: 'prepare', @@ -435,7 +442,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'), - actions: {} + actions: {}, + effects: [] }, workOnAProject: { id: 'workOnAProject', @@ -443,7 +451,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-diagram-project', img: 'icons/skills/social/thumbsup-approval-like.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'), - actions: {} + actions: {}, + effects: [] } }) };