# Work Summary - 2026-02-06

## Bugfix: MysticAssistantRedux - Crystal Doctrine Stone -1 Quantity

### Current State: COMPLETE

**User Report:** Crystal Doctrine Stones show -1 quantity in the Mystic Assistant shop for players with the Woolhaven DLC, preventing purchases. Missing winter doctrines.

**Root Cause:** The mod calculated max crystal doctrines using only the hardcoded constant `Interaction_MysticShop.maxAmountOfCrystalDoctrines` (24), but the vanilla game adds 4 more for Woolhaven DLC: `24 + (MAJOR_DLC ? 4 : 0) = 28`. When a DLC player had purchased >24 from the vanilla shop, the stock calculation went negative (`24 - 25 = -1`), and the `== 0` out-of-stock check didn't catch it.

**Fix (4 changes across 2 files):**

| File | Change |
|------|--------|
| `MysticAssistantRedux/InventoryManager.cs` | Constructor: Added `+ (MAJOR_DLC ? 4 : 0)` to max doctrine calculation |
| `MysticAssistantRedux/InventoryManager.cs` | `PopulateShopInventory`: Changed `== 0` to `<= 0` for out-of-stock check |
| `MysticAssistantRedux/InventoryManager.cs` | `ChangeShopStockByQuantity`: Added `Math.Max(0, ...)` clamp |
| `MysticAssistantRedux/Patches/MysticShopPatches.cs` | Purchase cap check: Added DLC offset to match vanilla |

**Changelog Updated:** `Thunderstore/mysticassistantredux/CHANGELOG.md` - Added fix to 0.1.1 entry, updated date to 06/02/2026

---

## Feature: TraitControl 0.1.6 - Safer Trait Replacement

### Current State: COMPLETE

Two user-reported issues addressed with new configuration options.

### Issue 1: Rot Followers Being "Healed"

**User Report:** When "Enable Trait Replacement" is on, Rot (Mutated) followers have their trait removed, which breaks gameplay for users who intentionally want Rot followers for rituals/DLC content.

**Root Cause:** Game classifies `Mutated` as a negative trait, so `IsPositiveTrait(Mutated)` returns false and it gets replaced.

**Solution:** Added "Preserve Rot Followers" config option (default: ON)

**Files Modified:**
- `TraitControl/Configs.cs` - Added `PreserveMutatedTrait` property
- `TraitControl/Plugin.cs` - Bound config with Order 5
- `TraitControl/Patches/NoNegativeTraits.cs` - Added skip check in `ProcessTraitReplacement()`:
```csharp
if (Plugin.PreserveMutatedTrait.Value && trait == FollowerTrait.TraitType.Mutated)
{
    Plugin.L($"\tSkipping Mutated (Rot) trait - preservation enabled");
    continue;
}
```

### Issue 2: Immortal Trait from Golden Skull Necklace Lost

**User Report:** When toggling "Enable Trait Replacement", the Immortal trait granted by the Golden Skull necklace is lost.

**Root Cause:** User expected the feature to only affect NEW followers, but it was processing ALL existing followers. The backup/restore cycle could lose traits granted after the backup was created.

**Initial Attempted Fix (REVERTED):** Refactored backup system to track replacements instead of original traits. This was overly complex and reverted pending more user feedback.

**Final Solution:** Added "Apply To Existing Followers" config option (default: OFF)

**Files Modified:**
- `TraitControl/Configs.cs` - Added `ApplyToExistingFollowers` property
- `TraitControl/Plugin.cs`:
  - Bound config with Order 9 and CustomDrawer for warning popup
  - Updated `UpdateNoNegativeTraits()` to check `ApplyToExistingFollowers` before processing
  - Added `OnApplyToExistingChanged()` handler for toggling the option
  - Added `DrawApplyToExistingToggle()` with confirmation warning UI

**Behavior Change:**
- Default: Trait replacement only affects NEW followers (safe)
- Optional: Enable "Apply To Existing Followers" to also process existing followers
- Warning popup when enabling: "WARNING: This will modify ALL existing followers! Traits from necklaces may be lost on restore."

