Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of GorilaDeconstruct v1.0.0
GorilaDeconstruct.dll
Decompiled 2 months agousing 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"; } }