# Work Summary - 2026-02-05

## Feature: MysticAssistantRedux 0.1.2 - Apple Arcade Content

### Current State: TESTED - MOSTLY WORKING

Apple Arcade exclusive content added to Mystic Assistant shop.

| Content | Status | Notes |
|---------|--------|-------|
| **Fleece 680** | ✅ WORKING | Custom embedded icon, localized name |
| **Apple Clothing** | ✅ WORKING | "Garden Dress" & "Outdoor Jacket" |
| **Apple Decorations** | ❓ UNTESTED | May already be unlocked in saves |
| **Apple Follower Skins** | ❌ BROKEN | Spine skeleton missing skin definitions |

### What Works

#### 1. Fleece 680 ("Fleece of the Verdant") - FULLY WORKING
- Purchasable from shop
- Appears in Temple altar fleece menu (regular section, not DLC)
- Equippable and functional
- Localization: `TarotCards/Fleece680/Name` = "Fleece of the Verdant"
- **Custom icon** loaded from embedded resource (`assets/Fleece_680.png`)
- Icon appears in both shop and altar menus

#### 2. Apple Clothing - FULLY WORKING
- `Apple_1` = **"Garden Dress"**
- `Apple_2` = **"Outdoor Jacket"**
- Appears in **Special Clothing** section of outfit menu
- Renders correctly on followers
- Added to Tailor on purchase so it's immediately available (not greyed out)

#### 3. Apple Decorations - UNTESTED
- May already be unlocked by default in PC version
- Shop shows 0 stock if already unlocked

### What Does NOT Work

**Apple Follower Skins (Apple_1 through Apple_5)** - BROKEN, STILL IN SHOP (purchasable but non-functional)

**Root Cause:**
- `Follower.atlas` contains Apple textures but `Follower.skel` lacks skin definitions
- Runtime error: `Skin not found: Apple_1`
- Cannot fix without modifying binary `.skel` file

### Session 2 Changes (Continued Development)

#### 1. Removed Fleece Icon Extraction Debug Feature
- Deleted `UIPlayerUpgradesMenuController_ExtractFleeceIcons_Postfix` method
- No longer extracts PNGs to disk

#### 2. Added Custom Fleece 680 Icon from Embedded Resource
- Added `assets/Fleece_680.png` as `<EmbeddedResource>` in csproj
- `LoadFleece680Sprite()` loads PNG from assembly manifest resource
- Sprite cached after first load
- Applied to both `FleeceIconMapping.GetImage` (altar) and `InventoryIconMapping.GetImage` (shop)

#### 3. Cleaned Up Harmony Field Injection
- Changed `____params`, `____category`, etc. to `__instance._params`, `__instance._category`
- Assemblies are publicized, no need for Harmony field injection

#### 4. Fixed Fleece Unlock Notification
- Changed from `$"Fleece {fleeceId} unlocked!"` to localized name
- Now shows: "Fleece of the Verdant unlocked!"

#### 5. Fixed Shop Label for Fleece
- Changed `"Fleece (Apple)"` to `LocalizationManager.GetTranslation("TarotCards/Fleece680/Name")`

#### 6. Apple Clothing - Full Implementation

**Problem:** Clothing enum values exist but no `ClothingData` ScriptableObjects in PC version.

**Solution:** Inject `ClothingData` at runtime:
```csharp
[HarmonyPostfix]
[HarmonyPatch(typeof(TailorManager), nameof(TailorManager.ClothingData), MethodType.Getter)]
public static void TailorManager_ClothingData_Postfix(ref ClothingData[] __result)
```

**ClothingData fields set:**
- `ClothingType` = enum value
- `SpecialClothing` = true (appears in Special section, no crafting required)
- `Variants` = `["Clothes/{clothingType}"]`
- `SlotAndColours` = one empty entry (prevents index out of range crash)

**Availability fix:** Call `TailorManager.AddClothingToTailor()` on purchase so clothing isn't greyed out.

#### 7. Added Debug Logging for Outfit Menu
- Logs all clothing items when outfit menu opens
- Shows: type, localized name, unlocked status, available status
- Specifically highlights Apple clothing with all flags

### Files Modified (Session 2)