### Config Order (Trait Replacement Section)

| Order | Setting | Default |
|-------|---------|---------|
| 10 | Enable Trait Replacement | OFF |
| 9 | Apply To Existing Followers | OFF |
| 8 | Use Unlocked Traits Only | ON |
| 7 | Use All Traits Pool | OFF |
| 6 | Prefer Exclusive Counterparts | ON |
| 5 | Preserve Rot Followers | ON |
| 4 | Minimum Traits | 2 |
| 3 | Maximum Traits | 3 |
| 2 | Randomize Traits on Re-indoctrination | OFF |
| 1 | Trait Reroll via Reeducation | OFF |
| 0 | Protect Trait Count on Reroll | ON |

### Documentation Updated

- `Thunderstore/traitcontrol/CHANGELOG.md` - Added 0.1.6 entries
- `Thunderstore/traitcontrol/README.md` - Updated Trait Replacement section and examples
- `Thunderstore/traitcontrol/nexusmods_description.txt` - Same updates in BBCode

### Key Technical Details

**Warning Popup Implementation:**
```csharp
private static bool _showApplyToExistingWarning;

private static void DrawApplyToExistingToggle(ConfigEntryBase entry)
{
    var configEntry = (ConfigEntry<bool>)entry;

    if (_showApplyToExistingWarning)
    {
        GUILayout.Label("WARNING: This will modify ALL existing followers!");
        GUILayout.Label("Traits from necklaces may be lost on restore.");
        // Confirm/Cancel buttons
    }
    else
    {
        // Normal toggle, shows warning before enabling
    }
}
```

**Logic Flow:**
1. `NoNegativeTraits.SettingChanged` → `UpdateNoNegativeTraits()` → checks `ApplyToExistingFollowers.Value`
2. `ApplyToExistingFollowers.SettingChanged` → `OnApplyToExistingChanged()` → processes or restores based on current state
3. `FollowerBrain` constructor postfix → always processes new followers when `NoNegativeTraits.Value` is true

### Files Changed

| File | Changes |
|------|---------|
| `TraitControl/Configs.cs` | +2 properties |
| `TraitControl/Plugin.cs` | +3 methods, config bindings, order adjustments |
| `TraitControl/Patches/NoNegativeTraits.cs` | +1 skip check for Mutated trait |
| `Thunderstore/traitcontrol/CHANGELOG.md` | 0.1.6 entries |
| `Thunderstore/traitcontrol/README.md` | Updated features and examples |
| `Thunderstore/traitcontrol/nexusmods_description.txt` | Same as README |

---

## Feature: MysticAssistantRedux - Fix Apple Follower Skins

### Current State: COMPLETE (untested in-game)

### Problem

The mod's Apple follower skin support was built around the assumption that skins are named `Apple_1` through `Apple_5`. Investigation of `.skel`, `.atlas`, and runtime `WorshipperData.ExportCSV` revealed this was completely wrong:

- The Apple follower forms are actually named **Orangutan, Robin, Trout, Beaver, Lobster** (real animal names)
- There are NO `Apple_1`/`Apple_2`/etc. skin entries in the Spine skeleton — only `Clothes/Apple_1` and `Clothes/Apple_2` (clothing, already working)
- The old code searched for `Skin[0].Skin.Contains("Apple")` which never matched any follower form
- `SetFollowerSkinUnlocked("Apple_1")` → `StripNumbers` → stores `"Apple_"` which matched nothing

### Asset & Runtime Status

| Wiki Name | Real Skin Name | Atlas Textures | .skel Skins | WorshipperData | Renderable? |
|---|---|---|---|---|---|
| Apple_1 | Orangutan | ✅ | ✅ (3 variants) | ✅ (46 colors) | ✅ Yes |
| Apple_2 | Robin | ✅ | ✅ (3 variants) | ✅ (46 colors) | ✅ Yes |
| Apple_3 | Trout | ✅ | ✅ (3 variants) | ✅ (46 colors) | ✅ Yes |
| Apple_4 | Beaver | ✅ | ✅ (3 variants) | ✅ (46 colors) | ✅ Yes |
| Apple_5 | Lobster | ✅ | ✅ (3 variants) | ✅ (46 colors) | ✅ Yes |

