Decompiled source of CustomStack v1.1.8

CustomStack-IL2CPP.dll

Decompiled 9 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using CustomStack;
using CustomStack.Helpers;
using CustomStack.Patches;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Management;
using Il2CppScheduleOne.Money;
using Il2CppScheduleOne.ObjectScripts;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.Storage;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.UI.Items;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(global::CustomStack.CustomStack), "CustomStack", "1.1.8", "k073l", null)]
[assembly: MelonColor(1, 255, 0, 0)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("CustomStack-IL2CPP")]
[assembly: AssemblyConfiguration("IL2CPP")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+34e336381dff4000536384980ccea01904d555a4")]
[assembly: AssemblyProduct("CustomStack-IL2CPP")]
[assembly: AssemblyTitle("CustomStack-IL2CPP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CustomStack
{
	public static class BuildInfo
	{
		public const string Name = "CustomStack";

		public const string Description = "Custom stack limits, per item";

		public const string Author = "k073l";

		public const string Version = "1.1.8";
	}
	public class CustomStack : MelonMod
	{
		private static Instance _logger;

		private static MelonPreferences_Category _category;

		private static readonly Dictionary<string, MelonPreferences_Entry<int>> Entries = new Dictionary<string, MelonPreferences_Entry<int>>();

		private static Dictionary<string, int> _defaultLimits = new Dictionary<string, int>();

		private static bool _populatedDefaultLimits = false;

		private static MelonPreferences_Category _stations;

		internal static MelonPreferences_Entry<int> MaxMixQuantity;

		internal static MelonPreferences_Entry<int> DryingItemCapacity;

		public override void OnInitializeMelon()
		{
			_logger = ((MelonBase)this).LoggerInstance;
			_logger.Msg("CustomStack initialized.");
			_category = MelonPreferences.CreateCategory("CustomStack", "Stack Sizes");
			_category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomStack.cfg"));
			_stations = MelonPreferences.CreateCategory("CustomStack_Stations", "Station Capacities");
			_stations.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomStack.cfg"));
			MaxMixQuantity = _stations.CreateEntry<int>("MaxMixQuantity", 20, "Max Mixing Quantity", "Controls how much you can mix in Mixing Stations", false, false, (ValueValidator)(object)new ValueRange<int>(1, 10000), (string)null);
			((MelonEventBase<LemonAction<int, int>>)(object)MaxMixQuantity.OnEntryValueChanged).Subscribe((LemonAction<int, int>)MixingStationPatches.ApplyCustomLimitRuntime, 0, false);
			DryingItemCapacity = _stations.CreateEntry<int>("DryingItemCapacity", 20, "Drying Rack Capacity", "Controls how many items you can dry in Drying Racks", false, false, (ValueValidator)(object)new ValueRange<int>(1, 101000), (string)null);
			((MelonEventBase<LemonAction<int, int>>)(object)DryingItemCapacity.OnEntryValueChanged).Subscribe((LemonAction<int, int>)DryingRackPatches.ApplyCustomLimitRuntime, 0, false);
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName == "Menu")
			{
				LoadAllItems();
			}
			_logger.Debug("Scene loaded: " + sceneName);
		}

		private static void LoadAllItems()
		{
			if (_populatedDefaultLimits)
			{
				_logger.Msg("Item limits already set, skipping.");
				return;
			}
			_logger.Msg("Loading item stack limits...");
			IOrderedEnumerable<ItemRegister> orderedEnumerable = from register in Singleton<Registry>.Instance.ItemRegistry.AsEnumerable<ItemRegister>()
				orderby register.name
				select register;
			_category.LoadFromFile(false);
			foreach (ItemRegister item in orderedEnumerable)
			{
				string iD = item.ID;
				int stackLimit = ((BaseItemDefinition)item.Definition).StackLimit;
				if (!Entries.ContainsKey(iD))
				{
					MelonPreferences_Entry<int> value = _category.CreateEntry<int>(iD, stackLimit, ((BaseItemDefinition)item.Definition).Name, (string)null, false, false, (ValueValidator)(object)((iD == "cash") ? new ValueRange<int>(1, 100000) : new ValueRange<int>(1, 10000)), (string)null);
					Entries[iD] = value;
				}
				if (!_populatedDefaultLimits && !_defaultLimits.ContainsKey(iD))
				{
					_defaultLimits[iD] = stackLimit;
				}
				MelonPreferences_Entry<int> val = Entries[iD];
				int value2 = val.Value;
				string text = iD;
				string text2 = text;
				if (!(text2 == "cash"))
				{
					if (text2 == "defaultweed")
					{
						((MelonPreferences_Entry)Entries[iD]).Description = "Changes to this value may be required for changing stack size of mixed weed products.";
					}
					((BaseItemDefinition)item.Definition).StackLimit = value2;
					if (value2 != _defaultLimits[iD])
					{
						_logger.Msg($"{iD} limit overridden: {_defaultLimits[iD]} → {value2}");
					}
				}
				else if (value2 == _defaultLimits[iD])
				{
					_logger.Msg("Cash stack limit not configured, forcing runtime limit to 1k (config not changed).");
					((BaseItemDefinition)item.Definition).StackLimit = 1000;
				}
				else
				{
					((BaseItemDefinition)item.Definition).StackLimit = value2;
					if (value2 != _defaultLimits[iD])
					{
						_logger.Msg($"cash limit overridden by user: {_defaultLimits[iD]} → {value2}");
					}
				}
			}
			_populatedDefaultLimits = true;
			foreach (KeyValuePair<string, MelonPreferences_Entry<int>> entryKv in Entries)
			{
				((MelonEventBase<LemonAction<int, int>>)(object)entryKv.Value.OnEntryValueChanged).Subscribe((LemonAction<int, int>)delegate(int was, int currentLimit)
				{
					UpdateLimitRuntime(was, currentLimit, entryKv.Key, entryKv.Value);
				}, 0, false);
			}
		}

		private static void UpdateLimitRuntime(int previousLimit, int currentLimit, string itemID, MelonPreferences_Entry<int> entry)
		{
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if (previousLimit == currentLimit)
			{
				return;
			}
			if (previousLimit > currentLimit)
			{
				Melon<CustomStack>.Logger.Warning($"Cannot lower stack limit ({previousLimit} -> {currentLimit}) of {itemID} at runtime! Close the game and edit the file manually if you want to proceed with a potentially destructive action.");
				entry.Value = previousLimit;
				return;
			}
			ItemRegister val = Singleton<Registry>.Instance.ItemRegistry.AsEnumerable<ItemRegister>().FirstOrDefault((Func<ItemRegister, bool>)((ItemRegister register) => register.ID == itemID));
			if (val == null)
			{
				return;
			}
			ItemDefinition definition = val.Definition;
			if (definition == null)
			{
				return;
			}
			_ = ((BaseItemDefinition)definition).StackLimit;
			if (false)
			{
				return;
			}
			((BaseItemDefinition)val.Definition).StackLimit = currentLimit;
			List<string> idsToSearch = new List<string>(1) { itemID };
			if (Utils.Is<ProductDefinition>((object)val.Definition, out ProductDefinition result) && NetworkSingleton<ProductManager>.InstanceExists)
			{
				EDrugType val2 = (EDrugType)0;
				try
				{
					val2 = result.DrugType;
				}
				catch (Exception)
				{
					Melon<CustomStack>.Logger.Warning("Failed to read drug type for " + itemID + ". Assuming weed.");
				}
				Enumerator<ProductDefinition> enumerator = NetworkSingleton<ProductManager>.Instance.AllProducts.GetEnumerator();
				while (enumerator.MoveNext())
				{
					ProductDefinition current = enumerator.Current;
					try
					{
						if ((Object)(object)current != (Object)null && current.DrugType == val2)
						{
							idsToSearch.Add(((BaseItemDefinition)current).ID);
						}
					}
					catch (Exception ex2)
					{
						Melon<CustomStack>.Logger.Warning("Failed to read DrugType for product '" + (((current != null) ? ((BaseItemDefinition)current).ID : null) ?? "<null>") + "': " + ex2.Message);
					}
				}
			}
			IEnumerable<BaseItemDefinition> enumerable = from def in ((IEnumerable<BaseItemDefinition>)Object.FindObjectsOfType<BaseItemDefinition>()).AsEnumerable()
				where idsToSearch.Contains(def.ID)
				select def;
			foreach (BaseItemDefinition item in enumerable)
			{
				item.StackLimit = currentLimit;
			}
			IEnumerable<string> values = enumerable.Select((BaseItemDefinition def) => def.ID);
			Melon<CustomStack>.Logger.Msg($"Updated limit of {string.Join(',', values)} to {currentLimit}.");
		}
	}
}
namespace CustomStack.Patches
{
	[HarmonyPatch(typeof(ItemUIManager))]
	public static class CashPatches
	{
		private static int CashItemLimit;

		private static Instance Logger;

		[HarmonyPatch("StartDragCash")]
		[HarmonyPrefix]
		private static bool StartDragCash(ItemUIManager __instance)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			Logger.Debug("StartDragCash: top");
			ItemInstance itemInstance = __instance.draggedSlot.assignedSlot.ItemInstance;
			if (!Utils.Is<CashInstance>((object)itemInstance, out CashInstance result))
			{
				Logger.Debug("StartDragCash: cashInstance is null");
			}
			Logger.Debug("StartDragCash: got cashInstance");
			__instance.draggedCashAmount = Mathf.Min(result.Balance, (float)CashItemLimit);
			__instance.draggedAmount = 1;
			__instance.customDragAmount = GameInput.GetCurrentInputDeviceIsGamepad();
			Logger.Debug("Set draggedCashAmount and draggedAmount");
			if (GameInput.GetButtonDown((ButtonCode)1) || __instance.controllerQuickMoveSingle)
			{
				Logger.Debug("StartDragCash: secondary click");
				__instance.draggedAmount = 1;
				__instance.draggedCashAmount = Mathf.Min(result.Balance, Mathf.Floor((float)CashItemLimit / 10f));
				__instance.mouseOffset += new Vector2(-10f, -15f);
				__instance.customDragAmount = true;
			}
			Logger.Debug("StartDragCash: draggedAmount check");
			if (__instance.draggedCashAmount <= 0f)
			{
				Logger.Debug("StartDragCash: no cash to drag");
				__instance.draggedSlot = null;
			}
			else if ((Input.GetKey((KeyCode)304) || __instance.canControllerQuickMove) && __instance.QuickMoveEnabled)
			{
				Logger.Debug("StartDragCash: quickmove");
				List<ItemSlot> list = Il2CppListExtensions.ConvertToList<ItemSlot>(__instance.GetQuickMoveSlots(__instance.draggedSlot.assignedSlot));
				if (list.Count > 0)
				{
					Logger.Debug("StartDragCash: quickmove slots found");
					Debug.Log(Object.op_Implicit("Quick-moving " + __instance.draggedAmount + " items..."));
					float draggedCashAmount = __instance.draggedCashAmount;
					float num = 0f;
					for (int i = 0; i < list.Count; i++)
					{
						if (num >= (float)__instance.draggedAmount)
						{
							break;
						}
						ItemSlot val = list[i];
						if (val.ItemInstance != null)
						{
							if (Utils.Is<CashInstance>((object)val.ItemInstance, out CashInstance result2))
							{
								float num2 = 0f;
								num2 = ((!Utils.Is<CashSlot>((object)val, out CashSlot _)) ? Mathf.Min(draggedCashAmount, (float)CashItemLimit - result2.Balance) : Mathf.Min(draggedCashAmount, float.MaxValue - result2.Balance));
								result2.ChangeBalance(num2);
								val.ReplicateStoredInstance();
								num += num2;
							}
						}
						else
						{
							if (!Utils.Is<CashInstance>((object)Registry.GetItem("cash").GetDefaultInstance(1), out CashInstance result4))
							{
								Logger.Error("StartDragCash: failed to get default CashInstance");
								return false;
							}
							result4.SetBalance(__instance.draggedCashAmount, false);
							val.SetStoredItem((ItemInstance)(object)result4, false);
							num += __instance.draggedCashAmount;
						}
					}
					if (num >= result.Balance)
					{
						__instance.draggedSlot.assignedSlot.ClearStoredInstance(false);
					}
					else
					{
						result.ChangeBalance(0f - num);
						__instance.draggedSlot.assignedSlot.ReplicateStoredInstance();
					}
				}
				if (__instance.onItemMoved != null)
				{
					Logger.Debug("StartDragCash: invoking onItemMoved");
					__instance.onItemMoved.Invoke();
				}
				__instance.draggedSlot = null;
			}
			else
			{
				Logger.Debug("StartDragCash: standard drag");
				if (__instance.onDragStart != null)
				{
					Logger.Debug("StartDragCash: invoking onDragStart");
					__instance.onDragStart.Invoke();
				}
				if ((object)__instance.draggedSlot.assignedSlot != PlayerSingleton<PlayerInventory>.Instance.cashSlot)
				{
					Logger.Debug("StartDragCash: play cash slot hint anim");
					Singleton<HUD>.Instance.CashSlotHintAnim.Play();
				}
				__instance.tempIcon = __instance.draggedSlot.DuplicateIcon(((Component)Singleton<HUD>.Instance).transform, __instance.draggedAmount);
				((TMP_Text)((Component)((Transform)__instance.tempIcon).Find("Balance")).GetComponent<TextMeshProUGUI>()).text = MoneyManager.FormatAmount(__instance.draggedCashAmount, false, false);
				__instance.draggedSlot.IsBeingDragged = true;
				if (__instance.draggedCashAmount >= result.Balance)
				{
					Logger.Debug("StartDragCash: hide dragged slot");
					__instance.draggedSlot.SetVisible(false);
				}
				else
				{
					Logger.Debug("StartDragCash: update cash balance display");
					if (Utils.Is<ItemUI_Cash>((object)__instance.draggedSlot.ItemUI, out ItemUI_Cash result5))
					{
						result5.SetDisplayedBalance(result.Balance - __instance.draggedCashAmount);
					}
					else
					{
						Logger.Debug("StartDragCash: draggedSlot is not ItemUI_Cash, cannot update balance display");
					}
				}
			}
			Logger.Debug("StartDragCash: end");
			return false;
		}

		[HarmonyPatch("EndCashDrag")]
		[HarmonyPrefix]
		private static bool EndCashDrag(ItemUIManager __instance)
		{
			CashInstance val = null;
			if ((Object)(object)__instance.draggedSlot != (Object)null && __instance.draggedSlot.assignedSlot != null)
			{
				if (!Utils.Is<CashInstance>((object)__instance.draggedSlot.assignedSlot.ItemInstance, out CashInstance result))
				{
					Logger.Error("EndCashDrag: draggedSlot.assignedSlot.ItemInstance is not CashInstance");
					return true;
				}
				val = result;
			}
			if (val == null)
			{
				Logger.Error("EndCashDrag: cashInstance is null");
				return true;
			}
			Singleton<HUD>.Instance.CashSlotHintAnim.Stop();
			Singleton<HUD>.Instance.CashSlotHintAnimCanvasGroup.alpha = 0f;
			bool flag = false;
			ItemSlotUI hoveredSlot = __instance.HoveredSlot;
			ItemSlot val2 = ((hoveredSlot != null) ? hoveredSlot.assignedSlot : null);
			ItemSlotUI draggedSlot = __instance.draggedSlot;
			object obj;
			if (draggedSlot == null)
			{
				obj = null;
			}
			else
			{
				ItemSlot assignedSlot = draggedSlot.assignedSlot;
				obj = ((assignedSlot != null) ? assignedSlot.ItemInstance : null);
			}
			ItemInstance val3 = (ItemInstance)obj;
			if (__instance.CanDragFromSlot(__instance.draggedSlot) && (Object)(object)__instance.HoveredSlot != (Object)null && __instance.CanCashBeDraggedIntoSlot(__instance.HoveredSlot) && val2 != null && val3 != null && !val2.IsLocked && !val2.IsAddLocked)
			{
				MethodInfo method = ((object)val2).GetType().GetMethod("DoesItemMatchHardFilters");
				MethodInfo method2 = ((object)val2).GetType().GetMethod("DoesItemMatchPlayerFilters");
				if (method != null && method2 != null)
				{
					bool flag2 = (bool)method.Invoke(val2, new object[1] { val3 });
					bool flag3 = (bool)method2.Invoke(val2, new object[1] { val3 });
					flag = flag2 && flag3;
				}
				else
				{
					MethodInfo method3 = ((object)val2).GetType().GetMethod("DoesItemMatchFilters");
					if (method3 != null)
					{
						flag = (bool)method3.Invoke(val2, new object[1] { val3 });
					}
				}
			}
			if (flag)
			{
				if (Utils.Is<HotbarSlot>((object)__instance.HoveredSlot.assignedSlot, out HotbarSlot _) && !Utils.Is<CashSlot>((object)__instance.HoveredSlot.assignedSlot, out CashSlot result3))
				{
					__instance.HoveredSlot = (ItemSlotUI)(object)((Component)Singleton<HUD>.Instance.cashSlotUI).GetComponent<CashSlotUI>();
				}
				float num = Mathf.Min(__instance.draggedCashAmount, val.Balance);
				if (num > 0f)
				{
					float num2 = num;
					if (__instance.HoveredSlot.assignedSlot.ItemInstance != null)
					{
						if (!Utils.Is<CashInstance>((object)__instance.HoveredSlot.assignedSlot.ItemInstance, out CashInstance result4))
						{
							Logger.Error("EndCashDrag: HoveredSlot.assignedSlot.ItemInstance is not CashInstance");
						}
						num2 = ((!Utils.Is<CashSlot>((object)__instance.HoveredSlot.assignedSlot, out result3)) ? Mathf.Min(num, (float)CashItemLimit - result4.Balance) : Mathf.Min(num, float.MaxValue - result4.Balance));
						result4.ChangeBalance(num2);
						__instance.HoveredSlot.assignedSlot.ReplicateStoredInstance();
					}
					else
					{
						if (!Utils.Is<CashInstance>((object)Registry.GetItem("cash").GetDefaultInstance(1), out CashInstance result5))
						{
							Logger.Error("EndCashDrag: failed to get default CashInstance");
						}
						result5.SetBalance(num2, false);
						__instance.HoveredSlot.assignedSlot.SetStoredItem((ItemInstance)(object)result5, false);
					}
					if (num2 >= val.Balance)
					{
						__instance.draggedSlot.assignedSlot.ClearStoredInstance(false);
					}
					else
					{
						val.ChangeBalance(0f - num2);
						__instance.draggedSlot.assignedSlot.ReplicateStoredInstance();
					}
				}
			}
			__instance.draggedSlot.SetVisible(true);
			__instance.draggedSlot.UpdateUI();
			__instance.draggedSlot.IsBeingDragged = false;
			__instance.draggedSlot = null;
			Object.Destroy((Object)(object)((Component)__instance.tempIcon).gameObject);
			Singleton<CursorManager>.Instance.SetCursorAppearance((ECursorType)0);
			return false;
		}

		[HarmonyPatch("UpdateCashDragAmount")]
		[HarmonyPrefix]
		private static bool UpdateCashDragAmount(ItemUIManager __instance, CashInstance instance)
		{
			float[] array = new float[3] { 50f, 10f, 1f };
			float[] array2 = new float[3] { 100f, 10f, 1f };
			float num = 0f;
			if (GameInput.MouseScrollDelta > 0f)
			{
				for (int i = 0; i < array.Length; i++)
				{
					if (__instance.draggedCashAmount >= array2[i])
					{
						num = array[i];
						break;
					}
				}
			}
			else if (GameInput.MouseScrollDelta < 0f)
			{
				for (int j = 0; j < array.Length; j++)
				{
					if (__instance.draggedCashAmount > array2[j])
					{
						num = 0f - array[j];
						break;
					}
				}
			}
			if (num != 0f)
			{
				__instance.draggedCashAmount = Mathf.Clamp(__instance.draggedCashAmount + num, 1f, Mathf.Min(instance.Balance, (float)CashItemLimit));
			}
			return false;
		}

		[HarmonyPatch(typeof(ItemUIManager), "Update")]
		[HarmonyPrefix]
		private static bool Update(ItemUIManager __instance)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			if (GameInput.GetCurrentInputDeviceIsGamepad())
			{
				__instance.UpdateControllerTooltip();
				return false;
			}
			__instance.HoveredSlot = null;
			if (__instance.DraggingEnabled)
			{
				ECursorType cursorAppearance = (ECursorType)0;
				__instance.HoveredSlot = __instance.GetHoveredItemSlot();
				if ((Object)(object)__instance.HoveredSlot != (Object)null && __instance.CanDragFromSlot(__instance.HoveredSlot))
				{
					cursorAppearance = (ECursorType)2;
				}
				if ((Object)(object)__instance.HoveredSlot != (Object)null && (Object)(object)__instance.draggedSlot == (Object)null && __instance.HoveredSlot.assignedSlot != null && __instance.HoveredSlot.assignedSlot.Quantity > 0)
				{
					if (__instance.InfoPanel.CurrentItem != __instance.HoveredSlot.assignedSlot.ItemInstance && __instance.HoveredSlot.ShowItemInfoPanelWhenHovered())
					{
						__instance.InfoPanel.Open(__instance.HoveredSlot.assignedSlot.ItemInstance, __instance.HoveredSlot.Rect);
					}
				}
				else
				{
					ItemDefinitionInfoHoverable hoveredItemInfo = __instance.GetHoveredItemInfo();
					if ((Object)(object)hoveredItemInfo != (Object)null && Utils.Is<RectTransform>((object)((Component)hoveredItemInfo).transform, out RectTransform result))
					{
						__instance.InfoPanel.Open(hoveredItemInfo.AssignedItem, result);
					}
					else if (__instance.InfoPanel.IsOpen)
					{
						__instance.InfoPanel.Close();
					}
				}
				if ((Object)(object)__instance.draggedSlot != (Object)null)
				{
					cursorAppearance = (ECursorType)3;
					if (!GameInput.GetButton((ButtonCode)0) && !GameInput.GetButton((ButtonCode)1) && !GameInput.GetButton((ButtonCode)2))
					{
						__instance.EndDrag();
					}
				}
				else if ((GameInput.GetButtonDown((ButtonCode)0) || GameInput.GetButtonDown((ButtonCode)1) || GameInput.GetButtonDown((ButtonCode)2)) && (Object)(object)__instance.HoveredSlot != (Object)null)
				{
					__instance.SlotClicked(__instance.HoveredSlot);
				}
				Singleton<CursorManager>.Instance.SetCursorAppearance(cursorAppearance);
			}
			if ((Object)(object)__instance.draggedSlot != (Object)null && __instance.customDragAmount)
			{
				if (__instance.isDraggingCash && Utils.Is<CashInstance>((object)__instance.draggedSlot.assignedSlot.ItemInstance, out CashInstance result2))
				{
					__instance.UpdateCashDragAmount(result2);
				}
				else if (GameInput.MouseScrollDelta > 0f)
				{
					__instance.SetDraggedAmount(Mathf.Clamp(__instance.draggedAmount + 1, 1, __instance.draggedSlot.assignedSlot.Quantity));
				}
				else if (GameInput.MouseScrollDelta < 0f)
				{
					__instance.SetDraggedAmount(Mathf.Clamp(__instance.draggedAmount - 1, 1, __instance.draggedSlot.assignedSlot.Quantity));
				}
			}
			return false;
		}

		static CashPatches()
		{
			ItemRegister? obj = Singleton<Registry>.Instance.ItemRegistry.AsEnumerable<ItemRegister>().FirstOrDefault((Func<ItemRegister, bool>)((ItemRegister x) => x.ID == "cash"));
			int? obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				ItemDefinition definition = obj.Definition;
				obj2 = ((definition != null) ? new int?(((BaseItemDefinition)definition).StackLimit) : ((int?)null));
			}
			CashItemLimit = obj2 ?? 1000;
			Logger = Melon<CustomStack>.Logger;
		}
	}
	[HarmonyPatch(typeof(DryingRack))]
	internal static class DryingRackPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void ApplyCustomLimitAwake(DryingRack __instance)
		{
			if (__instance == null)
			{
				return;
			}
			_ = __instance.ItemCapacity;
			if (false)
			{
				return;
			}
			__instance.ItemCapacity = CustomStack.DryingItemCapacity.Value;
			if (__instance == null)
			{
				return;
			}
			DryingRackConfiguration stationConfiguration = __instance.stationConfiguration;
			if (stationConfiguration == null)
			{
				return;
			}
			NumberField startThreshold = stationConfiguration.StartThreshold;
			if (startThreshold != null)
			{
				_ = startThreshold.MaxValue;
				if (0 == 0)
				{
					__instance.stationConfiguration.StartThreshold.MaxValue = CustomStack.DryingItemCapacity.Value;
				}
			}
		}

		internal static void ApplyCustomLimitRuntime(int _, int current)
		{
			Il2CppArrayBase<DryingRack> val = Object.FindObjectsOfType<DryingRack>();
			foreach (DryingRack item in val)
			{
				item.ItemCapacity = current;
				item.stationConfiguration.StartThreshold.MaxValue = current;
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation))]
	internal static class MixingStationPatches
	{
		[HarmonyPatch("NetworkInitialize___Early")]
		[HarmonyPostfix]
		private static void ApplyCustomLimitAwake(MixingStation __instance)
		{
			if (__instance == null)
			{
				return;
			}
			_ = __instance.MaxMixQuantity;
			if (false)
			{
				return;
			}
			__instance.MaxMixQuantity = CustomStack.MaxMixQuantity.Value;
			if (__instance == null)
			{
				return;
			}
			MixingStationConfiguration stationConfiguration = __instance.stationConfiguration;
			if (stationConfiguration == null)
			{
				return;
			}
			NumberField startThrehold = stationConfiguration.StartThrehold;
			if (startThrehold != null)
			{
				_ = startThrehold.MaxValue;
				if (0 == 0)
				{
					__instance.stationConfiguration.StartThrehold.MaxValue = CustomStack.MaxMixQuantity.Value;
				}
			}
		}

		internal static void ApplyCustomLimitRuntime(int _, int current)
		{
			Il2CppArrayBase<MixingStation> val = Object.FindObjectsOfType<MixingStation>();
			foreach (MixingStation item in val)
			{
				item.MaxMixQuantity = current;
				item.stationConfiguration.StartThrehold.MaxValue = current;
			}
		}
	}
	internal static class StorageVisualizerState
	{
		public class State
		{
			public bool RefreshQueued;
		}

		public static ConditionalWeakTable<StorageVisualizer, State> Table = new ConditionalWeakTable<StorageVisualizer, State>();
	}
	[HarmonyPatch(typeof(StorageVisualizer), "QueueRefresh")]
	internal class QueueRefreshPatch
	{
		private static bool Prefix(StorageVisualizer __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (__instance.BlockRefreshes)
			{
				return false;
			}
			StorageVisualizerState.State orCreateValue = StorageVisualizerState.Table.GetOrCreateValue(__instance);
			if (orCreateValue == null)
			{
				return true;
			}
			if (orCreateValue.RefreshQueued)
			{
				Melon<CustomStack>.Logger.Debug("Queued refresh for " + ((__instance != null) ? ((Object)__instance).name : null) + ". Skipping.");
				return false;
			}
			orCreateValue.RefreshQueued = true;
			Melon<CustomStack>.Logger.Debug("Starting delayed refresh for " + ((__instance != null) ? ((Object)__instance).name : null));
			MelonCoroutines.Start(DelayedRefresh(__instance));
			return false;
		}

		private static IEnumerator DelayedRefresh(StorageVisualizer instance)
		{
			yield return null;
			StorageVisualizerState.State state = StorageVisualizerState.Table.GetOrCreateValue(instance);
			if (state != null)
			{
				state.RefreshQueued = false;
				if ((Object)(object)instance != (Object)null)
				{
					instance.RefreshVisuals();
				}
			}
		}
	}
}
namespace CustomStack.Helpers
{
	public static class MelonLoggerExtensions
	{
		public static void Debug(this Instance logger, string message, bool stacktrace = true)
		{
			MelonDebug.Msg(stacktrace ? ("[" + GetCallerInfo() + "] " + message) : message);
		}

