using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("InfiniteStacks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("InfiniteStacks")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("2b503c2b-8ca8-43aa-a404-7c9e1133c06f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace WacoDev.Valheim.InfiniteStacks;
[BepInPlugin("wacodev.valheim.infinitestacks", "InfiniteStacks", "1.0.2")]
public class InfiniteStacksPlugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(ZNet), "Awake")]
private static class ZNet_Awake_Patch
{
private static void Postfix(ZNet __instance)
{
if (__instance.IsServer())
{
Log.LogInfo((object)"ZNet.Awake (server) — scheduling ApplyInfiniteStacks when ObjectDB is ready.");
((MonoBehaviour)Instance).StartCoroutine(WaitAndApplyOnServer());
}
else
{
Log.LogInfo((object)"ZNet.Awake (client) — not modifying ObjectDB.");
}
}
}
[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
public static class InventoryGrid_UpdateGui_Patch
{
private static void Postfix(InventoryGrid __instance)
{
Log.LogInfo((object)"InfiniteStacks 1.0.2 UpdateGui Postfix.");
if (!_hideMaxInTooltip.Value || !(AccessTools.Field(typeof(InventoryGrid), "m_elements")?.GetValue(__instance) is IList list))
{
return;
}
foreach (object item in list)
{
if (item == null)
{
continue;
}
object obj = AccessTools.Field(item.GetType(), "m_amount")?.GetValue(item);
if (obj == null)
{
continue;
}
PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "text");
if (propertyInfo == null || !propertyInfo.CanRead || !propertyInfo.CanWrite)
{
continue;
}
string text = propertyInfo.GetValue(obj) as string;
if (string.IsNullOrEmpty(text))
{
continue;
}
int num = text.IndexOf('/');
if (num > 0)
{
string text2 = text.Substring(0, num);
if ((object)text != text2)
{
propertyInfo.SetValue(obj, text2);
}
}
}
}
private static Component FindFirstTextComponent(GameObject go)
{
Component[] componentsInChildren = go.GetComponentsInChildren<Component>(true);
Component[] array = componentsInChildren;
foreach (Component val in array)
{
PropertyInfo property = ((object)val).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.CanWrite)
{
return val;
}
}
return null;
}
}
[HarmonyPatch(typeof(Hud), "Update")]
public static class Hud_Update_Patch
{
private static void Postfix(Hud __instance)
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
Transform[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Transform>(true);
foreach (Transform val in componentsInChildren)
{
if (!Object.op_Implicit((Object)(object)val) || string.IsNullOrEmpty(((Object)val).name) || !((Object)val).name.ToLowerInvariant().Contains("hotkey"))
{
continue;
}
Component[] componentsInChildren2 = ((Component)val).gameObject.GetComponentsInChildren<Component>(true);
Component[] array = componentsInChildren2;
foreach (Component val2 in array)
{
if ((Object)(object)val2 == (Object)null)
{
continue;
}
PropertyInfo property = ((object)val2).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
if (property == null || !property.CanRead || !property.CanWrite)
{
continue;
}
string text = property.GetValue(val2) as string;
if (string.IsNullOrEmpty(text))
{
continue;
}
int num = text.IndexOf('/');
if (num > 0)
{
string text2 = text.Substring(0, num);
if ((object)text != text2)
{
property.SetValue(val2, text2);
}
}
}
}
}
}
[CompilerGenerated]
private sealed class <WaitAndApplyOnServer>d__10 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitAndApplyOnServer>d__10(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
goto IL_0044;
case 1:
<>1__state = -1;
goto IL_0044;
case 2:
{
<>1__state = -1;
break;
}
IL_0044:
if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
{
<>2__current = null;
<>1__state = 1;
return true;
}
break;
}
if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null || ObjectDB.instance.m_items.Count == 0)
{
<>2__current = null;
<>1__state = 2;
return true;
}
Log.LogInfo((object)"ObjectDB ready on server; applying InfiniteStacks…");
ApplyInfiniteStacks(ObjectDB.instance);
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public const string PluginGuid = "wacodev.valheim.infinitestacks";
public const string PluginName = "InfiniteStacks";
public const string PluginVersion = "1.0.2";
private static ConfigEntry<int> _maxStackSize;
private static ConfigEntry<bool> _includeEquipment;
private static ConfigEntry<bool> _hideMaxInTooltip;
public static ManualLogSource Log;
internal static InfiniteStacksPlugin Instance;
private void Awake()
{
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Expected O, but got Unknown
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Expected O, but got Unknown
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Expected O, but got Unknown
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_0126: Expected O, but got Unknown
Instance = this;
int num = 1000;
_maxStackSize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Max Stack Size", int.MaxValue, new ConfigDescription("Max stack size to assign to eligible items. Default is effectively infinite.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, int.MaxValue), new object[1]
{
new ConfigurationManagerAttributes
{
Category = "General",
Order = num--
}
}));
_includeEquipment = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Include Equipment (Advanced)", false, new ConfigDescription("If true, also applies to weapons/armor/tools. Not recommended as stacked gear can be awkward.", (AcceptableValueBase)null, new object[1]
{
new ConfigurationManagerAttributes
{
Category = "General",
Order = num--,
IsAdvanced = true
}
}));
_hideMaxInTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Max In Tooltip", true, new ConfigDescription("If true, tooltips show only the current stack count (e.g., “Stack: 1234”) and hide the max (e.g., “/999999”).", (AcceptableValueBase)null, new object[1]
{
new ConfigurationManagerAttributes
{
Category = "General",
Order = num--
}
}));
Harmony val = new Harmony("wacodev.valheim.infinitestacks");
val.PatchAll();
Log = ((BaseUnityPlugin)this).Logger;
((BaseUnityPlugin)this).Logger.LogInfo((object)"InfiniteStacks 1.0.2 loaded.");
}
internal static bool IsEligible(SharedData sd, bool includeEquipment)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Invalid comparison between Unknown and I4
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Invalid comparison between Unknown and I4
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Invalid comparison between Unknown and I4
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Invalid comparison between Unknown and I4
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Invalid comparison between Unknown and I4
ItemType itemType = sd.m_itemType;
if (includeEquipment)
{
return true;
}
return (int)itemType == 1 || (int)itemType == 2 || (int)itemType == 9 || (int)itemType == 13 || (int)itemType == 16;
}
[IteratorStateMachine(typeof(<WaitAndApplyOnServer>d__10))]
internal static IEnumerator WaitAndApplyOnServer()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitAndApplyOnServer>d__10(0);
}
internal static void ApplyInfiniteStacks(ObjectDB db)
{
if (((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) || (Object)(object)db == (Object)null || db.m_items == null)
{
return;
}
int num = Math.Max(1, _maxStackSize.Value);
foreach (GameObject item in db.m_items.Where((GameObject i) => Object.op_Implicit((Object)(object)i)))
{
ItemDrop component = item.GetComponent<ItemDrop>();
if (component?.m_itemData?.m_shared != null)
{
SharedData shared = component.m_itemData.m_shared;
if (IsEligible(shared, _includeEquipment.Value) && shared.m_maxStackSize < num)
{
Log.LogInfo((object)string.Format("{0} {1} Applying {2} to {3}.", "InfiniteStacks", "1.0.2", num, ((Object)component).name));
shared.m_maxStackSize = num;
}
}
}
}
}
internal sealed class ConfigurationManagerAttributes
{
public string Category { get; set; }
public int? Order { get; set; }
public bool? IsAdvanced { get; set; }
public bool? Browsable { get; set; }
public bool? ReadOnly { get; set; }
public bool? HideDefaultButton { get; set; }
public bool? ShowRangeAsPercent { get; set; }
public bool? RequiresRestart { get; set; }
public string Description { get; set; }
}