using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5e2d14ab692a8caa079858192fbfdad2db188d47")]
[assembly: AssemblyProduct("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyTitle("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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 QuickStackStoreItemDrawersCompat
{
[BepInPlugin("pavel.quickstackstore.itemdrawers_compat", "QSS - ItemDrawers Compat (UseItem, MP-safe)", "1.0.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class QuickStackStoreItemDrawersCompatPlugin : BaseUnityPlugin
{
public const string ModGuid = "pavel.quickstackstore.itemdrawers_compat";
public const string ModName = "QSS - ItemDrawers Compat (UseItem, MP-safe)";
public const string ModVersion = "1.0.3";
internal static ConfigEntry<float> SearchRadius;
internal static ConfigEntry<bool> IncludeHotbar;
internal static ConfigEntry<int> MaxDrawersToCheck;
internal static ConfigEntry<bool> PreferQssRadiusIfAvailable;
internal static ConfigEntry<bool> FillEmptyDrawers;
internal static ConfigEntry<bool> DebugLogging;
private void Awake()
{
SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SearchRadius", 10f, "Drawer search radius (meters). Used if PreferQssRadiusIfAvailable=false, or if QSS radius cannot be read.");
IncludeHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeHotbar", false, "If true, quickstack will also move items from hotbar.");
MaxDrawersToCheck = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxDrawersToCheck", 200, "Safety limit: max number of drawers to evaluate (nearest first).");
PreferQssRadiusIfAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreferQssRadiusIfAvailable", true, "If true, tries to read QSS nearby-range config and use it instead of SearchRadius.");
FillEmptyDrawers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FillEmptyDrawers", false, "If false: only drawers that are already set to the same item will be used. If true: if no matching drawer exists, may place into empty drawers.");
DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enable debug logging.");
Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "pavel.quickstackstore.itemdrawers_compat");
((BaseUnityPlugin)this).Logger.LogInfo((object)"QSS - ItemDrawers Compat (UseItem, MP-safe) v1.0.3 loaded");
}
internal static void Dbg(string msg)
{
if (DebugLogging.Value)
{
Logger.CreateLogSource("QSS - ItemDrawers Compat (UseItem, MP-safe)").LogInfo((object)("[QSS-DrawersCompat] " + msg));
}
}
}
[HarmonyPatch]
internal static class QssReportPatch
{
[HarmonyTargetMethod]
private static MethodBase? TargetMethod()
{
Type type = AccessTools.TypeByName("QuickStackStore.QuickStackModule");
if (!(type == null))
{
return AccessTools.Method(type, "ReportQuickStackResult", new Type[2]
{
typeof(Player),
typeof(int)
}, (Type[])null);
}
return null;
}
[HarmonyPrefix]
private static void Prefix(Player player, ref int movedCount)
{
try
{
if (!((Object)(object)player == (Object)null) && !(AccessTools.TypeByName("kg_ItemDrawers.DrawerComponent") == null))
{
int num = DrawerQuickStacker.TryQuickStackIntoNearbyDrawers(player);
if (num > 0)
{
movedCount += num;
}
}
}
catch (Exception arg)
{
Debug.LogError((object)$"[QSS-DrawersCompat] Error: {arg}");
}
}
}
internal static class DrawerQuickStacker
{
private readonly struct DrawerInfo
{
public readonly Component Comp;
public readonly float Distance;
public readonly string CurrentPrefab;
public readonly int CurrentAmount;
public DrawerInfo(Component comp, float dist, string currentPrefab, int currentAmount)
{
Comp = comp;
Distance = dist;
CurrentPrefab = currentPrefab;
CurrentAmount = currentAmount;
}
}
private const string DrawerTypeName = "kg_ItemDrawers.DrawerComponent";
private static MethodInfo? _useItemMethod;
private static MethodInfo? _getInventoryMethod;
private static FieldInfo? _inventoryField;
private static PropertyInfo? _piCurrentPrefab;
private static PropertyInfo? _piCurrentAmount;
public static int TryQuickStackIntoNearbyDrawers(Player player)
{
Player player2 = player;
Type type = AccessTools.TypeByName("kg_ItemDrawers.DrawerComponent");
if (type == null)
{
return 0;
}
if ((object)_useItemMethod == null)
{
_useItemMethod = AccessTools.Method(type, "UseItem", new Type[2]
{
typeof(Humanoid),
typeof(ItemData)
}, (Type[])null);
}
if (_useItemMethod == null)
{
return 0;
}
if ((object)_piCurrentPrefab == null)
{
_piCurrentPrefab = AccessTools.Property(type, "CurrentPrefab");
}
if ((object)_piCurrentAmount == null)
{
_piCurrentAmount = AccessTools.Property(type, "CurrentAmount");
}
float range = GetEffectiveRange();
if (range <= 0.05f)
{
return 0;
}
Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0);
if (array == null || array.Length == 0)
{
return 0;
}
int num = Math.Max(1, QuickStackStoreItemDrawersCompatPlugin.MaxDrawersToCheck.Value);
List<DrawerInfo> list = (from o in array
select (Component)(object)((o is Component) ? o : null) into c
where (Object)(object)c != (Object)null && (Object)(object)c.gameObject != (Object)null && c.gameObject.activeInHierarchy
select new DrawerInfo(c, Vector3.Distance(((Component)player2).transform.position, c.transform.position), ReadCurrentPrefab(c), ReadCurrentAmount(c)) into x
where x.Distance <= range
orderby x.Distance
select x).Take(num).ToList();
if (list.Count == 0)
{
return 0;
}
QuickStackStoreItemDrawersCompatPlugin.Dbg($"Nearby drawers found: {list.Count} (radius={range}, max={num})");
Inventory humanoidInventory = GetHumanoidInventory((Humanoid)(object)player2);
if (humanoidInventory == null)
{
return 0;
}
List<ItemData> list2 = humanoidInventory.GetAllItems()?.ToList();
if (list2 == null || list2.Count == 0)
{
return 0;
}
bool includeHotbar = QuickStackStoreItemDrawersCompatPlugin.IncludeHotbar.Value;
List<ItemData> list3 = (from i in list2
where i != null
where i.m_stack > 0
where includeHotbar || !IsHotbarItem(i)
where i.m_customData == null || i.m_customData.Count == 0
where (Object)(object)i.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)i.m_dropPrefab).name)
select i).ToList();
if (list3.Count == 0)
{
return 0;
}
Dictionary<string, List<DrawerInfo>> dictionary = new Dictionary<string, List<DrawerInfo>>(StringComparer.Ordinal);
List<DrawerInfo> list4 = new List<DrawerInfo>();
foreach (DrawerInfo item in list)
{
if (!string.IsNullOrEmpty(item.CurrentPrefab))
{
if (!dictionary.TryGetValue(item.CurrentPrefab, out var value))
{
value = new List<DrawerInfo>();
dictionary[item.CurrentPrefab] = value;
}
value.Add(item);
}
else
{
list4.Add(item);
}
}
foreach (KeyValuePair<string, List<DrawerInfo>> item2 in dictionary)
{
item2.Value.Sort(delegate(DrawerInfo a, DrawerInfo b)
{
float distance3 = a.Distance;
return distance3.CompareTo(b.Distance);
});
}
list4.Sort(delegate(DrawerInfo a, DrawerInfo b)
{
float distance2 = a.Distance;
return distance2.CompareTo(b.Distance);
});
bool value2 = QuickStackStoreItemDrawersCompatPlugin.FillEmptyDrawers.Value;
int num2 = 0;
foreach (ItemData item3 in list3)
{
if (item3 == null || !humanoidInventory.ContainsItem(item3))
{
continue;
}
string name = ((Object)item3.m_dropPrefab).name;
bool flag = false;
if (dictionary.TryGetValue(name, out var value3) && value3.Count > 0)
{
foreach (DrawerInfo item4 in value3)
{
if (TryUseItem(player2, item3, item4.Comp))
{
num2++;
flag = true;
break;
}
}
}
if (flag || !value2 || list4.Count <= 0)
{
continue;
}
foreach (DrawerInfo item5 in list4)
{
if (!TryUseItem(player2, item3, item5.Comp))
{
continue;
}
num2++;
flag = true;
string text = ReadCurrentPrefab(item5.Comp);
int num3 = ReadCurrentAmount(item5.Comp);
if (!string.IsNullOrEmpty(text) && num3 > 0)
{
list4.Remove(item5);
if (!dictionary.TryGetValue(text, out var value4))
{
value4 = (dictionary[text] = new List<DrawerInfo>());
}
value4.Add(new DrawerInfo(item5.Comp, item5.Distance, text, num3));
value4.Sort(delegate(DrawerInfo a, DrawerInfo b)
{
float distance = a.Distance;
return distance.CompareTo(b.Distance);
});
}
break;
}
}
if (num2 > 0)
{
QuickStackStoreItemDrawersCompatPlugin.Dbg($"Moved stacks into drawers: {num2}");
}
return num2;
}
private static bool TryUseItem(Player player, ItemData item, Component drawerComp)
{
try
{
ZNetView component = drawerComp.GetComponent<ZNetView>();
if ((Object)(object)component != (Object)null && component.IsValid())
{
component.ClaimOwnership();
}
object obj = _useItemMethod.Invoke(drawerComp, new object[2] { player, item });
bool flag = default(bool);
int num;
if (obj is bool)
{
flag = (bool)obj;
num = 1;
}
else
{
num = 0;
}
return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
}
catch
{
return false;
}
}
private static float GetEffectiveRange()
{
float result = Math.Max(0f, QuickStackStoreItemDrawersCompatPlugin.SearchRadius.Value);
if (!QuickStackStoreItemDrawersCompatPlugin.PreferQssRadiusIfAvailable.Value)
{
return result;
}
float num = TryGetQssNearbyRangeOrNaN();
if (!float.IsNaN(num) && num > 0.05f)
{
return num;
}
return result;
}
private static float TryGetQssNearbyRangeOrNaN()
{
try
{
Type type = AccessTools.TypeByName("QuickStackStore.QSSConfig+QuickStackConfig");
if (type == null)
{
return float.NaN;
}
FieldInfo fieldInfo = AccessTools.Field(type, "QuickStackToNearbyRange");
if (fieldInfo == null)
{
return float.NaN;
}
object value = fieldInfo.GetValue(null);
if (value == null)
{
return float.NaN;
}
PropertyInfo property = value.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
if (property == null)
{
return float.NaN;
}
return (property.GetValue(value) is float num) ? num : float.NaN;
}
catch
{
return float.NaN;
}
}
private static Inventory? GetHumanoidInventory(Humanoid h)
{
try
{
if ((object)_getInventoryMethod == null)
{
_getInventoryMethod = AccessTools.Method(((object)h).GetType(), "GetInventory", Type.EmptyTypes, (Type[])null);
}
if (_getInventoryMethod != null)
{
object? obj = _getInventoryMethod.Invoke(h, Array.Empty<object>());
Inventory val = (Inventory)((obj is Inventory) ? obj : null);
if (val != null)
{
return val;
}
}
}
catch
{
}
try
{
if ((object)_inventoryField == null)
{
_inventoryField = AccessTools.Field(((object)h).GetType(), "m_inventory");
}
if (_inventoryField != null)
{
object? value = _inventoryField.GetValue(h);
Inventory val2 = (Inventory)((value is Inventory) ? value : null);
if (val2 != null)
{
return val2;
}
}
}
catch
{
}
return null;
}
private static bool IsHotbarItem(ItemData item)
{
try
{
FieldInfo fieldInfo = AccessTools.Field(((object)item).GetType(), "m_gridPos");
if (fieldInfo == null)
{
return false;
}
object value = fieldInfo.GetValue(item);
if (value == null)
{
return false;
}
FieldInfo field = value.GetType().GetField("y", BindingFlags.Instance | BindingFlags.Public);
if (field == null)
{
return false;
}
return field.GetValue(value) is int num && num == 0;
}
catch
{
return false;
}
}
private static string ReadCurrentPrefab(Component drawerComp)
{
try
{
if (_piCurrentPrefab == null)
{
return string.Empty;
}
return (_piCurrentPrefab.GetValue(drawerComp) as string) ?? string.Empty;
}
catch
{
return string.Empty;
}
}
private static int ReadCurrentAmount(Component drawerComp)
{
try
{
if (_piCurrentAmount == null)
{
return 0;
}
return (_piCurrentAmount.GetValue(drawerComp) is int num) ? num : 0;
}
catch
{
return 0;
}
}
}
}