Decompiled source of CustomSlotItemLib v1.1.1

CustomSlotItemLib.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CustomSlotItemLib")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Riintouge")]
[assembly: AssemblyProduct("CustomSlotItemLib")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("6bca374e-6da0-4450-b33c-ceeed956ac8b")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.1.1.0")]
namespace CustomSlotItemLib;

public class CustomSlotData : MonoBehaviour
{
	public string slotName;
}
[BepInPlugin("com.riintouge.customslotitemlib", "CustomSlotItemLib", "1.1.1")]
[BepInProcess("valheim.exe")]
public class CustomSlotItemLib : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Humanoid))]
	public class HumanoidPatch
	{
		public static HashSet<StatusEffect> GetStatusEffectsFromCustomSlotItems(Humanoid __instance)
		{
			HashSet<StatusEffect> hashSet = new HashSet<StatusEffect>();
			foreach (ItemData item in CustomSlotManager.AllSlotItems(__instance))
			{
				if (Object.op_Implicit((Object)(object)item.m_shared.m_equipStatusEffect))
				{
					hashSet.Add(item.m_shared.m_equipStatusEffect);
				}
				if (Traverse.Create((object)__instance).Method("HaveSetEffect", new Type[1] { typeof(ItemData) }, (object[])null).GetValue<bool>(new object[1] { item }))
				{
					hashSet.Add(item.m_shared.m_setStatusEffect);
				}
			}
			return hashSet;
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePostfix(ref Humanoid __instance)
		{
			CustomSlotManager.Register(__instance);
		}

		[HarmonyPatch("EquipItem")]
		[HarmonyPostfix]
		private static void EquipItemPostfix(ref bool __result, ref Humanoid __instance, ItemData item, bool triggerEquipEffects = true)
		{
			if (__result && CustomSlotManager.IsCustomSlotItem(item))
			{
				string customSlotName = CustomSlotManager.GetCustomSlotName(item);
				ItemData slotItem = CustomSlotManager.GetSlotItem(__instance, customSlotName);
				if (slotItem != null)
				{
					__instance.UnequipItem(slotItem, triggerEquipEffects);
				}
				CustomSlotManager.SetSlotItem(__instance, customSlotName, item);
				item.m_equipped = __instance.IsItemEquiped(item);
				Traverse.Create((object)__instance).Method("SetupEquipment", Array.Empty<object>()).GetValue();
				if (item.m_equipped && triggerEquipEffects)
				{
					Traverse.Create((object)__instance).Method("TriggerEquipEffect", new Type[1] { typeof(ItemData) }, (object[])null).GetValue(new object[1] { item });
				}
				__result = true;
			}
		}

		[HarmonyPatch("GetEquipmentWeight")]
		[HarmonyPostfix]
		private static void GetEquipmentWeightPostfix(ref float __result, ref Humanoid __instance)
		{
			foreach (ItemData item in CustomSlotManager.AllSlotItems(__instance))
			{
				__result += item.m_shared.m_weight;
			}
		}

		[HarmonyPatch("GetSetCount")]
		[HarmonyPostfix]
		private static void GetSetCountPostfix(ref int __result, ref Humanoid __instance, string setName)
		{
			foreach (ItemData item in CustomSlotManager.AllSlotItems(__instance))
			{
				if (item.m_shared.m_setName == setName)
				{
					__result++;
				}
			}
		}

		[HarmonyPatch("IsItemEquiped")]
		[HarmonyPostfix]
		private static void IsItemEquipedPostfix(ref bool __result, ref Humanoid __instance, ItemData item)
		{
			if (CustomSlotManager.IsCustomSlotItem(item))
			{
				string customSlotName = CustomSlotManager.GetCustomSlotName(item);
				__result |= CustomSlotManager.GetSlotItem(__instance, customSlotName) == item;
			}
		}

		[HarmonyPatch("UnequipAllItems")]
		[HarmonyPostfix]
		private static void UnequipAllItemsPostfix(ref Humanoid __instance)
		{
			foreach (ItemData item in CustomSlotManager.AllSlotItems(__instance))
			{
				__instance.UnequipItem(item, false);
			}
		}

		[HarmonyPatch("UnequipItem")]
		[HarmonyPostfix]
		private static void UnequipItemPostfix(ref Humanoid __instance, ItemData item, bool triggerEquipEffects = true)
		{
			if (item != null)
			{
				string customSlotName = CustomSlotManager.GetCustomSlotName(item);
				if (item == CustomSlotManager.GetSlotItem(__instance, customSlotName))
				{
					CustomSlotManager.SetSlotItem(__instance, customSlotName, null);
					item.m_equipped = __instance.IsItemEquiped(item);
					Traverse.Create((object)__instance).Method("UpdateEquipmentStatusEffects", Array.Empty<object>()).GetValue();
				}
			}
		}

		[HarmonyPatch("UpdateEquipmentStatusEffects")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> UpdateEquipmentStatusEffectsTranspiler(IEnumerable<CodeInstruction> instructionsIn)
		{
			List<CodeInstruction> instructions = instructionsIn.ToList();
			if (instructions[0].opcode != OpCodes.Newobj || instructions[1].opcode != OpCodes.Stloc_0)
			{
				throw new Exception("CustomSlotItemLib transpiler injection point not found!");
			}
			yield return instructions[0];
			yield return instructions[1];
			yield return new CodeInstruction(OpCodes.Ldloc_0, (object)null);
			yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			yield return CodeInstruction.Call(typeof(HumanoidPatch), "GetStatusEffectsFromCustomSlotItems", (Type[])null, (Type[])null);
			yield return CodeInstruction.Call(typeof(HashSet<StatusEffect>), "UnionWith", (Type[])null, (Type[])null);
			for (int index = 2; index < instructions.Count; index++)
			{
				yield return instructions[index];
			}
		}
	}

	[HarmonyPatch(typeof(ItemData))]
	private class ItemDropItemDataPatch
	{
		[HarmonyPatch("IsEquipable")]
		[HarmonyPostfix]
		private static void IsEquipablePostfix(ref bool __result, ref ItemData __instance)
		{
			__result |= CustomSlotManager.IsCustomSlotItem(__instance);
		}
	}

	[HarmonyPatch(typeof(Player))]
	internal class PlayerPatch
	{
		[HarmonyPatch("Load")]
		[HarmonyPostfix]
		private static void LoadPostfix(ref Player __instance)
		{
			foreach (ItemData equippedItem in ((Humanoid)__instance).GetInventory().GetEquippedItems())
			{
				string customSlotName = CustomSlotManager.GetCustomSlotName(equippedItem);
				if (customSlotName != null)
				{
					CustomSlotManager.SetSlotItem((Humanoid)(object)__instance, customSlotName, equippedItem);
				}
			}
		}
	}

	[HarmonyPatch(typeof(ZNetScene))]
	public class ZNetScenePatch
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePostfix(ref ZNetScene __instance)
		{
			if (Utility.IsNullOrWhiteSpace(ItemSlotPairs.Value))
			{
				return;
			}
			try
			{
				string[] array = ItemSlotPairs.Value.Split(new char[1] { ';' });
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = ValidateItemSlotPair(array[i]);
					CustomSlotManager.ApplyCustomSlotItem(__instance.GetPrefab(array2[0]), array2[1]);
				}
			}
			catch (Exception value)
			{
				Console.WriteLine(value);
			}
		}
	}

	public static ConfigEntry<bool> LoadOnStart;

	public static ConfigEntry<string> ItemSlotPairs;

	private readonly Harmony Harmony = new Harmony("com.riintouge.customslotitemlib");

	private void Awake()
	{
		LoadOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Core", "LoadOnStart", true, "Whether this plugin loads on game start.");
		ItemSlotPairs = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "ItemSlotPairs", "Demister,wisplight;Wishbone,wishbone", "\"ItemName1,SlotName;...;ItemNameN,SlotName\"\nMultiple items can go in the same slot (not all at once), but an item cannot go in multiple slots.\nThe game must be restarted for changes to take effect.");
		if (LoadOnStart.Value)
		{
			Harmony.PatchAll();
		}
	}

	private static string[] ValidateItemSlotPair(string rawPair)
	{
		if (rawPair == null)
		{
			throw new ArgumentNullException("rawPair");
		}
		string[] array = rawPair.Split(new char[1] { ',' });
		if (array.Length < 2)
		{
			throw new ArgumentException("Item slot pair does not name a slot!");
		}
		if (array.Length > 2)
		{
			throw new ArgumentException("Item slot pair lists more than a Item and a slot!");
		}
		if (Utility.IsNullOrWhiteSpace(array[0]))
		{
			throw new ArgumentException("Item name is null or whitespace!");
		}
		if (Utility.IsNullOrWhiteSpace(array[1]))
		{
			throw new ArgumentException("Slot name is null or whitespace!");
		}
		if ((Object)(object)ZNetScene.instance.GetPrefab(array[0]) == (Object)null)
		{
			throw new NullReferenceException("Item \"" + array[0] + "\" is NULL!");
		}
		return array;
	}
}
public static class CustomSlotManager
{
	private static readonly Dictionary<Humanoid, Dictionary<string, ItemData>> customSlotItemData = new Dictionary<Humanoid, Dictionary<string, ItemData>>();

