RCInet / LastEpoch_Mods

MelonLoader Mods for LastEpoch
42 stars 15 forks source link

Setting Blessings to 5 results in Null Reference Exception on reward panel #15

Open Silver-D opened 1 month ago

Silver-D commented 1 month ago

Setting Blessings to 5 when in Normal results in empty slots. MelonLoader throas a NULL Reference Exception for PlayerFinder.getPlayerActor().generateItems.initialiseRandomItemData() in Monoliths_BlessingSlots.cs

Been debugging all through the night, and all I found is that in Monoliths_BlessingSlots.cs

foreach (System.Int32 b in Run.timeline.difficulties[difficulty].anySlotBlessings) { result.Add((byte)b); }

that produces garbage values. As in, anySlotBlessings/otherSlotBlessings contains blessing_ids that are not actual blessing_ids that exist, making initialiseRandomItemData() throw a NULL exception.

I wasn't able to fix it, I was looking at the LE.dll through dnSpy and I have no idea why anySlotBlessings doesn't contain blessing IDs anymore.

Or I'm way off. Maybe it contains them, but MonolithRun is corrputed somehow, or is getting the wrong instance. Or Maybe it's because I'm in Normal and the difficulty list is wrong. I can't tell. (The amount of values in those Lists are correct -- for example, in the Black Sun, the combined list has 12 values, which is correct, except the resulting values aren't really blessing ids.. for some reason?)

Anyway, it's not working and I have no idea how to fix. :(

Silver-D commented 1 month ago

I figured it out. I'm in Normal, and it just so happens that I turned on the Blessing Slots: 5 option while doing Black Sun.

This is how it's breaking -- MonlithRun instance gotten from MonolithRun_calculateIncreasedRarityAndExperienceFromMods is getting the instance for Blood, Frost, and Death -- a timeline after Black Sun. Since i just finished Black Sun, it's probably getting the instance for the next Monolith at the time of its invocation instead.

This is why the values in anySlotBlessings/otherSlotBlessings in GetAllTimelineBlessings() didn't match those that you're supposed to get in Black Sun.

Now, PlayerFinder.getPlayerActor().generateItems.initialiseRandomItemData is broken either way. I replaced it with ItemDataUnpacked CreateBlessing(int blessing_id) from Character_Blessings.cs so it looks like this

b_container.container.TryAddItem(CreateBlessing(new_blessing_id), 1, Context.DEFAULT);

I'm going to fix this thing for myself -- will try and find a way to get the proper MonolithRun -- the one I currently finished, not the upcoming Monolith.

The issue was two-fold, and it confused the hell out of me:

1) GetAllTimelineBlessings() was returning blessing IDs those that did not match the Monolith that I just currently finished. 2) initialiseRandomItemData() is broken, and it lead me to believe that it was failing because it was being supplied non-existing blessing ids -- though they do exist and they are correct -- just not for Black Sun. The Blessing list is generated for the Monolith that comes after. (Probably. Either, way, MonolithRun isn't pointing to the proper expected instance). 3) Also. The "extra" Blessings that you add this way should be marked as "Discovered". They currently do not get added to your pool of Discovered Blessings when they are generated this way.

But I hope I saved you a little bit of debugging, if you care to properly fix the issue in your code.

Silver-D commented 1 month ago

Oh my gosh darn it. I fixed it. I fixed everything. I am so very sorry to spam you, I was just so excited to hack at this game. I really apologize .

This is all you need for unlocking Blessing Slots in Monoliths_BlessingSlots.cs

[HarmonyPatch(typeof(LE.Gameplay.Monolith.MonolithUIManager), "OpenBlessingsRewardPanelAfterDelay")]
public class MonolithUIManager_OpenBlessingsRewardPanelAfterDelay
{
    [HarmonyPrefix]
    static void Prefix(Cysharp.Threading.Tasks.UniTaskVoid __result, TimelineID __0, int __1, float __2, ref int __3)
    {
        if (CanRun())
        {
            int blessingSlots = (int)Save_Manager.instance.data.Scenes.Monoliths.BlessingSlots;

            // in case we unlocked more slots due to difficulty/corruption, but we chose a lesser value for a minimum
            if (__3 > blessingSlots) { blessingSlots = __3; }

             __3 = blessingSlots;
        }
    }
}

[HarmonyPatch(typeof(ItemContainersManager), "populateBlessingOptions")]
public class ItemContainersManager_populateBlessingOptions
{
    [HarmonyPrefix]
    static void Prefix(ref ItemContainersManager __instance, TimelineID __0, int __1, ref int __2, ref int __3)
    {
        if (CanRun())
        {
            int numStandardBlessings = 3;
            int numExtraBlessings    = (int)(Save_Manager.instance.data.Scenes.Monoliths.BlessingSlots - numStandardBlessings);

            // in case we unlocked more slots due to difficulty/corruption, but we chose a lesser value for a minimum
            if (__3 > numExtraBlessings) { numExtraBlessings = __3; }

            __2 = numStandardBlessings;
            __3 = numExtraBlessings;
        }
    }
}

That is all! Well, and the CanRun() method of course. No RunManagers, no Lists, no nothing. The Game takes care of everything for you.

I'm sorry again. Have a very goood day!

RCInet commented 1 month ago

Code updated with this fix, thanks