		private static string GetCallerInfo()
		{
			StackTrace stackTrace = new StackTrace();
			for (int i = 2; i < stackTrace.FrameCount; i++)
			{
				StackFrame frame = stackTrace.GetFrame(i);
				MethodBase method = frame.GetMethod();
				if (!(method?.DeclaringType == null))
				{
					return method.DeclaringType.FullName + "." + method.Name;
				}
			}
			return "unknown";
		}
	}
	public static class Il2CppListExtensions
	{
		public static IEnumerable<T> AsEnumerable<T>(this List<T> list)
		{
			return list ?? new List<T>();
		}

		public static List<T> ToIl2CppList<T>(this IEnumerable<T> source)
		{
			List<T> val = new List<T>();
			foreach (T item in source)
			{
				val.Add(item);
			}
			return val;
		}

		public static List<T> ConvertToList<T>(List<T> il2CppList)
		{
			List<T> list = new List<T>();
			T[] collection = Il2CppArrayBase<T>.op_Implicit(il2CppList.ToArray());
			list.AddRange(collection);
			return list;
		}

		public static IEnumerable<T> AsEnumerable<T>(this List<T> list)
		{
			IEnumerable<T> result;
			if (list != null)
			{
				result = ((IEnumerable<T>)list._items).Take(list._size);
			}
			else
			{
				IEnumerable<T> enumerable = Array.Empty<T>();
				result = enumerable;
			}
			return result;
		}
	}
	public static class Utils
	{
		private static readonly Instance Logger = new Instance("CustomStack-Utils");

