136 lines
3.1 KiB
JavaScript

import {
MODULE_ID,
EFFECT_FLAG,
ADAPTATION_CONFIG,
ADAPTATION_TYPES,
PROFANE_DAMAGE_TYPES,
PROFANE_BYPASSES
} from "./constants.js";
import {
getAdaptiveTriggerEffects,
getDamageTypeLabel
} from "./utils.js";
/**
* Entfernt sämtliche dynamisch erzeugten adaptiven Schutzwirkungen.
* Eine neu ausgelöste Anpassung ersetzt damit immer die vorherige.
*/
export async function removeOldAdaptiveEffects(actor) {
const oldEffects = actor.effects.filter(
effect => effect.getFlag(MODULE_ID, EFFECT_FLAG)
);
if (!oldEffects.length) return;
await actor.deleteEmbeddedDocuments(
"ActiveEffect",
oldEffects.map(effect => effect.id)
);
}
/**
* Entfernt adaptive Schutzwirkungen, deren auslösende Quelle nicht mehr
* auf dem Actor angewendet wird.
*
* Beispiel:
* Ein getragenes Amulett verleiht adaptive Feuerresistenz. Wird das Amulett
* abgelegt, verschwindet sein Marker-Effect aus actor.appliedEffects und die
* zuvor erzeugte Feuerresistenz wird ebenfalls entfernt.
*/
export async function removeOrphanedAdaptiveEffects(actor) {
if (!actor) return;
const appliedSourceUuids = new Set(
getAdaptiveTriggerEffects(actor).map(effect => effect.uuid)
);
const orphanedEffects = actor.effects.filter(effect => {
const data = effect.getFlag(MODULE_ID, EFFECT_FLAG);
return data?.sourceEffectUuid
&& !appliedSourceUuids.has(data.sourceEffectUuid);
});
if (!orphanedEffects.length) return;
await actor.deleteEmbeddedDocuments(
"ActiveEffect",
orphanedEffects.map(effect => effect.id)
);
}
function getAdaptiveChanges(config, damageType) {
const changes = [
{
key: config.traitKey,
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: damageType,
priority: 20
}
];
if (PROFANE_DAMAGE_TYPES.includes(damageType)) {
const bypassKey = config.traitKey.replace(/\.value$/, ".bypasses");
for (const bypass of PROFANE_BYPASSES) {
changes.push({
key: bypassKey,
mode: CONST.ACTIVE_EFFECT_MODES.ADD,
value: bypass,
priority: 20
});
}
}
return changes;
}
/**
* Erzeugt die konkrete adaptive Resistenz oder Immunität auf dem Actor.
*/
export async function createAdaptiveEffect(
actor,
damageType,
adaptationType = ADAPTATION_TYPES.RESISTANCE,
sourceEffectUuid = null
) {
const config = ADAPTATION_CONFIG[adaptationType]
?? ADAPTATION_CONFIG[ADAPTATION_TYPES.RESISTANCE];
const label = getDamageTypeLabel(damageType);
await actor.createEmbeddedDocuments("ActiveEffect", [
{
name: game.i18n.format(config.effectNameKey, { type: label }),
img: config.icon,
disabled: false,
transfer: false,
flags: {
[MODULE_ID]: {
[EFFECT_FLAG]: {
adaptationType: config.id,
damageType,
sourceEffectUuid
}
}
},
changes: getAdaptiveChanges(config, damageType)
}
]);
}
export async function createAdaptiveResistanceEffect(
actor,
damageType,
sourceEffectUuid = null
) {
return createAdaptiveEffect(
actor,
damageType,
ADAPTATION_TYPES.RESISTANCE,
sourceEffectUuid
);
}