[Feature] 494 - Adversaries Tier 1 (#500)

* Some work

* More work

* Finished Tier 1
This commit is contained in:
WBHarry 2025-08-01 16:43:59 +02:00 committed by GitHub
parent 3014be79ad
commit 263dfa69ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 15171 additions and 972 deletions

View file

@ -311,8 +311,12 @@ export default function DHApplicationMixin(Base) {
name: 'CONTROLS.CommonEdit',
icon: 'fa-solid fa-pen-to-square',
condition: target => {
const { dataset } = target.closest('[data-item-uuid]');
const doc = getDocFromElementSync(target);
return !doc || !doc.hasOwnProperty('systemPath') || doc.inCollection;
return (
(!dataset.noCompendiumEdit && !doc) ||
(doc && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection))
);
},
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
}

View file

@ -43,6 +43,12 @@ export const range = {
}
};
export const templateTypes = {
...CONST.MEASURED_TEMPLATE_TYPES,
EMANATION: 'emanation',
INFRONT: 'inFront'
};
export const rangeInclusion = {
withinRange: {
id: 'withinRange',

View file

@ -10,10 +10,8 @@ export default class DHDamageAction extends DHBaseAction {
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;
const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde');
if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt;
}
return formulaValue;
@ -47,7 +45,9 @@ export default class DHDamageAction extends DHBaseAction {
formulas = this.formatFormulas(formulas, systemData);
const config = {
title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }),
title: game.i18n.format(`DAGGERHEART.UI.Chat.${this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, {
damage: game.i18n.localize(this.name)
}),
roll: formulas,
targets: systemData.targets?.filter(t => t.hit) ?? data.targets,
hasSave: this.hasSave,

View file

@ -1,9 +1,11 @@
import BaseEffect from './baseEffect.mjs';
import BeastformEffect from './beastformEffect.mjs';
import HordeEffect from './hordeEffect.mjs';
export { BaseEffect, BeastformEffect };
export { BaseEffect, BeastformEffect, HordeEffect };
export const config = {
base: BaseEffect,
beastform: BeastformEffect
beastform: BeastformEffect,
horde: HordeEffect
};

View file

@ -0,0 +1,3 @@
import BaseEffect from './baseEffect.mjs';
export default class HordeEffect extends BaseEffect {}

View file

@ -117,29 +117,46 @@ export default class DhpAdversary extends BaseDataActor {
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)
);
const autoHordeDamage = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Automation
).hordeDamage;
if (autoHordeDamage && changes.system?.resources?.hitPoints?.value) {
const hordeActiveEffect = this.parent.effects.find(x => x.type === 'horde');
if (hordeActiveEffect) {
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 hordeActiveEffect.update({ disabled: false });
} else if (raisedAboveHalf) {
await hordeActiveEffect.update({ disabled: true });
}
}
}
}
}
_onUpdate(changes, options, userId) {
super._onUpdate(changes, options, userId);
if (game.user.id === userId) {
if (changes.system.type) {
const existingHordeEffect = this.parent.effects.find(x => x.type === 'horde');
if (changes.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {
if (!existingHordeEffect)
this.parent.createEmbeddedDocuments('ActiveEffect', [
{
type: 'horde',
name: game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label'),
img: 'icons/magic/movement/chevrons-down-yellow.webp',
disabled: true
}
]);
} else {
existingHordeEffect?.delete();
}
}
}

View file

@ -1,7 +1,6 @@
import { itemAbleRollParse } from '../helpers/utils.mjs';
export default class DhActiveEffect extends foundry.documents.ActiveEffect {
/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */
@ -78,8 +77,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
/**
* Altered Foundry safeEval to allow non-numeric return
* @param {string} expression
* @returns
* @param {string} expression
* @returns
*/
static effectSafeEval(expression) {
let result;
@ -95,13 +94,15 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
}
/**
* Generates a list of localized tags based on this item's type-specific properties.
* @returns {string[]} An array of localized tag strings.
*/
* Generates a list of localized tags based on this item's type-specific properties.
* @returns {string[]} An array of localized tag strings.
*/
_getTags() {
const tags = [
`${game.i18n.localize(this.parent.system.metadata.label)}: ${this.parent.name}`,
game.i18n.localize(this.isTemporary ? 'DAGGERHEART.EFFECTS.Duration.temporary' : 'DAGGERHEART.EFFECTS.Duration.passive')
game.i18n.localize(
this.isTemporary ? 'DAGGERHEART.EFFECTS.Duration.temporary' : 'DAGGERHEART.EFFECTS.Duration.passive'
)
];
for (const statusId of this.statuses) {

View file

@ -74,8 +74,8 @@ export default class DHItem extends foundry.documents.Item {
isInventoryItem === true
? 'Inventory Items' //TODO localize
: isInventoryItem === false
? 'Character Items' //TODO localize
: 'Other'; //TODO localize
? 'Character Items' //TODO localize
: 'Other'; //TODO localize
return { value: type, label, group };
}
@ -137,10 +137,10 @@ export default class DHItem extends foundry.documents.Item {
this.type === 'ancestry'
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
: this.type === 'community'
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
: this.type === 'feature'
? game.i18n.localize('TYPES.Item.feature')
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
: this.type === 'feature'
? game.i18n.localize('TYPES.Item.feature')
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
origin: origin,
img: this.img,
name: this.name,

View file

@ -11,7 +11,7 @@ export default function DhTemplateEnricher(match, _options) {
if (split.length === 2) {
switch (split[0]) {
case 'type':
const matchedType = Object.values(CONST.MEASURED_TEMPLATE_TYPES).find(
const matchedType = Object.values(CONFIG.DH.GENERAL.templateTypes).find(
x => x.toLowerCase() === split[1]
);
type = matchedType;
@ -28,10 +28,12 @@ export default function DhTemplateEnricher(match, _options) {
if (!type || !range) return match[0];
const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`);
const templateElement = document.createElement('span');
templateElement.innerHTML = `
<button class="measured-template-button" data-type="${type}" data-range="${range}">
${game.i18n.localize(`TEMPLATE.TYPES.${type}`)} - ${game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.name`)}
${label} - ${game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.name`)}
</button>
`;
@ -45,16 +47,26 @@ export const renderMeasuredTemplate = async event => {
if (!type || !range || !game.canvas.scene) return;
const distance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement)[range];
const usedType = type === 'inFront' ? 'cone' : type === 'emanation' ? 'circle' : type;
const angle =
type === CONST.MEASURED_TEMPLATE_TYPES.CONE
? CONFIG.MeasuredTemplate.defaults.angle
: type === CONFIG.DH.GENERAL.templateTypes.INFRONT
? '180'
: undefined;
const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement)[range];
const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance;
const { width, height } = game.canvas.scene.dimensions;
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [
{
x: width / 2,
y: height / 2,
t: type,
t: usedType,
distance: distance,
width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined,
angle: type === CONST.MEASURED_TEMPLATE_TYPES.CONE ? CONFIG.MeasuredTemplate.defaults.angle : undefined
angle: angle
}
]);
};

View file

@ -7,7 +7,7 @@ export { DhDamageEnricher, DhDualityRollEnricher, DhEffectEnricher, DhTemplateEn
export const enricherConfig = [
{
pattern: /^@Damage\[(.*)\]({.*})?$/g,
pattern: /@Damage\[(.*)\]({.*})?/g,
enricher: DhDamageEnricher
},
{
@ -15,11 +15,11 @@ export const enricherConfig = [
enricher: DhDualityRollEnricher
},
{
pattern: /^@Effect\[(.*)\]({.*})?$/g,
pattern: /@Effect\[(.*)\]({.*})?/g,
enricher: DhEffectEnricher
},
{
pattern: /^@Template\[(.*)\]({.*})?$/g,
pattern: /@Template\[(.*)\]({.*})?/g,
enricher: DhTemplateEnricher
}
];