		public static T FindObjectByName<T>(string objectName) where T : Object
		{
			try
			{
				foreach (T item in Resources.FindObjectsOfTypeAll<T>())
				{
					if (((Object)item).name != objectName)
					{
						continue;
					}
					Logger.Debug($"Found {typeof(T).Name} '{objectName}' directly in loaded objects");
					return item;
				}
				return default(T);
			}
			catch (Exception ex)
			{
				Logger.Error($"Error finding {typeof(T).Name} '{objectName}': {ex.Message}");
				return default(T);
			}
		}

		public static List<T> GetAllComponentsInChildrenRecursive<T>(GameObject obj) where T : Component
		{
			List<T> list = new List<T>();
			if ((Object)(object)obj == (Object)null)
			{
				return list;
			}
			T[] array = Il2CppArrayBase<T>.op_Implicit(obj.GetComponents<T>());
			if (array.Length != 0)
			{
				list.AddRange(array);
			}
			for (int i = 0; i < obj.transform.childCount; i++)
			{
				Transform child = obj.transform.GetChild(i);
				list.AddRange(GetAllComponentsInChildrenRecursive<T>(((Component)child).gameObject));
			}
			return list;
		}

		public static bool Is<T>(object obj, out T result) where T : Object
		{
			Object val = (Object)((obj is Object) ? obj : null);
			if (val != null)
			{
				Type val2 = Il2CppType.Of<T>();
				Type il2CppType = val.GetIl2CppType();
				if (val2.IsAssignableFrom(il2CppType))
				{
					result = ((Il2CppObjectBase)val).TryCast<T>();
					return result != null;
				}
			}
			result = default(T);
			return false;
		}

