diff --git a/daggerheart.mjs b/daggerheart.mjs index 05b57ac9..1987ec12 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -420,7 +420,10 @@ const updateActorsRangeDependentEffects = async token => { // Get required distance and special case 5 feet to test adjacency const required = rangeMeasurement[range]; const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id; - const inRange = userTarget.distanceTo(token.object) <= required; + const inRange = + required === 5 + ? userTarget.isAdjacentWith(token.object) + : userTarget.distanceTo(token.object) <= required; if (reverse ? inRange : !inRange) { enabledEffect = false; break; diff --git a/lang/en.json b/lang/en.json index 82193133..937de844 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1166,12 +1166,12 @@ }, "far": { "name": "Far", - "description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility roll to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.", + "description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility check to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.", "short": "Far" }, "veryFar": { "name": "Very Far", - "description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility roll to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.", + "description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility check to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.", "short": "V. Far" } }, @@ -1294,7 +1294,6 @@ "triggerTexts": { "strangePatternsContentTitle": "Matched {nr} times.", "strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.", - "strangePatternsActionExplanation": "Left click to increase, right click to decrease", "ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?", "ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you." }, @@ -2806,7 +2805,7 @@ "title": "Domain Card" }, "dualityRoll": { - "abilityCheckTitle": "{ability} Roll" + "abilityCheckTitle": "{ability} Check" }, "effectSummary": { "title": "Effects Applied", @@ -2821,7 +2820,7 @@ "selectLeader": "Select a Leader", "selectMember": "Select a Member", "rerollTitle": "Reroll Group Roll", - "rerollContent": "Are you sure you want to reroll your {trait} roll?", + "rerollContent": "Are you sure you want to reroll your {trait} check?", "rerollTooltip": "Reroll", "wholePartySelected": "The whole party is selected" }, @@ -2987,8 +2986,7 @@ "tokenActorMissing": "{name} is missing an Actor", "tokenActorsMissing": "[{names}] missing Actors", "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used", - "knowTheTide": "Know The Tide gained a token", - "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}" + "knowTheTide": "Know The Tide gained a token" }, "Sidebar": { "actorDirectory": { diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 083c468f..51bf439e 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -187,7 +187,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli }); } - game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); this.render(); } @@ -228,7 +227,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli } }); - game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); this.render(); } @@ -248,8 +246,6 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli await this.settings.updateSource({ [`${path}.-=${id}`]: null }); - - game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); this.render(); } diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 1f2ba5e0..28db0efe 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -4,42 +4,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac constructor(options) { super(options); - this.changeChoices = DhActiveEffectConfig.getChangeChoices(); - } - - static DEFAULT_OPTIONS = { - classes: ['daggerheart', 'sheet', 'dh-style'] - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, - tabs: { template: 'templates/generic/tab-navigation.hbs' }, - details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, - settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, - changes: { - template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', - scrollable: ['ol[data-changes]'] - }, - footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } - }; - - static TABS = { - sheet: { - tabs: [ - { id: 'details', icon: 'fa-solid fa-book' }, - { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, - { id: 'changes', icon: 'fa-solid fa-gears' } - ], - initial: 'details', - labelPrefix: 'EFFECT.TABS' - } - }; - - /** - * Get ChangeChoices for the changes autocomplete. Static for use in this class aswell as in settings-active-effect-config.mjs - * @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]} - */ - static getChangeChoices() { const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty']; const getAllLeaves = (root, group, parentPath = '') => { @@ -59,7 +23,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac return leaves; }; - return Object.keys(game.system.api.models.actors).reduce((acc, key) => { + this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => { if (ignoredActorKeys.includes(key)) return acc; const model = game.system.api.models.actors[key]; @@ -98,6 +62,34 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac }, []); } + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'sheet', 'dh-style'] + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, + tabs: { template: 'templates/generic/tab-navigation.hbs' }, + details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, + changes: { + template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', + scrollable: ['ol[data-changes]'] + }, + footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } + }; + + static TABS = { + sheet: { + tabs: [ + { id: 'details', icon: 'fa-solid fa-book' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, + { id: 'changes', icon: 'fa-solid fa-gears' } + ], + initial: 'details', + labelPrefix: 'EFFECT.TABS' + } + }; + _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); const changeChoices = this.changeChoices; diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs index 12ac90d1..fe36e37f 100644 --- a/module/applications/sheets-configs/setting-active-effect-config.mjs +++ b/module/applications/sheets-configs/setting-active-effect-config.mjs @@ -7,7 +7,19 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi super({}); this.effect = foundry.utils.deepClone(effect); - this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices(); + const ignoredActorKeys = ['config', 'DhEnvironment']; + this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => { + if (!ignoredActorKeys.includes(key)) { + const model = game.system.api.models.actors[key]; + const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model); + const group = game.i18n.localize(model.metadata.label); + const choices = CONFIG.Token.documentClass + .getTrackedAttributeChoices(attributes, model) + .map(x => ({ ...x, group: group })); + acc.push(...choices); + } + return acc; + }, []); } static DEFAULT_OPTIONS = { diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index 28e686ea..e8bf6109 100644 --- a/module/applications/sheets-configs/setting-feature-config.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -73,11 +73,9 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App return context; } - static async updateData(_event, _element, formData) { + static async updateData(event, element, formData) { const data = foundry.utils.expandObject(formData.object); - await this.updateMove({ - [`${this.movePath}`]: data - }); + foundry.utils.mergeObject(this.move, data); this.render(); } @@ -137,7 +135,9 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App } ); - await this.updateMove({ [`${this.actionsPath}.${action.id}`]: action }); + await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); } @@ -150,12 +150,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect); if (!updatedEffect) return; - await this.updateMove({ + await this.settings.updateSource({ [`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => { acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect); return acc; }, []) }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); this.render(); } else { const action = this.move.actions.get(id); @@ -170,13 +171,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App : existingEffectIndex === -1 ? [...currentEffects, effectData] : currentEffects.with(existingEffectIndex, effectData); - await this.updateMove({ + await this.settings.updateSource({ [`${this.movePath}.effects`]: updatedEffects }); } - await this.updateMove({ [`${this.actionsPath}.${id}`]: updatedMove }); - + await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); this.render(); return updatedEffects; }).render(true); @@ -198,34 +199,31 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App }); } } - await this.updateMove({ + await this.settings.updateSource({ [this.movePath]: { effects: move.effects.filter(x => x.id !== id), actions: move.actions } }); } else { - await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); + await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); } + this.move = foundry.utils.getProperty(this.settings, this.movePath); this.render(); } - static async addEffect() { + static async addEffect(_, target) { const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`); - - await this.updateMove({ + await this.settings.updateSource({ [`${this.movePath}.effects`]: [ ...currentEffects, game.system.api.data.activeEffects.BaseEffect.getDefaultObject() ] }); - this.render(); - } - async updateMove(update) { - await this.settings.updateSource(update); this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); } static resetMoves() {} diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index 1b1722db..d78519cb 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -6,6 +6,7 @@ import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs'; import { socketEvent } from '../../../systemRegistration/socket.mjs'; import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs'; import DhpActor from '../../../documents/actor.mjs'; +import DHItem from '../../../documents/item.mjs'; export default class Party extends DHBaseActorSheet { constructor(options) { @@ -268,6 +269,15 @@ export default class Party extends DHBaseActorSheet { ).render({ force: true }); } + /** + * Get the set of ContextMenu options for Consumable and Loot. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {CharacterSheet} + * @protected + */ + static #getItemContextOptions() { + return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true }); + } /* -------------------------------------------- */ /* Filter Tracking */ /* -------------------------------------------- */ diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 982e661f..407c8250 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -431,18 +431,18 @@ export default function DHApplicationMixin(Base) { { name: 'disableEffect', icon: 'fa-solid fa-lightbulb', - condition: element => { - const target = element.closest('[data-item-uuid]'); - return !target.dataset.disabled && target.dataset.itemType !== 'beastform'; + condition: target => { + const doc = getDocFromElementSync(target); + return doc && !doc.disabled && doc.type !== 'beastform'; }, callback: async target => (await getDocFromElement(target)).update({ disabled: true }) }, { name: 'enableEffect', icon: 'fa-regular fa-lightbulb', - condition: element => { - const target = element.closest('[data-item-uuid]'); - return target.dataset.disabled && target.dataset.itemType !== 'beastform'; + condition: target => { + const doc = getDocFromElementSync(target); + return doc && doc.disabled && doc.type !== 'beastform'; }, callback: async target => (await getDocFromElement(target)).update({ disabled: false }) } @@ -539,9 +539,9 @@ export default function DHApplicationMixin(Base) { options.push({ name: 'CONTROLS.CommonDelete', icon: 'fa-solid fa-trash', - condition: element => { - const target = element.closest('[data-item-uuid]'); - return target.dataset.itemType !== 'beastform'; + condition: target => { + const doc = getDocFromElementSync(target); + return doc && doc.type !== 'beastform'; }, callback: async (target, event) => { const doc = await getDocFromElement(target); diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 6f994faf..85ecd616 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { ], dragDrop: [ { dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }, - { dragSelector: '.currency[data-currency] .drag-handle', dropSelector: null } + { dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null } ] }; @@ -92,7 +92,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { value: context.source.system.gold[key] }; } - context.inventory.hasCurrency = Object.values(context.inventory.currencies).some(c => c.enabled); + context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled); } return context; @@ -270,9 +270,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { currency }); if (quantity) { - originActor.update({ - [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) - }); + originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) }); this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity }); } return; @@ -294,15 +292,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { /* Handling transfer of inventoryItems */ if (item.system.metadata.isInventoryItem) { - if (!this.document.testUserPermission(game.user, 'OWNER', { exact: true })) { - return ui.notifications.error( - game.i18n.format('DAGGERHEART.UI.Notifications.lackingItemTransferPermission', { - user: game.user.name, - target: this.document.name - }) - ); - } - if (item.system.metadata.isQuantifiable) { const actorItem = originActor.items.get(data.originId); const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ @@ -311,6 +300,14 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { }); if (quantityTransfered) { + if (quantityTransfered === actorItem.system.quantity) { + await originActor.deleteEmbeddedDocuments('Item', [data.originId]); + } else { + await actorItem.update({ + 'system.quantity': actorItem.system.quantity - quantityTransfered + }); + } + const existingItem = this.document.items.find(x => itemIsIdentical(x, item)); if (existingItem) { await existingItem.update({ @@ -328,18 +325,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { } ]); } - - if (quantityTransfered === actorItem.system.quantity) { - await originActor.deleteEmbeddedDocuments('Item', [data.originId]); - } else { - await actorItem.update({ - 'system.quantity': actorItem.system.quantity - quantityTransfered - }); - } } } else { - await this.document.createEmbeddedDocuments('Item', [item.toObject()]); await originActor.deleteEmbeddedDocuments('Item', [data.originId]); + await this.document.createEmbeddedDocuments('Item', [item.toObject()]); } } } @@ -350,7 +339,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { */ async _onDragStart(event) { // Handle drag/dropping currencies - const currencyEl = event.currentTarget.closest('.currency[data-currency]'); + const currencyEl = event.currentTarget.closest(".currency[data-currency]"); if (currencyEl) { const currency = currencyEl.dataset.currency; const data = { type: 'Currency', currency, originActor: this.document.uuid }; @@ -370,8 +359,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { event.dataTransfer.setData('text/plain', JSON.stringify(attackData)); event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0); return; - } - + } + const item = await getDocFromElement(event.target); if (item) { const dragData = { diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 148466c1..068f21e1 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -54,58 +54,30 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { if (this === target) return 0; const originPoint = this.center; - const targetPoint = target.center; - const thisBounds = this.bounds; - const targetBounds = target.bounds; - const adjacencyBuffer = canvas.grid.distance * 1.75; // handles diagonals with one square elevation difference - - // Figure out the elevation difference. - // This intends to return "grid distance" for adjacent ones, so we add that number if not overlapping. - const sizePerUnit = canvas.grid.size / canvas.grid.distance; - const thisHeight = Math.max(thisBounds.width, thisBounds.height) / sizePerUnit; - const targetHeight = Math.max(targetBounds.width, targetBounds.height) / sizePerUnit; - const thisElevation = [this.document.elevation, this.document.elevation + thisHeight]; - const targetElevation = [target.document.elevation, target.document.elevation + targetHeight]; - const isSameAltitude = - thisElevation[0] < targetElevation[1] && // bottom of this must be at or below the top of target - thisElevation[1] > targetElevation[0]; // top of this must be at or above the bottom of target - const [lower, higher] = [targetElevation, thisElevation].sort((a, b) => a[1] - b[1]); - const elevation = isSameAltitude ? 0 : higher[0] - lower[1] + canvas.grid.distance; + const destinationPoint = target.center; // Compute for gridless. This version returns circular edge to edge + grid distance, // so that tokens that are touching return 5. if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) { const boundsCorrection = canvas.grid.distance / canvas.grid.size; - const originRadius = (thisBounds.width * boundsCorrection) / 2; - const targetRadius = (targetBounds.width * boundsCorrection) / 2; - const measuredDistance = canvas.grid.measurePath([ - { ...originPoint, elevation: 0 }, - { ...targetPoint, elevation } - ]).distance; - const distance = Math.floor(measuredDistance - originRadius - targetRadius + canvas.grid.distance); - return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance); + const originRadius = (this.bounds.width * boundsCorrection) / 2; + const targetRadius = (target.bounds.width * boundsCorrection) / 2; + const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance; + return Math.floor(distance - originRadius - targetRadius + canvas.grid.distance); } // Compute what the closest grid space of each token is, then compute that distance - const originEdge = this.#getEdgeBoundary(thisBounds, originPoint, targetPoint); - const targetEdge = this.#getEdgeBoundary(targetBounds, originPoint, targetPoint); - const adjustedOriginPoint = originEdge - ? canvas.grid.getTopLeftPoint({ - x: originEdge.x + Math.sign(originPoint.x - originEdge.x), - y: originEdge.y + Math.sign(originPoint.y - originEdge.y) - }) - : originPoint; - const adjustDestinationPoint = targetEdge - ? canvas.grid.getTopLeftPoint({ - x: targetEdge.x + Math.sign(targetPoint.x - targetEdge.x), - y: targetEdge.y + Math.sign(targetPoint.y - targetEdge.y) - }) - : targetPoint; - const distance = canvas.grid.measurePath([ - { ...adjustedOriginPoint, elevation: 0 }, - { ...adjustDestinationPoint, elevation } - ]).distance; - return Math.min(distance, distance > adjacencyBuffer ? Infinity : canvas.grid.distance); + const originEdge = this.#getEdgeBoundary(this.bounds, originPoint, destinationPoint); + const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint); + const adjustedOriginPoint = canvas.grid.getTopLeftPoint({ + x: originEdge.x + Math.sign(originPoint.x - originEdge.x), + y: originEdge.y + Math.sign(originPoint.y - originEdge.y) + }); + const adjustDestinationPoint = canvas.grid.getTopLeftPoint({ + x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x), + y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y) + }); + return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance; } _onHoverIn(event, options) { @@ -131,7 +103,8 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { // Determine the actual range const ranges = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement; - const distanceResult = DhMeasuredTemplate.getRangeLabels(originToken.distanceTo(this), ranges); + const distanceNum = originToken.distanceTo(this); + const distanceResult = DhMeasuredTemplate.getRangeLabels(distanceNum, ranges); const distanceLabel = `${distanceResult.distance} ${distanceResult.units}`.trim(); // Create the element @@ -183,6 +156,11 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { return null; } + /** Tests if the token is at least adjacent with another, with some leeway for diagonals */ + isAdjacentWith(token) { + return this.distanceTo(token) <= canvas.grid.distance * 1.5; + } + /** @inheritDoc */ _drawBar(number, bar, data) { const val = Number(data.value); diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index b424c707..d6dad595 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -467,7 +467,9 @@ export const allArmorFeatures = () => { }; export const orderedArmorFeatures = () => { - const allFeatures = allArmorFeatures(); + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .armorFeatures; + const allFeatures = { ...armorFeatures, ...homebrewFeatures }; const all = Object.keys(allFeatures).map(key => { const feature = allFeatures[key]; return { @@ -1402,7 +1404,9 @@ export const allWeaponFeatures = () => { }; export const orderedWeaponFeatures = () => { - const allFeatures = allWeaponFeatures(); + const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures + .weaponFeatures; + const allFeatures = { ...weaponFeatures, ...homebrewFeatures }; const all = Object.keys(allFeatures).map(key => { const feature = allFeatures[key]; return { diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 2288c548..437e92a0 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -190,10 +190,6 @@ export default class DhpAdversary extends DhCreature { } } - prepareDerivedData() { - this.attack.roll.isStandardAttack = true; - } - _getTags() { const tags = [ game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`), @@ -268,12 +264,12 @@ export default class DhpAdversary extends DhCreature { } // Update damage in item actions - // Parse damage, and convert all formula matches in the descriptions to the new damage for (const action of Object.values(item.system.actions)) { + if (!action.damage) continue; + + // Parse damage, and convert all formula matches in the descriptions to the new damage try { const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' }); - if (!result) continue; - for (const { previousFormula, formula } of Object.values(result)) { const oldFormulaRegexp = new RegExp( previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?') @@ -375,11 +371,9 @@ export default class DhpAdversary extends DhCreature { /** * Updates damage to reflect a specific value. * @throws if damage structure is invalid for conversion - * @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage + * @returns the converted formula and value as a simplified term */ #adjustActionDamage(action, damageMeta) { - if (!action.damage?.parts.hitPoints) return null; - const result = {}; for (const property of ['value', 'valueAlt']) { const data = action.damage.parts.hitPoints[property]; diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 930814e2..bedd9680 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -4,4 +4,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/damageField.mjs b/module/data/fields/action/damageField.mjs index e79a91a2..d904c3a0 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -166,8 +166,7 @@ export default class DamageField extends fields.SchemaField { if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; const isAdversary = this.actor.type === 'adversary'; - const isHorde = this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id; - if (isAdversary && isHorde && this.roll?.isStandardAttack) { + if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde'); if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt; } diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 202c8ecc..89c3c287 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. @@ -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); } 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 0958a9f3..2d31c290 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -23,7 +23,9 @@ export default class DHArmor extends AttachableItem { armorFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ - required: true + required: true, + choices: CONFIG.DH.ITEM.allArmorFeatures, + blank: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), actionIds: new fields.ArrayField(new fields.StringField({ required: true })) @@ -56,7 +58,7 @@ export default class DHArmor extends AttachableItem { async getDescriptionData() { const baseDescription = this.description; const allFeatures = CONFIG.DH.ITEM.allArmorFeatures(); - const features = this.armorFeatures.map(x => allFeatures[x.value]).filter(x => x); + const features = this.armorFeatures.map(x => allFeatures[x.value]); const prefix = await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/sheets/items/armor/description.hbs', diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 82ee6c83..c9543288 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -38,7 +38,9 @@ export default class DHWeapon extends AttachableItem { weaponFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ - required: true + required: true, + choices: CONFIG.DH.ITEM.allWeaponFeatures, + blank: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), actionIds: new fields.ArrayField(new fields.StringField({ required: true })) @@ -119,7 +121,7 @@ export default class DHWeapon extends AttachableItem { const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label); const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures(); - const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x); + const features = this.weaponFeatures.map(x => allFeatures[x.value]); const prefix = await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/sheets/items/weapon/description.hbs', diff --git a/module/data/registeredTriggers.mjs b/module/data/registeredTriggers.mjs index ab86351c..ee4f3b49 100644 --- a/module/data/registeredTriggers.mjs +++ b/module/data/registeredTriggers.mjs @@ -75,7 +75,7 @@ export default class RegisteredTriggers extends Map { unregisterSceneEnvironmentTriggers(flagSystemData) { const sceneData = new game.system.api.data.scenes.DHScene(flagSystemData); for (const environment of sceneData.sceneEnvironments) { - if (!environment || environment.pack) continue; + if (environment.pack) continue; this.unregisterItemTriggers(environment.system.features); } } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 8592908e..72575a75 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -119,8 +119,8 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {} }), maxTags: typeof maxTags === 'function' ? maxTags() : maxTags, dropdown: { - searchKeys: ['value', 'name'], mapValueTo: 'name', + searchKeys: ['value'], enabled: 0, maxItems: 100, closeOnSelect: true, diff --git a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json index ce1f499f..1d56f732 100644 --- a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json +++ b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json @@ -85,7 +85,7 @@ { "trigger": "dualityRoll", "triggeringActorType": "self", - "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" + "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" } ] } diff --git a/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json b/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json index 22042f7d..8dcc66f5 100644 --- a/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json +++ b/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json @@ -53,7 +53,7 @@ "difficulty": null, "damageMod": "none" }, - "name": "Agility Roll", + "name": "Agility Check", "img": "icons/skills/melee/sword-engraved-glow-purple.webp", "range": "close" } diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index d703d189..9045baf5 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -103,9 +103,10 @@ display: flex; align-items: center; justify-content: end; + gap: 8px; a { - width: 20px; + width: 15px; text-align: center; } @@ -274,10 +275,8 @@ grid-area: controls; align-self: start; padding-top: 0.3125rem; + gap: 4px; margin-bottom: -1px; - a { - width: 18px; - } } > .item-labels { align-self: start; @@ -335,27 +334,6 @@ border-radius: 6px; } - .recall-cost { - position: absolute; - right: 4px; - top: 4px; - width: 1.75em; - height: 1.75em; - - align-items: center; - background: @dark-blue; - border-radius: 50%; - border: 1px solid @golden; - color: @golden; - display: flex; - justify-content: center; - padding-top: 0.1em; // compensate for font - - i { - font-size: 0.68em; - } - } - .card-label { display: flex; flex-direction: column; diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 23db088a..bf6393f4 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -183,11 +183,6 @@ } } - .domain-details { - display: flex; - flex-direction: column; - } - .level-details { align-self: center; } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 3591fc65..1e723ed7 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -158,7 +158,7 @@ .daggerheart, #chat-notifications { .chat-message { - --text-color: light-dark(@dark-blue, @golden); + --text-color: @golden; --bg-color: @golden-40; [data-use-perm='false'] { @@ -233,7 +233,7 @@ font-family: @font-subtitle; font-size: var(--font-size-18); font-weight: bold; - color: var(--text-color); + color: light-dark(@dark-blue, var(--text-color)); margin-bottom: -2px; } diff --git a/system.json b/system.json index 40de5fa1..fb23ad7b 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.7.3", + "version": "1.7.2", "compatibility": { "minimum": "13.346", "verified": "13.351", diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs index 54e44e64..ae95b7af 100644 --- a/templates/sheets/global/partials/domain-card-item.hbs +++ b/templates/sheets/global/partials/domain-card-item.hbs @@ -1,9 +1,5 @@
  • - - {{item.system.recallCost}} - -
  • +
  • {{!-- Image --}}
    - + {{/if}} {{#if (eq type 'domainCard')}} @@ -125,7 +121,7 @@ Parameters: {{/if}} {{#if (hasProperty item "toChat")}} - + {{/if}} {{else}} @@ -138,7 +134,7 @@ Parameters: {{/unless}} {{#unless hideContextMenu}} - + {{/unless}} {{/if}} diff --git a/templates/sheets/global/tabs/tab-effects.hbs b/templates/sheets/global/tabs/tab-effects.hbs index 1f0967b8..c89c6ff4 100644 --- a/templates/sheets/global/tabs/tab-effects.hbs +++ b/templates/sheets/global/tabs/tab-effects.hbs @@ -2,21 +2,20 @@ data-group='{{tabs.effects.group}}'> {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.activeEffects' - type='effect' - isGlassy=true - collection=effects.actives - canCreate=true - hideResources=true + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true }} {{> 'daggerheart.inventory-items' - title='DAGGERHEART.GENERAL.inactiveEffects' - type='effect' - disabledEffect=true - isGlassy=true - collection=effects.inactives - canCreate=true - hideResources=true + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true }} \ No newline at end of file