fix: make ActiveEffects work properly
This commit is contained in:
parent
486aaa1aaf
commit
e181426882
11 changed files with 112 additions and 98 deletions
12
lang/de.json
12
lang/de.json
|
@ -173,11 +173,11 @@
|
|||
"DS4.SpellCasterClassWizard": "Zauberer",
|
||||
"DS4.SpellPrice": "Preis (Gold)",
|
||||
"DS4.SpellPriceDescription": "Der Kaufpreis des Zauberspruchs.",
|
||||
"DS4.EffectEnabled": "Aktiv",
|
||||
"DS4.EffectEnabledAbbr": "A",
|
||||
"DS4.EffectEffectivelyEnabled": "Effektiv Aktiv (unter Betrachtung, ob ein eventuelles Quellen-Item ausgerüstet ist usw.)",
|
||||
"DS4.EffectEffectivelyEnabledAbbr": "E",
|
||||
"DS4.EffectLabel": "Bezeichnung",
|
||||
"DS4.EffectEnabled": "Eingeschaltet",
|
||||
"DS4.EffectEnabledAbbr": "E",
|
||||
"DS4.EffectActive": "Aktiv (unter Betrachtung, ob ein eventuelles Quellen-Item ausgerüstet ist usw.)",
|
||||
"DS4.EffectActiveAbbr": "A",
|
||||
"DS4.EffectName": "Name",
|
||||
"DS4.EffectSourceName": "Quelle",
|
||||
"DS4.EffectFactor": "Faktor (wie oft der Effekt angewendet wird)",
|
||||
"DS4.EffectFactorAbbr": "F",
|
||||
|
@ -382,7 +382,7 @@
|
|||
"DS4.NewLanguageName": "Neue Sprache",
|
||||
"DS4.NewAlphabetName": "Neue Schriftzeichen",
|
||||
"DS4.NewSpecialCreatureAbilityName": "Neue Besondere Kreaturenfähigkeit",
|
||||
"DS4.NewEffectLabel": "Neuer Effekt",
|
||||
"DS4.NewEffectName": "Neuer Effekt",
|
||||
|
||||
"DS4.ActiveEffectApplyToItems": "Auf Items Anwenden",
|
||||
"DS4.ActiveEffectItemName": "Itemname",
|
||||
|
|
|
@ -175,9 +175,9 @@
|
|||
"DS4.SpellPriceDescription": "The price to purchase the spell.",
|
||||
"DS4.EffectEnabled": "Enabled",
|
||||
"DS4.EffectEnabledAbbr": "E",
|
||||
"DS4.EffectEffectivelyEnabled": "Effectively Enabled (taking into account whether a potential source item is equipped etc.)",
|
||||
"DS4.EffectEffectivelyEnabledAbbr": "EE",
|
||||
"DS4.EffectLabel": "Label",
|
||||
"DS4.EffectActive": "Active (taking into account whether a potential source item is equipped etc.)",
|
||||
"DS4.EffectActiveAbbr": "A",
|
||||
"DS4.EffectName": "Name",
|
||||
"DS4.EffectSourceName": "Source",
|
||||
"DS4.EffectFactor": "Factor (the number of times the effect is being applied)",
|
||||
"DS4.EffectFactorAbbr": "F",
|
||||
|
@ -382,7 +382,7 @@
|
|||
"DS4.NewLanguageName": "New Language",
|
||||
"DS4.NewAlphabetName": "New Alphabet",
|
||||
"DS4.NewSpecialCreatureAbilityName": "New Special Creature Ability",
|
||||
"DS4.NewEffectLabel": "New Effect",
|
||||
"DS4.NewEffectName": "New Effect",
|
||||
|
||||
"DS4.ActiveEffectApplyToItems": "Apply to Items",
|
||||
"DS4.ActiveEffectItemName": "Item Name",
|
||||
|
|
|
@ -51,9 +51,9 @@ export class DS4ActorSheet extends ActorSheet {
|
|||
const enrichedEffectPromises = this.actor.effects.map(async (effect) => {
|
||||
return {
|
||||
...effect.toObject(),
|
||||
sourceName: await effect.getCurrentSourceName(),
|
||||
sourceName: effect.sourceName,
|
||||
factor: effect.factor,
|
||||
isEffectivelyEnabled: !effect.disabled && !effect.isSurpressed,
|
||||
active: effect.active,
|
||||
};
|
||||
});
|
||||
const enrichedEffects = await Promise.all(enrichedEffectPromises);
|
||||
|
|
|
@ -35,11 +35,8 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
*/
|
||||
source = undefined;
|
||||
|
||||
/**
|
||||
* Whether or not this effect is currently surpressed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isSurpressed() {
|
||||
/** @override */
|
||||
get isSuppressed() {
|
||||
const originatingItem = this.originatingItem;
|
||||
if (!originatingItem) {
|
||||
return false;
|
||||
|
@ -82,30 +79,6 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
return super.apply(document, change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current source name based on the cached source object.
|
||||
* @returns {Promise<string>} The current source name
|
||||
*/
|
||||
async getCurrentSourceName() {
|
||||
const game = getGame();
|
||||
const origin = await this.getSource();
|
||||
if (origin === null) return game.i18n.localize("None");
|
||||
return origin.name ?? game.i18n.localize("Unknown");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source document for this effect. Uses the cached {@link DS4ActiveEffect#source} if it has already been
|
||||
* set.
|
||||
* @protected
|
||||
* @returns {Promise<foundry.abstract.Document | null>}
|
||||
*/
|
||||
async getSource() {
|
||||
if (this.source === undefined) {
|
||||
this.source = this.origin != null ? await fromUuid(this.origin) : null;
|
||||
}
|
||||
return this.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DS4ActiveEffect} using default values.
|
||||
*
|
||||
|
@ -115,7 +88,7 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
*/
|
||||
static async createDefault(parent) {
|
||||
const createData = {
|
||||
label: getGame().i18n.localize(`DS4.NewEffectLabel`),
|
||||
name: getGame().i18n.localize(`DS4.NewEffectName`),
|
||||
icon: this.FALLBACK_ICON,
|
||||
};
|
||||
|
||||
|
@ -141,13 +114,25 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
* @param {import("./item/item").DS4Item | import("./actor/actor").DS4Actor} document The Actor or Item to which to apply the effects
|
||||
* @param {DS4ActiveEffect[]} effetcs The effects to apply
|
||||
* @param {(change: EffectChangeData) => boolean} [predicate=() => true] Apply only changes that fullfill this predicate
|
||||
* @returns {Set<string>} The statuses that are applied by this effect
|
||||
*/
|
||||
static applyEffetcs(document, effetcs, predicate = () => true) {
|
||||
/** @type {Record<string, unknown>} */
|
||||
const overrides = {};
|
||||
|
||||
// Organize non-disabled and -surpressed effects by their application priority
|
||||
const changesWithEffect = effetcs.flatMap((e) => e.getFactoredChangesWithEffect(predicate));
|
||||
/** @type {Set<string>} */
|
||||
const statuses = new Set();
|
||||
|
||||
// Organize active effect changes by their application priority
|
||||
const changesWithEffect = effetcs.flatMap((e) => {
|
||||
if (!e.active) {
|
||||
return [];
|
||||
}
|
||||
for (const statusId of e.statuses) {
|
||||
statuses.add(statusId);
|
||||
}
|
||||
return e.getFactoredChangesWithEffect(predicate);
|
||||
});
|
||||
changesWithEffect.sort((a, b) => (a.change.priority ?? 0) - (b.change.priority ?? 0));
|
||||
|
||||
// Apply all changes
|
||||
|
@ -162,6 +147,8 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
...foundry.utils.flattenObject(document.overrides),
|
||||
...overrides,
|
||||
});
|
||||
|
||||
return statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -171,13 +158,10 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
* @protected
|
||||
*/
|
||||
getFactoredChangesWithEffect(predicate = () => true) {
|
||||
if (this.disabled || this.isSurpressed) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.changes.filter(predicate).flatMap((change) => {
|
||||
change.priority = change.priority ?? change.mode * 10;
|
||||
return Array(this.factor).fill({ effect: this, change });
|
||||
const c = foundry.utils.deepClone(change);
|
||||
c.priority = c.priority ?? c.mode * 10;
|
||||
return Array(this.factor).fill({ effect: this, change: c });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ import { isAttribute, isTrait } from "./actor-data-source-base";
|
|||
* The Actor class for DS4
|
||||
*/
|
||||
export class DS4Actor extends Actor {
|
||||
/** @type {Set<string>} */
|
||||
newStatuses = new Set();
|
||||
|
||||
/** @override */
|
||||
prepareData() {
|
||||
this.prepareBaseData();
|
||||
|
@ -23,11 +26,14 @@ export class DS4Actor extends Actor {
|
|||
this.applyActiveEffectsToBaseData();
|
||||
this.prepareDerivedData();
|
||||
this.applyActiveEffectsToDerivedData();
|
||||
this.handleStatusChanges();
|
||||
this.prepareFinalDerivedData();
|
||||
}
|
||||
|
||||
/** @override */
|
||||
prepareBaseData() {
|
||||
this.newStatuses = new Set();
|
||||
|
||||
this.system.rolling = {
|
||||
minimumFumbleResult: 20,
|
||||
maximumCoupResult: 1,
|
||||
|
@ -60,7 +66,13 @@ export class DS4Actor extends Actor {
|
|||
* @protected
|
||||
*/
|
||||
get actorEffects() {
|
||||
return this.effects.filter((effect) => !effect.flags.ds4?.itemEffectConfig?.applyToItems);
|
||||
const effects = [];
|
||||
for (const effect of this.allApplicableEffects()) {
|
||||
if (!effect.flags.ds4?.itemEffectConfig?.applyToItems) {
|
||||
effects.push(effect);
|
||||
}
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +81,8 @@ export class DS4Actor extends Actor {
|
|||
* @returns {import("../active-effect").DS4ActiveEffect[]} The array of effects that are candidates to be applied to the item
|
||||
*/
|
||||
itemEffects(item) {
|
||||
return this.effects.filter((effect) => {
|
||||
/** @type {(effect: DS4ActiveEffect) => boolean} */
|
||||
const shouldEffectBeAppliedToItem = (effect, item) => {
|
||||
const { applyToItems, itemName, condition } = effect.flags.ds4?.itemEffectConfig ?? {};
|
||||
|
||||
if (!applyToItems || (itemName !== undefined && itemName !== "" && itemName !== item.name)) {
|
||||
|
@ -78,11 +91,7 @@ export class DS4Actor extends Actor {
|
|||
|
||||
if (condition !== undefined && condition !== "") {
|
||||
try {
|
||||
const replacedCondition = DS4Actor.replaceFormulaData(condition, {
|
||||
item,
|
||||
actor: this,
|
||||
effect,
|
||||
});
|
||||
const replacedCondition = DS4Actor.replaceFormulaData(condition, { item, actor: this, effect });
|
||||
return replacedCondition !== undefined ? Boolean(mathEvaluator.evaluate(replacedCondition)) : false;
|
||||
} catch (error) {
|
||||
logger.warn(error);
|
||||
|
@ -91,7 +100,15 @@ export class DS4Actor extends Actor {
|
|||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
const effects = [];
|
||||
for (const effect of this.allApplicableEffects()) {
|
||||
if (shouldEffectBeAppliedToItem(effect, item)) {
|
||||
effects.push(effect);
|
||||
}
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +170,8 @@ export class DS4Actor extends Actor {
|
|||
*/
|
||||
applyActiveEffectsToItem(item) {
|
||||
item.overrides = {};
|
||||
DS4ActiveEffect.applyEffetcs(item, this.itemEffects(item));
|
||||
item.reset();
|
||||
DS4ActiveEffect.applyEffetcs(item, this.itemEffects(item)).forEach(this.newStatuses.add.bind(this.newStatuses));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,7 +186,7 @@ export class DS4Actor extends Actor {
|
|||
(change) =>
|
||||
!this.derivedDataProperties.includes(change.key) &&
|
||||
!this.finalDerivedDataProperties.includes(change.key),
|
||||
);
|
||||
).forEach(this.newStatuses.add.bind(this.newStatuses));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,7 +196,7 @@ export class DS4Actor extends Actor {
|
|||
applyActiveEffectsToDerivedData() {
|
||||
DS4ActiveEffect.applyEffetcs(this, this.actorEffects, (change) =>
|
||||
this.derivedDataProperties.includes(change.key),
|
||||
);
|
||||
).forEach(this.newStatuses.add.bind(this.newStatuses));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +223,30 @@ export class DS4Actor extends Actor {
|
|||
return combatValueProperties.concat(checkProperties);
|
||||
}
|
||||
|
||||
handleStatusChanges() {
|
||||
this.statuses ??= new Set();
|
||||
|
||||
// Identify which special statuses had been active
|
||||
const specialStatuses = new Map();
|
||||
for (const statusId of Object.values(CONFIG.specialStatusEffects)) {
|
||||
specialStatuses.set(statusId, this.statuses.has(statusId));
|
||||
}
|
||||
this.statuses.clear();
|
||||
|
||||
// set new statuses
|
||||
this.newStatuses.forEach(this.statuses.add.bind(this.statuses));
|
||||
|
||||
// Apply special statuses that changed to active tokens
|
||||
const tokens = this.getActiveTokens();
|
||||
for (const [statusId, wasActive] of specialStatuses) {
|
||||
const isActive = this.statuses.has(statusId);
|
||||
if (isActive === wasActive) continue;
|
||||
for (const token of tokens) {
|
||||
token._onApplyStatusEffect(statusId, isActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply final transformations to the Actor data after all effects have been applied.
|
||||
*/
|
||||
|
|
|
@ -38,12 +38,8 @@
|
|||
"minimum": "10.290",
|
||||
"verified": "10"
|
||||
},
|
||||
"esmodules": [
|
||||
"ds4.js"
|
||||
],
|
||||
"styles": [
|
||||
"css/ds4.css"
|
||||
],
|
||||
"esmodules": ["ds4.js"],
|
||||
"styles": ["css/ds4.css"],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "en",
|
||||
|
|
|
@ -9,7 +9,9 @@ SPDX-License-Identifier: MIT
|
|||
<!-- Effect Header -->
|
||||
<header class="sheet-header">
|
||||
<img class="effect-icon" src="{{ data.icon }}" data-edit="icon">
|
||||
<h1 class="effect-title">{{ data.label }}</h1>
|
||||
<h1 class="effect-title">
|
||||
<input name="name" type="text" value="{{data.name}}" placeholder="{{ localize 'Name' }}" />
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<!-- Effect Configuration Tabs -->
|
||||
|
@ -21,30 +23,19 @@ SPDX-License-Identifier: MIT
|
|||
|
||||
<!-- Details Tab -->
|
||||
<section class="tab" data-tab="details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Label" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="label" value="{{ data.label }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Icon" }}</label>
|
||||
<div class="form-fields">
|
||||
{{filePicker target="icon" type="image"}}
|
||||
<input class="image" type="text" name="icon" placeholder="path/image.png" value="{{data.icon}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.IconTint" }}</label>
|
||||
<div class="form-fields">
|
||||
<input class="color" type="text" name="tint" value="{{data.tint}}" />
|
||||
<input type="color" value="{{data.tint}}" data-edit="tint" />
|
||||
{{colorPicker name="tint" value=data.tint}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group stacked">
|
||||
<label>{{ localize "EFFECT.Description" }}</label>
|
||||
{{editor descriptionHTML target="description" button=false editable=editable engine="prosemirror"
|
||||
collaborate=false}}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Disabled" }}</label>
|
||||
<div class="form-fields">
|
||||
|
@ -63,10 +54,11 @@ SPDX-License-Identifier: MIT
|
|||
|
||||
{{#if isItemEffect}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Transfer" }}</label>
|
||||
<label>{{ labels.transfer.name }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="transfer" {{checked data.transfer}} />
|
||||
</div>
|
||||
<p class="hint">{{ labels.transfer.hint }}</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
|
|
@ -15,15 +15,15 @@ SPDX-License-Identifier: MIT
|
|||
type="checkbox" {{checked (ne effectData.disabled true)}} data-dtype="Boolean" data-property="disabled"
|
||||
data-inverted="true" title="{{localize 'DS4.EffectEnabled'}}">
|
||||
|
||||
{{!-- effectively enabled --}}
|
||||
{{#if effectData.isEffectivelyEnabled}}<i class="fas fa-check"></i>{{else}}<i class="fas fa-ban"></i>{{/if}}
|
||||
{{!-- active --}}
|
||||
{{#if effectData.active}}<i class="fas fa-check"></i>{{else}}<i class="fas fa-ban"></i>{{/if}}
|
||||
|
||||
{{!-- icon --}}
|
||||
{{> systems/ds4/templates/sheets/shared/components/rollable-image.hbs rollable=false src=effectData.icon
|
||||
alt=(localize "DS4.DocumentImageAltText" name=effectData.label) title=effectData.label}}
|
||||
|
||||
{{!-- label --}}
|
||||
<div title="{{effectData.label}}">{{effectData.label}}</div>
|
||||
{{!-- name --}}
|
||||
<div title="{{effectData.name}}">{{effectData.name}}</div>
|
||||
|
||||
{{!-- source name --}}
|
||||
<div title="{{effectData.sourceName}}">{{effectData.sourceName}}</div>
|
||||
|
|
|
@ -12,14 +12,14 @@ SPDX-License-Identifier: MIT
|
|||
{{!-- enabled --}}
|
||||
<div title="{{localize 'DS4.EffectEnabled'}}">{{localize 'DS4.EffectEnabledAbbr'}}</div>
|
||||
|
||||
{{!-- effectively enabled --}}
|
||||
<div title="{{localize 'DS4.EffectEffectivelyEnabled'}}">{{localize 'DS4.EffectEffectivelyEnabledAbbr'}}</div>
|
||||
{{!-- active --}}
|
||||
<div title="{{localize 'DS4.EffectActive'}}">{{localize 'DS4.EffectActiveAbbr'}}</div>
|
||||
|
||||
{{!-- icon --}}
|
||||
<div></div>
|
||||
|
||||
{{!-- label --}}
|
||||
<div>{{localize 'DS4.EffectLabel'}}</div>
|
||||
{{!-- name --}}
|
||||
<div>{{localize 'DS4.EffectName'}}</div>
|
||||
|
||||
{{!-- source name --}}
|
||||
<div>{{localize 'DS4.EffectSourceName'}}</div>
|
||||
|
|
|
@ -13,8 +13,8 @@ SPDX-License-Identifier: MIT
|
|||
{{> systems/ds4/templates/sheets/shared/components/rollable-image.hbs rollable=false src=effectData.icon
|
||||
alt=(localize "DS4.DocumentImageAltText" name=effectData.label) title=effectData.label}}
|
||||
|
||||
{{!-- label --}}
|
||||
<div title="{{effectData.label}}">{{effectData.label}}</div>
|
||||
{{!-- name --}}
|
||||
<div title="{{effectData.name}}">{{effectData.name}}</div>
|
||||
|
||||
{{!-- control button group --}}
|
||||
{{> systems/ds4/templates/sheets/shared/components/control-button-group.hbs documentType="effect"
|
||||
|
|
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: MIT
|
|||
<div></div>
|
||||
|
||||
{{!-- label --}}
|
||||
<div>{{localize 'DS4.EffectLabel'}}</div>
|
||||
<div>{{localize 'DS4.EffectName'}}</div>
|
||||
|
||||
{{!-- control buttons placeholder --}}
|
||||
<div></div>
|
||||
|
|
Loading…
Reference in a new issue