Decompiled source of FinalSunsetPermadeath v1.4.1

plugins\FinalSunsetPermadeath.dll

Decompiled 2 weeks ago
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<int> __state)
		{
			__state = null;
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			__state = new HashSet<int>();
			TombStone[] array = Object.FindObjectsOfType<TombStone>();
			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<ItemData> 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<int> __state)
		{
			if (__state == null)
			{
				return;
			}
			RealDeath = true;
			TombStone[] array = Object.FindObjectsOfType<TombStone>();
			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<Container>();
			if ((Object)(object)component == (Object)null)
			{
				KeepAtDeath.Clear();
				return;
			}
			Inventory inventory = component.GetInventory();
			if (inventory == null)
			{
				KeepAtDeath.Clear();
				return;
			}
			List<ItemData> list = new List<ItemData>(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<MethodBase> TargetMethods()
		{
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			List<MethodBase> list = new List<MethodBase>();
			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<ItemData> KeepAtDeath = new HashSet<ItemData>();

	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";
	}
}