### Key Technical Insights

1. **Spine atlas structure:** Atlas entries are `Category/SpriteName` (e.g., `Head/HeadOrangutan_Btm`, `Body/Body_Apple1_Top`). `Apple_` entries in the atlas are all Body/Sleeve = **clothing**, not follower skins. Follower skins use `Head/Head{AnimalName}_*` entries.

2. **Binary .skel analysis:** Python script searched the binary for string references. Found `Clothes/Apple_1` and `Clothes/Apple_2` as skin names, but no standalone `Apple_1`-`Apple_5`. The real skins are `Orangutan`, `Orangutan2`, `Orangutan3`, `Beaver`, `Beaver2`, `Beaver3`, `Lobster`, `Lobster2`, `Lobster3`.

3. **WorshipperData CSV export:** Confirmed all 5 (Orangutan, Robin, Trout, Beaver, Lobster) exist at runtime with 46 color variants each.

4. **No StripNumbers issue:** Real animal names have no digits, so `SetFollowerSkinUnlocked("Orangutan")` stores `"Orangutan"` correctly. No bypass needed.

### Changes Made

**Previous code (from earlier session) replaced:**
- `DiscoverAppleSkinData()` — searched for "Apple" in skin names (never found anything)
- `UnlockAppleSkin()` — complex bypass of `SetFollowerSkinUnlocked`, directly manipulated `FollowerSkinsUnlocked`
- `GetFollowerSkinUnlocked_Postfix` — Harmony postfix to handle `Apple_1` → `Apple_` mismatch
- `GetSkinIndexFromName_Postfix` — Harmony postfix for Title fallback matching

**New code:**

| File | Changes |
|------|---------|
| `ExclusiveContent.cs` | `AppleSkins`: `string[]` → `(string SkinName, bool HasPcAssets)[]` with 5 real animal names |
| `MysticShopPatches.cs` | `ValidateAppleSkins()` — checks which animals exist in WorshipperData at runtime, logs details |
| `MysticShopPatches.cs` | `UnlockAppleSkin()` — simplified to just call `DataManager.SetFollowerSkinUnlocked(skinName)` |
| `MysticShopPatches.cs` | Removed `GetFollowerSkinUnlocked_Postfix` and `GetSkinIndexFromName_Postfix` (no longer needed) |
| `MysticShopPatches.cs` | Added legacy migration: `"Apple_"` in save → replaced with real animal names |
| `InventoryManager.cs` | `PopulateAppleSkins()` — uses real names, skips skins without PC assets |

### Backward Compatibility

If a save has `"Apple_"` in `FollowerSkinsUnlocked` (from old code), `ValidateAppleSkins()` migrates it:
- Removes `"Apple_"`
- Adds all 5 real names (`"Orangutan"`, `"Robin"`, `"Trout"`, `"Beaver"`, `"Lobster"`) via `SetFollowerSkinUnlocked`

---

## Feature: MysticAssistantRedux - Woolhaven DLC Necklaces

### Current State: COMPLETE (untested in-game)

Added 5 Woolhaven DLC necklaces to the Mystic Assistant shop, gated behind `DataManager.Instance.MAJOR_DLC`.

### Necklaces Added