| File | Changes |
|------|---------|
| `MysticAssistantRedux.csproj` | Added `<EmbeddedResource>` for `assets/Fleece_680.png` |
| `GlobalUsings.cs` | Added `System.IO` (for MemoryStream) |
| `InventoryInfo.cs` | Fleece shop label uses localized string |
| `Patches/MysticShopPatches.cs` | See detailed list below |

**MysticShopPatches.cs changes:**
- Removed `_fleeceIconsExtracted` and extraction method
- Added `_fleece680Sprite` cache and `LoadFleece680Sprite()` loader
- Updated `FleeceIconMapping_GetImage_Postfix` to use custom sprite
- Added `InventoryIconMapping.GetImage` postfix for shop icon
- Cleaned up `____` field injection → `__instance._field`
- Removed empty `FleeceSelectionMenu_OnHideCompleted_Postfix`
- Fixed `ShowUnlockedFleeces` to use localized name
- Added Apple clothing injection region:
  - `TailorManager_ClothingData_Postfix` - injects ClothingData
  - `_appleClothingInjected` tracking flag
- Added `AddClothingToTailor` call in purchase handler
- Added debug logging region:
  - `UIAppearanceMenuController_Outfit_OnShowStarted_Postfix`

### Key Technical Insights (Session 2)

1. **Embedded resources:** Use `<EmbeddedResource>` in csproj, access via `Assembly.GetManifestResourceStream("Namespace.folder.file")`

2. **ClothingData injection:** Game loads `ClothingData[]` from Resources, but we can append to the array in a postfix patch

3. **SlotAndColours crash:** `ConfigureFollowerOutfit` accesses `SlotAndColours[index]` - must have at least one entry or crashes with index -1

4. **Clothing availability:** Game considers clothing "available" if in Tailor storage or worn by follower. Must call `AddClothingToTailor()` for freshly unlocked clothing.

5. **Two icon systems:** `FleeceIconMapping` (altar menus) vs `InventoryIconMapping` (shop) - need to patch both for consistent icons

### Remaining Work

