[Feature/Fixes] Adversary Touchups (#359)

* Added support for automatic horde damage

* Active effects are shown on the token

* Fixed logic

* Fixed d20 dice lightmode color
This commit is contained in:
WBHarry 2025-07-16 20:17:34 +02:00 committed by GitHub
parent 727cb692b4
commit b40d053201
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 134 additions and 24 deletions

View file

@ -1,4 +1,5 @@
export { default as DhMeasuredTemplate } from './measuredTemplate.mjs';
export { default as DhRuler } from './ruler.mjs';
export { default as DhTemplateLayer } from './templateLayer.mjs';
export { default as DhTokenPlaceable } from './token.mjs';
export { default as DhTokenRuler } from './tokenRuler.mjs';

View file

@ -0,0 +1,36 @@
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
/** @inheritDoc */
async _drawEffects() {
this.effects.renderable = false;
// Clear Effects Container
this.effects.removeChildren().forEach(c => c.destroy());
this.effects.bg = this.effects.addChild(new PIXI.Graphics());
this.effects.bg.zIndex = -1;
this.effects.overlay = null;
// Categorize effects
const activeEffects = this.actor ? Array.from(this.actor.effects).filter(x => !x.disabled) : [];
const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag('core', 'overlay'));
// Draw effects
const promises = [];
for (const [i, effect] of activeEffects.entries()) {
if (!effect.img) continue;
const promise =
effect === overlayEffect
? this._drawOverlay(effect.img, effect.tint)
: this._drawEffect(effect.img, effect.tint);
promises.push(
promise.then(e => {
if (e) e.zIndex = i;
})
);
}
await Promise.allSettled(promises);
this.effects.sortChildren();
this.effects.renderable = true;
this.renderFlags.set({ refreshEffects: true });
}
}

View file

@ -6,6 +6,15 @@ export default class DHDamageAction extends DHBaseAction {
getFormulaValue(part, data) {
let formulaValue = part.value;
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
const isAdversary = this.actor.type === 'adversary';
if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {
const hasHordeDamage = this.actor.effects.find(
x => x.name === game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label')
);
if (hasHordeDamage) return part.valueAlt;
}
return formulaValue;
}
@ -21,7 +30,7 @@ export default class DHDamageAction extends DHBaseAction {
bonusDamage = [];
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData));
const config = {
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
roll: { formula },

View file

@ -52,7 +52,7 @@ export default class DhpAdversary extends BaseDataActor {
})
}),
resources: new fields.SchemaField({
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true),
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
}),
attack: new ActionField({
@ -109,4 +109,37 @@ export default class DhpAdversary extends BaseDataActor {
get features() {
return this.parent.items.filter(x => x.type === 'feature');
}
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
if (this.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {
if (changes.system?.resources?.hitPoints?.value) {
const halfHP = Math.ceil(this.resources.hitPoints.max / 2);
const newHitPoints = changes.system.resources.hitPoints.value;
const previouslyAboveHalf = this.resources.hitPoints.value < halfHP;
const loweredBelowHalf = previouslyAboveHalf && newHitPoints >= halfHP;
const raisedAboveHalf = !previouslyAboveHalf && newHitPoints < halfHP;
if (loweredBelowHalf) {
await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label'),
img: 'icons/magic/movement/chevrons-down-yellow.webp',
disabled: !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation)
.hordeDamage
}
]);
} else if (raisedAboveHalf) {
const hordeEffects = this.parent.effects.filter(
x => x.name === game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label')
);
await this.parent.deleteEmbeddedDocuments(
'ActiveEffect',
hordeEffects.map(x => x.id)
);
}
}
}
}
}

View file

@ -21,7 +21,7 @@ export default class DhCharacter extends BaseDataActor {
return {
...super.defineSchema(),
resources: new fields.SchemaField({
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true),
stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true),
hope: resourceField(6, 'DAGGERHEART.GENERAL.hope')
}),

View file

@ -24,7 +24,7 @@ export default class DHClass extends BaseDataItem {
integer: true,
min: 1,
initial: 5,
label: 'DAGGERHEART.GENERAL.hitPoints'
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
}),
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
hopeFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),

View file

@ -1,6 +1,4 @@
export default class DhAutomation extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Automation']; // Doesn't work for some reason
static defineSchema() {
const fields = foundry.data.fields;
return {
@ -20,6 +18,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.actionPoints.label'
}),
hordeDamage: new fields.BooleanField({
required: true,
initial: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hordeDamage.label'
})
};
}