Decompiled source of EpicLootStorageMaterials v1.2.0

EpicLootStorageMaterials.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot_UnityLib;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("EpicLootStorageMaterials")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EpicLootStorageMaterials")]
[assembly: AssemblyTitle("EpicLootStorageMaterials")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EpicLootStorageMaterials
{
	[BepInPlugin("pieter.valheim.epiclootstoragematerials", "Epic Loot Storage Materials", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class ContainerRegistry
		{
			private static void Postfix(Container __instance)
			{
				Containers.Add(__instance);
			}
		}

		[HarmonyPatch(typeof(SacrificeUI), "SacrificeItems")]
		private static class SacrificePatch
		{
			private static void Prefix()
			{
				Sacrificed.Clear();
				if (_perItemDouble.Value && !(ProductsMethod == null) && !(GiveMethod == null))
				{
					EnchantingTableUI instance = EnchantingTableUI.instance;
					EnchantingTable val = ((instance != null) ? instance.SourceTable : null);
					if (!((Object)(object)val == (Object)null))
					{
						Tuple<float, float> featureCurrentValue = val.GetFeatureCurrentValue((EnchantingFeature)0);
						_doubleChance = (float.IsNaN(featureCurrentValue.Item1) ? 0f : (featureCurrentValue.Item1 / 100f));
						_sacrificing = true;
					}
				}
			}

			private static void Postfix(SacrificeUI __instance)
			{
				if (!_sacrificing)
				{
					return;
				}
				_sacrificing = false;
				List<Tuple<ItemData, int>> list = new List<Tuple<ItemData, int>>();
				foreach (Tuple<ItemData, int> item in Sacrificed)
				{
					int num = 0;
					for (int i = 0; i < item.Item2; i++)
					{
						if (Random.value < _doubleChance)
						{
							num++;
						}
					}
					if (num > 0)
					{
						list.Add(Tuple.Create<ItemData, int>(item.Item1, num));
					}
				}
				if (list.Count != 0)
				{
					object obj = ProductsMethod.Invoke(null, new object[1] { list });
					EnchantingTableUI instance = EnchantingTableUI.instance;
					if (instance != null)
					{
						instance.PlayEnchantBonusSFX();
					}
					if ((Object)(object)__instance.BonusPanel != (Object)null)
					{
						__instance.BonusPanel.Show();
					}
					GiveMethod.Invoke(null, new object[1] { obj });
					RefreshSacrifice?.Invoke(__instance, null);
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				_sacrificing = false;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(EnchantingTable), "GetFeatureCurrentValue")]
		private static class FeatureValuePatch
		{
			private static void Postfix(EnchantingFeature feature, ref Tuple<float, float> __result)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				if (_sacrificing && (int)feature == 0)
				{
					__result = Tuple.Create(float.NaN, float.NaN);
				}
			}
		}

		[HarmonyPatch(typeof(InventoryManagement), "RemoveExactItem")]
		private static class RemovedPatch
		{
			private static void Postfix(ItemData item, int __result)
			{
				if (_sacrificing && item != null && __result > 0)
				{
					Sacrificed.Add(Tuple.Create<ItemData, int>(item, __result));
				}
			}
		}

		[HarmonyPatch(typeof(SacrificeUI), "OnEnable")]
		private static class ChestToggleUi
		{
			private static void Postfix(SacrificeUI __instance)
			{
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Expected O, but got Unknown
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f5: Expected O, but got Unknown
				try
				{
					Toggle val = ((EnchantingTableUIPanelBase)__instance).AvailableItems?.SelectAllToggle;
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					Transform parent = ((Component)val).transform.parent;
					if (!((Object)(object)parent.Find("IncludeChestsToggle") != (Object)null))
					{
						GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, parent, false);
						((Object)val2).name = "IncludeChestsToggle";
						RectTransform val3 = (RectTransform)val2.transform;
						RectTransform val4 = (RectTransform)((Component)val).transform;
						Vector2 anchoredPosition = val4.anchoredPosition;
						Rect rect = val4.rect;
						val3.anchoredPosition = anchoredPosition + new Vector2(0f, ((Rect)(ref rect)).height + 4f);
						Text[] componentsInChildren = val2.GetComponentsInChildren<Text>(true);
						for (int i = 0; i < componentsInChildren.Length; i++)
						{
							componentsInChildren[i].text = "Include chests";
						}
						Toggle component = val2.GetComponent<Toggle>();
						component.onValueChanged = new ToggleEvent();
						component.SetIsOnWithoutNotify(_trophies.Value);
						((Selectable)component).interactable = true;
						((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
						{
							_trophies.Value = on;
							RefreshSacrifice?.Invoke(__instance, null);
						});
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("Could not add the Include chests toggle: " + ex.Message));
					}
				}
			}
		}

		public const string Guid = "pieter.valheim.epiclootstoragematerials";

		public const string Name = "Epic Loot Storage Materials";

		public const string Version = "1.2.0";

		private const string EpicLootGuid = "randyknapp.mods.epicloot";

		private const string ProviderId = "pieter.nearbychests";

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<bool> _trophies;

		private static ConfigEntry<bool> _perItemDouble;

		private static ManualLogSource _log;

		private static ConfigEntry<float> _range;

		private static readonly HashSet<Container> Containers = new HashSet<Container>();

		private static MethodInfo _checkAccess;

		[ThreadStatic]
		private static bool _sacrificing;

		private static float _doubleChance;

		private static readonly List<Tuple<ItemData, int>> Sacrificed = new List<Tuple<ItemData, int>>();

		private static readonly MethodInfo ProductsMethod = AccessTools.Method(AccessTools.TypeByName("EpicLoot.CraftingV2.EnchantingUIController"), "GetSacrificeProducts", new Type[1] { typeof(List<Tuple<ItemData, int>>) }, (Type[])null);

		private static readonly MethodInfo GiveMethod = AccessTools.Method(typeof(EnchantingTableUIPanelBase), "GiveItemsToPlayer", (Type[])null, (Type[])null);

		private static readonly MethodInfo RefreshSacrifice = AccessTools.Method(typeof(SacrificeUI), "RefreshAvailableItems", (Type[])null, (Type[])null);

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Use materials from nearby chests for Epic Loot enchanting table costs.");
			_range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range (m)", 50f, new ConfigDescription("How far from you chests are used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			_trophies = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Chest trophies in Sacrifice", true, "List trophies from nearby chests in the enchanting table's Sacrifice tab; sacrificing takes them from the chest. Also switchable with the Include chests checkbox in the Sacrifice tab.");
			_perItemDouble = ((BaseUnityPlugin)this).Config.Bind<bool>("Sacrifice", "Roll double output per item", true, "Roll the enchanting table's double-output chance for every sacrificed item instead of once for the whole batch (all doubled or nothing). Same average, but large batches give anything between 1x and 2x.");
			_log = ((BaseUnityPlugin)this).Logger;
			_checkAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);
			Type type = AccessTools.TypeByName("EpicLoot.API");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "RegisterInventoryProvider", (Type[])null, (Type[])null));
			if (methodInfo == null || _checkAccess == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Disabled, Epic Loot's inventory provider API or Container.CheckAccess was not found.");
				return;
			}
			Harmony val = new Harmony("pieter.valheim.epiclootstoragematerials");
			val.PatchAll(typeof(ContainerRegistry));
			val.PatchAll(typeof(SacrificePatch));
			val.PatchAll(typeof(FeatureValuePatch));
			val.PatchAll(typeof(RemovedPatch));
			val.PatchAll(typeof(ChestToggleUi));
			bool flag = (bool)methodInfo.Invoke(null, new object[5]
			{
				"pieter.nearbychests",
				new Func<List<ItemData>>(ChestTrophies),
				new Func<string, int>(CountItem),
				new Func<string, int, int>(RemoveItem),
				new Func<ItemData, int, int>(RemoveExactItem)
			});
			((BaseUnityPlugin)this).Logger.LogInfo((object)(flag ? "Epic Loot can now use materials from nearby chests." : "Epic Loot refused the inventory provider."));
		}

		private static IEnumerable<Container> UsableChests()
		{
			Player player = Player.m_localPlayer;
			if (!_enabled.Value || (Object)(object)player == (Object)null)
			{
				yield break;
			}
			Vector3 origin = ((Component)player).transform.position;
			float maxSqr = _range.Value * _range.Value;
			Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			foreach (Container item in Containers.OrderBy(delegate(Container c)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)c).transform.position - origin;
				return ((Vector3)(ref val2)).sqrMagnitude;
			}))
			{
				Vector3 val = ((Component)item).transform.position - origin;
				if (((Vector3)(ref val)).sqrMagnitude > maxSqr)
				{
					break;
				}
				if (IsUsable(item, player))
				{
					yield return item;
				}
			}
		}

		private static bool IsUsable(Container chest, Player player)
		{
			if (!((Behaviour)chest).isActiveAndEnabled || chest.GetInventory() == null)
			{
				return false;
			}
			Piece componentInParent = ((Component)chest).GetComponentInParent<Piece>();
			if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsPlacedByPlayer())
			{
				return false;
			}
			if ((Object)(object)chest.m_wagon != (Object)null || (Object)(object)((Component)chest).GetComponentInParent<Ship>() != (Object)null)
			{
				return false;
			}
			if (!chest.IsOwner() || chest.IsInUse())
			{
				return false;
			}
			return (bool)_checkAccess.Invoke(chest, new object[1] { player.GetPlayerID() });
		}

		private static int CountItem(string name)
		{
			int num = 0;
			foreach (Container item in UsableChests())
			{
				num += item.GetInventory().CountItems(name, -1, true);
			}
			return num;
		}

		private static List<ItemData> ChestTrophies()
		{
			List<ItemData> list = new List<ItemData>();
			if (!_trophies.Value)
			{
				return list;
			}
			foreach (Container item in UsableChests())
			{
				list.AddRange(from i in item.GetInventory().GetAllItems()
					where i != null && (int)(i.m_shared?.m_itemType).GetValueOrDefault() == 13
					select i);
			}
			return list;
		}

		private static int RemoveExactItem(ItemData item, int amount)
		{
			if (item == null || amount <= 0)
			{
				return 0;
			}
			foreach (Container item2 in UsableChests())
			{
				Inventory inventory = item2.GetInventory();
				if (inventory.ContainsItem(item))
				{
					int stack = item.m_stack;
					inventory.RemoveItem(item, Mathf.Min(amount, stack));
					return stack - (inventory.ContainsItem(item) ? item.m_stack : 0);
				}
			}
			return 0;
		}

		private static int RemoveItem(string name, int amount)
		{
			int num = 0;
			foreach (Container item in UsableChests())
			{
				if (num >= amount)
				{
					break;
				}
				Inventory inventory = item.GetInventory();
				int num2 = inventory.CountItems(name, -1, true);
				if (num2 > 0)
				{
					inventory.RemoveItem(name, Mathf.Min(num2, amount - num), -1, true);
					num += num2 - inventory.CountItems(name, -1, true);
				}
			}
			return num;
		}
	}
}