From 81adfb7ffd89531a6367a5103f36e0cf30b4d397 Mon Sep 17 00:00:00 2001 From: LeRatierBretonnier Date: Thu, 4 Jun 2026 23:14:38 +0200 Subject: [PATCH] fix(actor): prevent double ActiveEffect application in prepareData cycle - Move wound data initialization to prepareBaseData() (before effects are applied) - Initialize combatStatus in prepareBaseData() to prevent undefined errors - Add protection against recursive effect application in prepareEmbeddedDocuments() - This prevents the 'ActiveEffect application phase has already completed' error The error occurred because modify data in prepareDerivedData() (like combatStatus) could trigger observers that try to re-apply effects during the same cycle. By initializing all required data in prepareBaseData() and protecting prepareEmbeddedDocuments() from recursive calls, we ensure effects are applied exactly once per preparation cycle. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- module/documents/actor.mjs | 71 ++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 6d43860..b4f3606 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -6,19 +6,37 @@ export class VermineActor extends Actor { /** @override */ - prepareData() { - // Prepare data for the actor. Calling the super version of this executes - // the following, in order: data reset (to clear active effects), - // prepareBaseData(), prepareEmbeddedDocuments() (including active effects), - // prepareDerivedData(). + prepareBaseData() { + // Data modifications in this step occur before processing embedded + // documents or derived data. // Initialize wound data to prevent undefined errors with active effects if (!this.system.minorWound) this.system.minorWound = { value: 0, min: 0, max: 5, threshold: 1 }; if (!this.system.majorWound) this.system.majorWound = { value: 0, min: 0, max: 4, threshold: 4 }; if (!this.system.deadlyWound) this.system.deadlyWound = { value: 0, min: 0, max: 2, threshold: 8 }; - super.prepareData(); + // Initialize combatStatus to prevent errors + if (!this.system.combatStatus) { + this.system.combatStatus = { difficulty: "9", label: "Passif" }; + } + if (this.type == 'character') { + + } + } + + /** @override */ + prepareEmbeddedDocuments() { + // Prevent recursive effect application + // Foundry V11+ can sometimes call this multiple times in a preparation cycle + if (this._preparingEffects) return; + this._preparingEffects = true; + + try { + super.prepareEmbeddedDocuments(); + } finally { + delete this._preparingEffects; + } } /** @override */ @@ -86,20 +104,37 @@ export class VermineActor extends Actor { } prepareCombatStatus() { // Ensure combatStatus exists (defined in base template) - if (!this.system.combatStatus) return; + if (!this.system.combatStatus) { + this.system.combatStatus = { difficulty: "9", label: "Passif" }; + return; + } + + // Ensure difficulty exists + if (!this.system.combatStatus.difficulty) { + this.system.combatStatus.difficulty = "9"; + } //combat initiative reaction difficulty - switch (parseInt(this.system.combatStatus.difficulty)) { - case 5: this.system.combatStatus.label = "Offensif"; - break; - case 7: this.system.combatStatus.label = "Actif"; - break; - case 9: this.system.combatStatus.label = "Passif"; - break; - default: - this.system.combatStatus.label = "Passif"; - this.system.combatStatus.difficulty = "9"; - break; + const difficulty = parseInt(this.system.combatStatus.difficulty) || 9; + + // Only update if values are different to avoid triggering unnecessary updates + const currentLabel = this.system.combatStatus.label; + let newLabel = "Passif"; + + switch (difficulty) { + case 5: newLabel = "Offensif"; break; + case 7: newLabel = "Actif"; break; + case 9: newLabel = "Passif"; break; + } + + // Only update if label changed + if (currentLabel !== newLabel) { + this.system.combatStatus.label = newLabel; + } + + // Only update difficulty if it was undefined or invalid + if (!this.system.combatStatus.difficulty || isNaN(parseInt(this.system.combatStatus.difficulty))) { + this.system.combatStatus.difficulty = "9"; } }