From 692d311558c3101dfdc9628abcf1ac6a0ea9160a Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Sun, 25 Jun 2023 14:33:18 +0200 Subject: [PATCH] fix: make ActiveEffects work properly --- lang/de.json | 12 ++-- lang/en.json | 8 +-- src/apps/actor/base-sheet.js | 4 +- src/documents/active-effect.js | 60 +++++++---------- src/documents/actor/actor.js | 64 +++++++++++++++---- system.json | 8 +-- .../active-effect/active-effect-config.hbs | 32 ++++------ .../actor/components/effect-list-entry.hbs | 8 +-- .../actor/components/effect-list-header.hbs | 8 +-- .../item/components/effect-list-entry.hbs | 4 +- .../item/components/effect-list-header.hbs | 2 +- 11 files changed, 112 insertions(+), 98 deletions(-) diff --git a/lang/de.json b/lang/de.json index 0b264e8a..c135f6ed 100644 --- a/lang/de.json +++ b/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", diff --git a/lang/en.json b/lang/en.json index f7e43014..918ec800 100644 --- a/lang/en.json +++ b/lang/en.json @@ -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", diff --git a/src/apps/actor/base-sheet.js b/src/apps/actor/base-sheet.js index 1a059acd..0ca15542 100644 --- a/src/apps/actor/base-sheet.js +++ b/src/apps/actor/base-sheet.js @@ -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); diff --git a/src/documents/active-effect.js b/src/documents/active-effect.js index 8410e574..1a0aae84 100644 --- a/src/documents/active-effect.js +++ b/src/documents/active-effect.js @@ -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} 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} - */ - 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} The statuses that are applied by this effect */ static applyEffetcs(document, effetcs, predicate = () => true) { /** @type {Record} */ const overrides = {}; - // Organize non-disabled and -surpressed effects by their application priority - const changesWithEffect = effetcs.flatMap((e) => e.getFactoredChangesWithEffect(predicate)); + /** @type {Set} */ + 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 }); }); } } diff --git a/src/documents/actor/actor.js b/src/documents/actor/actor.js index 9cf91eef..e0e93cb6 100644 --- a/src/documents/actor/actor.js +++ b/src/documents/actor/actor.js @@ -15,6 +15,9 @@ import { isAttribute, isTrait } from "./actor-data-source-base"; * The Actor class for DS4 */ export class DS4Actor extends Actor { + /** @type {Set} */ + 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. */ diff --git a/system.json b/system.json index 128c22a2..d22c4050 100644 --- a/system.json +++ b/system.json @@ -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", diff --git a/templates/sheets/active-effect/active-effect-config.hbs b/templates/sheets/active-effect/active-effect-config.hbs index 8c961fa7..2d42a394 100644 --- a/templates/sheets/active-effect/active-effect-config.hbs +++ b/templates/sheets/active-effect/active-effect-config.hbs @@ -9,7 +9,9 @@ SPDX-License-Identifier: MIT
-

{{ data.label }}

+

+ +

@@ -21,30 +23,19 @@ SPDX-License-Identifier: MIT
- -
- -
- -
-
- -
- -
- {{filePicker target="icon" type="image"}} - -
-
-
- - + {{colorPicker name="tint" value=data.tint}}
+
+ + {{editor descriptionHTML target="description" button=false editable=editable engine="prosemirror" + collaborate=false}} +
+
@@ -63,10 +54,11 @@ SPDX-License-Identifier: MIT {{#if isItemEffect}}
- +
+

{{ labels.transfer.hint }}

{{/if}} diff --git a/templates/sheets/actor/components/effect-list-entry.hbs b/templates/sheets/actor/components/effect-list-entry.hbs index 13dedb69..48126391 100644 --- a/templates/sheets/actor/components/effect-list-entry.hbs +++ b/templates/sheets/actor/components/effect-list-entry.hbs @@ -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}}{{else}}{{/if}} + {{!-- active --}} + {{#if effectData.active}}{{else}}{{/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 --}} -
{{effectData.label}}
+ {{!-- name --}} +
{{effectData.name}}
{{!-- source name --}}
{{effectData.sourceName}}
diff --git a/templates/sheets/actor/components/effect-list-header.hbs b/templates/sheets/actor/components/effect-list-header.hbs index fa433700..113103f2 100644 --- a/templates/sheets/actor/components/effect-list-header.hbs +++ b/templates/sheets/actor/components/effect-list-header.hbs @@ -12,14 +12,14 @@ SPDX-License-Identifier: MIT {{!-- enabled --}}
{{localize 'DS4.EffectEnabledAbbr'}}
- {{!-- effectively enabled --}} -
{{localize 'DS4.EffectEffectivelyEnabledAbbr'}}
+ {{!-- active --}} +
{{localize 'DS4.EffectActiveAbbr'}}
{{!-- icon --}}
- {{!-- label --}} -
{{localize 'DS4.EffectLabel'}}
+ {{!-- name --}} +
{{localize 'DS4.EffectName'}}
{{!-- source name --}}
{{localize 'DS4.EffectSourceName'}}
diff --git a/templates/sheets/item/components/effect-list-entry.hbs b/templates/sheets/item/components/effect-list-entry.hbs index 9d4a323e..c3e0ea41 100644 --- a/templates/sheets/item/components/effect-list-entry.hbs +++ b/templates/sheets/item/components/effect-list-entry.hbs @@ -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 --}} -
{{effectData.label}}
+ {{!-- name --}} +
{{effectData.name}}
{{!-- control button group --}} {{> systems/ds4/templates/sheets/shared/components/control-button-group.hbs documentType="effect" diff --git a/templates/sheets/item/components/effect-list-header.hbs b/templates/sheets/item/components/effect-list-header.hbs index 6b0cc803..8aa48fb9 100644 --- a/templates/sheets/item/components/effect-list-header.hbs +++ b/templates/sheets/item/components/effect-list-header.hbs @@ -12,7 +12,7 @@ SPDX-License-Identifier: MIT
{{!-- label --}} -
{{localize 'DS4.EffectLabel'}}
+
{{localize 'DS4.EffectName'}}
{{!-- control buttons placeholder --}}