		public static IEnumerator WaitForPlayer(IEnumerator routine)
		{
			while ((Object)(object)Player.Local == (Object)null || (Object)(object)((Component)Player.Local).gameObject == (Object)null)
			{
				yield return null;
			}
			MelonCoroutines.Start(routine);
		}

		public static IEnumerator WaitForNetwork(IEnumerator routine)
		{
			while (!InstanceFinder.IsServer && !InstanceFinder.IsClient)
			{
				yield return null;
			}
			MelonCoroutines.Start(routine);
		}

		public static IEnumerator WaitForNotNull(object? obj, float timeout = float.NaN, Action onTimeout = null, Action onFinish = null)
		{
			float startTime = Time.time;
			while (obj == null)
			{
				if (!float.IsNaN(timeout) && Time.time - startTime > timeout)
				{
					onTimeout?.Invoke();
					yield break;
				}
				yield return null;
			}
			onFinish?.Invoke();
		}

		public static IEnumerator WaitForNetworkSingleton<T>(IEnumerator coroutine) where T : NetworkSingleton<T>
		{
			while (!NetworkSingleton<T>.InstanceExists)
			{
				yield return null;
			}
			yield return coroutine;
		}
	}
}

CustomStack-Mono.dll

Decompiled 9 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using CustomStack;
using CustomStack.Helpers;
using CustomStack.Patches;
using FishNet;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using ScheduleOne;
using ScheduleOne.Core.Items.Framework;
using ScheduleOne.DevUtilities;
using ScheduleOne.ItemFramework;
using ScheduleOne.Management;
using ScheduleOne.Money;
using ScheduleOne.ObjectScripts;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Product;
using ScheduleOne.Storage;
using ScheduleOne.UI;
using ScheduleOne.UI.Items;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(global::CustomStack.CustomStack), "CustomStack", "1.1.8", "k073l", null)]
[assembly: MelonColor(1, 255, 0, 0)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CustomStack-Mono")]
[assembly: AssemblyConfiguration("Mono")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+34e336381dff4000536384980ccea01904d555a4")]
[assembly: AssemblyProduct("CustomStack-Mono")]
[assembly: AssemblyTitle("CustomStack-Mono")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CustomStack
{
	public static class BuildInfo
	{
		public const string Name = "CustomStack";

		public const string Description = "Custom stack limits, per item";

		public const string Author = "k073l";

		public const string Version = "1.1.8";
	}
	public class CustomStack : MelonMod
	{
		private static Instance _logger;

		private static MelonPreferences_Category _category;

		private static readonly Dictionary<string, MelonPreferences_Entry<int>> Entries = new Dictionary<string, MelonPreferences_Entry<int>>();

		private static Dictionary<string, int> _defaultLimits = new Dictionary<string, int>();

		private static bool _populatedDefaultLimits = false;

		private static MelonPreferences_Category _stations;

		internal static MelonPreferences_Entry<int> MaxMixQuantity;

		internal static MelonPreferences_Entry<int> DryingItemCapacity;

		public override void OnInitializeMelon()
		{
			_logger = ((MelonBase)this).LoggerInstance;
			_logger.Msg("CustomStack initialized.");
			_category = MelonPreferences.CreateCategory("CustomStack", "Stack Sizes");
			_category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomStack.cfg"));
			_stations = MelonPreferences.CreateCategory("CustomStack_Stations", "Station Capacities");
			_stations.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomStack.cfg"));
			MaxMixQuantity = _stations.CreateEntry<int>("MaxMixQuantity", 20, "Max Mixing Quantity", "Controls how much you can mix in Mixing Stations", false, false, (ValueValidator)(object)new ValueRange<int>(1, 10000), (string)null);
			((MelonEventBase<LemonAction<int, int>>)(object)MaxMixQuantity.OnEntryValueChanged).Subscribe((LemonAction<int, int>)MixingStationPatches.ApplyCustomLimitRuntime, 0, false);
			DryingItemCapacity = _stations.CreateEntry<int>("DryingItemCapacity", 20, "Drying Rack Capacity", "Controls how many items you can dry in Drying Racks", false, false, (ValueValidator)(object)new ValueRange<int>(1, 101000), (string)null);
			((MelonEventBase<LemonAction<int, int>>)(object)DryingItemCapacity.OnEntryValueChanged).Subscribe((LemonAction<int, int>)DryingRackPatches.ApplyCustomLimitRuntime, 0, false);
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName == "Menu")
			{
				LoadAllItems();
			}
			_logger.Debug("Scene loaded: " + sceneName);
		}

		private static void LoadAllItems()
		{
			if (_populatedDefaultLimits)
			{
				_logger.Msg("Item limits already set, skipping.");
				return;
			}
			_logger.Msg("Loading item stack limits...");
			IOrderedEnumerable<ItemRegister> orderedEnumerable = from register in Singleton<Registry>.Instance.ItemRegistry.AsEnumerable()
				orderby register.name
				select register;
			_category.LoadFromFile(false);
			foreach (ItemRegister item in orderedEnumerable)
			{
				string iD = item.ID;
				int stackLimit = ((BaseItemDefinition)item.Definition).StackLimit;
				if (!Entries.ContainsKey(iD))
				{
					MelonPreferences_Entry<int> value = _category.CreateEntry<int>(iD, stackLimit, ((BaseItemDefinition)item.Definition).Name, (string)null, false, false, (ValueValidator)(object)((iD == "cash") ? new ValueRange<int>(1, 100000) : new ValueRange<int>(1, 10000)), (string)null);
					Entries[iD] = value;
				}
				if (!_populatedDefaultLimits && !_defaultLimits.ContainsKey(iD))
				{
					_defaultLimits[iD] = stackLimit;
				}
				MelonPreferences_Entry<int> val = Entries[iD];
				int value2 = val.Value;
				string text = iD;
				string text2 = text;
				if (!(text2 == "cash"))
				{
					if (text2 == "defaultweed")
					{
						((MelonPreferences_Entry)Entries[iD]).Description = "Changes to this value may be required for changing stack size of mixed weed products.";
					}
					((BaseItemDefinition)item.Definition).StackLimit = value2;
					if (value2 != _defaultLimits[iD])
					{
						_logger.Msg($"{iD} limit overridden: {_defaultLimits[iD]} → {value2}");
					}
				}
				else if (value2 == _defaultLimits[iD])
				{
					_logger.Msg("Cash stack limit not configured, forcing runtime limit to 1k (config not changed).");
					((BaseItemDefinition)item.Definition).StackLimit = 1000;
				}
				else
				{
					((BaseItemDefinition)item.Definition).StackLimit = value2;
					if (value2 != _defaultLimits[iD])
					{
						_logger.Msg($"cash limit overridden by user: {_defaultLimits[iD]} → {value2}");
					}
				}
			}
			_populatedDefaultLimits = true;
			foreach (KeyValuePair<string, MelonPreferences_Entry<int>> entryKv in Entries)
			{
				((MelonEventBase<LemonAction<int, int>>)(object)entryKv.Value.OnEntryValueChanged).Subscribe((LemonAction<int, int>)delegate(int was, int currentLimit)
				{
					UpdateLimitRuntime(was, currentLimit, entryKv.Key, entryKv.Value);
				}, 0, false);
			}
		}

		private static void UpdateLimitRuntime(int previousLimit, int currentLimit, string itemID, MelonPreferences_Entry<int> entry)
		{
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			if (previousLimit == currentLimit)
			{
				return;
			}
			if (previousLimit > currentLimit)
			{
				Melon<CustomStack>.Logger.Warning($"Cannot lower stack limit ({previousLimit} -> {currentLimit}) of {itemID} at runtime! " + "Close the game and edit the file manually if you want to proceed with a potentially destructive action.");
				entry.Value = previousLimit;
				return;
			}
			ItemRegister val = Singleton<Registry>.Instance.ItemRegistry.AsEnumerable().FirstOrDefault((Func<ItemRegister, bool>)((ItemRegister register) => register.ID == itemID));
			if (val == null)
			{
				return;
			}
			ItemDefinition definition = val.Definition;
			if (definition == null)
			{
				return;
			}
			_ = ((BaseItemDefinition)definition).StackLimit;
			if (false)
			{
				return;
			}
			((BaseItemDefinition)val.Definition).StackLimit = currentLimit;
			List<string> idsToSearch = new List<string>(1) { itemID };
			if (Utils.Is<ProductDefinition>(val.Definition, out var result) && NetworkSingleton<ProductManager>.InstanceExists)
			{
				EDrugType val2 = (EDrugType)0;
				try
				{
					val2 = result.DrugType;
				}
				catch (Exception)
				{
					Melon<CustomStack>.Logger.Warning("Failed to read drug type for " + itemID + ". Assuming weed.");
				}
				foreach (ProductDefinition allProduct in NetworkSingleton<ProductManager>.Instance.AllProducts)
				{
					try
					{
						if ((Object)(object)allProduct != (Object)null && allProduct.DrugType == val2)
						{
							idsToSearch.Add(((BaseItemDefinition)allProduct).ID);
						}
					}
					catch (Exception ex2)
					{
						Melon<CustomStack>.Logger.Warning("Failed to read DrugType for product '" + (((BaseItemDefinition)(allProduct?)).ID ?? "<null>") + "': " + ex2.Message);
					}
				}
			}
			IEnumerable<BaseItemDefinition> enumerable = from def in Object.FindObjectsOfType<BaseItemDefinition>().AsEnumerable()
				where idsToSearch.Contains(def.ID)
				select def;
			foreach (BaseItemDefinition item in enumerable)
			{
				item.StackLimit = currentLimit;
			}
			IEnumerable<string> values = enumerable.Select((BaseItemDefinition def) => def.ID);
			Melon<CustomStack>.Logger.Msg($"Updated limit of {string.Join(',', values)} to {currentLimit}.");
		}
	}
}
namespace CustomStack.Patches
{
	[HarmonyPatch(typeof(ItemUIManager))]
	public static class CashPatches
	{
		private static int CashItemLimit = ((BaseItemDefinition)(Singleton<Registry>.Instance.ItemRegistry.AsEnumerable().FirstOrDefault((Func<ItemRegister, bool>)((ItemRegister x) => x.ID == "cash"))?.Definition?)).StackLimit ?? 1000;

		private static Instance Logger = Melon<CustomStack>.Logger;

		[HarmonyPatch("StartDragCash")]
		[HarmonyPrefix]
		private static bool StartDragCash(ItemUIManager __instance)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			Logger.Debug("StartDragCash: top");
			ItemInstance itemInstance = __instance.draggedSlot.assignedSlot.ItemInstance;
			if (!Utils.Is<CashInstance>(itemInstance, out var result))
			{
				Logger.Debug("StartDragCash: cashInstance is null");
			}
			Logger.Debug("StartDragCash: got cashInstance");
			__instance.draggedCashAmount = Mathf.Min(result.Balance, (float)CashItemLimit);
			__instance.draggedAmount = 1;
			__instance.customDragAmount = GameInput.GetCurrentInputDeviceIsGamepad();
			Logger.Debug("Set draggedCashAmount and draggedAmount");
			if (GameInput.GetButtonDown((ButtonCode)1) || __instance.controllerQuickMoveSingle)
			{
				Logger.Debug("StartDragCash: secondary click");
				__instance.draggedAmount = 1;
				__instance.draggedCashAmount = Mathf.Min(result.Balance, Mathf.Floor((float)CashItemLimit / 10f));
				__instance.mouseOffset += new Vector2(-10f, -15f);
				__instance.customDragAmount = true;
			}
			Logger.Debug("StartDragCash: draggedAmount check");
			if (__instance.draggedCashAmount <= 0f)
			{
				Logger.Debug("StartDragCash: no cash to drag");
				__instance.draggedSlot = null;
			}
			else if ((Input.GetKey((KeyCode)304) || __instance.canControllerQuickMove) && __instance.QuickMoveEnabled)
			{
				Logger.Debug("StartDragCash: quickmove");
				List<ItemSlot> quickMoveSlots = __instance.GetQuickMoveSlots(__instance.draggedSlot.assignedSlot);
				if (quickMoveSlots.Count > 0)
				{
					Logger.Debug("StartDragCash: quickmove slots found");
					Debug.Log((object)("Quick-moving " + __instance.draggedAmount + " items..."));
					float draggedCashAmount = __instance.draggedCashAmount;
					float num = 0f;
					for (int i = 0; i < quickMoveSlots.Count; i++)
					{
						if (num >= (float)__instance.draggedAmount)
						{
							break;
						}
						ItemSlot val = quickMoveSlots[i];
						if (val.ItemInstance != null)
						{
							if (Utils.Is<CashInstance>(val.ItemInstance, out var result2))
							{
								float num2 = 0f;
								num2 = ((!Utils.Is<CashSlot>(val, out var _)) ? Mathf.Min(draggedCashAmount, (float)CashItemLimit - result2.Balance) : Mathf.Min(draggedCashAmount, float.MaxValue - result2.Balance));
								result2.ChangeBalance(num2);
								val.ReplicateStoredInstance();
								num += num2;
							}
						}
						else
						{
							if (!Utils.Is<CashInstance>(Registry.GetItem("cash").GetDefaultInstance(1), out var result4))
							{
								Logger.Error("StartDragCash: failed to get default CashInstance");
								return false;
							}
							result4.SetBalance(__instance.draggedCashAmount, false);
							val.SetStoredItem((ItemInstance)(object)result4, false);
							num += __instance.draggedCashAmount;
						}
					}
					if (num >= result.Balance)
					{
						__instance.draggedSlot.assignedSlot.ClearStoredInstance(false);
					}
					else
					{
						result.ChangeBalance(0f - num);
						__instance.draggedSlot.assignedSlot.ReplicateStoredInstance();
					}
				}
				if (__instance.onItemMoved != null)
				{
					Logger.Debug("StartDragCash: invoking onItemMoved");
					__instance.onItemMoved.Invoke();
				}
				__instance.draggedSlot = null;
			}
			else
			{
				Logger.Debug("StartDragCash: standard drag");
				if (__instance.onDragStart != null)
				{
					Logger.Debug("StartDragCash: invoking onDragStart");
					__instance.onDragStart.Invoke();
				}
				if ((object)__instance.draggedSlot.assignedSlot != PlayerSingleton<PlayerInventory>.Instance.cashSlot)
				{
					Logger.Debug("StartDragCash: play cash slot hint anim");
					Singleton<HUD>.Instance.CashSlotHintAnim.Play();
				}
				__instance.tempIcon = __instance.draggedSlot.DuplicateIcon(((Component)Singleton<HUD>.Instance).transform, __instance.draggedAmount);
				((TMP_Text)((Component)((Transform)__instance.tempIcon).Find("Balance")).GetComponent<TextMeshProUGUI>()).text = MoneyManager.FormatAmount(__instance.draggedCashAmount, false, false);
				__instance.draggedSlot.IsBeingDragged = true;
				if (__instance.draggedCashAmount >= result.Balance)
				{
					Logger.Debug("StartDragCash: hide dragged slot");
					__instance.draggedSlot.SetVisible(false);
				}
				else
				{
					Logger.Debug("StartDragCash: update cash balance display");
					if (Utils.Is<ItemUI_Cash>(__instance.draggedSlot.ItemUI, out var result5))
					{
						result5.SetDisplayedBalance(result.Balance - __instance.draggedCashAmount);
					}
					else
					{
						Logger.Debug("StartDragCash: draggedSlot is not ItemUI_Cash, cannot update balance display");
					}
				}
			}
			Logger.Debug("StartDragCash: end");
			return false;
		}

		[HarmonyPatch("EndCashDrag")]
		[HarmonyPrefix]
		private static bool EndCashDrag(ItemUIManager __instance)
		{
			CashInstance val = null;
			if ((Object)(object)__instance.draggedSlot != (Object)null && __instance.draggedSlot.assignedSlot != null)
			{
				if (!Utils.Is<CashInstance>(__instance.draggedSlot.assignedSlot.ItemInstance, out var result))
				{
					Logger.Error("EndCashDrag: draggedSlot.assignedSlot.ItemInstance is not CashInstance");
					return true;
				}
				val = result;
			}
			if (val == null)
			{
				Logger.Error("EndCashDrag: cashInstance is null");
				return true;
			}
			Singleton<HUD>.Instance.CashSlotHintAnim.Stop();
			Singleton<HUD>.Instance.CashSlotHintAnimCanvasGroup.alpha = 0f;
			bool flag = false;
			ItemSlotUI hoveredSlot = __instance.HoveredSlot;
			ItemSlot val2 = ((hoveredSlot != null) ? hoveredSlot.assignedSlot : null);
			ItemSlotUI draggedSlot = __instance.draggedSlot;
			object obj;
			if (draggedSlot == null)
			{
				obj = null;
			}
			else
			{
				ItemSlot assignedSlot = draggedSlot.assignedSlot;
				obj = ((assignedSlot != null) ? assignedSlot.ItemInstance : null);
			}
			ItemInstance val3 = (ItemInstance)obj;
			if (__instance.CanDragFromSlot(__instance.draggedSlot) && (Object)(object)__instance.HoveredSlot != (Object)null && __instance.CanCashBeDraggedIntoSlot(__instance.HoveredSlot) && val2 != null && val3 != null && !val2.IsLocked && !val2.IsAddLocked)
			{
				MethodInfo method = ((object)val2).GetType().GetMethod("DoesItemMatchHardFilters");
				MethodInfo method2 = ((object)val2).GetType().GetMethod("DoesItemMatchPlayerFilters");
				if (method != null && method2 != null)
				{
					bool flag2 = (bool)method.Invoke(val2, new object[1] { val3 });
					bool flag3 = (bool)method2.Invoke(val2, new object[1] { val3 });
					flag = flag2 && flag3;
				}
				else
				{
					MethodInfo method3 = ((object)val2).GetType().GetMethod("DoesItemMatchFilters");
					if (method3 != null)
					{
						flag = (bool)method3.Invoke(val2, new object[1] { val3 });
					}
				}
			}
			if (flag)
			{
				if (Utils.Is<HotbarSlot>(__instance.HoveredSlot.assignedSlot, out var _) && !Utils.Is<CashSlot>(__instance.HoveredSlot.assignedSlot, out var result3))
				{
					__instance.HoveredSlot = (ItemSlotUI)(object)((Component)Singleton<HUD>.Instance.cashSlotUI).GetComponent<CashSlotUI>();
				}
				float num = Mathf.Min(__instance.draggedCashAmount, val.Balance);
				if (num > 0f)
				{
					float num2 = num;
					if (__instance.HoveredSlot.assignedSlot.ItemInstance != null)
					{
						if (!Utils.Is<CashInstance>(__instance.HoveredSlot.assignedSlot.ItemInstance, out var result4))
						{
							Logger.Error("EndCashDrag: HoveredSlot.assignedSlot.ItemInstance is not CashInstance");
						}
						num2 = ((!Utils.Is<CashSlot>(__instance.HoveredSlot.assignedSlot, out result3)) ? Mathf.Min(num, (float)CashItemLimit - result4.Balance) : Mathf.Min(num, float.MaxValue - result4.Balance));
						result4.ChangeBalance(num2);
						__instance.HoveredSlot.assignedSlot.ReplicateStoredInstance();
					}
					else
					{
						if (!Utils.Is<CashInstance>(Registry.GetItem("cash").GetDefaultInstance(1), out var result5))
						{
							Logger.Error("EndCashDrag: failed to get default CashInstance");
						}
						result5.SetBalance(num2, false);
						__instance.HoveredSlot.assignedSlot.SetStoredItem((ItemInstance)(object)result5, false);
					}
					if (num2 >= val.Balance)
					{
						__instance.draggedSlot.assignedSlot.ClearStoredInstance(false);
					}
					else
					{
						val.ChangeBalance(0f - num2);
						__instance.draggedSlot.assignedSlot.ReplicateStoredInstance();
					}
				}
			}
			__instance.draggedSlot.SetVisible(true);
			__instance.draggedSlot.UpdateUI();
			__instance.draggedSlot.IsBeingDragged = false;
			__instance.draggedSlot = null;
			Object.Destroy((Object)(object)((Component)__instance.tempIcon).gameObject);
			Singleton<CursorManager>.Instance.SetCursorAppearance((ECursorType)0);
			return false;
		}

		[HarmonyPatch("UpdateCashDragAmount")]
		[HarmonyPrefix]
		private static bool UpdateCashDragAmount(ItemUIManager __instance, CashInstance instance)
		{
			float[] array = new float[3] { 50f, 10f, 1f };
			float[] array2 = new float[3] { 100f, 10f, 1f };
			float num = 0f;
			if (GameInput.MouseScrollDelta > 0f)
			{
				for (int i = 0; i < array.Length; i++)
				{
					if (__instance.draggedCashAmount >= array2[i])
					{
						num = array[i];
						break;
					}
				}
			}
			else if (GameInput.MouseScrollDelta < 0f)
			{
				for (int j = 0; j < array.Length; j++)
				{
					if (__instance.draggedCashAmount > array2[j])
					{
						num = 0f - array[j];
						break;
					}
				}
			}
			if (num != 0f)
			{
				__instance.draggedCashAmount = Mathf.Clamp(__instance.draggedCashAmount + num, 1f, Mathf.Min(instance.Balance, (float)CashItemLimit));
			}
			return false;
		}

		[HarmonyPatch(typeof(ItemUIManager), "Update")]
		[HarmonyPrefix]
		private static bool Update(ItemUIManager __instance)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			if (GameInput.GetCurrentInputDeviceIsGamepad())
			{
				__instance.UpdateControllerTooltip();
				return false;
			}
			__instance.HoveredSlot = null;
			if (__instance.DraggingEnabled)
			{
				ECursorType cursorAppearance = (ECursorType)0;
				__instance.HoveredSlot = __instance.GetHoveredItemSlot();
				if ((Object)(object)__instance.HoveredSlot != (Object)null && __instance.CanDragFromSlot(__instance.HoveredSlot))
				{
					cursorAppearance = (ECursorType)2;
				}
				if ((Object)(object)__instance.HoveredSlot != (Object)null && (Object)(object)__instance.draggedSlot == (Object)null && __instance.HoveredSlot.assignedSlot != null && __instance.HoveredSlot.assignedSlot.Quantity > 0)
				{
					if (__instance.InfoPanel.CurrentItem != __instance.HoveredSlot.assignedSlot.ItemInstance && __instance.HoveredSlot.ShowItemInfoPanelWhenHovered())
					{
						__instance.InfoPanel.Open(__instance.HoveredSlot.assignedSlot.ItemInstance, __instance.HoveredSlot.Rect);
					}
				}
				else
				{
					ItemDefinitionInfoHoverable hoveredItemInfo = __instance.GetHoveredItemInfo();
					if ((Object)(object)hoveredItemInfo != (Object)null && Utils.Is<RectTransform>(((Component)hoveredItemInfo).transform, out var result))
					{
						__instance.InfoPanel.Open(hoveredItemInfo.AssignedItem, result);
					}
					else if (__instance.InfoPanel.IsOpen)
					{
						__instance.InfoPanel.Close();
					}
				}
				if ((Object)(object)__instance.draggedSlot != (Object)null)
				{
					cursorAppearance = (ECursorType)3;
					if (!GameInput.GetButton((ButtonCode)0) && !GameInput.GetButton((ButtonCode)1) && !GameInput.GetButton((ButtonCode)2))
					{
						__instance.EndDrag();
					}
				}
				else if ((GameInput.GetButtonDown((ButtonCode)0) || GameInput.GetButtonDown((ButtonCode)1) || GameInput.GetButtonDown((ButtonCode)2)) && (Object)(object)__instance.HoveredSlot != (Object)null)
				{
					__instance.SlotClicked(__instance.HoveredSlot);
				}
				Singleton<CursorManager>.Instance.SetCursorAppearance(cursorAppearance);
			}
			if ((Object)(object)__instance.draggedSlot != (Object)null && __instance.customDragAmount)
			{
				if (__instance.isDraggingCash && Utils.Is<CashInstance>(__instance.draggedSlot.assignedSlot.ItemInstance, out var result2))
				{
					__instance.UpdateCashDragAmount(result2);
				}
				else if (GameInput.MouseScrollDelta > 0f)
				{
					__instance.SetDraggedAmount(Mathf.Clamp(__instance.draggedAmount + 1, 1, __instance.draggedSlot.assignedSlot.Quantity));
				}
				else if (GameInput.MouseScrollDelta < 0f)
				{
					__instance.SetDraggedAmount(Mathf.Clamp(__instance.draggedAmount - 1, 1, __instance.draggedSlot.assignedSlot.Quantity));
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(DryingRack))]
	internal static class DryingRackPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void ApplyCustomLimitAwake(DryingRack __instance)
		{
			if (__instance == null)
			{
				return;
			}
			_ = __instance.ItemCapacity;
			if (false)
			{
				return;
			}
			__instance.ItemCapacity = CustomStack.DryingItemCapacity.Value;
			if (__instance == null)
			{
				return;
			}
			DryingRackConfiguration stationConfiguration = __instance.stationConfiguration;
			if (stationConfiguration == null)
			{
				return;
			}
			NumberField startThreshold = stationConfiguration.StartThreshold;
			if (startThreshold != null)
			{
				_ = startThreshold.MaxValue;
				if (0 == 0)
				{
					__instance.stationConfiguration.StartThreshold.MaxValue = CustomStack.DryingItemCapacity.Value;
				}
			}
		}

		internal static void ApplyCustomLimitRuntime(int _, int current)
		{
			DryingRack[] array = Object.FindObjectsOfType<DryingRack>();
			DryingRack[] array2 = array;
			foreach (DryingRack val in array2)
			{
				val.ItemCapacity = current;
				val.stationConfiguration.StartThreshold.MaxValue = current;
			}
		}
	}
	[HarmonyPatch(typeof(MixingStation))]
	internal static class MixingStationPatches
	{
		[HarmonyPatch("NetworkInitialize___Early")]
		[HarmonyPostfix]
		private static void ApplyCustomLimitAwake(MixingStation __instance)
		{
			if (__instance == null)
			{
				return;
			}
			_ = __instance.MaxMixQuantity;
			if (false)
			{
				return;
			}
			__instance.MaxMixQuantity = CustomStack.MaxMixQuantity.Value;
			if (__instance == null)
			{
				return;
			}
			MixingStationConfiguration stationConfiguration = __instance.stationConfiguration;
			if (stationConfiguration == null)
			{
				return;
			}
			NumberField startThrehold = stationConfiguration.StartThrehold;
			if (startThrehold != null)
			{
				_ = startThrehold.MaxValue;
				if (0 == 0)
				{
					__instance.stationConfiguration.StartThrehold.MaxValue = CustomStack.MaxMixQuantity.Value;
				}
			}
		}

		internal static void ApplyCustomLimitRuntime(int _, int current)
		{
			MixingStation[] array = Object.FindObjectsOfType<MixingStation>();
			MixingStation[] array2 = array;
			foreach (MixingStation val in array2)
			{
				val.MaxMixQuantity = current;
				val.stationConfiguration.StartThrehold.MaxValue = current;
			}
		}
	}
	internal static class StorageVisualizerState
	{
		public class State
		{
			public bool RefreshQueued;
		}

		public static ConditionalWeakTable<StorageVisualizer, State> Table = new ConditionalWeakTable<StorageVisualizer, State>();
	}
	[HarmonyPatch(typeof(StorageVisualizer), "QueueRefresh")]
	internal class QueueRefreshPatch
	{
		private static bool Prefix(StorageVisualizer __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (__instance.BlockRefreshes)
			{
				return false;
			}
			StorageVisualizerState.State orCreateValue = StorageVisualizerState.Table.GetOrCreateValue(__instance);
			if (orCreateValue == null)
			{
				return true;
			}
			if (orCreateValue.RefreshQueued)
			{
				Melon<CustomStack>.Logger.Debug("Queued refresh for " + ((__instance != null) ? ((Object)__instance).name : null) + ". Skipping.");
				return false;
			}
			orCreateValue.RefreshQueued = true;
			Melon<CustomStack>.Logger.Debug("Starting delayed refresh for " + ((__instance != null) ? ((Object)__instance).name : null));
			MelonCoroutines.Start(DelayedRefresh(__instance));
			return false;
		}

		private static IEnumerator DelayedRefresh(StorageVisualizer instance)
		{
			yield return null;
			StorageVisualizerState.State state = StorageVisualizerState.Table.GetOrCreateValue(instance);
			if (state != null)
			{
				state.RefreshQueued = false;
				if ((Object)(object)instance != (Object)null)
				{
					instance.RefreshVisuals();
				}
			}
		}
	}
}
namespace CustomStack.Helpers
{
	public static class MelonLoggerExtensions
	{
		public static void Debug(this Instance logger, string message, bool stacktrace = true)
		{
			MelonDebug.Msg(stacktrace ? ("[" + GetCallerInfo() + "] " + message) : message);
		}

		private static string GetCallerInfo()
		{
			StackTrace stackTrace = new StackTrace();
			for (int i = 2; i < stackTrace.FrameCount; i++)
			{
				StackFrame frame = stackTrace.GetFrame(i);
				MethodBase method = frame.GetMethod();
				if (!(method?.DeclaringType == null))
				{
					return method.DeclaringType.FullName + "." + method.Name;
				}
			}
			return "unknown";
		}
	}
	public static class Il2CppListExtensions
	{
		public static IEnumerable<T> AsEnumerable<T>(this List<T> list)
		{
			return list ?? new List<T>();
		}
	}
	public static class Utils
	{
		private static readonly Instance Logger = new Instance("CustomStack-Utils");

		public static T FindObjectByName<T>(string objectName) where T : Object
		{
			try
			{
				T[] array = Resources.FindObjectsOfTypeAll<T>();
				foreach (T val in array)
				{
					if (!(((Object)val).name != objectName))
					{
						Logger.Debug("Found " + typeof(T).Name + " '" + objectName + "' directly in loaded objects");
						return val;
					}
				}
				return default(T);
			}
			catch (Exception ex)
			{
				Logger.Error("Error finding " + typeof(T).Name + " '" + objectName + "': " + ex.Message);
				return default(T);
			}
		}

		public static List<T> GetAllComponentsInChildrenRecursive<T>(GameObject obj) where T : Component
		{
			List<T> list = new List<T>();
			if ((Object)(object)obj == (Object)null)
			{
				return list;
			}
			T[] components = obj.GetComponents<T>();
			if (components.Length != 0)
			{
				list.AddRange(components);
			}
			for (int i = 0; i < obj.transform.childCount; i++)
			{
				Transform child = obj.transform.GetChild(i);
				list.AddRange(GetAllComponentsInChildrenRecursive<T>(((Component)child).gameObject));
			}
			return list;
		}

		public static bool Is<T>(object obj, out T result) where T : class
		{
			if (obj is T val)
			{
				result = val;
				return true;
			}
			result = null;
			return false;
		}

		public static IEnumerator WaitForPlayer(IEnumerator routine)
		{
			while ((Object)(object)Player.Local == (Object)null || (Object)(object)((Component)Player.Local).gameObject == (Object)null)
			{
				yield return null;
			}
			MelonCoroutines.Start(routine);
		}

		public static IEnumerator WaitForNetwork(IEnumerator routine)
		{
			while (!InstanceFinder.IsServer && !InstanceFinder.IsClient)
			{
				yield return null;
			}
			MelonCoroutines.Start(routine);
		}

		public static IEnumerator WaitForNotNull(object? obj, float timeout = float.NaN, Action onTimeout = null, Action onFinish = null)
		{
			float startTime = Time.time;
			while (obj == null)
			{
				if (!float.IsNaN(timeout) && Time.time - startTime > timeout)
				{
					onTimeout?.Invoke();
					yield break;
				}
				yield return null;
			}
			onFinish?.Invoke();
		}

		public static IEnumerator WaitForNetworkSingleton<T>(IEnumerator coroutine) where T : NetworkSingleton<T>
		{
			while (!NetworkSingleton<T>.InstanceExists)
			{
				yield return null;
			}
			yield return coroutine;
		}
	}
}