| Necklace | Effect |
|----------|--------|
| `Necklace_Deaths_Door` | Death's Door |
| `Necklace_Winter` | Winter effect (can't equip on snowmen) |
| `Necklace_Frozen` | Prevents followers from freezing |
| `Necklace_Targeted` | Makes followers more likely targeted |
| `Necklace_Weird` | Prevents followers from dying from rot |

### Implementation

Only `InventoryInfo.cs` needed changes. The DLC necklaces are inserted between the base necklaces and the rest of the shop items:

```csharp
// After base necklaces...
if (DataManager.Instance.MAJOR_DLC)
{
    items.Add(CreateTraderItem(InventoryItem.ITEM_TYPE.Necklace_Deaths_Door));
    items.Add(CreateTraderItem(InventoryItem.ITEM_TYPE.Necklace_Winter));
    items.Add(CreateTraderItem(InventoryItem.ITEM_TYPE.Necklace_Frozen));
    items.Add(CreateTraderItem(InventoryItem.ITEM_TYPE.Necklace_Targeted));
    items.Add(CreateTraderItem(InventoryItem.ITEM_TYPE.Necklace_Weird));
}
```

No purchase handler changes needed — the existing `default` case in `GivePlayerBoughtItem` handles all necklaces via `Inventory.ChangeItemQuantity((int)boughtItemType, 1, 0)`. Shop labels use `LocalizationManager.GetTranslation($"Inventory/{itemType}")` which already has localized names for DLC necklaces.

### File Changed

| File | Change |
|------|--------|
| `MysticAssistantRedux/InventoryInfo.cs` | Added 5 DLC necklaces to `GetShopItemTypeList()`, gated behind `MAJOR_DLC` |

---

## Feature: MysticAssistantRedux - Boss Follower Skins

### Current State: COMPLETE (untested in-game)

Added 31 boss follower skins as a new shop item. Boss skins are normally excluded from the game's random skin pools — `SetFollowerSkinUnlocked` already skips `StripNumbers` for names containing "Boss" (DataManager.cs:5748).

### Skins Added

All WorshipperData entries starting with "Boss": Mama Worm, Mama Maggot, Flying Burp Frog, Egg Hopper, Burrow Worm, Mortar Hopper, Scuttle Turret, Spiker, Charger, Beholder 1-6, Spider Jump, Millipede Poisoner, Scorpion, Death Cat, Aym, Baal, Dog 1-6, Rot 1-4.

### Implementation

Follows the same pattern as Apple skins across 4 files:

| File | Changes |
|------|---------|
| `ExclusiveContent.cs` | Added `BossSkins` string array (31 entries) |
| `InventoryManager.cs` | Added `BossSkinType = SPECIAL_WOOL_7`, pool, flags, getters, `PopulateBossSkins()` |
| `InventoryInfo.cs` | Added shop label "Follower Skin (Boss)" and shop list entry |
| `MysticShopPatches.cs` | Added purchase case and icon redirect → `FOUND_ITEM_FOLLOWERSKIN` |

---

## Feature: MysticAssistantRedux - Palworld Skins (Commented Out)

### Current State: COMMENTED OUT

Palworld crossover skins (PalworldOne-Five) exist in WorshipperData but have no atlas textures — they render with body shape/color but missing head features (no eyes). Likely unfinished due to the Palworld lawsuit. Code is preserved but commented out across all 4 files for re-enabling later.

---

## Refactor: Removed EnableAppleArcadeContent Config

### Current State: COMPLETE

Removed the `EnableAppleArcadeContent` config toggle from `Plugin.cs`. Exclusive content (Apple skins, boss skins, clothing, decorations, fleece) is now always available in the shop. Removed all guard clauses (`if (!Plugin.EnableAppleArcadeContent.Value) return;`) from `InventoryManager.cs`, `InventoryInfo.cs`, and `MysticShopPatches.cs`.

---

## Debug: God Tears Function

Added `DebugAddGodTears()` method to `MysticShopPatches.cs` with `[System.Diagnostics.Conditional("DEBUG")]` attribute. Gives 999 God Tears before shop opens in Debug builds only — call site is automatically stripped by the compiler in Release builds.

---

## Documentation Updated

All changelogs, READMEs, and NexusMods descriptions updated for MysticAssistantRedux 0.1.1:
- `Thunderstore/mysticassistantredux/CHANGELOG.md`
- `Thunderstore/mysticassistantredux/README.md`
- `Thunderstore/mysticassistantredux/nexusmods_description.txt`