- [ ] Remove Apple Skins from shop entirely OR add warning they don't work
- [ ] Test Apple Decorations on fresh save
- [ ] Consider adding clothing tooltip (vanilla doesn't have one for clothing, only necklaces)

---

## CultOfQoL: Bug Fixes and Feature Requests

### Source: User Feedback (Feature Requests + Bug Reports)

### Bug Fixes

#### 1. God Tears Double Collection - FIXED

**Problem:** "Collect All God Tears At Once" was collecting two tears when only one was available.

**Root Cause:** Coroutine postfix timing issue. When patching `GiveGodTearIE()` (a coroutine), the Harmony postfix runs when the method returns the IEnumerator, BEFORE the coroutine executes. At that point, `UpgradeSystem.AbilityPoints` hadn't been decremented yet.

**Fix:** Reserve 1 tear for vanilla to handle:
```csharp
var additional = UpgradeSystem.AbilityPoints - 1;  // Reserve 1 for vanilla
if (additional <= 0) return;
Inventory.AddItem((int)InventoryItem.ITEM_TYPE.GOD_TEAR, additional);
UpgradeSystem.AbilityPoints = 1;  // Leave exactly 1 for vanilla coroutine
```

**File:** `Patches/Systems/FastCollectingPatches.cs` (lines 853-868)

#### 2. Mass Level Up Not Leveling Followers - FIXED

**Problem:** Mass level up was spawning souls but not actually incrementing follower levels. Followers stayed "stuck at 1 XP away from level up until a sermon."

**Root Cause:** UI state desync. The code was setting `Stats.Adoration = 0` and `++Info.XPLevel` but not syncing the UI:
- Missing `BarController.SetBarSize(0f)` to update the adoration bar
- Missing `AdorationUI.SetObjects()` to refresh the level-up indicator
- Missing `levelText.text` update to show the new level number

**Fix:** Added UI synchronization + validation:
```csharp
if (!follower.Brain.CanLevelUp()) return;  // Validate first
// ... apply effects ...
follower.AdorationUI?.BarController?.SetBarSize(0f, false, true);
follower.AdorationUI?.SetObjects();

// Update level text (SetObjects doesn't touch levelText)
if (follower.AdorationUI?.levelText != null)
{
    follower.AdorationUI.levelText.text = follower.Brain.Info.XPLevel.ToNumeral();
}
```

**Note:** `ToNumeral()` respects the accessibility setting - returns Arabic numerals ("28") when Roman numerals are disabled, or Roman ("XXVIII") when enabled.

**File:** `Patches/Followers/MassActionEffects.cs`

### New Features

#### 3. Exclude Grass From Seed Deposit - IMPLEMENTED

**Request:** "When I choose 'deposit all seeds' into the seed storage, it's also depositing Grass... my farm ends up becoming a grass farm!"

**Solution:** Added transpiler to filter `InventoryItem.AllSeeds` in `Interaction_SiloSeeder.OnSecondaryInteract`:
```csharp
public static List<InventoryItem.ITEM_TYPE> GetFilteredSeeds()
{
    var seeds = InventoryItem.AllSeeds;
    if (Plugin.ExcludeGrassFromSeedDeposit.Value)
        seeds.Remove(InventoryItem.ITEM_TYPE.GRASS);
    return seeds;
}
```

**Config:** `── Structures ──` → `Exclude Grass From Seed Deposit` (default: false)

**Files Modified:**
- `Configs.cs` - Added property
- `Plugin.cs` - Added binding (Order 16 in Structures section)
- `Patches/Structures/StructurePatches.cs` - Transpiler + helper method

#### 4. Elder Light Work Mode - IMPLEMENTED

**Request:** "An option somewhere between vanilla elder behavior and working elders, to allow elders to worship at the shrine or cook in the kitchen but not do more physically demanding work like mining or body disposal."

**Solution:** Changed `MakeOldFollowersWork` (bool) to `ElderWorkMode` (enum) with three options:

| Mode | Behavior |
|------|----------|
| `Disabled` | Elders don't work (vanilla) |
| `AllWork` | Elders perform all tasks |
| `LightWorkOnly` | Elders only do light tasks |

**Heavy Work Tasks Excluded in Light Mode:**
- Physical: `Build`, `Farm`, `ClearRubble`, `ClearWeeds`, `Forage`
- Mining: `MineBloodStone`, `MineRotstone`, `ResourceStation`
- Body Handling: `BuryBody`, `Undertaker`
- Industrial: `Blacksmith`, `Refinery`, `Lumberjack`, `ChopTrees`
- Sanitation: `Janitor`, `CleanWaste`, `Handyman`

**Light Work Allowed:**
- Spiritual: `Pray`, `PassivePray`, `CollectDevotion`
- Culinary: `Cook`, `Brew`
- Intellectual: `Research`, `Study`, `AttendTeaching`
- Animal Care: `Rancher`, `Fisherman`

**Config:** `── Followers ──` → `Elder Work Mode**` (default: Disabled)

**Files Modified:**
- `Configs.cs` - Added `ElderWorkMode` enum, changed property type
- `Plugin.cs` - Updated binding with description
- `Patches/Followers/FollowerPatches.cs` - Added `HeavyWorkTasks` HashSet, updated all patches

### Key Technical Insights

1. **Coroutine Postfix Timing:** Harmony postfixes on IEnumerator methods run at method return, not coroutine completion. Solution: Account for what vanilla will do and adjust values accordingly.

2. **Transpilers vs Prefixes:** Transpilers are preferred when modifying iteration behavior (like filtering a list) because they preserve all surrounding game logic. The grass exclusion uses a transpiler to swap `AllSeeds` with a filtered version.

3. **UI State Sync:** When directly modifying game state (like follower levels), always check if there are associated UI elements that need updating. The game often has UI components listening to state changes.

### Files Summary

| File | Changes |
|------|---------|
| `Configs.cs` | +`ElderWorkMode` enum, +`ExcludeGrassFromSeedDeposit`, changed `MakeOldFollowersWork` → `ElderWorkMode` |
| `Plugin.cs` | Updated bindings for new configs |
| `Patches/Systems/FastCollectingPatches.cs` | Fixed God Tears double collection |
| `Patches/Followers/FollowerPatches.cs` | Elder work mode with task filtering |
| `Patches/Followers/MassActionEffects.cs` | Fixed mass level up UI sync |
| `Patches/Structures/StructurePatches.cs` | Grass seed exclusion transpiler |

---

## TraitControl 0.1.6: Reeducation Deterministic Seed Fix

### Source: Bug Report

**User Report:** "I can't get new traits from doing the 'trait re-roll via re-education'. I keep getting the same 3 to 4 traits over and over again."

### Root Cause Analysis

The `RandomisedTraits()` method in the game uses seeded randomization:

```csharp
public List<FollowerTrait.TraitType> RandomisedTraits(int seed)
{
    UnityEngine.Random.InitState(seed);  // Deterministic!
    ...
}
```

TraitControl was calculating the seed as:
```csharp
var seed = followerInfo.ID + TimeManager.CurrentDay;
```

**Problem:** Same follower + Same in-game day = Same seed = Same traits every time!

Re-educating the same follower multiple times on the same day always produced identical results because the seed never changed.

### Fix

Added `Environment.TickCount` for entropy:
```csharp
var seed = followerInfo.ID + TimeManager.CurrentDay + Environment.TickCount;
```

This ensures each re-education attempt (even milliseconds apart) produces different traits.

### Files Modified

| File | Change |
|------|--------|
| `TraitControl/Patches/TraitWeights.cs` | Added `Environment.TickCount` to seed in both `WrapReeducateRoutine` and `Reindoctrinate_Prefix` |
| `TraitControl/TraitControl.csproj` | Version → 0.1.6 |
| `TraitControl/Plugin.cs` | PluginVer → 0.1.6 |
| `Thunderstore/traitcontrol/manifest.json` | version_number → 0.1.6 |
| `Thunderstore/traitcontrol/CHANGELOG.md` | Added 0.1.6 entry |

### Related Investigation: Doctrine Cult Traits (Not a Bug)

A separate report about Fertility/Allegiance traits appearing on followers was investigated and determined to be **expected game behavior**:

- The game has two separate trait systems: **Individual Traits** (2-3 per follower) and **Cult Traits** (from doctrines, apply to ALL followers)
- Cult traits appear in a dedicated UI section on every follower
- TraitControl already filters cult traits from individual trait assignment (lines 554-563)
- No code changes needed

---

## TraitControl 0.1.6: Exorcism Altar Infinite Loop Fix

### Source: Bug Report

**User Report:** Game freezes when trying to add traits via Exorcism Altar. Log shows repeated "Follower will receive fewer traits than configured minimum" warnings. UI displayed "Traits/None".

### Root Cause Analysis

The Exorcism Altar (Trait Manipulator) calls trait generation methods on existing followers:
- **Shuffle**: Calls `FollowerInfo.RandomisedTraits()`
- **Add**: Calls `FollowerInfo.CompletelyNewRandomisedTraits()`

Both call `GetStartingTrait()` / `GetRareTrait()` (patched by TraitControl) in a do-while loop.

**Problem:** TraitControl's `TraitsAssignedThisSession` HashSet was only reset when:
1. Creating a new follower (`FollowerBrain` constructor)
2. Re-indoctrinating
3. Re-educating

The Exorcism Altar doesn't go through these paths, so the session accumulated traits from ALL previous follower creations. After enough followers were created (86+ traits), the entire trait pool was exhausted.

**The Infinite Loop:**
1. `GetStartingTrait()` returns `None` (all traits filtered out)
2. Vanilla's do-while adds `None` to the result list
3. Second iteration: `traitTypeList.Contains(None)` is TRUE
4. Loop condition always true → infinite loop

### Fix

Added two Harmony prefix patches to reset session tracking before trait generation:

```csharp
[HarmonyPrefix]
[HarmonyPatch(typeof(FollowerInfo), nameof(FollowerInfo.RandomisedTraits))]
private static void RandomisedTraits_Prefix() => ResetSessionTracking();

[HarmonyPrefix]
[HarmonyPatch(typeof(FollowerInfo), nameof(FollowerInfo.CompletelyNewRandomisedTraits))]
private static void CompletelyNewRandomisedTraits_Prefix() => ResetSessionTracking();
```

### Files Modified

| File | Change |
|------|--------|
| `TraitControl/Patches/TraitWeights.cs` | Added `RandomisedTraits_Prefix` and `CompletelyNewRandomisedTraits_Prefix` patches |
| `Thunderstore/traitcontrol/CHANGELOG.md` | Added fix to 0.1.6 entry |

---

## TraitControl 0.1.6: Ex-Bishop (BishopOfCult) Trait Support

### Source: Feature Request

**User Request:** "I would like to have [Ex-Bishop] as one of the options for the unique trait and guarantee. What the trait does is basically it prevents followers with this trait from dissenting against the cult, no matter how low the faith goes."

### Implementation

Added `BishopOfCult` (displayed as "Ex-Bishop") to the unique trait system, following the same pattern as existing unique traits (Immortal, Disciple, etc.).

**What It Does:**
- Followers with this trait will **never become dissenters**, regardless of faith level
- This is vanilla game behavior - `CultFaithManager.cs:408` explicitly checks `!followerBrain.Info.HasTrait(FollowerTrait.TraitType.BishopOfCult)` before making a follower dissent
- Normally only granted when converting enemy bishops during crusades

**New Config Options:**
- **Include Ex-Bishop** - Allows the trait to appear in trait pools (default: false)
- **Guarantee Ex-Bishop** - New followers will always receive this trait (default: false)

### Files Modified

| File | Change |
|------|--------|
| `TraitControl/Configs.cs` | Added `IncludeBishopOfCult` and `GuaranteeBishopOfCult` config properties |
| `TraitControl/Plugin.cs` | Added config bindings, removed unconditional exclusion, updated visibility switch |
| `TraitControl/Patches/TraitWeights.cs` | Made BishopOfCult filtering conditional in `RefreshAllTraitsList`, `PopulateGuaranteedTraits`, and `GetFilteredTraits` |
| `Thunderstore/traitcontrol/CHANGELOG.md` | Added feature to 0.1.6 entry |
| `Thunderstore/traitcontrol/README.md` | Updated Unique Traits list, removed from Always Excluded, added config example |
| `Thunderstore/traitcontrol/nexusmods_description.txt` | Same updates in BBCode format |

### Key Technical Details

- `BishopOfCult` is already in `FollowerTrait.UniqueTraits` (only one follower can have it) and `FollowerTrait.GoodTraits` (counts as positive)
- Existing validation logic handles single-use restrictions automatically
- No patching of dissent logic needed - the trait's power is built into vanilla game

---

## TraitControl 0.1.6: ChosenOne Event Trait Fix

### Source: User Bug Report

**User Report:** "You can roll into the 'Chosen Child' trait, which probably shouldn't be in the pool (I have disabled the 'Event Traits' and I think it should probably be in that pool)"

### Root Cause Analysis

`ChosenOne` is categorized as a DLC trait in the game's `FollowerTrait.MajorDLCTraits`, but it's actually granted through the **PureBlood breeding event chain**:

1. Parent has `PureBlood` trait
2. Child gets `PureBlood_1`
3. Grandchild gets `PureBlood_2`
4. Great-grandchild gets `PureBlood_3`
5. Great-great-grandchild gets `ChosenOne`

This makes it an event-driven trait that should be filtered out when "Include Event Traits" is disabled.

### Fix

Added `ChosenOne` to the `StoryEventTraits` HashSet in `Plugin.cs`:

```csharp
FollowerTrait.TraitType.PureBlood_3,
FollowerTrait.TraitType.ChosenOne, // Granted to 4th generation child in PureBlood breeding chain
FollowerTrait.TraitType.FreezeImmune
```

---

## TraitControl 0.1.6: Mutated Followers Reeducation Fix

### Source: User Bug Report

**User Report:** "I can't reeducate certain followers... It seems to be the Rot followers, but even after I healed them, I can't"

### Root Cause Analysis

The game's `FollowerCommandGroups.DefaultCommands()` uses a priority-based routing system:

```csharp
// Line 44 in FollowerCommandGroups.cs
return follower.Brain.HasTrait(FollowerTrait.TraitType.Mutated)
    ? FollowerCommandGroups.MutatedCommands(follower)
    : FollowerCommandGroups.NormalCommands(follower);
```

TraitControl was only patching `NormalCommands()` to add the Reeducate command:

```csharp
[HarmonyPatch(typeof(FollowerCommandGroups), nameof(FollowerCommandGroups.NormalCommands))]
private static void FollowerCommandGroups_NormalCommands_AddReeducate(...)
```

**Problem:** Followers with the `Mutated` trait (Rot followers) get routed to `MutatedCommands()`, not `NormalCommands()`, so the patch never runs for them.

### Fix

Added a second postfix patch for `MutatedCommands()`:

```csharp
[HarmonyPostfix]
[HarmonyPatch(typeof(FollowerCommandGroups), nameof(FollowerCommandGroups.MutatedCommands))]
private static void FollowerCommandGroups_MutatedCommands_AddReeducate(ref List<CommandItem> __result)
{
    if (!Plugin.TraitRerollOnReeducation.Value) return;
    __result.Add(FollowerCommandItems.Reeducate());
}
```

### Key Insight: Command Routing Pattern

The game uses **command routing** based on follower state. Different follower types get different command sets:
- `NormalCommands()` - healthy, non-special followers
- `MutatedCommands()` - followers with Mutated (Rot) trait
- `DissenterCommands()` - dissenters
- `ZombieCommands()` - followers with Zombie trait
- etc.

When adding commands, patches need to target ALL relevant command methods, not just `NormalCommands()`.

### Files Modified

| File | Change |
|------|--------|
| `TraitControl/Plugin.cs` | Added `ChosenOne` to `StoryEventTraits` HashSet |
| `TraitControl/Patches/TraitWeights.cs` | Added `FollowerCommandGroups_MutatedCommands_AddReeducate` patch |
| `Thunderstore/traitcontrol/CHANGELOG.md` | Added both fixes to 0.1.6 entry |

---

## CultOfQoL: Bug Fixes - Refinery Crash & Follower Coroutine Errors

### Source: User Bug Report

Two errors occurring when player is away from the base:

1. **Refinery ArgumentOutOfRangeException** - Crash when refinery task completes
2. **"Coroutine couldn't be started" errors** - Spam when leaving base

### Bug 1: Refinery QueuedRefineryVariants Desync - FIXED

**Error:**
```
ArgumentOutOfRangeException: Index was out of range.
Structures_Refinery.RefineryDeposit()
FollowerTask_Refinery.TaskTick()
SimFollower.Tick()
```

**Root Cause:** The game's `RefineryDeposit()` method assumes `QueuedResources` and `QueuedRefineryVariants` lists are always the same length:
```csharp
this.Data.QueuedResources.RemoveAt(0);
this.Data.QueuedRefineryVariants.RemoveAt(0);  // Crashes if list is shorter
```

These lists can desync (vanilla game bug) causing `ArgumentOutOfRangeException` when the simulation runs `RefineryDeposit()`.

**Fix:** Added prefix patch to sync lists before removal:
```csharp
[HarmonyPrefix]
[HarmonyPatch(typeof(Structures_Refinery), nameof(Structures_Refinery.RefineryDeposit))]
public static void Structures_Refinery_RefineryDeposit_Prefix(Structures_Refinery __instance)
{
    while (__instance.Data.QueuedRefineryVariants.Count < __instance.Data.QueuedResources.Count)
    {
        __instance.Data.QueuedRefineryVariants.Add(0);
    }
}
```

**File:** `CultOfQoL/Patches/Structures/StructurePatches.cs`

### Bug 2: Follower Coroutine Errors on Location Change - VANILLA BUG (INVESTIGATION ONLY)

**Error:**
```
Coroutine couldn't be started because the game object 'Follower Festor' is inactive!
Coroutine couldn't be started because the game object 'Follower Hagar' is inactive!
```

**Timing:** Errors occur within 2 seconds of leaving the base (entering dungeon).

**Root Cause Investigation:**

1. When player leaves base, follower GameObjects at base become **inactive**
2. Vanilla game systems (weather, seasons, tasks) continue iterating over `Follower.Followers`
3. Multiple places in vanilla code call `follower.StartCoroutine(...)` without checking `activeInHierarchy`
4. Unity throws error when StartCoroutine is called on inactive GameObject

**Key Code Paths Identified:**
- `Follower.cs:1923` - `follower.StartCoroutine(follower.TakeHoodOffRoutine(...))`
- `Follower.cs:2306` - `follower.StartCoroutine(follower.GiveOutfitIE())`
- `FollowerBrain.cs:1457` - `follower.StartCoroutine(this.AddAdorationIE(...))`
- Many `FollowerTask_*.cs` files start coroutines on followers

**Conclusion:** This is a **vanilla game bug**. The errors are harmless log spam - they don't crash the game. The coroutines simply don't start, and the game continues working.

**Potential Fix (NOT YET IMPLEMENTED):** Use `HarmonyFinalizer` patches on methods that call `follower.StartCoroutine()` to catch and suppress the exceptions, or add prefix patches to check `activeInHierarchy` before the coroutine starts.

### Files Modified

| File | Changes |
|------|---------|
| `CultOfQoL/Patches/Structures/StructurePatches.cs` | Added `Structures_Refinery_RefineryDeposit_Prefix` to sync variant lists |

### Key Technical Insights

1. **Simulation runs while away from base:** `SimFollower.Tick()` → `FollowerBrain.Tick()` → `FollowerTask.Tick()` runs even when player is in dungeons

2. **Vanilla game bug pattern:** Game iterates `Follower.Followers` (static list containing all followers) without checking if GameObjects are active

3. **HarmonyFinalizer for exception handling:** To suppress exceptions from vanilla code without modifying the original method, use `HarmonyFinalizer` - it runs after the method (even if it throws) and can modify the exception

---

## MysticAssistantRedux: Apple Decorations Investigation

### Source: Testing Apple Arcade Content

### Status: CONFIRMED BROKEN - Assets Missing from PC Build

### Investigation

Apple decorations (`DECORATION_APPLE_BUSH`, `DECORATION_APPLE_LANTERN`, `DECORATION_APPLE_STATUE`, `DECORATION_APPLE_VASE`, `DECORATION_APPLE_WELL`) were tested after adding Build Menu injection.

**Initial Problem:** Decorations weren't appearing in Build Menu because they weren't in any `DataManager.DecorationsForType()` category list.

**Fix Applied:** Added Harmony postfix to inject Apple decorations into `DecorationType.DLC` list:
```csharp
[HarmonyPostfix]
[HarmonyPatch(typeof(DataManager), nameof(DataManager.DecorationsForType))]
public static void DataManager_DecorationsForType_Postfix(DataManager.DecorationType decorationType, ref List<StructureBrain.TYPES> __result)
{
    if (decorationType == DataManager.DecorationType.DLC)
    {
        foreach (var deco in ExclusiveContent.AppleDecorations)
        {
            if (!__result.Contains(deco))
                __result.Add(deco);
        }
    }
}
```

**Result:** Decorations now appear in Build Menu DLC section. However, when attempting to place them:

```
InvalidKeyException: No Location found for Key=74ef43502ee983e4bb3561e7cffe0326
ArgumentException: The Object you want to instantiate is null.
```

### Root Cause Analysis

The prefab loading chain:
1. `PlacementRegion.PlayRoutine()` calls `TypeAndPlacementObjects.GetByType(structureType)`
2. Returns a `TypeAndPlacementObject` with `Addr_PlacementObject` (AssetReferenceGameObject containing GUID)
3. `PlacementObject` property getter calls `Addressables.LoadAssetAsync(this.Addr_PlacementObject)`
4. **The GUID exists in the addressable catalog metadata but the actual prefab files weren't included in the PC build**

This is the same pattern as the Apple follower skins - the code/metadata exists but the assets don't.

### Apple Arcade Content Status (Updated)

| Content | Status | Reason |
|---------|--------|--------|
| **Fleece 680** | ✅ WORKING | Uses existing render system, just a skin index |
| **Apple Clothing** | ✅ WORKING | Textures exist in Spine atlas |
| **Apple Follower Skins** | ❌ BROKEN | Spine skeleton missing skin definitions |
| **Apple Decorations** | ❌ BROKEN | Addressable prefabs not in PC build |

### Files Modified

| File | Changes |
|------|---------|
| `MysticAssistantRedux/Patches/MysticShopPatches.cs` | Added `DataManager_DecorationsForType_Postfix` to inject Apple decorations into DLC list; added logging |
| `MysticAssistantRedux/InventoryManager.cs` | Added logging for Apple decorations population |
| `MysticAssistantRedux/ExclusiveContent.cs` | Added note about prefabs not existing |

### Potential Future Solutions

1. **Extract from Apple Arcade build** - Would need access to iOS/macOS version
2. **Create substitute prefabs** - Would require Unity asset bundling and injection into addressable system
3. **Mark as unavailable** - Remove from shop or add warning

### Key Technical Insights

1. **Build Menu category lists:** `AestheticCategory.Populate()` only creates `BuildMenuItem`s for decorations returned by `DataManager.DecorationsForType()` - decorations not in any list are invisible

2. **Addressable asset pattern:** Game uses Unity Addressables with GUID references. The catalog can contain references to assets that don't exist in the build (common for platform-specific DLC)

3. **TypeAndPlacementObjects:** Serialized list in scene that maps `StructureBrain.TYPES` to placement prefabs. Entry exists for Apple decorations but prefab is missing
