Added the ability for effects to have stacks

This commit is contained in:
WBHarry 2026-02-17 18:58:47 +01:00
parent 4aab5d315a
commit 79057b0718
11 changed files with 187 additions and 18 deletions

View file

@ -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();
}

View file

@ -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

View file

@ -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 }),
})
};
}

View file

@ -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