diff --git a/lang/en.json b/lang/en.json index 0186ae3e..e358977f 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2405,14 +2405,6 @@ "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/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index e6c0f299..aa764c56 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}`] = _del; + acc[`-=${key}`] = null; return acc; }, {}) } diff --git a/module/applications/dialogs/characterResetDialog.mjs b/module/applications/dialogs/characterResetDialog.mjs index aecebc7c..0836af9c 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', _replace({})); - foundry.utils.setProperty(update, 'prototypeToken.ring', _replace({})); + foundry.utils.setProperty(update, 'prototypeToken.==texture', {}); + foundry.utils.setProperty(update, 'prototypeToken.==ring', {}); } 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: _replace(system ?? {}) + '==system': system ?? {} }); const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot']; diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index c28b773c..d1a1e123 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}`]: _del }; + const update = { [`members.-=${button.dataset.characterId}`]: null }; 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 c4616d9a..ba6110cc 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] = _del; + acc[`-=${key}`] = null; 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}`] = _del; + update[`${basePath}.-=${button.dataset.option}`] = null; } else { - update[`${basePath}.${button.dataset.option}.${button.dataset.checkboxNr}`] = _del; + update[`${basePath}.${button.dataset.option}.-=${button.dataset.checkboxNr}`] = null; } } 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 ceb403cf..98e18f09 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -62,15 +62,7 @@ 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; @@ -118,7 +110,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] = _del; + submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null; } } diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 38fe9ece..6e2e665d 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -165,8 +165,7 @@ 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: [], - effects: [] + actions: [] } }); } else if (['armorFeatures', 'weaponFeatures'].includes(type)) { @@ -229,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}`]: _del + [`${path}.-=${id}`]: null }); this.render(); } @@ -251,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] = _del; + acc[`-=${key}`] = null; return acc; }, {}); @@ -311,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] = _del; + acc[`-=${key}`] = null; return acc; }, {}) @@ -384,12 +383,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli if (!confirmed) return; await this.settings.updateSource({ - [`domains.${this.selected.domain}`]: _del + [`domains.-=${this.selected.domain}`]: null }); 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}`]: _del }); + await currentSettings.updateSource({ [`domains.-=${this.selected.domain}`]: null }); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, currentSettings); } @@ -436,7 +435,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli static async deleteAdversaryType(_, target) { const { key } = target.dataset; - await this.settings.updateSource({ [`adversaryTypes.${key}`]: _del }); + await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null }); 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 6593f23d..d3d215be 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}`]: _del }); + await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); } async _onDragStart(event) { diff --git a/module/applications/sheets-configs/character-settings.mjs b/module/applications/sheets-configs/character-settings.mjs index c655b23f..20a09cfc 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}`] = - _del; + updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.-=${data.experience}`] = + null; }); } 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}`]: _del + [`system.experiences.-=${target.dataset.experience}`]: null }); } } diff --git a/module/applications/sheets-configs/companion-settings.mjs b/module/applications/sheets-configs/companion-settings.mjs index 6c1265af..8aa21479 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}`]: _del }); + await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); } } diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 5c039f85..15f5701d 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}`]: _del }); + await this.actor.update({ [`system.potentialAdversaries.-=${target.dataset.categoryId}`]: null }); } /** diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index e8ff7818..e8bf6109 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}`]: _del }); + await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); } 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 a23aa7f4..c29b54ff 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: _del, actorLink: _del }; + const deletions = { '-=actorId': null, '-=actorLink': null }; 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 0c9991c4..880c0796 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}`]: _del + [`system.advantageOn.-=${event.detail.data.value}`]: null }); } } diff --git a/module/applications/sheets/rollTables/rollTable.mjs b/module/applications/sheets/rollTables/rollTable.mjs index d7498e80..9ead6814 100644 --- a/module/applications/sheets/rollTables/rollTable.mjs +++ b/module/applications/sheets/rollTables/rollTable.mjs @@ -108,15 +108,14 @@ 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] = _del; + if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[`-=${formulaKey}`] = null; return acc; }, { altFormula: {} } ); - const flagData = this.daggerheartFlag.toObject(); - return { ...flagData, altFormula: { ...flagData.altFormula, ...deleteUpdate.altFormula } }; + return { ['flags.daggerheart']: foundry.utils.mergeObject(this.daggerheartFlag.toObject(), deleteUpdate) }; } static async #addFormula() { @@ -128,7 +127,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa static async #removeFormula(_event, target) { await this.daggerheartFlag.updateSource({ - [`altFormula.${target.dataset.key}`]: _del + [`altFormula.-=${target.dataset.key}`]: null }); this.render({ internalRefresh: true }); } diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 8bb9fc1d..7f1deea3 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}`]: _del }); + this.updateSetting({ [`countdowns.-=${countdownId}`]: null }); } } diff --git a/module/applications/ui/sceneNavigation.mjs b/module/applications/ui/sceneNavigation.mjs index bc906dac..0a3e08a5 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.active; + const hasEnvironments = environments.length > 0 && x.isView; return { ...x, hasEnvironments, @@ -39,10 +39,9 @@ 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/module/canvas/placeables/measuredTemplate.mjs b/module/canvas/placeables/measuredTemplate.mjs index e1ea79f5..ef7d284b 100644 --- a/module/canvas/placeables/measuredTemplate.mjs +++ b/module/canvas/placeables/measuredTemplate.mjs @@ -18,9 +18,8 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur static getRangeLabels(distanceValue, settings) { let result = { distance: distanceValue, units: '' }; - 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; @@ -28,9 +27,31 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur return result; } - 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`); + 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'); + } + return result; } } diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 7bd92226..2266d0da 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -1,5 +1,3 @@ -import DhMeasuredTemplate from './measuredTemplate.mjs'; - export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { /** @inheritdoc */ async _draw(options) { @@ -80,60 +78,6 @@ 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/config/generalConfig.mjs b/module/config/generalConfig.mjs index 969dc513..677351cd 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -256,8 +256,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, clearStress: { id: 'clearStress', @@ -290,8 +289,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, repairArmor: { id: 'repairArmor', @@ -324,8 +322,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, prepare: { id: 'prepare', @@ -333,8 +330,7 @@ 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: {}, - effects: [] + actions: {} } }), longRest: () => ({ @@ -369,8 +365,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, clearStress: { id: 'clearStress', @@ -403,8 +398,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, repairArmor: { id: 'repairArmor', @@ -437,8 +431,7 @@ export const defaultRestOptions = { ] } } - }, - effects: [] + } }, prepare: { id: 'prepare', @@ -446,8 +439,7 @@ 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: {}, - effects: [] + actions: {} }, workOnAProject: { id: 'workOnAProject', @@ -455,8 +447,7 @@ 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: {}, - effects: [] + actions: {} } }) }; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index e843027b..115e6463 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -114,24 +114,9 @@ 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. */ @@ -140,7 +125,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel ? this.item : this.item?.parent instanceof DhpActor ? this.item.parent - : null; + : this.item?.actor; } static getRollType(parent) { diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 47e28b4c..53430ba3 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: _del, beastformSubjectTexture: _del } + 'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null } }; }; diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 833f1222..08308eab 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -169,7 +169,9 @@ 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]: _del } : {} + members: Object.keys(tagTeam.members).find(x => x === this.parent.id) + ? { [`-=${this.parent.id}`]: null } + : {} }); 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 3eddf235..236d65db 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] = _del; + acc[`-=${key}`] = null; } return acc; diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 58423979..2a8ba454 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -3,4 +3,5 @@ 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/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 6afd470b..2233a383 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.applyEffects ?? this.item.effects).get(e._id); + const effect = 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.applyEffects ?? this.item.effects).get(e._id)), + effects: this.effects.map(e => 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), + ...effect.toObject(), disabled: false, transfer: false, origin: effect.uuid diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 21dafd60..a6d0abbe 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -1,5 +1,6 @@ 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. @@ -10,9 +11,9 @@ export class ActionCollection extends Collection { constructor(model, entries) { super(); this.#model = model; - for (const [key, value] of entries) { - if (!(value instanceof game.system.api.models.actions.actionsTypes.base)) continue; - this.set(key, value); + for (const entry of entries) { + if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue; + this.set(entry._id, entry); } } @@ -60,7 +61,7 @@ export class ActionCollection extends Collection { /** * Field that stores actions. */ -export class ActionsField extends foundry.data.fields.TypedObjectField { +export class ActionsField extends MappingField { constructor(options) { super(new ActionField(), options); } @@ -69,7 +70,7 @@ export class ActionsField extends foundry.data.fields.TypedObjectField { /** @inheritDoc */ initialize(value, model, options) { - const actions = Object.entries(super.initialize(value, model, options)); + const actions = Object.values(super.initialize(value, model, options)); return new ActionCollection(model, actions); } } @@ -159,7 +160,6 @@ 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}`; } @@ -243,11 +243,11 @@ export function ActionMixin(Base) { : foundry.utils.getProperty(result, basePath); } - async delete() { + 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; - await this.item.update({ [`system.${this.systemPath}.${this.id}`]: _del }); // Does not work. Unsure why. It worked in v13 <_<' + this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null }); this.constructor._sheets.get(this.uuid)?.close(); } diff --git a/module/data/fields/mappingField.mjs b/module/data/fields/mappingField.mjs new file mode 100644 index 00000000..31d91c76 --- /dev/null +++ b/module/data/fields/mappingField.mjs @@ -0,0 +1,128 @@ +/** + * 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); + } +} diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 050b66d4..3d4a62fa 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] = _del; + acc[`-=${id}`] = null; return acc; }, {}); diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 447da3bf..84f39103 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) => { - const action = changed.system.actions[key]; - if (action && Object.keys(action).length === 0) { - acc.push(...this.actions.get(key).triggers.map(x => x.trigger)); + if (!changed.system.actions[key]) { + const strippedKey = key.replace('-=', ''); + acc.push(...this.actions.get(strippedKey).triggers.map(x => x.trigger)); } return acc; diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 85849a83..f333e5f3 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] = _del; + acc[`-=${id}`] = null; return acc; }, {}); diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index d7a638d7..2b8d3b27 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -42,25 +42,6 @@ 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/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index b8804fa7..0138713c 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -12,20 +12,6 @@ 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; @@ -119,11 +105,37 @@ 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(restMoveField(), { initial: defaultRestOptions.longRest() }) + 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() } + ) }), shortRest: new fields.SchemaField({ nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), - moves: new fields.TypedObjectField(restMoveField(), { initial: defaultRestOptions.shortRest() }) + 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() } + ) }) }), domains: new fields.TypedObjectField( diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index cb51a255..e8bea0bf 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] = _del; + if (Number(level) > usedLevel) acc[`-=${level}`] = null; 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] = _del; + acc[`-=${key}`] = null; return acc; }, {}) }); diff --git a/module/documents/token.mjs b/module/documents/token.mjs index b9507c2f..317f3acf 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 = DHToken.#getHexagonalShape(height, width, shape, false); + const rowData = BaseToken.#getHexagonalShape(height, width, shape, false); if (!rowData) return null; // Transpose the offsets/points of the shape in row orientation diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 7ca2c41c..bb9a0cfa 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}`] = _del; + acc[`${key}.-=${innerProperty}`] = null; } } else { - acc[`${key}`] = _del; + acc[`-=${key}`] = null; } return acc; diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 4216c38f..743d42a4 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] = _del; + acc[`-=${id}`] = null; } return acc; }, {}); diff --git a/styles/less/ui/scene-config/scene-config.less b/styles/less/ui/scene-config/scene-config.less index ba1afb0b..664e7526 100644 --- a/styles/less/ui/scene-config/scene-config.less +++ b/styles/less/ui/scene-config/scene-config.less @@ -13,8 +13,6 @@ .application.sheet.scene-config { .sheet-tabs.tabs { - font-size: 12px; - a[data-tab='dh'] { display: flex; align-items: center; diff --git a/templates/settings/appearance-settings/main.hbs b/templates/settings/appearance-settings/main.hbs index 32dd9e63..75a7e634 100644 --- a/templates/settings/appearance-settings/main.hbs +++ b/templates/settings/appearance-settings/main.hbs @@ -16,10 +16,6 @@ value=setting.showGenericStatusEffects localize=true}} {{formGroup - fields.showTokenDistance - value=setting.showTokenDistance - localize=true}} - {{formGroup fields.hideAttribution value=setting.hideAttribution localize=true}} diff --git a/templates/ui/sceneNavigation/scene-navigation.hbs b/templates/ui/sceneNavigation/scene-navigation.hbs index 933d2074..41e9e3e8 100644 --- a/templates/ui/sceneNavigation/scene-navigation.hbs +++ b/templates/ui/sceneNavigation/scene-navigation.hbs @@ -1,40 +1,10 @@ - - - -{{#*inline ".scene"}} -
  • -
    - {{ name }} - {{#if users}} -
      - {{#each users}} -
    • {{ letter }}
    • - {{/each}} -
    - {{/if}} -
    - {{#if hasEnvironments}} - - {{/if}} -
  • -{{/inline}} -