Decompiled source of GorilaDeconstruct v1.0.0

GorilaDeconstruct.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GorilaDeconstruct")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GorilaDeconstruct")]
[assembly: AssemblyTitle("GorilaDeconstruct")]
[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 GorilaDeconstruct
{
	internal static class ConfigSync
	{
		private const string RequestRpc = "GorilaDeconstruct_ConfigRequest";

		private const string ConfigRpc = "GorilaDeconstruct_Config";

		private static bool _registered;

		internal static int? ServerPercent { get; private set; }

		internal static void Register()
		{
			if (!_registered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register("GorilaDeconstruct_ConfigRequest", (Action<long>)OnConfigRequested);
				ZRoutedRpc.instance.Register<ZPackage>("GorilaDeconstruct_Config", (Action<long, ZPackage>)OnConfigReceived);
				_registered = true;
			}
		}

		internal static void Reset()
		{
			_registered = false;
			ServerPercent = null;
		}

		internal static void RequestFromServer()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC("GorilaDeconstruct_ConfigRequest", Array.Empty<object>());
				if (ModConfig.Verbose.Value)
				{
					GorilaDeconstructPlugin.Log.LogInfo((object)"Asked the server for its deconstruct settings.");
				}
			}
		}

		private static void OnConfigRequested(long sender)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				ZPackage val = new ZPackage();
				val.Write(ModConfig.ReturnPercent.Value);
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "GorilaDeconstruct_Config", new object[1] { val });
				GorilaDeconstructPlugin.Log.LogInfo((object)$"Sent deconstruct settings to peer {sender}: returns {ModConfig.ReturnPercent.Value}%.");
			}
		}

		private static void OnConfigReceived(long sender, ZPackage package)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && package != null)
			{
				ServerPercent = package.ReadInt();
				GorilaDeconstructPlugin.Log.LogInfo((object)$"Server set deconstruct returns to {ServerPercent}%. Your local ReturnPercent is ignored while connected.");
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNetAwakePatch
	{
		private static void Postfix()
		{
			ConfigSync.Reset();
			ConfigSync.Register();
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class ZNetPeerInfoPatch
	{
		private static void Postfix()
		{
			ConfigSync.Register();
			ConfigSync.RequestFromServer();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownPatch
	{
		private static void Postfix()
		{
			ConfigSync.Reset();
		}
	}
	internal static class DeconstructPanel
	{
		private struct Entry
		{
			internal Recipe Recipe;

			internal ItemData Item;

			internal bool CanDeconstruct;

			internal string Name;
		}

		internal const string ButtonLabel = "Deconstruct";

		private static ItemData _pendingItem;

		private static Recipe _pendingRecipe;

		internal static void BuildList(InventoryGui gui)
		{
			Player localPlayer = Player.m_localPlayer;
			IList list = GuiAccess.AvailableRecipes(gui);
			foreach (object item in list)
			{
				Object.Destroy((Object)(object)GuiAccess.ElementOf(item));
			}
			list.Clear();
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			List<Entry> list2 = new List<Entry>();
			foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
			{
				Recipe val = Salvage.FindRecipe(localPlayer, allItem);
				if (!((Object)(object)val == (Object)null))
				{
					list2.Add(new Entry
					{
						Recipe = val,
						Item = allItem,
						CanDeconstruct = (Salvage.Refusal(localPlayer, allItem, val) == null),
						Name = Localization.instance.Localize(allItem.m_shared.m_name)
					});
				}
			}
			list2.Sort(delegate(Entry a, Entry b)
			{
				int num2 = b.CanDeconstruct.CompareTo(a.CanDeconstruct);
				if (num2 == 0)
				{
					num2 = string.Compare(a.Name, b.Name, StringComparison.CurrentCulture);
				}
				if (num2 == 0)
				{
					num2 = b.Item.m_quality.CompareTo(a.Item.m_quality);
				}
				if (num2 == 0)
				{
					num2 = a.Item.m_gridPos.y.CompareTo(b.Item.m_gridPos.y);
				}
				if (num2 == 0)
				{
					num2 = a.Item.m_gridPos.x.CompareTo(b.Item.m_gridPos.x);
				}
				return num2;
			});
			foreach (Entry item2 in list2)
			{
				GuiAccess.AddRecipeToList(gui, localPlayer, item2.Recipe, item2.Item, item2.CanDeconstruct);
			}
			float num = Mathf.Max(GuiAccess.RecipeListBaseSize(gui), (float)list.Count * gui.m_recipeListSpace);
			gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num);
		}

		internal static void UpdateDetails(InventoryGui gui, Player player)
		{
			GuiAccess.Selected(gui, out var recipe, out var item);
			if (!((Object)(object)recipe == (Object)null) && item != null)
			{
				string text = Salvage.Refusal(player, item, recipe);
				int effectivePercent = ModConfig.EffectivePercent;
				string text2 = Localization.instance.Localize(item.m_shared.m_name);
				gui.m_recipeName.text = ((recipe.m_amount > 1) ? $"{text2} x{recipe.m_amount}" : text2);
				gui.m_recipeDecription.text = Localization.instance.Localize(ItemData.GetTooltip(item, item.m_quality, false, (float)Game.m_worldLevel, -1, false));
				((Component)gui.m_itemCraftType).gameObject.SetActive(true);
				gui.m_itemCraftType.text = text ?? $"Returns {effectivePercent}% of the materials";
				((Component)gui.m_variantButton).gameObject.SetActive(false);
				((Component)gui.m_minStationLevelIcon).gameObject.SetActive(false);
				ShowMaterials(gui, Salvage.Returns(recipe, item.m_quality, effectivePercent));
				((Selectable)gui.m_craftButton).interactable = text == null;
				TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = "Deconstruct";
				}
				UITooltip component = ((Component)gui.m_craftButton).GetComponent<UITooltip>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_text = text ?? "";
				}
			}
		}

		internal static void Begin(InventoryGui gui)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			GuiAccess.Selected(gui, out var recipe, out var item);
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)recipe == (Object)null || item == null)
			{
				return;
			}
			string text = Salvage.Refusal(localPlayer, item, recipe);
			if (text != null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
				return;
			}
			GuiAccess.FocusCraftingPanel(gui);
			_pendingItem = item;
			_pendingRecipe = recipe;
			GuiAccess.StartTimer(gui, recipe);
			if ((Object)(object)gui.CraftingVibration != (Object)null)
			{
				Object.Instantiate<GameObject>(gui.CraftingVibration);
			}
			CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null)
			{
				currentCraftingStation.m_craftItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
			else
			{
				gui.m_craftItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		internal static void ClearPending()
		{
			_pendingItem = null;
			_pendingRecipe = null;
		}

		internal static bool Finish(InventoryGui gui, Player player)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (_pendingItem == null)
			{
				return false;
			}
			ItemData pendingItem = _pendingItem;
			Recipe pendingRecipe = _pendingRecipe;
			ClearPending();
			if (Salvage.Deconstruct(player, pendingItem, pendingRecipe))
			{
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				if ((Object)(object)currentCraftingStation != (Object)null)
				{
					currentCraftingStation.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
				else
				{
					gui.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			GuiAccess.UpdateCraftingPanel(gui);
			return true;
		}

		private static void ShowMaterials(InventoryGui gui, List<Material> materials)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			GameObject[] recipeRequirementList = gui.m_recipeRequirementList;
			if (recipeRequirementList == null || recipeRequirementList.Length == 0)
			{
				return;
			}
			int num = 0;
			if (materials.Count > recipeRequirementList.Length)
			{
				int num2 = Mathf.CeilToInt((float)materials.Count / (float)recipeRequirementList.Length);
				num = (int)Time.fixedTime % num2 * recipeRequirementList.Length;
			}
			for (int i = 0; i < recipeRequirementList.Length; i++)
			{
				Transform transform = recipeRequirementList[i].transform;
				int num3 = num + i;
				if (num3 >= materials.Count)
				{
					InventoryGui.HideRequirement(transform);
					continue;
				}
				Material material = materials[num3];
				ItemData itemData = material.Item.m_itemData;
				string text = Localization.instance.Localize(itemData.m_shared.m_name);
				Image component = ((Component)transform.Find("res_icon")).GetComponent<Image>();
				TMP_Text component2 = ((Component)transform.Find("res_name")).GetComponent<TMP_Text>();
				TMP_Text component3 = ((Component)transform.Find("res_amount")).GetComponent<TMP_Text>();
				((Component)component).gameObject.SetActive(true);
				((Component)component2).gameObject.SetActive(true);
				((Component)component3).gameObject.SetActive(true);
				component.sprite = itemData.GetIcon();
				((Graphic)component).color = Color.white;
				component2.text = text;
				component3.text = material.Amount.ToString();
				((Graphic)component3).color = Color.white;
				UITooltip component4 = ((Component)transform).GetComponent<UITooltip>();
				if ((Object)(object)component4 != (Object)null)
				{
					component4.m_text = text;
				}
			}
		}
	}
	internal static class DeconstructTab
	{
		internal const string Label = "Deconstruct";

		private static Button _button;

		internal static bool Active { get; private set; }

		internal static void Create(InventoryGui gui)
		{
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			Button tabUpgrade = gui.m_tabUpgrade;
			Button tabCraft = gui.m_tabCraft;
			if ((Object)(object)tabUpgrade == (Object)null || (Object)(object)tabCraft == (Object)null)
			{
				GorilaDeconstructPlugin.Log.LogError((object)"InventoryGui has no Craft or Upgrade tab to copy, the Deconstruct tab stays off.");
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)tabUpgrade).gameObject, ((Component)tabUpgrade).transform.parent, false);
			((Object)val).name = "TabDeconstruct";
			UIGamePad[] componentsInChildren = val.GetComponentsInChildren<UIGamePad>(true);
			foreach (UIGamePad val2 in componentsInChildren)
			{
				if ((Object)(object)val2.m_hint != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val2.m_hint);
				}
				Object.DestroyImmediate((Object)(object)val2);
			}
			Localize[] componentsInChildren2 = val.GetComponentsInChildren<Localize>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			UITooltip[] componentsInChildren3 = val.GetComponentsInChildren<UITooltip>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				componentsInChildren3[i].m_text = "";
			}
			RectTransform val3 = (RectTransform)((Component)tabUpgrade).transform;
			RectTransform val4 = (RectTransform)((Component)tabCraft).transform;
			RectTransform val5 = (RectTransform)val.transform;
			Vector2 val6 = val3.anchoredPosition - val4.anchoredPosition;
			Rect rect;
			if (((Vector2)(ref val6)).sqrMagnitude < 1f)
			{
				rect = val3.rect;
				((Vector2)(ref val6))..ctor(((Rect)(ref rect)).width, 0f);
			}
			val5.anchoredPosition = val3.anchoredPosition + val6;
			val.transform.SetSiblingIndex(((Component)tabUpgrade).transform.GetSiblingIndex() + 1);
			_button = val.GetComponent<Button>();
			_button.onClick = new ButtonClickedEvent();
			((UnityEvent)_button.onClick).AddListener((UnityAction)delegate
			{
				Select(gui);
			});
			SetCaption();
			Active = false;
			((Selectable)_button).interactable = true;
			val.SetActive(false);
			if (ModConfig.Verbose.Value)
			{
				ManualLogSource log = GorilaDeconstructPlugin.Log;
				object arg = val4.anchoredPosition;
				rect = val4.rect;
				string text = $"Tabs: craft at {arg} size {((Rect)(ref rect)).size}, ";
				object arg2 = val3.anchoredPosition;
				rect = val3.rect;
				string text2 = $"upgrade at {arg2} size {((Rect)(ref rect)).size}, ";
				object arg3 = val5.anchoredPosition;
				Transform parent = ((Component)tabUpgrade).transform.parent;
				log.LogInfo((object)(text + text2 + $"deconstruct at {arg3}, parent {((parent != null) ? ((Object)parent).name : null)}."));
			}
		}

		internal static void Destroy()
		{
			if ((Object)(object)_button != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_button).gameObject);
			}
			_button = null;
			Active = false;
		}

		internal static bool Available(Player player)
		{
			if (!ModConfig.Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)_button == (Object)null)
			{
				return false;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_hasCraftTab)
			{
				return !currentCraftingStation.m_upgrader;
			}
			return false;
		}

		internal static void BeforePanelUpdate(InventoryGui gui, Player player)
		{
			if (!((Object)(object)_button == (Object)null))
			{
				bool flag = Available(player);
				if (!flag && Active)
				{
					Active = false;
					((Selectable)gui.m_tabCraft).interactable = false;
					((Selectable)gui.m_tabUpgrade).interactable = true;
				}
				((Component)_button).gameObject.SetActive(flag);
				((Selectable)_button).interactable = !Active;
				if (Active)
				{
					((Selectable)gui.m_tabCraft).interactable = true;
					((Selectable)gui.m_tabUpgrade).interactable = true;
				}
				SetCaption();
			}
		}

		internal static void Deselect()
		{
			Active = false;
			if ((Object)(object)_button != (Object)null)
			{
				((Selectable)_button).interactable = true;
			}
		}

		private static void Select(InventoryGui gui)
		{
			if (Available(Player.m_localPlayer))
			{
				GuiAccess.FocusCraftingPanel(gui);
				Active = true;
				((Selectable)gui.m_tabCraft).interactable = true;
				((Selectable)gui.m_tabUpgrade).interactable = true;
				((Selectable)_button).interactable = false;
				GuiAccess.UpdateCraftingPanel(gui);
			}
		}

		private static void SetCaption()
		{
			TMP_Text[] componentsInChildren = ((Component)_button).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if (val.text != "Deconstruct")
				{
					val.text = "Deconstruct";
				}
			}
		}
	}
	[BepInPlugin("dev.trentini.goriladeconstruct", "GorilaDeconstruct", "1.0.0")]
	public class GorilaDeconstructPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "dev.trentini.goriladeconstruct";

		public const string PluginName = "GorilaDeconstruct";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			if (!GuiAccess.Resolve())
			{
				Log.LogError((object)"GorilaDeconstruct 1.0.0: the crafting panel changed in this game version, the Deconstruct tab stays off.");
				return;
			}
			_harmony = new Harmony("dev.trentini.goriladeconstruct");
			_harmony.PatchAll(typeof(GorilaDeconstructPlugin).Assembly);
			Log.LogInfo((object)("GorilaDeconstruct 1.0.0 loaded. " + $"Enabled {ModConfig.Enabled.Value}, returns {ModConfig.ReturnPercent.Value}% of the materials."));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			DeconstructTab.Destroy();
		}
	}
	internal static class GuiAccess
	{
		private static FieldInfo _availableRecipes;

		private static FieldInfo _selectedRecipe;

		private static FieldInfo _craftTimer;

		private static FieldInfo _craftRecipe;

		private static FieldInfo _multiCrafting;

		private static FieldInfo _recipeListBaseSize;

		private static PropertyInfo _pairRecipe;

		private static PropertyInfo _pairItem;

		private static PropertyInfo _pairElement;

		private static MethodInfo _addRecipeToList;

		private static MethodInfo _updateCraftingPanel;

		private static MethodInfo _setActiveGroup;

		internal const int CraftingGroup = 3;

		internal static bool Resolve()
		{
			Type typeFromHandle = typeof(InventoryGui);
			Type type = AccessTools.Inner(typeFromHandle, "RecipeDataPair");
			_availableRecipes = AccessTools.Field(typeFromHandle, "m_availableRecipes");
			_selectedRecipe = AccessTools.Field(typeFromHandle, "m_selectedRecipe");
			_craftTimer = AccessTools.Field(typeFromHandle, "m_craftTimer");
			_craftRecipe = AccessTools.Field(typeFromHandle, "m_craftRecipe");
			_multiCrafting = AccessTools.Field(typeFromHandle, "m_multiCrafting");
			_recipeListBaseSize = AccessTools.Field(typeFromHandle, "m_recipeListBaseSize");
			if (type != null)
			{
				_pairRecipe = AccessTools.Property(type, "Recipe");
				_pairItem = AccessTools.Property(type, "ItemData");
				_pairElement = AccessTools.Property(type, "InterfaceElement");
			}
			_addRecipeToList = AccessTools.Method(typeFromHandle, "AddRecipeToList", new Type[4]
			{
				typeof(Player),
				typeof(Recipe),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			_updateCraftingPanel = AccessTools.Method(typeFromHandle, "UpdateCraftingPanel", new Type[1] { typeof(bool) }, (Type[])null);
			_setActiveGroup = AccessTools.Method(typeFromHandle, "SetActiveGroup", new Type[2]
			{
				typeof(int),
				typeof(bool)
			}, (Type[])null);
			(string, object)[] obj = new(string, object)[13]
			{
				("InventoryGui.RecipeDataPair", type),
				("InventoryGui.m_availableRecipes", _availableRecipes),
				("InventoryGui.m_selectedRecipe", _selectedRecipe),
				("InventoryGui.m_craftTimer", _craftTimer),
				("InventoryGui.m_craftRecipe", _craftRecipe),
				("InventoryGui.m_multiCrafting", _multiCrafting),
				("InventoryGui.m_recipeListBaseSize", _recipeListBaseSize),
				("RecipeDataPair.Recipe", _pairRecipe),
				("RecipeDataPair.ItemData", _pairItem),
				("RecipeDataPair.InterfaceElement", _pairElement),
				("InventoryGui.AddRecipeToList", _addRecipeToList),
				("InventoryGui.UpdateCraftingPanel", _updateCraftingPanel),
				("InventoryGui.SetActiveGroup(int, bool)", _setActiveGroup)
			};
			bool result = true;
			(string, object)[] array = obj;
			for (int i = 0; i < array.Length; i++)
			{
				(string, object) tuple = array[i];
				var (text, _) = tuple;
				if (tuple.Item2 == null)
				{
					GorilaDeconstructPlugin.Log.LogError((object)("Missing game member " + text + "."));
					result = false;
				}
			}
			return result;
		}

		internal static IList AvailableRecipes(InventoryGui gui)
		{
			return (IList)_availableRecipes.GetValue(gui);
		}

		internal static GameObject ElementOf(object pair)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			return (GameObject)_pairElement.GetValue(pair);
		}

		internal static void Selected(InventoryGui gui, out Recipe recipe, out ItemData item)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			object value = _selectedRecipe.GetValue(gui);
			recipe = (Recipe)_pairRecipe.GetValue(value);
			item = (ItemData)_pairItem.GetValue(value);
		}

		internal static float CraftTimer(InventoryGui gui)
		{
			return (float)_craftTimer.GetValue(gui);
		}

		internal static float RecipeListBaseSize(InventoryGui gui)
		{
			return (float)_recipeListBaseSize.GetValue(gui);
		}

		internal static void StartTimer(InventoryGui gui, Recipe recipe)
		{
			_craftRecipe.SetValue(gui, recipe);
			_multiCrafting.SetValue(gui, false);
			_craftTimer.SetValue(gui, 0f);
		}

		internal static void AddRecipeToList(InventoryGui gui, Player player, Recipe recipe, ItemData item, bool canCraft)
		{
			_addRecipeToList.Invoke(gui, new object[4] { player, recipe, item, canCraft });
		}

		internal static void UpdateCraftingPanel(InventoryGui gui)
		{
			_updateCraftingPanel.Invoke(gui, new object[1] { false });
		}

		internal static void FocusCraftingPanel(InventoryGui gui)
		{
			if (gui.m_uiGroups != null && gui.m_uiGroups.Length > 3)
			{
				_setActiveGroup.Invoke(gui, new object[2] { 3, true });
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	internal static class InventoryGuiAwakePatch
	{
		private static void Postfix(InventoryGui __instance)
		{
			DeconstructTab.Create(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnDestroy")]
	internal static class InventoryGuiOnDestroyPatch
	{
		private static void Postfix()
		{
			DeconstructTab.Destroy();
			DeconstructPanel.ClearPending();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel", new Type[] { typeof(bool) })]
	internal static class InventoryGuiUpdateCraftingPanelPatch
	{
		private static void Prefix(InventoryGui __instance)
		{
			DeconstructTab.BeforePanelUpdate(__instance, Player.m_localPlayer);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList", new Type[] { typeof(List<Recipe>) })]
	internal static class InventoryGuiUpdateRecipeListPatch
	{
		private static bool Prefix(InventoryGui __instance)
		{
			if (!DeconstructTab.Active)
			{
				return true;
			}
			DeconstructPanel.BuildList(__instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe", new Type[]
	{
		typeof(Player),
		typeof(float)
	})]
	internal static class InventoryGuiUpdateRecipePatch
	{
		private static void Postfix(InventoryGui __instance, Player player)
		{
			if (DeconstructTab.Active)
			{
				DeconstructPanel.UpdateDetails(__instance, player);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	internal static class InventoryGuiOnCraftPressedPatch
	{
		private static bool Prefix(InventoryGui __instance)
		{
			if (!DeconstructTab.Active)
			{
				DeconstructPanel.ClearPending();
				return true;
			}
			DeconstructPanel.Begin(__instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
	internal static class InventoryGuiDoCraftingPatch
	{
		private static bool Prefix(InventoryGui __instance, Player player)
		{
			return !DeconstructPanel.Finish(__instance, player);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnTabCraftPressed")]
	internal static class InventoryGuiOnTabCraftPressedPatch
	{
		private static void Prefix()
		{
			DeconstructTab.Deselect();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnTabUpgradePressed")]
	internal static class InventoryGuiOnTabUpgradePressedPatch
	{
		private static void Prefix()
		{
			DeconstructTab.Deselect();
		}
	}
	internal static class ModConfig
	{
		internal const int MinPercent = 50;

		internal const int MaxPercent = 100;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> ReturnPercent;

		internal static ConfigEntry<bool> RespectFavorites;

		internal static ConfigEntry<bool> Verbose;

		internal static int EffectivePercent
		{
			get
			{
				int num = ConfigSync.ServerPercent ?? ReturnPercent.Value;
				if (num >= 50)
				{
					if (num <= 100)
					{
						return num;
					}
					return 100;
				}
				return 50;
			}
		}

		internal static void Bind(ConfigFile config)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			Enabled = config.Bind<bool>("1 - Deconstruct", "Enabled", true, "Show the Deconstruct tab at crafting stations.");
			ReturnPercent = config.Bind<int>("1 - Deconstruct", "ReturnPercent", 100, new ConfigDescription("How much of the materials comes back, from 50 to 100. Each material is rounded down on its own, so at 50% an item that cost 3 wood gives 1 back. On a server this value is handed to every client that connects.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 100), Array.Empty<object>()));
			RespectFavorites = config.Bind<bool>("1 - Deconstruct", "RespectFavorites", true, "Refuse to deconstruct a stack marked as favorite with GorilaChestMod's Alt click.");
			Verbose = config.Bind<bool>("2 - Debug", "Verbose", false, "Log every deconstruction and the layout of the crafting tabs to the BepInEx log.");
		}
	}
	internal struct Material
	{
		internal ItemDrop Item;

		internal int Amount;
	}
	internal static class Salvage
	{
		private const string FavoriteKey = "GorilaChestMod.favorite";

		internal static Recipe FindRecipe(Player player, ItemData item)
		{
			if ((Object)(object)player == (Object)null || item?.m_shared == null || item.m_shared.m_questItem || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			Recipe recipe = ObjectDB.instance.GetRecipe(item);
			if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || (Object)(object)recipe.m_item == (Object)null || recipe.m_resources == null)
			{
				return null;
			}
			if (recipe.m_requireOnlyOneIngredient)
			{
				return null;
			}
			if (!player.RequiredCraftingStation(recipe, 1, false))
			{
				return null;
			}
			if (Returns(recipe, item.m_quality, 100).Count <= 0)
			{
				return null;
			}
			return recipe;
		}

		internal static string Refusal(Player player, ItemData item, Recipe recipe)
		{
			if ((Object)(object)player == (Object)null || item == null || (Object)(object)recipe == (Object)null || !((Humanoid)player).GetInventory().ContainsItem(item))
			{
				return "The item is no longer in your inventory";
			}
			if (item.m_equipped || ((Humanoid)player).IsItemEquiped(item))
			{
				return "Unequip it first";
			}
			if (ModConfig.RespectFavorites.Value && IsFavorite(item))
			{
				return "Marked as favorite, Alt click it to unmark";
			}
			if (item.m_stack < recipe.m_amount)
			{
				return $"Needs a full stack of {recipe.m_amount}";
			}
			if (Returns(recipe, item.m_quality, ModConfig.EffectivePercent).Count == 0)
			{
				return "Too little would come back at this percentage";
			}
			return null;
		}

		internal static List<Material> Returns(Recipe recipe, int quality, int percent)
		{
			List<Material> list = new List<Material>();
			int num = Mathf.Max(1, recipe.m_item.m_itemData.m_shared.m_maxQuality);
			int num2 = Mathf.Clamp(quality, 1, num);
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement req in resources)
			{
				if (req == null || (Object)(object)req.m_resItem == (Object)null || req.m_upgraderResource || !req.m_recover)
				{
					continue;
				}
				int num3 = 0;
				for (int j = 1; j <= num2; j++)
				{
					num3 += req.GetAmount(j);
				}
				int num4 = num3 * percent / 100;
				if (num4 > 0)
				{
					int num5 = list.FindIndex((Material m) => m.Item.m_itemData.m_shared.m_name == req.m_resItem.m_itemData.m_shared.m_name);
					if (num5 >= 0)
					{
						Material value = list[num5];
						value.Amount += num4;
						list[num5] = value;
					}
					else
					{
						list.Add(new Material
						{
							Item = req.m_resItem,
							Amount = num4
						});
					}
				}
			}
			return list;
		}

		internal static bool Deconstruct(Player player, ItemData item, Recipe recipe)
		{
			string text = Refusal(player, item, recipe);
			if (text != null)
			{
				if (player != null)
				{
					((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
				return false;
			}
			int effectivePercent = ModConfig.EffectivePercent;
			List<Material> list = Returns(recipe, item.m_quality, effectivePercent);
			string name = item.m_shared.m_name;
			int quality = item.m_quality;
			bool cheated = item.m_cheated;
			if (!((Humanoid)player).GetInventory().RemoveItem(item, recipe.m_amount))
			{
				return false;
			}
			int num = 0;
			foreach (Material item2 in list)
			{
				num += Give(player, item2, cheated);
			}
			if (num > 0)
			{
				((Character)player).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false);
			}
			if (ModConfig.Verbose.Value)
			{
				List<string> list2 = new List<string>();
				foreach (Material item3 in list)
				{
					list2.Add($"{item3.Item.m_itemData.m_shared.m_name} x{item3.Amount}");
				}
				GorilaDeconstructPlugin.Log.LogInfo((object)(string.Format("Deconstructed {0} x{1} at quality {2}, {3}%: {4}", name, recipe.m_amount, quality, effectivePercent, string.Join(", ", list2)) + ((num > 0) ? $", {num} dropped on the ground" : "") + "."));
			}
			return true;
		}

		private static int Give(Player player, Material material, bool cheated)
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ObjectDB.instance.GetItemPrefab(((Object)((Component)material.Item).gameObject).name);
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)material.Item).gameObject;
			}
			ItemDrop component = val.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				GorilaDeconstructPlugin.Log.LogWarning((object)("No item prefab for " + ((Object)((Component)material.Item).gameObject).name + ", skipped."));
				return 0;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize);
			int num2 = material.Amount;
			int num3 = 0;
			while (num2 > 0)
			{
				int num4 = Mathf.Min(num2, num);
				num2 -= num4;
				ItemData val2 = NewItem(component, val, num4, cheated);
				int num5 = Mathf.Min(inventory.FindFreeStackSpace(val2.m_shared.m_name, (float)val2.m_worldLevel) + inventory.GetEmptySlots() * num, num4);
				if (num5 > 0)
				{
					inventory.AddItem(NewItem(component, val, num5, cheated));
				}
				if (num4 > num5)
				{
					Transform transform = ((Component)player).transform;
					ItemDrop.DropItem(val2, num4 - num5, transform.position + transform.forward + Vector3.up, transform.rotation);
					num3 += num4 - num5;
				}
			}
			((Character)player).ShowPickupMessage(component.m_itemData, material.Amount);
			return num3;
		}

		private static ItemData NewItem(ItemDrop template, GameObject prefab, int stack, bool cheated)
		{
			ItemData obj = template.m_itemData.Clone();
			obj.m_dropPrefab = prefab;
			obj.m_stack = stack;
			obj.m_durability = obj.GetMaxDurability();
			obj.m_worldLevel = (byte)Game.m_worldLevel;
			obj.m_cheated = cheated;
			return obj;
		}

		private static bool IsFavorite(ItemData item)
		{
			if (item.m_customData != null && item.m_customData.TryGetValue("GorilaChestMod.favorite", out var value))
			{
				return value == "1";
			}
			return false;
		}
	}
	internal static class BuildInfo
	{
		internal const string Version = "1.0.0";
	}
}