	public static void Register(Humanoid humanoid)
	{
		customSlotItemData[humanoid] = new Dictionary<string, ItemData>();
	}

	public static void Unregister(Humanoid humanoid)
	{
		customSlotItemData.Remove(humanoid);
	}

	public static bool IsCustomSlotItem(ItemData item)
	{
		return GetCustomSlotName(item) != null;
	}

	public static string GetCustomSlotName(ItemData item)
	{
		if (item == null)
		{
			return null;
		}
		GameObject dropPrefab = item.m_dropPrefab;
		if (dropPrefab == null)
		{
			return null;
		}
		return dropPrefab.GetComponent<CustomSlotData>()?.slotName;
	}

	private static Dictionary<string, ItemData> GetCustomSlots(Humanoid humanoid)
	{
		if (!((Object)(object)humanoid != (Object)null) || !customSlotItemData.ContainsKey(humanoid))
		{
			return null;
		}
		return customSlotItemData[humanoid];
	}

	public static bool DoesSlotExist(Humanoid humanoid, string slotName)
	{
		return GetCustomSlots(humanoid)?.ContainsKey(slotName) ?? false;
	}

	public static bool IsSlotOccupied(Humanoid humanoid, string slotName)
	{
		return GetSlotItem(humanoid, slotName) != null;
	}

