Decompiled source of Lodged v0.1.0

plugins/Lodged.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Text;
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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 Lodged
{
	internal sealed class Entry
	{
		internal string Bone;

		internal Vector3 Pos;

		internal Quaternion Rot;

		internal int Look;

		internal long Born;

		internal byte[] Item;
	}
	internal static class ItemBytes
	{
		private static int version = -1;

		private static int Current()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Expected O, but got Unknown
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (version < 0)
			{
				ZPackage val = new ZPackage();
				new Inventory("", (Sprite)null, 1, 1).Save(val);
				version = new ZPackage(val.GetArray()).ReadInt();
			}
			return version;
		}

		internal static byte[] Write(ItemData item)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(Current());
			item.Save(val);
			return val.GetArray();
		}

		internal static ItemData Read(byte[] data)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			try
			{
				ZPackage val = new ZPackage(data);
				Item val2 = (Item)val.ReadInt();
				ValueTuple<int, ItemData> valueTuple = ItemData.Load(val, val2);
				int item = valueTuple.Item1;
				ItemData item2 = valueTuple.Item2;
				GameObject val3 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(item) : null);
				ItemDrop val4 = (Object.op_Implicit((Object)(object)val3) ? val3.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val4 == (Object)null)
				{
					return null;
				}
				ItemData obj = val4.m_itemData.Clone();
				obj.m_dropPrefab = val3;
				obj.m_stack = Mathf.Max(1, item2.m_stack);
				obj.m_durability = item2.m_durability;
				obj.m_quality = item2.m_quality;
				obj.m_variant = item2.m_variant;
				obj.m_crafterID = item2.m_crafterID;
				obj.m_crafterName = item2.m_crafterName;
				obj.m_customData = item2.m_customData;
				obj.m_worldLevel = item2.m_worldLevel;
				obj.m_pickedUp = item2.m_pickedUp;
				obj.m_cheated = item2.m_cheated;
				return obj;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("stuck item data: " + ex.Message));
				return null;
			}
		}
	}
	internal static class Impaled
	{
		private sealed class Sent
		{
			internal ZDOID Id;

			internal int Nonce;

			internal byte[] Item;

			internal Vector3 Point;

			internal Quaternion Rotation;

			internal float Deadline;
		}

		private const string RpcAdd = "chistix.lodged.add";

		private const string RpcLook = "chistix.lodged.look";

		internal static readonly int KeyIn = StringExtensionMethods.GetStableHashCode("chistix.lodged.in");

		internal static readonly int KeyRev = StringExtensionMethods.GetStableHashCode("chistix.lodged.inrev");

		private const float AnswerSeconds = 10f;

		private static readonly List<Sent> sent = new List<Sent>();

		private static int nonce;

		private static ZRoutedRpc registered;

		private static readonly List<ImpaledHost> hosts = new List<ImpaledHost>();

		private static int cursor;

		private static readonly HashSet<Transform> seen = new HashSet<Transform>();

		private static readonly List<Transform> walk = new List<Transform>();

		private static readonly List<Transform> bones = new List<Transform>();

		internal static void Tick()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != registered)
			{
				registered = instance;
				Impaled.sent.Clear();
				instance.Register<ZDOID, ZPackage>("chistix.lodged.add", (Action<long, ZDOID, ZPackage>)OnAdd);
				instance.Register<ZDOID, long, int>("chistix.lodged.look", (Action<long, ZDOID, long, int>)OnLook);
			}
			float time = Time.time;
			for (int num = Impaled.sent.Count - 1; num >= 0; num--)
			{
				if (!(time < Impaled.sent[num].Deadline))
				{
					Sent sent = Impaled.sent[num];
					Impaled.sent.RemoveAt(num);
					try
					{
						Fall(sent.Item, sent.Point, sent.Rotation);
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("unanswered stuck item: " + ex.Message));
					}
				}
			}
			if (hosts.Count == 0)
			{
				return;
			}
			int i = 0;
			for (int num2 = Mathf.Min(hosts.Count, 4); i < num2; i++)
			{
				if (hosts.Count <= 0)
				{
					break;
				}
				if (cursor >= hosts.Count)
				{
					cursor = 0;
				}
				ImpaledHost impaledHost = hosts[cursor++];
				if (!((Object)(object)impaledHost == (Object)null))
				{
					try
					{
						impaledHost.Visit(time);
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogWarning((object)("stuck in creature: " + ex2.Message));
					}
				}
			}
		}

		internal static void Register(ImpaledHost h)
		{
			if (!hosts.Contains(h))
			{
				hosts.Add(h);
			}
		}

		internal static void Unregister(ImpaledHost h)
		{
			hosts.Remove(h);
		}

		internal static void Bones(Character ch, List<Transform> into)
		{
			into.Clear();
			seen.Clear();
			SkinnedMeshRenderer[] componentsInChildren = ((Component)ch).GetComponentsInChildren<SkinnedMeshRenderer>(false);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Transform[] array = componentsInChildren[i].bones;
				foreach (Transform val in array)
				{
					if (Object.op_Implicit((Object)(object)val) && ((Component)val).gameObject.activeInHierarchy && seen.Add(val))
					{
						into.Add(val);
					}
				}
			}
			if (into.Count > 0)
			{
				return;
			}
			Animator val2 = (((Object)(object)ch.m_animator != (Object)null) ? ch.m_animator : ((Component)ch).GetComponentInChildren<Animator>());
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return;
			}
			walk.Clear();
			walk.Add(((Component)val2).transform);
			while (walk.Count > 0)
			{
				Transform val3 = walk[walk.Count - 1];
				walk.RemoveAt(walk.Count - 1);
				if (!(((Object)val3).name == "lodged_look") && ((Component)val3).gameObject.activeInHierarchy)
				{
					into.Add(val3);
					for (int num = val3.childCount - 1; num >= 0; num--)
					{
						walk.Add(val3.GetChild(num));
					}
				}
			}
		}

		internal static Transform Bone(Character ch, string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return ((Component)ch).transform;
			}
			Bones(ch, bones);
			foreach (Transform bone in bones)
			{
				if (((Object)bone).name == name)
				{
					return bone;
				}
			}
			return ((Component)ch).transform;
		}

		private static Transform Closest(Character ch, Vector3 point)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			Bones(ch, bones);
			Transform result = null;
			float num = float.MaxValue;
			foreach (Transform bone in bones)
			{
				Vector3 val = bone.position - point;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					result = bone;
				}
			}
			return result;
		}

		internal static bool Add(Character ch, ItemData item, int look, Vector3 hitPoint, Quaternion rotation)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			ItemData obj = item.Clone();
			obj.m_stack = 1;
			obj.m_equipped = false;
			Transform val = Closest(ch, hitPoint);
			Transform val2 = (Object.op_Implicit((Object)(object)val) ? val : ((Component)ch).transform);
			Vector3 val3 = hitPoint + (Object.op_Implicit((Object)(object)val) ? ((val.position - hitPoint) * 0.25f) : Vector3.zero) + rotation * Vector3.forward * Visuals.Depth(Visuals.Get(look));
			byte[] array = ItemBytes.Write(obj);
			ZDO zDO = ch.m_nview.GetZDO();
			Sent sent = new Sent
			{
				Id = zDO.m_uid,
				Nonce = ++nonce,
				Item = array,
				Point = hitPoint - rotation * Vector3.forward * 0.3f,
				Rotation = rotation,
				Deadline = Time.time + 10f
			};
			Impaled.sent.Add(sent);
			ZPackage val4 = new ZPackage();
			val4.Write(Object.op_Implicit((Object)(object)val) ? ((Object)val).name : "");
			val4.Write(val2.InverseTransformPoint(val3));
			val4.Write(Quaternion.Inverse(val2.rotation) * rotation);
			val4.Write(look);
			val4.Write(ZNet.instance.GetTime().Ticks);
			val4.Write(array);
			val4.Write(sent.Point);
			val4.Write(rotation);
			val4.Write(sent.Nonce);
			long owner = zDO.GetOwner();
			try
			{
				if (owner != 0L)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(owner, "chistix.lodged.add", new object[2] { zDO.m_uid, val4 });
				}
				else
				{
					ZRoutedRpc.instance.InvokeRoutedRPC("chistix.lodged.add", new object[2] { zDO.m_uid, val4 });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("stuck in creature, send: " + ex.Message));
			}
			return true;
		}

		private static void OnAdd(long sender, ZDOID id, ZPackage pkg)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_00d2: 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_015c: Unknown result type (might be due to invalid IL or missing references)
			Entry entry = new Entry
			{
				Bone = pkg.ReadString(),
				Pos = pkg.ReadVector3(),
				Rot = pkg.ReadQuaternion(),
				Look = pkg.ReadInt(),
				Born = pkg.ReadLong(),
				Item = pkg.ReadByteArray()
			};
			Vector3 point = pkg.ReadVector3();
			Quaternion rotation = pkg.ReadQuaternion();
			int num = pkg.ReadInt();
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(id) : null);
			GameObject val2 = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(id) : null);
			Character val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Character>() : null);
			bool flag = false;
			try
			{
				if (val == null || !val.IsOwner() || (Object)(object)val3 == (Object)null || val3.IsDead())
				{
					Fall(entry.Item, point, rotation);
					flag = true;
					return;
				}
				List<Entry> list = Read(val);
				list.Add(entry);
				int value = Plugin.PerCreature.Value;
				List<Entry> list2 = new List<Entry>();
				while (list.Count > value)
				{
					list2.Add(list[0]);
					list.RemoveAt(0);
				}
				Write(val, list);
				flag = true;
				foreach (Entry item in list2)
				{
					Fall(item.Item, WorldPoint(val3, item), Rotation(val3, item));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("stuck in creature: " + ex.Message));
				if (!flag)
				{
					try
					{
						Fall(entry.Item, point, rotation);
						return;
					}
					catch
					{
						return;
					}
				}
			}
			finally
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "chistix.lodged.look", new object[3] { id, sender, num });
			}
		}

		private static void OnLook(long from, ZDOID id, long shooter, int number)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (shooter == ZDOMan.GetSessionID())
			{
				for (int i = 0; i < sent.Count; i++)
				{
					if (sent[i].Nonce == number && sent[i].Id == id)
					{
						sent.RemoveAt(i);
						break;
					}
				}
			}
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(id) : null);
			Character val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Character>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				Ensure(val2);
			}
		}

		internal static void Ensure(Character ch)
		{
			(((Component)ch).GetComponent<ImpaledHost>() ?? ((Component)ch).gameObject.AddComponent<ImpaledHost>()).Init(ch);
		}

		internal static Vector3 WorldPoint(Character ch, Entry e)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return Bone(ch, e.Bone).TransformPoint(e.Pos);
		}

		internal static Quaternion Rotation(Character ch, Entry e)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			return Bone(ch, e.Bone).rotation * e.Rot;
		}

		internal static void Fall(byte[] data, Vector3 point, Quaternion rotation)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = ItemBytes.Read(data);
			if (val != null)
			{
				ItemDrop val2 = ItemDrop.DropItem(val, 1, point, rotation);
				Rigidbody val3 = (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponent<Rigidbody>() : null);
				if (Object.op_Implicit((Object)(object)val3) && !val3.isKinematic)
				{
					val3.linearVelocity = Random.insideUnitSphere * 0.5f;
				}
			}
		}

		internal static List<Entry> Read(ZDO zdo)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			List<Entry> list = new List<Entry>();
			byte[] byteArray = zdo.GetByteArray(KeyIn, (byte[])null);
			if (byteArray == null || byteArray.Length < 2)
			{
				return list;
			}
			try
			{
				ZPackage val = new ZPackage(byteArray);
				if (val.ReadByte() != 1)
				{
					return list;
				}
				int num = val.ReadInt();
				for (int i = 0; i < num; i++)
				{
					list.Add(new Entry
					{
						Bone = val.ReadString(),
						Pos = val.ReadVector3(),
						Rot = val.ReadQuaternion(),
						Look = val.ReadInt(),
						Born = val.ReadLong(),
						Item = val.ReadByteArray()
					});
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("stuck list: " + ex.Message));
			}
			return list;
		}

		internal static void Write(ZDO zdo, List<Entry> list)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (list.Count == 0)
			{
				zdo.Set(KeyIn, new byte[0]);
			}
			else
			{
				ZPackage val = new ZPackage();
				val.Write((byte)1);
				val.Write(list.Count);
				foreach (Entry item in list)
				{
					val.Write(item.Bone ?? "");
					val.Write(item.Pos);
					val.Write(item.Rot);
					val.Write(item.Look);
					val.Write(item.Born);
					val.Write(item.Item);
				}
				zdo.Set(KeyIn, val.GetArray());
			}
			zdo.Set(KeyRev, zdo.GetInt(KeyRev, 0) + 1, false);
		}

		internal static void Release(Character ch)
		{
			//IL_006b: 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)
			ZNetView val = (Object.op_Implicit((Object)(object)ch) ? ch.m_nview : null);
			if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner())
			{
				return;
			}
			ZDO zDO = val.GetZDO();
			byte[] byteArray = zDO.GetByteArray(KeyIn, (byte[])null);
			if (byteArray == null || byteArray.Length < 2)
			{
				return;
			}
			foreach (Entry item in Read(zDO))
			{
				Fall(item.Item, WorldPoint(ch, item), Rotation(ch, item));
			}
			Write(zDO, new List<Entry>());
		}
	}
	internal sealed class ImpaledHost : MonoBehaviour
	{
		private Character ch;

		private ZNetView view;

		private int seenRev = -1;

		private int shownCount;

		private float nextExpire;

		private float keepUntil;

		private readonly List<GameObject> shown = new List<GameObject>();

		private readonly Dictionary<string, Transform> boneCache = new Dictionary<string, Transform>();

		internal void Init(Character character)
		{
			ch = character;
			view = ch.m_nview;
			keepUntil = Time.time + 15f;
			Impaled.Register(this);
			Visit(Time.time);
		}

		internal void Visit(float now)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				return;
			}
			ZDO zDO = view.GetZDO();
			int num = zDO.GetInt(Impaled.KeyRev, 0);
			if (num != seenRev)
			{
				seenRev = num;
				Show(zDO);
			}
			if (zDO.IsOwner() && shownCount > 0 && now >= nextExpire)
			{
				nextExpire = now + 10f;
				List<Entry> list = Impaled.Read(zDO);
				DateTime time = ZNet.instance.GetTime();
				float lifetimeSeconds = Plugin.LifetimeSeconds;
				List<Entry> list2 = new List<Entry>();
				foreach (Entry item in list)
				{
					if ((time - new DateTime(item.Born)).TotalSeconds < (double)lifetimeSeconds)
					{
						list2.Add(item);
						continue;
					}
					ItemData val = ItemBytes.Read(item.Item);
					if (val != null && !Wear.Tracks(val))
					{
						Impaled.Fall(item.Item, Impaled.WorldPoint(ch, item), Impaled.Rotation(ch, item));
					}
				}
				if (list2.Count != list.Count)
				{
					Impaled.Write(zDO, list2);
				}
			}
			if (shownCount == 0 && now > keepUntil)
			{
				Object.Destroy((Object)(object)this);
			}
		}

		private Transform Bone(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return ((Component)ch).transform;
			}
			if (boneCache.TryGetValue(name, out var value) && Object.op_Implicit((Object)(object)value))
			{
				return value;
			}
			Transform val = Impaled.Bone(ch, name);
			boneCache[name] = val;
			return val;
		}

		private void Show(ZDO zdo)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			foreach (GameObject item in shown)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					Object.Destroy((Object)(object)item);
				}
			}
			shown.Clear();
			List<Entry> list = Impaled.Read(zdo);
			shownCount = list.Count;
			foreach (Entry item2 in list)
			{
				Transform val = Bone(item2.Bone);
				GameObject val2 = Visuals.Place(Visuals.Get(item2.Look), val);
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.transform.localPosition = item2.Pos;
					val2.transform.localRotation = item2.Rot;
					Vector3 lossyScale = val.lossyScale;
					val2.transform.localScale = new Vector3((lossyScale.x != 0f) ? (1f / lossyScale.x) : 1f, (lossyScale.y != 0f) ? (1f / lossyScale.y) : 1f, (lossyScale.z != 0f) ? (1f / lossyScale.z) : 1f);
					shown.Add(val2);
				}
			}
		}

		private void OnDestroy()
		{
			Impaled.Unregister(this);
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class ImpaledOnLoad
	{
		private static void Postfix(Character __instance)
		{
			byte[] array = (((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) ? __instance.m_nview.GetZDO().GetByteArray(Impaled.KeyIn, (byte[])null) : null);
			if (array != null && array.Length > 1)
			{
				Impaled.Ensure(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class CreatureDeath
	{
		private static void Prefix(Character __instance)
		{
			try
			{
				Impaled.Release(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("death drop: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerDeath
	{
		private static void Prefix(Player __instance)
		{
			try
			{
				Impaled.Release((Character)(object)__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("death drop: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Destroy")]
	internal static class CreatureGone
	{
		private static void Prefix(GameObject go)
		{
			if ((Object)(object)go == (Object)null || (Object)(object)go.GetComponent<ImpaledHost>() == (Object)null)
			{
				return;
			}
			try
			{
				Impaled.Release(go.GetComponent<Character>());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("despawn drop: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Shutdown")]
	internal static class LeavingWorld
	{
		private static void Prefix(Game __instance)
		{
			if (__instance.m_shuttingDown || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			try
			{
				Impaled.Release((Character)(object)Player.m_localPlayer);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("logout drop: " + ex.Message));
			}
		}
	}
	[BepInPlugin("chistix.lodged", "Lodged", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chistix.lodged";

		public const string Version = "0.1.0";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> Hits;

		internal static ConfigEntry<int> Minutes;

		internal static ConfigEntry<int> PerArea;

		internal static ConfigEntry<int> PerCreature;

		internal static ManualLogSource Log;

		private Harmony harmony;

		internal static bool On
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		internal static int Life
		{
			get
			{
				if (Hits != null)
				{
					return Hits.Value;
				}
				return 3;
			}
		}

		internal static float LifetimeSeconds
		{
			get
			{
				if (Minutes != null)
				{
					return (float)Minutes.Value * 60f;
				}
				return 1200f;
			}
		}

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Arrows, bolts and thrown spears stick where they hit and can be picked up. Off: they behave as in the game; what is already stuck stays and can be picked up. / Стрелы, болты и метательные копья втыкаются в место попадания, их можно подобрать. Выключено: как в игре; уже воткнутые остаются, их можно подобрать.");
			Hits = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ArrowHits", 3, new ConfigDescription("Hits an arrow or bolt lasts; it breaks on the last one and cannot be picked up. 1 = every arrow breaks on its first hit. Spears keep their own durability. / Сколько попаданий выдерживает стрела или болт; на последнем ломается и не подбирается. 1 = ломается с первого. У копий своя прочность.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			Minutes = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StuckMinutes", 20, new ConfigDescription("Minutes a stuck arrow or spear stays unpicked while its area is loaded; also cleared after six times that on the world clock. / Сколько минут неподобранная стрела или копьё остаётся, пока участок загружен; по часам мира убирается после шестикратного срока.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 240), Array.Empty<object>()));
			PerArea = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "PerArea", 50, new ConfigDescription("Most stuck arrows and spears in one area of the world (64 x 64 m); over it the oldest go. / Больше всего воткнутых стрел и копий на одном участке мира (64 x 64 м); сверх — исчезают самые старые.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 500), Array.Empty<object>()));
			PerCreature = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "PerCreature", 8, new ConfigDescription("Most arrows and spears stuck in one creature or player; over it the oldest falls to the ground. / Больше всего стрел и копий в одном существе или игроке; лишняя, самая старая, падает на землю.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), Array.Empty<object>()));
			harmony = new Harmony("chistix.lodged");
			harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)("Lodged 0.1.0: arrows and spears stick, " + Life + " hits per arrow, " + Minutes.Value + " min unpicked, " + PerArea.Value + " per area."));
		}

		private void Update()
		{
			Stuck.Tick();
			Impaled.Tick();
			Shots.Tick();
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	public struct ShotFacts
	{
		public bool FromAmmo;

		public bool ThrownItem;

		public float Pierce;

		public float Blunt;

		public float Slash;

		public float Area;

		public bool Tethered;

		public bool Bounces;
	}
	public enum ShotKind
	{
		None,
		Ammo,
		Thrown
	}
	public static class Rules
	{
		public static ShotKind Classify(ShotFacts f)
		{
			if (!f.FromAmmo && !f.ThrownItem)
			{
				return ShotKind.None;
			}
			if (f.Area > 0f || f.Tethered || f.Bounces)
			{
				return ShotKind.None;
			}
			if (!(f.Pierce > 0f) || f.Pierce < f.Blunt || f.Pierce < f.Slash)
			{
				return ShotKind.None;
			}
			if (!f.FromAmmo)
			{
				return ShotKind.Thrown;
			}
			return ShotKind.Ammo;
		}

		public static bool[] OverLimit(IList<long> born, int limit)
		{
			bool[] array = new bool[born.Count];
			int num = born.Count - Math.Max(0, limit);
			if (num <= 0)
			{
				return array;
			}
			int[] array2 = new int[born.Count];
			for (int i = 0; i < array2.Length; i++)
			{
				array2[i] = i;
			}
			Array.Sort(array2, delegate(int a, int b)
			{
				int num3 = born[a].CompareTo(born[b]);
				return (num3 == 0) ? a.CompareTo(b) : num3;
			});
			for (int num2 = 0; num2 < num; num2++)
			{
				array[array2[num2]] = true;
			}
			return array;
		}

		public static int ZoneKey(int x, int y)
		{
			return (x << 16) ^ (y & 0xFFFF);
		}

		public static string Plural(int n, string one, string few, string many)
		{
			int num = Math.Abs(n) % 100;
			int num2 = num % 10;
			if (num2 == 1 && num != 11)
			{
				return one;
			}
			if (num2 >= 2 && num2 <= 4 && (num < 12 || num > 14))
			{
				return few;
			}
			return many;
		}

		public static bool Expired(float loadedSeconds, double worldSeconds, float lifetimeSeconds)
		{
			if (lifetimeSeconds > 0f)
			{
				if (!(loadedSeconds >= lifetimeSeconds))
				{
					return worldSeconds >= (double)lifetimeSeconds * 6.0;
				}
				return true;
			}
			return false;
		}
	}
	internal sealed class Shot
	{
		internal ItemData Item;

		internal int Wear;

		internal bool FromAmmo;
	}
	internal static class Shots
	{
		private sealed class Token
		{
			internal ItemData Item;

			internal int Wear;

			internal float Time;
		}

		private static readonly Dictionary<ItemData, Token> tokens = new Dictionary<ItemData, Token>();

		private static readonly ConditionalWeakTable<Projectile, Shot> shots = new ConditionalWeakTable<Projectile, Shot>();

		internal static bool inUseAmmo;

		internal static ItemData removed;

		internal static List<Run> removedQ;

		private static float nextSweep;

		internal static void Fired(ItemData stack, List<Run> queue)
		{
			int wear = Mathf.Max(0, WearMath.PopFront(queue));
			Wear.Set(stack, queue);
			ItemData val = stack.Clone();
			val.m_stack = 1;
			val.m_equipped = false;
			val.m_customData.Remove("chistix.lodged.wear");
			tokens[stack] = new Token
			{
				Item = val,
				Wear = wear,
				Time = Time.time
			};
		}

		internal static void FiredFresh(ItemData template, GameObject projectile, GameObject prefab)
		{
			if (template == null || (Object)(object)projectile == (Object)null)
			{
				return;
			}
			Projectile component = projectile.GetComponent<Projectile>();
			if (!((Object)(object)component == (Object)null))
			{
				ItemData val = template.Clone();
				val.m_stack = 1;
				val.m_equipped = false;
				val.m_customData = new Dictionary<string, string>();
				if ((Object)(object)val.m_dropPrefab == (Object)null)
				{
					val.m_dropPrefab = prefab;
				}
				if (!((Object)(object)val.m_dropPrefab == (Object)null))
				{
					val.m_worldLevel = (byte)Game.m_worldLevel;
					shots.Remove(component);
					shots.Add(component, new Shot
					{
						Item = val,
						Wear = 0,
						FromAmmo = true
					});
				}
			}
		}

		internal static void Bind(Projectile projectile, ItemData ammo)
		{
			if (!((Object)(object)projectile == (Object)null) && ammo != null && tokens.TryGetValue(ammo, out var value))
			{
				tokens.Remove(ammo);
				if (!((Object)(object)value.Item.m_dropPrefab == (Object)null))
				{
					shots.Remove(projectile);
					shots.Add(projectile, new Shot
					{
						Item = value.Item,
						Wear = value.Wear,
						FromAmmo = true
					});
				}
			}
		}

		internal static Shot Take(Projectile projectile)
		{
			if ((Object)(object)projectile != (Object)null && shots.TryGetValue(projectile, out var value))
			{
				return value;
			}
			return null;
		}

		internal static void Forget(Projectile projectile)
		{
			if ((Object)(object)projectile != (Object)null)
			{
				shots.Remove(projectile);
			}
		}

		internal static void Tick()
		{
			if (tokens.Count == 0 || Time.time < nextSweep)
			{
				return;
			}
			nextSweep = Time.time + 5f;
			List<ItemData> list = null;
			foreach (KeyValuePair<ItemData, Token> token in tokens)
			{
				if (Time.time - token.Value.Time > 5f)
				{
					(list ?? (list = new List<ItemData>())).Add(token.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ItemData item in list)
			{
				tokens.Remove(item);
			}
		}

		internal static ShotFacts Facts(Projectile p, Shot shot)
		{
			ShotFacts result = new ShotFacts
			{
				FromAmmo = (shot?.FromAmmo ?? false),
				ThrownItem = (p.m_respawnItemOnHit && p.m_spawnItem != null),
				Pierce = p.m_damage.m_pierce,
				Blunt = p.m_damage.m_blunt,
				Slash = p.m_damage.m_slash,
				Area = p.m_aoe,
				Bounces = p.m_bounce
			};
			result.Tethered = (Object)(object)((Component)p).GetComponent<LineConnect>() != (Object)null || ((Object)(object)p.m_spawnOnHit != (Object)null && (Object)(object)p.m_spawnOnHit.GetComponent<GrapplingPoint>() != (Object)null);
			return result;
		}
	}
	[HarmonyPatch(typeof(Attack), "UseAmmo")]
	internal static class UseAmmoHook
	{
		private static void Prefix()
		{
			Shots.inUseAmmo = true;
			Shots.removed = null;
		}

		private static void Postfix(Attack __instance, bool __result)
		{
			Shots.inUseAmmo = false;
			ItemData removed = Shots.removed;
			Shots.removed = null;
			if (__result && Plugin.On && removed != null && removed == __instance.m_ammoItem)
			{
				Shots.Fired(removed, Shots.removedQ);
			}
		}

		private static void Finalizer()
		{
			Shots.inUseAmmo = false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class AmmoTaken
	{
		private static void Prefix(ItemData item)
		{
			if (Shots.inUseAmmo && Wear.Tracks(item))
			{
				Shots.removed = item;
				Shots.removedQ = Wear.Get(item);
			}
		}
	}
	[HarmonyPatch(typeof(Projectile), "Setup")]
	internal static class ProjectileSetup
	{
		private static void Postfix(Projectile __instance, ItemData ammo)
		{
			if (Plugin.On)
			{
				Shots.Bind(__instance, ammo);
			}
		}
	}
	[HarmonyPatch(typeof(Turret), "ShootProjectile")]
	internal static class TurretShot
	{
		private static void Postfix(Turret __instance)
		{
			if (Plugin.On && __instance.m_maxAmmo > 0 && !((Object)(object)__instance.m_lastProjectile == (Object)null) && __instance.m_lastAmmo != null)
			{
				GameObject prefab = null;
				try
				{
					ZNetScene instance = ZNetScene.instance;
					prefab = ((instance != null) ? instance.GetPrefab(__instance.GetAmmoType()) : null);
				}
				catch
				{
				}
				Shots.FiredFresh(__instance.m_lastAmmo, __instance.m_lastProjectile, prefab);
			}
		}
	}
	internal sealed class HitState
	{
		internal Shot Shot;

		internal ShotKind Kind;

		internal ItemData SpawnItem;

		internal Quaternion Rotation;
	}
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	internal static class ProjectileHit
	{
		private static void Prefix(Projectile __instance, bool water, out HitState __state)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (!Plugin.On || water || __instance.m_didHit)
			{
				return;
			}
			Shot shot = Shots.Take(__instance);
			if (shot == null && (!__instance.m_respawnItemOnHit || __instance.m_spawnItem == null))
			{
				return;
			}
			ShotKind shotKind = Rules.Classify(Shots.Facts(__instance, shot));
			if (shotKind != ShotKind.None)
			{
				__state = new HitState
				{
					Shot = shot,
					Kind = shotKind,
					SpawnItem = __instance.m_spawnItem,
					Rotation = ((Component)__instance).transform.rotation
				};
				if (shotKind == ShotKind.Thrown)
				{
					__instance.m_spawnItem = null;
				}
			}
		}

		private static void Postfix(Projectile __instance, Collider collider, Vector3 hitPoint, HitState __state)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: 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)
			//IL_012e: 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_013b: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null)
			{
				return;
			}
			if (!__instance.m_didHit)
			{
				if (__state.Kind == ShotKind.Thrown)
				{
					__instance.m_spawnItem = __state.SpawnItem;
				}
				return;
			}
			Shots.Forget(__instance);
			ItemData val = null;
			bool flag = false;
			Quaternion rotation = __state.Rotation;
			try
			{
				if (__state.Kind == ShotKind.Ammo)
				{
					if (WearMath.Breaks(__state.Shot.Wear, Plugin.Life))
					{
						return;
					}
					val = __state.Shot.Item.Clone();
					val.m_stack = 1;
					Wear.SetSingle(val, __state.Shot.Wear + 1);
				}
				else
				{
					val = __state.SpawnItem;
				}
				if (val != null && !((Object)(object)val.m_dropPrefab == (Object)null))
				{
					int stableHashCode = StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Component)__instance).gameObject));
					Character val2 = (Object.op_Implicit((Object)(object)collider) ? ((Component)collider).GetComponentInParent<Character>() : null);
					if ((Object)(object)val2 != (Object)null && (Object)(object)val2.m_nview != (Object)null && val2.m_nview.IsValid())
					{
						flag = Impaled.Add(val2, val, stableHashCode, hitPoint, rotation);
					}
					else if ((Object)(object)collider == (Object)null || (Object)(object)collider.attachedRigidbody != (Object)null)
					{
						Stuck.Loose(val, hitPoint - rotation * Vector3.forward * 0.3f, rotation);
						flag = true;
					}
					else
					{
						flag = Stuck.Create(val, stableHashCode, hitPoint, rotation);
					}
					ZNetView nview = __instance.m_nview;
					if ((Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner())
					{
						ZNetScene.instance.Destroy(((Component)__instance).gameObject);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("hit of " + ((Object)__instance).name + ": " + ex.Message));
			}
			finally
			{
				if (!flag && val != null && (Object)(object)val.m_dropPrefab != (Object)null)
				{
					try
					{
						Stuck.Loose(val, hitPoint - rotation * Vector3.forward * 0.3f, rotation);
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogWarning((object)("hit drop: " + ex2.Message));
					}
				}
			}
		}
	}
	internal sealed class StackSnap
	{
		internal int N;

		internal List<Run> Q;

		internal bool Split;

		internal List<ItemData> Targets;

		internal List<int> Stacks;

		internal List<List<Run>> Queues;

		internal ItemData Cell;

		internal int CellStack;

		internal List<Run> CellQ;
	}
	internal static class StackHooks
	{
		internal sealed class Pending
		{
			internal ItemData Item;

			internal int N;

			internal List<Run> Q;
		}

		internal static Pending DropPending;

		internal static List<Run> Worn(ItemData item)
		{
			if (!Wear.Tracks(item) || !Wear.Has(item))
			{
				return null;
			}
			List<Run> list = Wear.Get(item);
			if (!WearMath.AnyWorn(list))
			{
				return null;
			}
			return list;
		}

		internal static StackSnap Snap(Inventory inventory, ItemData item)
		{
			if (inventory == null)
			{
				return null;
			}
			List<Run> list = Worn(item);
			if (list == null)
			{
				return null;
			}
			StackSnap stackSnap = new StackSnap
			{
				N = item.m_stack,
				Q = list,
				Targets = new List<ItemData>(),
				Stacks = new List<int>(),
				Queues = new List<List<Run>>()
			};
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != item && allItem.m_shared != null && !(allItem.m_shared.m_name != item.m_shared.m_name))
				{
					stackSnap.Targets.Add(allItem);
					stackSnap.Stacks.Add(allItem.m_stack);
					stackSnap.Queues.Add(Wear.Get(allItem));
				}
			}
			return stackSnap;
		}

		internal static void Spread(ItemData item, StackSnap snap)
		{
			if (snap == null)
			{
				return;
			}
			int count = snap.Targets.Count;
			int[] array = new int[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = snap.Targets[i].m_stack - snap.Stacks[i];
			}
			List<Run>[] array2 = WearMath.Spread(snap.Q, array);
			for (int j = 0; j < count; j++)
			{
				if (array[j] > 0)
				{
					List<Run> q = snap.Queues[j];
					WearMath.Append(q, array2[j]);
					Wear.Set(snap.Targets[j], q);
				}
			}
			Wear.Set(item, snap.Q);
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
	internal static class AddAnywhere
	{
		private static void Prefix(Inventory __instance, ItemData item, out StackSnap __state)
		{
			__state = StackHooks.Snap(__instance, item);
		}

		private static void Postfix(ItemData item, StackSnap __state)
		{
			StackHooks.Spread(item, __state);
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(Vector2i)
	})]
	internal static class AddAtCell
	{
		private static void Prefix(Inventory __instance, ItemData item, out StackSnap __state)
		{
			__state = StackHooks.Snap(__instance, item);
		}

		private static void Postfix(ItemData item, StackSnap __state)
		{
			StackHooks.Spread(item, __state);
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class AddIntoCell
	{
		private static void Prefix(Inventory __instance, ItemData item, int amount, int x, int y, out StackSnap __state)
		{
			__state = null;
			List<Run> list = StackHooks.Worn(item);
			if (list != null)
			{
				ItemData itemAt = __instance.GetItemAt(x, y);
				__state = new StackSnap
				{
					N = item.m_stack,
					Q = list,
					Split = (amount < item.m_stack),
					Cell = itemAt,
					CellStack = (itemAt?.m_stack ?? 0),
					CellQ = ((itemAt == null) ? null : Wear.Get(itemAt))
				};
			}
		}

		private static void Postfix(Inventory __instance, ItemData item, int x, int y, StackSnap __state)
		{
			if (__state == null)
			{
				return;
			}
			ItemData val = __state.Cell;
			int num;
			List<Run> q;
			if (val != null)
			{
				if (val == item)
				{
					return;
				}
				num = val.m_stack - __state.CellStack;
				q = __state.CellQ;
			}
			else
			{
				val = __instance.GetItemAt(x, y);
				if (val == null || val == item)
				{
					return;
				}
				num = val.m_stack;
				q = new List<Run>();
			}
			if (num > 0)
			{
				List<Run> tail = (__state.Split ? WearMath.TakeShare(__state.Q, num) : WearMath.TakeFront(__state.Q, num));
				WearMath.Append(q, tail);
				Wear.Set(val, q);
				Wear.Set(item, __state.Q);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int)
	})]
	internal static class DropFromInventory
	{
		private static void Prefix(ItemData item, int amount)
		{
			StackHooks.DropPending = null;
			if (amount > 0)
			{
				List<Run> list = StackHooks.Worn(item);
				if (list != null)
				{
					StackHooks.DropPending = new StackHooks.Pending
					{
						Item = item,
						N = item.m_stack,
						Q = list
					};
				}
			}
		}

		private static void Finalizer()
		{
			StackHooks.DropPending = null;
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "DropItem")]
	internal static class DropAsItem
	{
		private static void Postfix(ItemData item, ItemDrop __result)
		{
			if (!((Object)(object)__result == (Object)null) && Wear.Has(item))
			{
				ItemData itemData = __result.m_itemData;
				StackHooks.Pending dropPending = StackHooks.DropPending;
				if (dropPending != null && dropPending.Item == item)
				{
					List<Run> q = WearMath.TakeShare(dropPending.Q, itemData.m_stack);
					Wear.Set(itemData, q);
					Wear.Set(item, dropPending.Q);
					StackHooks.DropPending = null;
				}
				else
				{
					Wear.Set(itemData, Wear.Get(itemData));
				}
				__result.Save();
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "AutoStackItems")]
	internal static class AutoStack
	{
		internal sealed class State
		{
			internal List<Run> Before;

			internal int Stack;

			internal List<KeyValuePair<ItemDrop, List<Run>>> Worn;
		}

		private static readonly List<Collider> hidden = new List<Collider>();

		private static int itemMask;

		private static bool Prefix(ItemDrop __instance, out State __state)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (Stuck.IsStuck(__instance))
			{
				return false;
			}
			Stuck.HideNear(((Component)__instance).transform.position, 5f, hidden);
			if (!Wear.Tracks(__instance.m_itemData))
			{
				return true;
			}
			if (itemMask == 0)
			{
				itemMask = LayerMask.GetMask(new string[1] { "item" });
			}
			Collider[] array = Physics.OverlapSphere(((Component)__instance).transform.position, 4f, itemMask);
			foreach (Collider obj in array)
			{
				Rigidbody attachedRigidbody = obj.attachedRigidbody;
				if (!Object.op_Implicit((Object)(object)attachedRigidbody))
				{
					continue;
				}
				ItemDrop other = ((Component)attachedRigidbody).GetComponent<ItemDrop>();
				if ((Object)(object)other == (Object)null || (Object)(object)other == (Object)(object)__instance || other.m_itemData.m_shared.m_name != __instance.m_itemData.m_shared.m_name || (__state != null && __state.Worn.Exists((KeyValuePair<ItemDrop, List<Run>> p) => (Object)(object)p.Key == (Object)(object)other)))
				{
					continue;
				}
				List<Run> list = StackHooks.Worn(other.m_itemData);
				if (list != null)
				{
					if (__state == null)
					{
						__state = new State
						{
							Before = Wear.Get(__instance.m_itemData),
							Stack = __instance.m_itemData.m_stack,
							Worn = new List<KeyValuePair<ItemDrop, List<Run>>>()
						};
					}
					__state.Worn.Add(new KeyValuePair<ItemDrop, List<Run>>(other, list));
				}
			}
			return true;
		}

		private static void Postfix(ItemDrop __instance, State __state)
		{
			if (__state == null)
			{
				return;
			}
			List<Run> before = __state.Before;
			bool flag = false;
			foreach (KeyValuePair<ItemDrop, List<Run>> item in __state.Worn)
			{
				ZNetView val = (((Object)(object)item.Key == (Object)null) ? null : item.Key.m_nview);
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					WearMath.Append(before, item.Value);
					flag = true;
				}
			}
			if (flag)
			{
				int num = __instance.m_itemData.m_stack - WearMath.Count(before);
				if (num > 0)
				{
					WearMath.Append(before, WearMath.Fresh(num));
				}
				Wear.Set(__instance.m_itemData, before);
				__instance.Save();
			}
		}

		private static void Finalizer()
		{
			foreach (Collider item in hidden)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					item.enabled = true;
				}
			}
			hidden.Clear();
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class WearTooltip
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			if (Wear.Tracks(item))
			{
				string text = Wear.Text(item);
				if (text != null)
				{
					__result = __result + "\n\n" + text;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "GetHoverText")]
	internal static class WearHover
	{
		private static void Postfix(ItemDrop __instance, ref string __result)
		{
			if (Wear.Tracks(__instance.m_itemData))
			{
				string text = Wear.Text(__instance.m_itemData);
				if (text != null)
				{
					__result = __result + "\n" + text;
				}
			}
		}
	}
	internal static class Stuck
	{
		internal static readonly int KeyLook = StringExtensionMethods.GetStableHashCode("chistix.lodged.look");

		internal static readonly int KeyBorn = StringExtensionMethods.GetStableHashCode("chistix.lodged.born");

		internal static readonly int KeyAge = StringExtensionMethods.GetStableHashCode("chistix.lodged.age");

		private static readonly List<StuckItem> all = new List<StuckItem>();

		private static readonly Dictionary<int, List<StuckItem>> zones = new Dictionary<int, List<StuckItem>>();

		private static readonly List<long> born = new List<long>();

		private static int cursor;

		private static int supportMask;

		private static int itemLayer = -1;

		private static float nextLimits;

		internal static int SupportMask
		{
			get
			{
				if (supportMask == 0)
				{
					return supportMask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain" });
				}
				return supportMask;
			}
		}

		internal static int ItemLayer
		{
			get
			{
				if (itemLayer < 0)
				{
					return itemLayer = LayerMask.NameToLayer("item");
				}
				return itemLayer;
			}
		}

		internal static int Count => all.Count;

		internal static bool Create(ItemData item, int look, Vector3 point, Quaternion rotation)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop val = ItemDrop.DropItem(item, 1, point, rotation);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				ZNetView nview = val.m_nview;
				if ((Object)(object)nview == (Object)null || !nview.IsValid())
				{
					return true;
				}
				ZDO zDO = nview.GetZDO();
				zDO.Set(KeyLook, (look == 0) ? 1 : look, false);
				zDO.Set(KeyBorn, ZNet.instance.GetTime().Ticks);
				zDO.Set(KeyAge, 0f);
				Attach(val);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("stuck item: " + ex.Message));
			}
			return true;
		}

		internal static void Loose(ItemData item, Vector3 point, Quaternion rotation)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop.DropItem(item, 1, point, rotation);
		}

		internal static void Attach(ItemDrop drop)
		{
			(((Component)drop).GetComponent<StuckItem>() ?? ((Component)drop).gameObject.AddComponent<StuckItem>()).Init();
		}

		internal static bool IsStuck(ItemDrop drop)
		{
			if ((Object)(object)drop != (Object)null)
			{
				return (Object)(object)((Component)drop).GetComponent<StuckItem>() != (Object)null;
			}
			return false;
		}

		internal static void Register(StuckItem s)
		{
			if (s.Slot < 0)
			{
				s.Slot = all.Count;
				all.Add(s);
			}
		}

		internal static void Unregister(StuckItem s)
		{
			int slot = s.Slot;
			if (slot >= 0 && slot < all.Count && !((Object)(object)all[slot] != (Object)(object)s))
			{
				int index = all.Count - 1;
				all[slot] = all[index];
				all[slot].Slot = slot;
				all.RemoveAt(index);
				s.Slot = -1;
			}
		}

		internal static void HideNear(Vector3 point, float radius, List<Collider> hidden)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			float num = radius * radius;
			foreach (StuckItem item in all)
			{
				Collider hover = item.Hover;
				if ((Object)(object)hover != (Object)null && hover.enabled)
				{
					Vector3 val = ((Component)item).transform.position - point;
					if (((Vector3)(ref val)).sqrMagnitude < num)
					{
						hover.enabled = false;
						hidden.Add(hover);
					}
				}
			}
		}

		internal static void Tick()
		{
			if (all.Count == 0)
			{
				return;
			}
			float time = Time.time;
			int num = Mathf.Min(all.Count, 6);
			for (int i = 0; i < num; i++)
			{
				if (all.Count <= 0)
				{
					break;
				}
				if (cursor >= all.Count)
				{
					cursor = 0;
				}
				StuckItem stuckItem = all[cursor++];
				if (!((Object)(object)stuckItem == (Object)null))
				{
					try
					{
						stuckItem.Visit(time);
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("stuck item: " + ex.Message));
					}
				}
			}
			if (time >= nextLimits)
			{
				nextLimits = time + 5f;
				Limits();
			}
		}

		private static void Limits()
		{
			foreach (List<StuckItem> value3 in zones.Values)
			{
				value3.Clear();
			}
			foreach (StuckItem item in all)
			{
				if (!((Object)(object)item == (Object)null) && item.Valid)
				{
					if (!zones.TryGetValue(item.Zone, out var value))
					{
						value = (zones[item.Zone] = new List<StuckItem>());
					}
					value.Add(item);
				}
			}
			int value2 = Plugin.PerArea.Value;
			foreach (List<StuckItem> value4 in zones.Values)
			{
				if (value4.Count <= value2)
				{
					continue;
				}
				born.Clear();
				foreach (StuckItem item2 in value4)
				{
					born.Add(item2.Born);
				}
				bool[] array = Rules.OverLimit(born, value2);
				for (int i = 0; i < value4.Count; i++)
				{
					if (array[i] && value4[i].IsOwner)
					{
						value4[i].Remove();
					}
				}
			}
		}
	}
	internal sealed class StuckItem : MonoBehaviour
	{
		internal int Slot = -1;

		internal int Zone;

		internal long Born;

		internal Collider Hover;

		private ItemDrop drop;

		private ZNetView view;

		private Rigidbody body;

		private ZSyncTransform sync;

		private Floating floating;

		private GameObject look;

		private Look shape;

		private bool floatingWas;

		private bool autoDestroyWas;

		private readonly List<Renderer> renderers = new List<Renderer>();

		private readonly List<Collider> colliders = new List<Collider>();

		private float age;

		private float lastVisit;

		private float nextWrite;

		private float nextSupport;

		private uint seen;

		private bool wasOwner;

		private bool frozen;

		internal bool Valid
		{
			get
			{
				if ((Object)(object)view != (Object)null)
				{
					return view.IsValid();
				}
				return false;
			}
		}

		internal bool IsOwner
		{
			get
			{
				if (Valid)
				{
					return view.IsOwner();
				}
				return false;
			}
		}

		private bool IsAmmo
		{
			get
			{
				if ((Object)(object)drop != (Object)null)
				{
					return Wear.Tracks(drop.m_itemData);
				}
				return false;
			}
		}

		internal void Init()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			drop = ((Component)this).GetComponent<ItemDrop>();
			view = ((Component)this).GetComponent<ZNetView>();
			body = ((Component)this).GetComponent<Rigidbody>();
			sync = ((Component)this).GetComponent<ZSyncTransform>();
			floating = ((Component)this).GetComponent<Floating>();
			if (Valid)
			{
				ZDO zDO = view.GetZDO();
				Born = zDO.GetLong(Stuck.KeyBorn, 0L);
				age = zDO.GetFloat(Stuck.KeyAge, 0f);
				seen = zDO.DataRevision;
				wasOwner = zDO.IsOwner();
				Vector2s zone = ZoneSystem.GetZone(((Component)this).transform.position);
				Zone = Rules.ZoneKey(zone.x, zone.y);
				lastVisit = Time.time;
				nextWrite = lastVisit + 30f;
				nextSupport = lastVisit + 2f + Random.value * 2f;
				if (!frozen)
				{
					Freeze(zDO.GetInt(Stuck.KeyLook, 0));
				}
				Stuck.Register(this);
			}
		}

		private void Freeze(int lookHash)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			frozen = true;
			if (Object.op_Implicit((Object)(object)body))
			{
				body.linearVelocity = Vector3.zero;
				body.angularVelocity = Vector3.zero;
				body.isKinematic = true;
			}
			if (Object.op_Implicit((Object)(object)sync))
			{
				sync.m_isKinematicBody = true;
			}
			if (Object.op_Implicit((Object)(object)floating))
			{
				floatingWas = ((Behaviour)floating).enabled;
				((Behaviour)floating).enabled = false;
			}
			if (Object.op_Implicit((Object)(object)drop))
			{
				drop.m_autoPickup = false;
				autoDestroyWas = drop.m_autoDestroy;
				drop.m_autoDestroy = false;
			}
			shape = Visuals.Get(lookHash);
			if (shape == null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (val.enabled)
				{
					val.enabled = false;
					renderers.Add(val);
				}
			}
			Collider[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren2)
			{
				if (val2.enabled)
				{
					val2.enabled = false;
					colliders.Add(val2);
				}
			}
			look = Visuals.Place(shape, ((Component)this).transform);
			Vector3 lossyScale = ((Component)this).transform.lossyScale;
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor((lossyScale.x != 0f) ? (1f / lossyScale.x) : 1f, (lossyScale.y != 0f) ? (1f / lossyScale.y) : 1f, (lossyScale.z != 0f) ? (1f / lossyScale.z) : 1f);
			float num = Visuals.Depth(shape);
			look.transform.localPosition = new Vector3(0f, 0f, num * val3.z);
			look.transform.localRotation = Quaternion.identity;
			look.transform.localScale = val3;
			GameObject val4 = new GameObject("lodged_hover")
			{
				layer = Stuck.ItemLayer
			};
			val4.transform.SetParent(look.transform, false);
			CapsuleCollider val5 = val4.AddComponent<CapsuleCollider>();
			float num2 = Mathf.Max(0.1f, shape.Back - num);
			val5.direction = 2;
			val5.center = new Vector3(0f, 0f, 0f - num - num2 * 0.5f);
			val5.height = num2 + 0.1f;
			val5.radius = Mathf.Clamp(shape.Width * 0.5f, 0.06f, 0.15f);
			Hover = (Collider)(object)val5;
		}

		private void Unfreeze()
		{
			frozen = false;
			if (Object.op_Implicit((Object)(object)look))
			{
				Object.Destroy((Object)(object)look);
			}
			look = null;
			Hover = null;
			foreach (Renderer renderer in renderers)
			{
				if (Object.op_Implicit((Object)(object)renderer))
				{
					renderer.enabled = true;
				}
			}
			renderers.Clear();
			foreach (Collider collider in colliders)
			{
				if (Object.op_Implicit((Object)(object)collider))
				{
					collider.enabled = true;
				}
			}
			colliders.Clear();
			if (Object.op_Implicit((Object)(object)sync))
			{
				sync.m_isKinematicBody = false;
			}
			if (Object.op_Implicit((Object)(object)body))
			{
				body.isKinematic = false;
			}
			if (Object.op_Implicit((Object)(object)floating))
			{
				((Behaviour)floating).enabled = floatingWas;
			}
			if (Object.op_Implicit((Object)(object)drop))
			{
				drop.m_autoPickup = true;
				drop.m_autoDestroy = autoDestroyWas;
			}
			Stuck.Unregister(this);
			Object.Destroy((Object)(object)this);
		}

		internal void Remove()
		{
			if (IsOwner)
			{
				if (IsAmmo)
				{
					view.Destroy();
				}
				else
				{
					Release();
				}
			}
		}

		private void Release()
		{
			if (IsOwner)
			{
				view.GetZDO().Set(Stuck.KeyLook, 0, false);
				Unfreeze();
			}
		}

		private bool Supported()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)this).transform.position;
			Vector3 forward = ((Component)this).transform.forward;
			if (Physics.CheckSphere(position, 0.1f, Stuck.SupportMask, (QueryTriggerInteraction)1))
			{
				return true;
			}
			float num = ((shape != null) ? Mathf.Max(0.3f, shape.Back) : 0.8f);
			return Physics.Raycast(position - forward * num, forward, num + 0.3f, Stuck.SupportMask, (QueryTriggerInteraction)1);
		}

		internal void Visit(float now)
		{
			if (!Valid)
			{
				return;
			}
			ZDO zDO = view.GetZDO();
			if (!zDO.IsOwner())
			{
				wasOwner = false;
				lastVisit = now;
				if (zDO.DataRevision != seen)
				{
					seen = zDO.DataRevision;
					if (zDO.GetInt(Stuck.KeyLook, 0) == 0)
					{
						Unfreeze();
					}
				}
				return;
			}
			if (!wasOwner)
			{
				wasOwner = true;
				age = Mathf.Max(age, zDO.GetFloat(Stuck.KeyAge, 0f));
			}
			age += Mathf.Min(now - lastVisit, 10f);
			lastVisit = now;
			if (now >= nextWrite)
			{
				nextWrite = now + 30f;
				zDO.Set(Stuck.KeyAge, age);
			}
			double worldSeconds = ((Born == 0L) ? 0.0 : (ZNet.instance.GetTime() - new DateTime(Born)).TotalSeconds);
			if (Rules.Expired(age, worldSeconds, Plugin.LifetimeSeconds))
			{
				Remove();
				return;
			}
			if (now >= nextSupport)
			{
				nextSupport = now + 2.5f + Random.value;
				if (!Supported())
				{
					Release();
				}
			}
			seen = zDO.DataRevision;
		}

		private void OnDestroy()
		{
			Stuck.Unregister(this);
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "Awake")]
	internal static class StuckOnLoad
	{
		private static void Postfix(ItemDrop __instance)
		{
			ZNetView nview = __instance.m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.GetZDO().GetInt(Stuck.KeyLook, 0) != 0)
			{
				Stuck.Attach(__instance);
			}
		}
	}
	internal sealed class Look
	{
		internal GameObject Template;

		internal float Back;

		internal float Front;

		internal float Width;
	}
	internal static class Visuals
	{
		private static readonly Dictionary<int, Look> looks = new Dictionary<int, Look>();

		private static GameObject shelf;

		internal const string LookName = "lodged_look";

		internal static Look Get(int prefabHash)
		{
			if (prefabHash == 0)
			{
				return null;
			}
			if (looks.TryGetValue(prefabHash, out var value))
			{
				return value;
			}
			value = Build(prefabHash);
			looks[prefabHash] = value;
			return value;
		}

		private static bool ActiveIn(Transform t, Transform root)
		{
			while ((Object)(object)t != (Object)null)
			{
				if (!((Component)t).gameObject.activeSelf)
				{
					return false;
				}
				if ((Object)(object)t == (Object)(object)root)
				{
					break;
				}
				t = t.parent;
			}
			return true;
		}

		private static Look Build(int prefabHash)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: 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_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: 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_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: 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)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabHash) : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Projectile component = val.GetComponent<Projectile>();
			GameObject val2 = (Object.op_Implicit((Object)(object)component) ? component.m_hideOnHit : null);
			if ((Object)(object)shelf == (Object)null)
			{
				shelf = new GameObject("Lodged looks");
				shelf.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)shelf);
			}
			GameObject val3 = new GameObject("lodged_" + ((Object)val).name);
			val3.transform.SetParent(shelf.transform, false);
			Matrix4x4 worldToLocalMatrix = val.transform.worldToLocalMatrix;
			bool flag = false;
			Bounds val4 = default(Bounds);
			MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val5 in componentsInChildren)
			{
				if (!((Renderer)val5).enabled || !ActiveIn(((Component)val5).transform, val.transform) || ((Object)(object)val2 != (Object)null && ((Component)val5).transform.IsChildOf(val2.transform)))
				{
					continue;
				}
				MeshFilter component2 = ((Component)val5).GetComponent<MeshFilter>();
				if ((Object)(object)component2 == (Object)null || (Object)(object)component2.sharedMesh == (Object)null)
				{
					continue;
				}
				Matrix4x4 val6 = worldToLocalMatrix * ((Component)val5).transform.localToWorldMatrix;
				GameObject val7 = new GameObject(((Object)val5).name);
				val7.transform.SetParent(val3.transform, false);
				val7.transform.localPosition = Vector4.op_Implicit(((Matrix4x4)(ref val6)).GetColumn(3));
				val7.transform.localRotation = ((Matrix4x4)(ref val6)).rotation;
				val7.transform.localScale = ((Matrix4x4)(ref val6)).lossyScale;
				val7.AddComponent<MeshFilter>().sharedMesh = component2.sharedMesh;
				MeshRenderer obj = val7.AddComponent<MeshRenderer>();
				((Renderer)obj).sharedMaterials = ((Renderer)val5).sharedMaterials;
				((Renderer)obj).shadowCastingMode = ((Renderer)val5).shadowCastingMode;
				((Renderer)obj).receiveShadows = ((Renderer)val5).receiveShadows;
				Bounds bounds = component2.sharedMesh.bounds;
				for (int j = 0; j < 8; j++)
				{
					Vector3 val8 = ((Matrix4x4)(ref val6)).MultiplyPoint3x4(new Vector3(((j & 1) == 0) ? ((Bounds)(ref bounds)).min.x : ((Bounds)(ref bounds)).max.x, ((j & 2) == 0) ? ((Bounds)(ref bounds)).min.y : ((Bounds)(ref bounds)).max.y, ((j & 4) == 0) ? ((Bounds)(ref bounds)).min.z : ((Bounds)(ref bounds)).max.z));
					if (!flag)
					{
						((Bounds)(ref val4))..ctor(val8, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref val4)).Encapsulate(val8);
					}
				}
			}
			if (!flag)
			{
				Object.Destroy((Object)(object)val3);
				return null;
			}
			return new Look
			{
				Template = val3,
				Back = 0f - ((Bounds)(ref val4)).min.z,
				Front = ((Bounds)(ref val4)).max.z,
				Width = Mathf.Max(((Bounds)(ref val4)).size.x, ((Bounds)(ref val4)).size.y)
			};
		}

		internal static float Depth(Look look)
		{
			if (look == null)
			{
				return 0f;
			}
			float num = Mathf.Max(0f, look.Front);
			float num2 = look.Back + num;
			return Mathf.Max(0f, Mathf.Clamp(num2 * 0.15f, 0.06f, 0.35f) - num);
		}

		internal static GameObject Place(Look look, Transform parent)
		{
			if (look == null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(look.Template, parent, false);
			((Object)obj).name = "lodged_look";
			obj.SetActive(true);
			return obj;
		}
	}
	internal static class Wear
	{
		internal const string Key = "chistix.lodged.wear";

		internal static bool Tracks(ItemData item)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			if (item?.m_shared == null)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType == 9 || (int)itemType == 23)
			{
				return item.m_shared.m_maxStackSize > 1;
			}
			return false;
		}

		internal static bool Has(ItemData item)
		{
			if (item?.m_customData != null)
			{
				return item.m_customData.ContainsKey("chistix.lodged.wear");
			}
			return false;
		}

		internal static List<Run> Get(ItemData item)
		{
			string value = null;
			if (item?.m_customData != null)
			{
				item.m_customData.TryGetValue("chistix.lodged.wear", out value);
			}
			return WearMath.Queue(value, item?.m_stack ?? 0, Plugin.Life);
		}

		internal static void Set(ItemData item, List<Run> q)
		{
			if (item?.m_customData != null)
			{
				if (q == null || !WearMath.AnyWorn(q))
				{
					item.m_customData.Remove("chistix.lodged.wear");
				}
				else
				{
					item.m_customData["chistix.lodged.wear"] = WearMath.Format(q);
				}
			}
		}

		internal static void SetSingle(ItemData item, int hits)
		{
			Set(item, new List<Run>
			{
				new Run(hits, 1)
			});
		}

		private static bool Russian()
		{
			try
			{
				return Localization.instance != null && Localization.instance.GetSelectedLanguage() == "Russian";
			}
			catch
			{
				return false;
			}
		}

		private static string Noun(ItemData item, int n, bool ru)
		{
			string text = (item.m_shared.m_ammoType ?? "").ToLowerInvariant();
			if (text.Contains("bolt"))
			{
				if (!ru)
				{
					if (n != 1)
					{
						return "bolts";
					}
					return "bolt";
				}
				return Rules.Plural(n, "болт", "болта", "болтов");
			}
			if (text.Contains("arrow"))
			{
				if (!ru)
				{
					if (n != 1)
					{
						return "arrows";
					}
					return "arrow";
				}
				return Rules.Plural(n, "стрела", "стрелы", "стрел");
			}
			if (!ru)
			{
				return "pcs";
			}
			return "шт.";
		}

		internal static string Text(ItemData item)
		{
			if (!Has(item))
			{
				return null;
			}
			List<Run> q = Get(item);
			if (!WearMath.AnyWorn(q))
			{
				return null;
			}
			int life = Plugin.Life;
			bool flag = Russian();
			List<KeyValuePair<int, int>> list = WearMath.Rows(q, life);
			if (item.m_stack <= 1 && list.Count == 1)
			{
				return (flag ? "Прочность: " : "Durability: ") + list[0].Value + "/" + life;
			}
			StringBuilder stringBuilder = new StringBuilder(flag ? "Прочность в пачке:" : "Durability in stack:");
			foreach (KeyValuePair<int, int> item2 in list)
			{
				stringBuilder.Append('\n').Append(item2.Key).Append(' ')
					.Append(Noun(item, item2.Key, flag))
					.Append(' ')
					.Append(item2.Value)
					.Append('/')
					.Append(life);
			}
			int num = WearMath.Next(q);
			if (num >= 0)
			{
				stringBuilder.Append('\n').Append(flag ? "Следующий выстрел: " : "Next shot: ").Append(life - num)
					.Append('/')
					.Append(life);
			}
			return stringBuilder.ToString();
		}
	}
	public struct Run
	{
		public int Wear;

		public int Count;

		public Run(int wear, int count)
		{
			Wear = wear;
			Count = count;
		}
	}
	public static class WearMath
	{
		public static List<Run> Fresh(int n)
		{
			List<Run> list = new List<Run>();
			if (n > 0)
			{
				list.Add(new Run(0, n));
			}
			return list;
		}

		public static int Count(List<Run> q)
		{
			int num = 0;
			foreach (Run item in q)
			{
				num += item.Count;
			}
			return num;
		}

		public static bool AnyWorn(List<Run> q)
		{
			foreach (Run item in q)
			{
				if (item.Wear > 0 && item.Count > 0)
				{
					return true;
				}
			}
			return false;
		}

		public static long HitsLeft(List<Run> q, int life)
		{
			long num = 0L;
			foreach (Run item in q)
			{
				num += (long)item.Count * (long)(life - item.Wear);
			}
			return num;
		}

		public static bool Breaks(int wear, int life)
		{
			return wear + 1 >= life;
		}

		public static List<Run> Tidy(List<Run> q)
		{
			List<Run> list = new List<Run>(q.Count);
			foreach (Run item in q)
			{
				if (item.Count > 0)
				{
					if (list.Count > 0 && list[list.Count - 1].Wear == item.Wear)
					{
						list[list.Count - 1] = new Run(item.Wear, list[list.Count - 1].Count + item.Count);
					}
					else
					{
						list.Add(item);
					}
				}
			}
			q.Clear();
			q.AddRange(list);
			return q;
		}

		public static int Next(List<Run> q)
		{
			Tidy(q);
			if (q.Count != 0)
			{
				return q[0].Wear;
			}
			return -1;
		}

		public static int PopFront(List<Run> q)
		{
			Tidy(q);
			if (q.Count == 0)
			{
				return -1;
			}
			Run run = q[0];
			if (run.Count == 1)
			{
				q.RemoveAt(0);
			}
			else
			{
				q[0] = new Run(run.Wear, run.Count - 1);
			}
			return run.Wear;
		}

		public static void Append(List<Run> q, List<Run> tail)
		{
			q.AddRange(tail);
			Tidy(q);
		}

		public static List<Run> TakeFront(List<Run> q, int k)
		{
			Tidy(q);
			List<Run> list = new List<Run>();
			while (k > 0 && q.Count > 0)
			{
				Run run = q[0];
				int num = Math.Min(k, run.Count);
				list.Add(new Run(run.Wear, num));
				k -= num;
				if (num == run.Count)
				{
					q.RemoveAt(0);
				}
				else
				{
					q[0] = new Run(run.Wear, run.Count - num);
				}
			}
			return list;
		}

		public static List<Run> TakeShare(List<Run> q, int k)
		{
			Tidy(q);
			int num = Count(q);
			if (k <= 0 || num <= 0)
			{
				return new List<Run>();
			}
			if (k >= num)
			{
				List<Run> result = new List<Run>(q);
				q.Clear();
				return result;
			}
			int[] array = new int[q.Count];
			long[] array2 = new long[q.Count];
			int i = 0;
			for (int j = 0; j < q.Count; j++)
			{
				long num2 = (long)q[j].Count * (long)k;
				array[j] = (int)(num2 / num);
				array2[j] = num2 % num;
				i += array[j];
			}
			for (; i < k; i++)
			{
				int num3 = -1;
				for (int l = 0; l < q.Count; l++)
				{
					if (array2[l] > 0 && (num3 < 0 || array2[l] > array2[num3]))
					{
						num3 = l;
					}
				}
				if (num3 < 0)
				{
					break;
				}
				array[num3]++;
				array2[num3] = 0L;
			}
			List<Run> list = new List<Run>();
			for (int m = 0; m < q.Count; m++)
			{
				list.Add(new Run(q[m].Wear, array[m]));
				q[m] = new Run(q[m].Wear, q[m].Count - array[m]);
			}
			Tidy(q);
			return Tidy(list);
		}

		public static List<Run>[] Spread(List<Run> source, int[] grown)
		{
			List<Run>[] array = new List<Run>[grown.Length];
			for (int i = 0; i < grown.Length; i++)
			{
				array[i] = ((grown[i] > 0) ? TakeFront(source, grown[i]) : new List<Run>());
			}
			return array;
		}

		public static string Format(List<Run> q)
		{
			Tidy(q);
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Run item in q)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(item.Wear).Append(':').Append(item.Count);
			}
			return stringBuilder.Append('/').Append(Count(q)).ToString();
		}

		public static List<Run> Queue(string text, int stack, int life)
		{
			List<Run> list = new List<Run>();
			int result = -1;
			if (!string.IsNullOrEmpty(text))
			{
				int num = text.LastIndexOf('/');
				string obj = ((num >= 0) ? text.Substring(0, num) : text);
				if (num >= 0 && !int.TryParse(text.Substring(num + 1), out result))
				{
					result = -1;
				}
				string[] array = obj.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text2 in array)
				{
					int num2 = text2.IndexOf(':');
					if (num2 >= 0 && int.TryParse(text2.Substring(0, num2), out var result2) && int.TryParse(text2.Substring(num2 + 1), out var result3) && result2 >= 0 && result3 > 0)
					{
						list.Add(new Run(Math.Min(result2, Math.Max(0, life - 1)), result3));
					}
				}
			}
			Tidy(list);
			int num3 = Count(list);
			if (stack <= 0)
			{
				return new List<Run>();
			}
			if (stack < num3)
			{
				return TakeShare(list, stack);
			}
			if (stack > num3)
			{
				Append(list, Fresh(stack - num3));
			}
			return list;
		}

		public static List<KeyValuePair<int, int>> Rows(List<Run> q, int life)
		{
			SortedDictionary<int, int> sortedDictionary = new SortedDictionary<int, int>();
			foreach (Run item in q)
			{
				if (item.Count > 0)
				{
					int key = life - item.Wear;
					sortedDictionary.TryGetValue(key, out var value);
					sortedDictionary[key] = value + item.Count;
				}
			}
			List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
			foreach (KeyValuePair<int, int> item2 in sortedDictionary)
			{
				list.Add(new KeyValuePair<int, int>(item2.Value, item2.Key));
			}
			return list;
		}
	}
}