From 79057b0718f749a88bfc352d388e447ee95a6c7c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 17 Feb 2026 18:58:47 +0100 Subject: [PATCH 1/4] Added the ability for effects to have stacks --- lang/en.json | 5 +- module/applications/ui/effectsDisplay.mjs | 24 ++++++++-- module/canvas/placeables/token.mjs | 36 +++++++++++++- module/data/activeEffect/baseEffect.mjs | 12 +++++ module/documents/activeEffect.mjs | 11 ++++- styles/less/global/elements.less | 4 ++ styles/less/ui/effects-display/sheet.less | 13 +++++ styles/less/ux/tooltip/bordered-tooltip.less | 50 +++++++++++++++++--- templates/sheets/activeEffect/settings.hbs | 11 +++++ templates/ui/effects-display.hbs | 3 ++ templates/ui/tooltip/effect-display.hbs | 36 ++++++++++++-- 11 files changed, 187 insertions(+), 18 deletions(-) diff --git a/lang/en.json b/lang/en.json index 24d6168a..86e4e633 100755 --- a/lang/en.json +++ b/lang/en.json @@ -138,7 +138,8 @@ "Config": { "rangeDependence": { "title": "Range Dependence" - } + }, + "stacking": { "title": "Stacking" } }, "RangeDependance": { "hint": "Settings for an optional distance at which this effect should activate", @@ -2864,6 +2865,8 @@ }, "EffectsDisplay": { "removeThing": "[Right Click] Remove {thing}", + "increaseStacks": "[Left Click] Increment Stacks", + "decreaseStacks": "[Right Click] Decrement Stacks", "appliedBy": "Applied By: {by}" }, "ItemBrowser": { diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index 3bc5e716..4df42479 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -52,7 +52,8 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica if (this.element) { this.element.querySelectorAll('.effect-container a').forEach(element => { - element.addEventListener('contextmenu', this.removeEffect.bind(this)); + element.addEventListener('click', this.effectLeftclick.bind(this)); + element.addEventListener('contextmenu', this.effectRightclick.bind(this)); }); } } @@ -87,11 +88,28 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica this.render(); } - async removeEffect(event) { + async effectLeftclick(event) { const element = event.target.closest('.effect-container'); const effects = DhEffectsDisplay.getTokenEffects(); const effect = effects.find(x => x.id === element.dataset.effectId); - await effect.delete(); + + if (!effect.system.stacking?.enabled) return; + + const newValue = Math.min(effect.system.stacking.value + 1, effect.system.stacking.max); + await effect.update({ 'system.stacking.value': newValue }); + this.render(); + } + + async effectRightclick(event) { + const element = event.target.closest('.effect-container'); + const effects = DhEffectsDisplay.getTokenEffects(); + const effect = effects.find(x => x.id === element.dataset.effectId); + if (effect.system.stacking?.enabled && effect.system.stacking.value > 1) { + await effect.update({ 'system.stacking.value': effect.system.stacking.value - 1 }); + } else { + await effect.delete(); + } + this.render(); } diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index b3a19a92..ad60b931 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -30,8 +30,8 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { if (!effect.img) continue; const promise = effect === overlayEffect - ? this._drawOverlay(effect.img, effect.tint) - : this._drawEffect(effect.img, effect.tint); + ? this._drawOverlay(effect.img, effect.tint, effect) + : this._drawEffect(effect.img, effect.tint, effect); promises.push( promise.then(e => { if (e) e.zIndex = i; @@ -45,6 +45,38 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { this.renderFlags.set({ refreshEffects: true }); } + /**@inheritdoc */ + async _drawEffect(src, tint, effect) { + if (!src) return; + const tex = await loadTexture(src, { fallback: 'icons/svg/hazard.svg' }); + const icon = new PIXI.Sprite(tex); + icon.tint = tint ?? 0xffffff; + + if (effect?.system?.stacking?.enabled) { + const stackOverlay = new PIXI.Text(effect.system.stacking.value, { + fill: '#f3c267', + stroke: '#000000', + fontSize: 96, + strokeThickness: 4 + }); + const nrDigits = Math.floor(effect.system.stacking.value / 10) + 1; + stackOverlay.x = icon.width - 56 * nrDigits; + stackOverlay.y = 4; + stackOverlay.anchor.set(0, 0); + + icon.addChild(stackOverlay); + } + + return this.effects.addChild(icon); + } + + async _drawOverlay(src, tint, effect) { + const icon = await this._drawEffect(src, tint, effect); + if (icon) icon.alpha = 0.8; + this.effects.overlay = icon ?? null; + return icon; + } + /** * Returns the distance from this token to another token object. * This value is corrected to handle alternate token sizes and other grid types diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index 98a961d7..3867fa23 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -65,6 +65,18 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { initial: CONFIG.DH.GENERAL.range.melee.id, label: 'DAGGERHEART.GENERAL.range' }) + }), + stacking: new fields.SchemaField({ + enabled: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.GENERAL.enabled' }), + value: new fields.NumberField({ + initial: 1, + min: 1, + integer: true, + nullable: false, + label: 'DAGGERHEART.GENERAL.value' + }), + max: new fields.NumberField({ label: 'DAGGERHEART.GENERAL.max' }) + // max: new fields.StringField({ required: true, nullable: false }), }) }; } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index f8b19a3a..8c7f0668 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -1,5 +1,5 @@ import { itemAbleRollParse } from '../helpers/utils.mjs'; -import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs'; +import { RefreshType } from '../systemRegistration/socket.mjs'; export default class DhActiveEffect extends foundry.documents.ActiveEffect { /* -------------------------------------------- */ @@ -182,10 +182,17 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { } catch (_) {} } - const evalValue = this.effectSafeEval(itemAbleRollParse(key, parseModel, effect.parent)); + const evalValue = this.effectSafeEval(this.effectRollParse(key, parseModel, effect.parent, effect)); return evalValue ?? key; } + static effectRollParse(value, actor, item, effect) { + const stackingParsedValue = effect.system.stacking?.enabled + ? Roll.replaceFormulaData(value, { stacks: effect.system.stacking.value }) + : value; + return itemAbleRollParse(stackingParsedValue, actor, item); + } + /** * Altered Foundry safeEval to allow non-numeric return * @param {string} expression diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 98c05348..6a229d58 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -460,6 +460,10 @@ &.even { grid-template-columns: 1fr 1fr; } + + &.full-width { + width: 100%; + } } line-div { diff --git a/styles/less/ui/effects-display/sheet.less b/styles/less/ui/effects-display/sheet.less index 1331b094..3b040725 100644 --- a/styles/less/ui/effects-display/sheet.less +++ b/styles/less/ui/effects-display/sheet.less @@ -35,6 +35,19 @@ color: @golden; filter: drop-shadow(0 0 3px black); } + + .stacking-value { + position: absolute; + top: 4px; + right: 4px; + font-size: 16px; + font-weight: bold; + color: @golden; + background: black; + padding: 1px; + border-radius: 6px; + border: 1px solid @golden; + } } } } diff --git a/styles/less/ux/tooltip/bordered-tooltip.less b/styles/less/ux/tooltip/bordered-tooltip.less index b3a5ed29..abec93b7 100644 --- a/styles/less/ux/tooltip/bordered-tooltip.less +++ b/styles/less/ux/tooltip/bordered-tooltip.less @@ -6,6 +6,7 @@ .daggerheart.dh-style.tooltip { display: flex; flex-direction: column; + align-items: start; text-align: start; width: 100%; gap: 5px; @@ -13,6 +14,7 @@ border-radius: 3px; .tooltip-header { + width: 100%; display: flex; flex-direction: column; align-items: center; @@ -35,12 +37,48 @@ } } - .close-hint { - border-radius: 3px; - padding: 3px; - background: @rustic-brown-80; - color: @golden; - font-size: 12px; + .effect-stacks-outer-container { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + + .effect-stacks-title { + font-size: var(--font-size-20); + font-weight: bold; + text-align: center; + } + + .effect-stacks-container { + display: flex; + justify-content: space-between; + + .effect-stacks-inner-container { + display: flex; + flex-direction: column; + gap: 2px; + + .effect-stack-title { + font-weight: bold; + } + } + } + } + + .close-hints { + margin-top: 0.5rem; + display: flex; + flex-direction: column; + gap: 4px; + + .close-hint { + border-radius: 3px; + padding: 3px; + background: @rustic-brown-80; + color: @golden; + font-size: 12px; + margin: 0; + } } .duration-container { diff --git a/templates/sheets/activeEffect/settings.hbs b/templates/sheets/activeEffect/settings.hbs index 9443edfb..67d7b5f2 100644 --- a/templates/sheets/activeEffect/settings.hbs +++ b/templates/sheets/activeEffect/settings.hbs @@ -1,4 +1,15 @@
+
+ {{localize "DAGGERHEART.ACTIVEEFFECT.Config.stacking.title"}} + + {{formGroup systemFields.stacking.fields.enabled value=source.system.stacking.enabled localize=true }} + +
+ {{formGroup systemFields.stacking.fields.value value=source.system.stacking.value localize=true }} + {{formGroup systemFields.stacking.fields.max value=source.system.stacking.max localize=true }} +
+
+
{{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}} diff --git a/templates/ui/effects-display.hbs b/templates/ui/effects-display.hbs index 95c6023c..13daa4de 100644 --- a/templates/ui/effects-display.hbs +++ b/templates/ui/effects-display.hbs @@ -8,6 +8,9 @@ + {{#if effect.system.stacking.enabled}} + {{effect.system.stacking.value}} + {{/if}} {{#if effect.condition}}{{/if}} {{/each}} diff --git a/templates/ui/tooltip/effect-display.hbs b/templates/ui/tooltip/effect-display.hbs index d37b5147..52419193 100644 --- a/templates/ui/tooltip/effect-display.hbs +++ b/templates/ui/tooltip/effect-display.hbs @@ -17,7 +17,6 @@ {{/if}} {{#if effect.system.duration.type}} -
{{localize "EFFECT.DURATION.Label"}}: @@ -25,10 +24,39 @@
{{/if}} + + {{#if effect.system.stacking.enabled}} +
+
{{localize "Stacks"}}
+
+
+ {{localize "Current"}} + {{effect.system.stacking.value}} +
+ {{#if effect.system.stacking.max}} +
+ {{localize "Max"}} + {{effect.system.stacking.max}} +
+ {{/if}} +
+
+ {{/if}} {{#unless effect.isLockedCondition}} -

- {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}} -

+
+ {{#if effect.system.stacking.enabled}} +

+ {{localize "DAGGERHEART.UI.EffectsDisplay.increaseStacks"}} +

+

+ {{localize "DAGGERHEART.UI.EffectsDisplay.decreaseStacks"}} +

+ {{else}} +

+ {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}} +

+ {{/if}} +
{{/unless}} \ No newline at end of file From 617f1d64c128c786593aa3c75b9225d044f8e59f Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 17 Feb 2026 21:15:36 +0100 Subject: [PATCH 2/4] Fixed effect stacking --- module/applications/ui/effectsDisplay.mjs | 5 ++++- module/canvas/placeables/token.mjs | 2 +- module/data/activeEffect/baseEffect.mjs | 3 +-- module/data/fields/action/effectsField.mjs | 13 +---------- module/documents/activeEffect.mjs | 14 ++++++++++++ templates/ui/effects-display.hbs | 2 +- templates/ui/tooltip/effect-display.hbs | 26 +++++++++++++--------- 7 files changed, 37 insertions(+), 28 deletions(-) diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index 4df42479..500ec39d 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -95,7 +95,10 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica if (!effect.system.stacking?.enabled) return; - const newValue = Math.min(effect.system.stacking.value + 1, effect.system.stacking.max); + const incrementedValue = effect.system.stacking.value + 1; + const newValue = effect.system.stacking.max + ? Math.min(incrementedValue, effect.system.stacking.max) + : incrementedValue; await effect.update({ 'system.stacking.value': newValue }); this.render(); } diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index ad60b931..26c96683 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -52,7 +52,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { const icon = new PIXI.Sprite(tex); icon.tint = tint ?? 0xffffff; - if (effect?.system?.stacking?.enabled) { + if (effect?.system?.stacking?.enabled && effect.system.stacking.value > 1) { const stackOverlay = new PIXI.Text(effect.system.stacking.value, { fill: '#f3c267', stroke: '#000000', diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index 3867fa23..c0ed1b36 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -75,8 +75,7 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { nullable: false, label: 'DAGGERHEART.GENERAL.value' }), - max: new fields.NumberField({ label: 'DAGGERHEART.GENERAL.max' }) - // max: new fields.StringField({ required: true, nullable: false }), + max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' }) }) }; } diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 6afd470b..df0c0b09 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -106,22 +106,11 @@ export default class EffectsField extends fields.ArrayField { } /** - * Apply an Effect to a target or enable it if already on it + * Apply an Effect to a target * @param {object} effect Effect object containing ActiveEffect UUID * @param {object} actor Actor Document */ static async applyEffect(effect, actor) { - const existingEffect = actor.effects.find(e => e.origin === effect.uuid); - if (existingEffect) { - return effect.update( - foundry.utils.mergeObject({ - ...effect.constructor.getInitialDuration(), - disabled: false - }) - ); - } - - // Otherwise, create a new effect on the target const effectData = foundry.utils.mergeObject({ ...(effect.toObject?.() ?? effect), disabled: false, diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 8c7f0668..2869ab98 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -106,6 +106,20 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { update.img = 'icons/magic/life/heart-cross-blue.webp'; } + const existingEffect = this.actor.effects.find(x => x.origin === data.origin); + const stacks = data.system?.stacking?.enabled; + if (existingEffect && !stacks) return false; + + if (existingEffect && stacks) { + const incrementedValue = existingEffect.system.stacking.value + 1; + await existingEffect.update({ + 'system.stacking.value': existingEffect.system.stacking.max + ? Math.min(incrementedValue, existingEffect.system.stacking.max) + : incrementedValue + }); + return false; + } + const statuses = Object.keys(data.statuses ?? {}); const immuneStatuses = statuses.filter( diff --git a/templates/ui/effects-display.hbs b/templates/ui/effects-display.hbs index 13daa4de..bdbd9fbe 100644 --- a/templates/ui/effects-display.hbs +++ b/templates/ui/effects-display.hbs @@ -8,7 +8,7 @@ - {{#if effect.system.stacking.enabled}} + {{#if (and effect.system.stacking.enabled (gt effect.system.stacking.value 1))}} {{effect.system.stacking.value}} {{/if}} {{#if effect.condition}}{{/if}} diff --git a/templates/ui/tooltip/effect-display.hbs b/templates/ui/tooltip/effect-display.hbs index 52419193..3dc6d9c3 100644 --- a/templates/ui/tooltip/effect-display.hbs +++ b/templates/ui/tooltip/effect-display.hbs @@ -24,8 +24,8 @@ {{/if}} - - {{#if effect.system.stacking.enabled}} + + {{#if (and effect.system.stacking.enabled effect.system.stacking.max)}}
{{localize "Stacks"}}
@@ -33,12 +33,10 @@ {{localize "Current"}} {{effect.system.stacking.value}}
- {{#if effect.system.stacking.max}} -
- {{localize "Max"}} - {{effect.system.stacking.max}} -
- {{/if}} +
+ {{localize "Max"}} + {{effect.system.stacking.max}} +
{{/if}} @@ -49,9 +47,15 @@

{{localize "DAGGERHEART.UI.EffectsDisplay.increaseStacks"}}

-

- {{localize "DAGGERHEART.UI.EffectsDisplay.decreaseStacks"}} -

+ {{#if (gt effect.system.stacking.value 1)}} +

+ {{localize "DAGGERHEART.UI.EffectsDisplay.decreaseStacks"}} +

+ {{else}} +

+ {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}} +

+ {{/if}} {{else}}

{{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}} From afef5ff3ae48977d9ee3edc752448f91de5e5cd0 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 17 Feb 2026 21:38:18 +0100 Subject: [PATCH 3/4] Improved token overlay spacing --- module/canvas/placeables/token.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 26c96683..e3cf9942 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -59,9 +59,10 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { fontSize: 96, strokeThickness: 4 }); - const nrDigits = Math.floor(effect.system.stacking.value / 10) + 1; - stackOverlay.x = icon.width - 56 * nrDigits; - stackOverlay.y = 4; + const nrDigits = Math.floor(Math.log10(effect.system.stacking.value)) + 1; + stackOverlay.y = -8; + /* This does not account for 1:s being much less wide than other digits. I don't think it's desired however as it makes it look jumpy */ + stackOverlay.x = icon.width - 8 - nrDigits * 56 + (lastDigitIsOne ? 16 : 0); stackOverlay.anchor.set(0, 0); icon.addChild(stackOverlay); From 0f334232d802c82a506dd5dffa68546a97e1a4e5 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Thu, 26 Feb 2026 20:30:34 +0100 Subject: [PATCH 4/4] Compendium updaetes --- module/applications/ux/contextMenu.mjs | 93 ----------- module/canvas/placeables/token.mjs | 4 +- .../feature_No_Mercy_njj2C3tMDeCHHOoh.json | 9 +- ...eature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json | 9 +- ...Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json | 49 ++++-- .../domainCard_Rage_Up_GRL0cvs96vrTDckZ.json | 148 ++++++++---------- 6 files changed, 112 insertions(+), 200 deletions(-) diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index 081e6ba0..4e4ec6a4 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -1,97 +1,4 @@ -/** - * @typedef ContextMenuEntry - * @property {string} name The context menu label. Can be localized. - * @property {string} [icon] A string containing an HTML icon element for the menu item. - * @property {string} [classes] Additional CSS classes to apply to this menu item. - * @property {string} [group] An identifier for a group this entry belongs to. - * @property {ContextMenuJQueryCallback} callback The function to call when the menu item is clicked. - * @property {ContextMenuCondition|boolean} [condition] A function to call or boolean value to determine if this entry - * appears in the menu. - */ - -/** - * @callback ContextMenuCondition - * @param {jQuery|HTMLElement} html The element of the context menu entry. - * @returns {boolean} Whether the entry should be rendered in the context menu. - */ - -/** - * @callback ContextMenuCallback - * @param {HTMLElement} target The element that the context menu has been triggered for. - * @returns {unknown} - */ - -/** - * @callback ContextMenuJQueryCallback - * @param {HTMLElement|jQuery} target The element that the context menu has been triggered for. Will - * either be a jQuery object or an HTMLElement instance, depending - * on how the ContextMenu was configured. - * @returns {unknown} - */ - -/** - * @typedef ContextMenuOptions - * @property {string} [eventName="contextmenu"] Optionally override the triggering event which can spawn the menu. If - * the menu is using fixed positioning, this event must be a MouseEvent. - * @property {ContextMenuCallback} [onOpen] A function to call when the context menu is opened. - * @property {ContextMenuCallback} [onClose] A function to call when the context menu is closed. - * @property {boolean} [fixed=false] If true, the context menu is given a fixed position rather than being - * injected into the target. - * @property {boolean} [jQuery=true] If true, callbacks will be passed jQuery objects instead of HTMLElement - * instances. - */ - -/** - * @typedef ContextMenuRenderOptions - * @property {Event} [event] The event that triggered the context menu opening. - * @property {boolean} [animate=true] Animate the context menu opening. - */ - -/** - * A subclass of ContextMenu. - * @extends {foundry.applications.ux.ContextMenu} - */ export default class DHContextMenu extends foundry.applications.ux.ContextMenu { - /** - * @param {HTMLElement|jQuery} container - The HTML element that contains the context menu targets. - * @param {string} selector - A CSS selector which activates the context menu. - * @param {ContextMenuEntry[]} menuItems - An Array of entries to display in the menu - * @param {ContextMenuOptions} [options] - Additional options to configure the context menu. - */ - constructor(container, selector, menuItems, options) { - super(container, selector, menuItems, options); - - /** @deprecated since v13 until v15 */ - this.#jQuery = options.jQuery; - } - - /** - * Whether to pass jQuery objects or HTMLElement instances to callback. - * @type {boolean} - */ - #jQuery; - - /**@inheritdoc */ - activateListeners(menu) { - menu.addEventListener('click', this.#onClickItem.bind(this)); - } - - /** - * Handle click events on context menu items. - * @param {PointerEvent} event The click event - */ - #onClickItem(event) { - event.preventDefault(); - event.stopPropagation(); - const element = event.target.closest('.context-item'); - if (!element) return; - const item = this.menuItems.find(i => i.element === element); - item?.callback(this.#jQuery ? $(this.target) : this.target, event); - this.close(); - } - - /* -------------------------------------------- */ - /** * Trigger a context menu event in response to a normal click on a additional options button. * @param {PointerEvent} event diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index c23785bc..cf6c8e21 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -48,7 +48,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { /**@inheritdoc */ async _drawEffect(src, tint, effect) { if (!src) return; - const tex = await loadTexture(src, { fallback: 'icons/svg/hazard.svg' }); + const tex = await foundry.canvas.loadTexture(src, { fallback: 'icons/svg/hazard.svg' }); const icon = new PIXI.Sprite(tex); icon.tint = tint ?? 0xffffff; @@ -62,7 +62,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { const nrDigits = Math.floor(Math.log10(effect.system.stacking.value)) + 1; stackOverlay.y = -8; /* This does not account for 1:s being much less wide than other digits. I don't think it's desired however as it makes it look jumpy */ - stackOverlay.x = icon.width - 8 - nrDigits * 56 + (lastDigitIsOne ? 16 : 0); + stackOverlay.x = icon.width - 8 - nrDigits * 56; stackOverlay.anchor.set(0, 0); icon.addChild(stackOverlay); diff --git a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json index b5239242..2f3ce20d 100644 --- a/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json +++ b/src/packs/classes/feature_No_Mercy_njj2C3tMDeCHHOoh.json @@ -71,13 +71,18 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "value": 1, + "type": "add", + "value": "@stacks", "priority": null, - "type": "add" + "phase": "initial" } ], "duration": { "type": "shortRest" + }, + "stacking": { + "enabled": true, + "max": null } }, "disabled": false, diff --git a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json index 0f31f491..c4bf8ce8 100644 --- a/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json +++ b/src/packs/classes/feature_Rogue_s_Dodge_hVaaPIjxoextIgSL.json @@ -69,14 +69,19 @@ "changes": [ { "key": "system.evasion", - "value": 2, + "type": "add", + "value": "2 * @stacks", "priority": null, - "type": "add" + "phase": "initial" } ], "duration": { "type": "temporary", "description": "

Until the next time an attack succeeds against you.

" + }, + "stacking": { + "enabled": true, + "max": null } }, "disabled": false, diff --git a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json index 09dff08a..064bf277 100644 --- a/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json +++ b/src/packs/domains/domainCard_Corrosive_Projectile_qJaSNTuDfbPVr8Lb.json @@ -127,7 +127,7 @@ "sort": 3400000, "effects": [ { - "name": "Corroded (1 stack)", + "name": "Corroded", "img": "icons/magic/acid/dissolve-bone-white.webp", "origin": "Compendium.daggerheart.domains.Item.qJaSNTuDfbPVr8Lb", "transfer": false, @@ -139,27 +139,32 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.difficulty", + "type": "add", + "value": "-@stack", + "priority": null, + "phase": "initial" + } + ], + "stacking": { + "enabled": true, + "max": null + }, + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.difficulty", - "mode": 2, - "value": "-1", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "

While a target is Corroded, they gain a −1 penalty to their Difficulty for every 2 Stress you spent. This condition can stack.

", + "description": "

While a target is Corroded, they gain a −1 penalty to their Difficulty for every 2 Stress you spent. This condition can stack.

", "tint": "#ffffff", "statuses": [ "corrode" @@ -169,6 +174,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!qJaSNTuDfbPVr8Lb.zB95bjSSdVlApQnR" } ], diff --git a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json index 6f8b481d..8baedd50 100644 --- a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json +++ b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json @@ -42,44 +42,8 @@ "type": "self", "amount": null }, - "name": "Mark 1 Stress", - "img": "icons/magic/control/silhouette-aura-energy.webp", - "range": "self" - }, - "fKY9NcYBwCFwMsgV": { - "type": "effect", - "_id": "fKY9NcYBwCFwMsgV", - "systemPath": "actions", - "description": "

You can mark a Stress to gain a bonus to your damage roll equal to twice your Strength.

", - "chatDisplay": true, - "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "stress", - "value": 2, - "step": null, - "consumeOnSuccess": false - } - ], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "effects": [ - { - "_id": "t6SIjQxB6UBUJ98f", - "onSave": false - } - ], - "target": { - "type": "self", - "amount": null - }, - "name": "Mark 2 Stress", - "img": "icons/magic/control/silhouette-aura-energy.webp", + "name": "Mark Stress", + "img": "icons/skills/wounds/injury-face-impact-orange.webp", "range": "self" } }, @@ -94,8 +58,8 @@ "sort": 3400000, "effects": [ { - "name": "Rage Up (1)", - "img": "systems/daggerheart/assets/icons/domains/domain-card/blade.png", + "name": "Rage Up", + "img": "icons/skills/wounds/injury-face-impact-orange.webp", "origin": "Compendium.daggerheart.domains.Item.GRL0cvs96vrTDckZ", "transfer": false, "_id": "bq1MhcmoP6Wo5CXF", @@ -106,33 +70,39 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.bonuses.damage.magical.bonus", + "type": "add", + "value": "2*@system.traits.strength.value*@stacks", + "priority": 21, + "phase": "initial" + }, + { + "key": "system.bonuses.damage.physical.bonus", + "type": "add", + "value": "2*@system.traits.strength.value*@stacks", + "priority": 21, + "phase": "initial" + } + ], + "stacking": { + "enabled": true, + "max": 2 + }, + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.bonuses.damage.magical.bonus", - "mode": 2, - "value": "2*@system.traits.strength.value", - "priority": 21 - }, - { - "key": "system.bonuses.damage.physical.bonus", - "mode": 2, - "value": "2*@system.traits.strength.value", - "priority": 21 - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "

For your next attack you have a bonus to your damage roll equal to twice your Strength.

", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -140,6 +110,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!GRL0cvs96vrTDckZ.bq1MhcmoP6Wo5CXF" }, { @@ -155,31 +135,28 @@ "type": "withinRange", "target": "hostile", "range": "melee" - } - }, - "changes": [ - { - "key": "system.bonuses.damage.magical.bonus", - "mode": 2, - "value": "4*@system.traits.strength.value", - "priority": 21 }, - { - "key": "system.bonuses.damage.physical.bonus", - "mode": 2, - "value": "4*@system.traits.strength.value", - "priority": 21 - } - ], + "changes": [ + { + "key": "system.bonuses.damage.magical.bonus", + "value": "4*@system.traits.strength.value", + "priority": 21, + "type": "add" + }, + { + "key": "system.bonuses.damage.physical.bonus", + "value": "4*@system.traits.strength.value", + "priority": 21, + "type": "add" + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "", "tint": "#ffffff", @@ -189,6 +166,9 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!GRL0cvs96vrTDckZ.t6SIjQxB6UBUJ98f" } ],