	public static ItemData GetSlotItem(Humanoid humanoid, string slotName)
	{
		Dictionary<string, ItemData> dictionary = ((slotName != null) ? GetCustomSlots(humanoid) : null);
		if (dictionary == null || !dictionary.ContainsKey(slotName))
		{
			return null;
		}
		return dictionary[slotName];
	}

	public static void SetSlotItem(Humanoid humanoid, string slotName, ItemData item)
	{
		if (!((Object)(object)humanoid == (Object)null) && slotName != null)
		{
			customSlotItemData[humanoid][slotName] = item;
		}
	}

	public static IEnumerable<ItemData> AllSlotItems(Humanoid humanoid)
	{
		if ((Object)(object)humanoid == (Object)null || !customSlotItemData.ContainsKey(humanoid))
		{
			return Enumerable.Empty<ItemData>();
		}
		return customSlotItemData[humanoid].Values.Where((ItemData x) => x != null).ToList();
	}

	public static void ApplyCustomSlotItem(GameObject gameObject, string slotName)
	{
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gameObject == (Object)null)
		{
			throw new ArgumentNullException("gameObject");
		}
		if (slotName == null)
		{
			throw new ArgumentNullException("slotName");
		}
		CustomSlotData component = gameObject.GetComponent<CustomSlotData>();
		if ((Object)(object)component != (Object)null)
		{
			if (component.slotName != slotName)
			{
				throw new InvalidOperationException("GameObject \"" + ((Object)gameObject).name + "\" already has component CustomSlotData! (\"" + component.slotName + "\" != \"" + slotName + "\")");
			}
		}
		else
		{
			if ((Object)(object)gameObject.GetComponent<ItemDrop>() == (Object)null)
			{
				throw new InvalidOperationException("GameObject \"" + ((Object)gameObject).name + "\" does not have component ItemDrop!");
			}
			gameObject.AddComponent<CustomSlotData>().slotName = slotName;
			gameObject.GetComponent<ItemDrop>().m_itemData.m_shared.m_itemType = (ItemType)0;
		}
	}
}