From bdb17cfac7685051a78e9bef17db5bbdd7178e2d Mon Sep 17 00:00:00 2001
From: Johannes Loher <johannes.loher@fg4f.de>
Date: Sat, 5 Nov 2022 02:42:01 +0100
Subject: [PATCH] fix: apply effects to embedded items after all embedded items
 have been prepared

---
 src/documents/actor/actor.ts | 31 +++++++++++++++++++++++++++----
 src/documents/item/item.ts   | 18 ------------------
 2 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/src/documents/actor/actor.ts b/src/documents/actor/actor.ts
index 800885b4..0b1be492 100644
--- a/src/documents/actor/actor.ts
+++ b/src/documents/actor/actor.ts
@@ -28,13 +28,11 @@ declare global {
  * The Actor class for DS4
  */
 export class DS4Actor extends Actor {
-    initialized: boolean | undefined;
-
     override prepareData(): void {
-        this.initialized = true;
         this.data.reset();
         this.prepareBaseData();
         this.prepareEmbeddedDocuments();
+        this.applyActiveEffectsToItems();
         this.applyActiveEffectsToBaseData();
         this.prepareDerivedData();
         this.applyActiveEffectsToDerivedData();
@@ -118,8 +116,33 @@ export class DS4Actor extends Actor {
         return;
     }
 
+    /**
+     * Apply active effects to items.
+     *
+     * @remarks
+     * Talents are handled before all other item types, because if the total rank of a talent is affected by any
+     * effects, that affects how many times effects provided by this talent need to be applied. At the moment, there is
+     * no special ordering among talents. This means that having a talents that provide effects that adjust the total
+     * rank of talents can lead to nondeterministic behavior and thus must be avoided.
+     */
+    applyActiveEffectsToItems(): void {
+        /* Handle talents before all other item types, because their rank might be affected, which in turn affects how
+           many times effects provided by that talent are applied */
+        for (const item of this.itemTypes.talent) {
+            this.applyActiveEffectsToItem(item);
+        }
+        for (const item of this.items) {
+            if (item.type === "talent") continue;
+            this.applyActiveEffectsToItem(item);
+        }
+    }
+
+    protected applyActiveEffectsToItem(item: DS4Item) {
+        item.overrides = {};
+        DS4ActiveEffect.applyEffetcs(item, this.itemEffects(item));
+    }
+
     applyActiveEffectsToBaseData(): void {
-        // reset overrides because our variant of applying active effects does not set them, it only adds overrides
         this.overrides = {};
         DS4ActiveEffect.applyEffetcs(
             this,
diff --git a/src/documents/item/item.ts b/src/documents/item/item.ts
index f1655ba0..50015e37 100644
--- a/src/documents/item/item.ts
+++ b/src/documents/item/item.ts
@@ -4,7 +4,6 @@
 // SPDX-License-Identifier: MIT
 
 import { getGame } from "../../utils/utils";
-import { DS4ActiveEffect } from "../active-effect";
 
 import type { ItemType } from "./item-data-source";
 
@@ -28,23 +27,6 @@ export class DS4Item extends Item {
     /** An object that tracks the changes to the data model which were applied by active effects */
     overrides: Record<string, unknown> = {};
 
-    override prepareData() {
-        this.data.reset();
-        this.prepareBaseData();
-        this.prepareEmbeddedDocuments();
-        this.prepareDerivedData();
-        this.applyActiveEffects();
-    }
-
-    applyActiveEffects(): void {
-        if (!this.actor?.initialized) {
-            return;
-        }
-
-        this.overrides = {};
-        DS4ActiveEffect.applyEffetcs(this, this.actor.itemEffects(this));
-    }
-
     override prepareDerivedData(): void {
         this.data.data.rollable = false;
     }