using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using HarmonyLib; using Jotunn.Utils; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("fix.permadeath.finalsunset", "FinalSunsetPermadeath", "1.4.1")] public class FinalSunsetPermadeath : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "CreateTombStone")] private static class Patch_CreateTombStone { [HarmonyPrefix] private static void Prefix(Player __instance, ref HashSet __state) { __state = null; if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } __state = new HashSet(); TombStone[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { __state.Add(((Object)array[i]).GetInstanceID()); } KeepAtDeath.Clear(); Inventory inventory = ((Humanoid)__instance).GetInventory(); if (inventory != null) { List allItems = inventory.GetAllItems(); for (int j = 0; j < allItems.Count; j++) { if (allItems[j] != null && allItems[j].m_equipped) { KeepAtDeath.Add(allItems[j]); } } } if (!(MGetAllExtraSlotsItems != null)) { return; } try { if (!(MGetAllExtraSlotsItems.Invoke(null, null) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { ItemData val = (ItemData)((item is ItemData) ? item : null); if (val != null) { KeepAtDeath.Add(val); } } } catch (Exception ex) { Debug.LogWarning((object)("[FinalSunsetPermadeath] ExtraSlots API query failed: " + ex.Message)); } } [HarmonyPostfix] private static void Postfix(Player __instance, HashSet __state) { if (__state == null) { return; } RealDeath = true; TombStone[] array = Object.FindObjectsOfType(); TombStone val = null; for (int i = 0; i < array.Length; i++) { if (!__state.Contains(((Object)array[i]).GetInstanceID())) { val = array[i]; break; } } if ((Object)(object)val == (Object)null) { Debug.Log((object)"[FinalSunsetPermadeath] No new tombstone detected — items may survive"); KeepAtDeath.Clear(); return; } Container component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { KeepAtDeath.Clear(); return; } Inventory inventory = component.GetInventory(); if (inventory == null) { KeepAtDeath.Clear(); return; } List list = new List(inventory.GetAllItems()); int num = 0; int num2 = 0; int num3 = 0; for (int j = 0; j < list.Count; j++) { ItemData val2 = list[j]; if (val2 != null && (KeepAtDeath.Contains(val2) || val2.m_equipped)) { int num4 = ClearBackpackContents(val2); if (num4 > 0) { num3 += num4; } num2++; } else { inventory.RemoveItem(val2); num++; } } if (num3 > 0 && FInvOnChanged != null && FInvOnChanged.GetValue(inventory) is Action action) { action(); } KeepAtDeath.Clear(); Debug.Log((object)("[FinalSunsetPermadeath] Tombstone [id=" + ((Object)val).GetInstanceID() + "]: kept " + num2 + " item(s) (equipped + extra slots) in grave, destroyed " + num + " item(s), wiped " + num3 + " item(s) from backpack(s)")); } } [HarmonyPatch(typeof(Player), "OnDeath")] [HarmonyAfter(new string[] { "shudnal.ExtraSlots", "balrond.astafaraios.BalrondSecondChance" })] private static class Patch_OnDeath { [HarmonyPrefix] private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { RealDeath = false; } } [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !RealDeath) { return; } RealDeath = false; if (FSkillData == null || FSkillLevel == null) { return; } Skills skills = ((Character)__instance).GetSkills(); if ((Object)(object)skills == (Object)null) { return; } object value = FSkillData.GetValue(skills); if (!(value is IDictionary dictionary)) { return; } int num = 0; foreach (object value2 in dictionary.Values) { if (FSkillLevel != null) { FSkillLevel.SetValue(value2, 0f); } if (FSkillAcc != null) { FSkillAcc.SetValue(value2, 0f); } num++; } Debug.Log((object)("[FinalSunsetPermadeath] Reset " + num + " skill(s) to level 0")); } } [HarmonyPatch] private static class Patch_NeutralizeDeathDeleteModifier { private static readonly string[] Suppressed = new string[2] { "deathdeleteitems", "deathdeleteunequipped" }; private static IEnumerable TargetMethods() { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; List list = new List(); MethodInfo[] methods = typeof(ZoneSystem).GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetGlobalKey" && methodInfo.ReturnType == typeof(bool) && methodInfo.GetParameters().Length == 1) { list.Add(methodInfo); } } return list; } private static bool Prefix(object[] __args, ref bool __result) { if (__args == null || __args.Length < 1 || __args[0] == null) { return true; } string a = __args[0].ToString(); for (int i = 0; i < Suppressed.Length; i++) { if (string.Equals(a, Suppressed[i], StringComparison.OrdinalIgnoreCase)) { __result = false; return false; } } return true; } } public const string PluginGUID = "fix.permadeath.finalsunset"; public const string PluginName = "FinalSunsetPermadeath"; public const string PluginVersion = "1.4.1"; internal static bool RealDeath; internal static readonly HashSet KeepAtDeath = new HashSet(); internal static FieldInfo FSkillData; internal static FieldInfo FSkillLevel; internal static FieldInfo FSkillAcc; internal static MethodInfo MGetAllExtraSlotsItems; internal static MethodInfo MGetBackpack; internal static FieldInfo FBackpackStructInv; internal static MethodInfo MItemInfoData; internal static MethodInfo MItemInfoSave; internal static FieldInfo FInvOnChanged; private void Awake() { //IL_014f: Unknown result type (might be due to invalid IL or missing references) BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FSkillData = typeof(Skills).GetField("m_skillData", bindingAttr); Type nestedType = typeof(Skills).GetNestedType("Skill", BindingFlags.Public | BindingFlags.NonPublic); if (nestedType != null) { FSkillLevel = nestedType.GetField("m_level", bindingAttr); FSkillAcc = nestedType.GetField("m_accumulator", bindingAttr); } MethodInfo method = typeof(Player).GetMethod("CreateTombStone", bindingAttr); Type type = AccessTools.TypeByName("ExtraSlots.API"); if (type != null) { MGetAllExtraSlotsItems = AccessTools.Method(type, "GetAllExtraSlotsItems", (Type[])null, (Type[])null); } Type type2 = AccessTools.TypeByName("AdventureBackpacks.API.ABAPI"); if (type2 != null) { MGetBackpack = AccessTools.Method(type2, "GetBackpack", (Type[])null, (Type[])null); } Type type3 = AccessTools.TypeByName("Vapok.Common.Managers.ItemExtensions"); if (type3 != null) { MItemInfoData = AccessTools.Method(type3, "Data", new Type[1] { typeof(ItemData) }, (Type[])null); } Type type4 = AccessTools.TypeByName("Vapok.Common.Managers.ItemInfo"); if (type4 != null) { MItemInfoSave = AccessTools.Method(type4, "Save", Type.EmptyTypes, (Type[])null); } FInvOnChanged = AccessTools.Field(typeof(Inventory), "m_onChanged"); new Harmony("fix.permadeath.finalsunset").PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("FinalSunsetPermadeath v1.4.1 loaded | skillData=" + R(FSkillData) + " level=" + R(FSkillLevel) + " acc=" + R(FSkillAcc) + " createTombStone=" + R(method) + " extraSlotsApi=" + R(MGetAllExtraSlotsItems) + " backpackApi=" + R(MGetBackpack) + " itemInfoData=" + R(MItemInfoData))); } private static int ClearBackpackContents(ItemData item) { if (MGetBackpack == null || item == null) { return -1; } object obj; try { obj = MGetBackpack.Invoke(null, new object[1] { item }); } catch (Exception ex) { Debug.LogWarning((object)("[FinalSunsetPermadeath] GetBackpack failed: " + ex.Message)); return -1; } if (obj == null) { return -1; } if (FBackpackStructInv == null) { FBackpackStructInv = obj.GetType().GetField("Inventory"); } if (FBackpackStructInv == null) { return -1; } object? value = FBackpackStructInv.GetValue(obj); Inventory val = (Inventory)((value is Inventory) ? value : null); if (val == null) { return -1; } int count = val.GetAllItems().Count; if (count == 0) { return 0; } val.RemoveAll(); if (MItemInfoData != null && MItemInfoSave != null) { try { object obj2 = MItemInfoData.Invoke(null, new object[1] { item }); if (obj2 != null) { MItemInfoSave.Invoke(obj2, null); } } catch (Exception ex2) { Debug.LogWarning((object)("[FinalSunsetPermadeath] backpack content save failed: " + ex2.Message)); } } return count; } private static string R(object o) { if (o == null) { return "NULL"; } return "ok"; } }