Decompiled source of ItemUnstuck v1.0.3

ItemUnstuck.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Tolga")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+abf9373dff71aca88819e1d28251b7dab3a82367")]
[assembly: AssemblyProduct("ItemUnstuck")]
[assembly: AssemblyTitle("ItemUnstuck")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
[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 ItemUnstuck
{
	internal static class ConfigEntries
	{
		internal static ConfigEntry<bool> Enable { get; set; }

		internal static ConfigEntry<bool> HudNotifications { get; set; }

		internal static ConfigEntry<bool> ShowInitialValueOnGrab { get; set; }

		internal static ConfigEntry<bool> EnableRelocation { get; set; }

		internal static ConfigEntry<bool> InvulnerableUntilGrab { get; set; }

		internal static ConfigEntry<bool> RestoreValueToInitial { get; set; }

		internal static ConfigEntry<bool> VerboseLogging { get; set; }

		internal static bool MidGameHudNotifications => true;

		internal static float ScanDelaySeconds => 1f;

		internal static float OccupiedDistance => 0.2f;

		internal static float StuckMinPenetrationDistance => 0.12f;

		internal static float StuckWeakPenetrationDistance => 0.03f;

		internal static bool RequireStaticWorldEvidenceForRelocation => true;

		internal static bool UseFallbackPositions => true;

		internal static bool EnableContainerDetection => true;

		internal static ConfigEntry<bool> EnableMidGameDetection { get; set; }

		internal static float MidGameScanDelaySeconds => 2.5f;

		internal static bool MotionProbeEnabled => true;

		internal static float MotionProbeDelaySeconds => 2f;

		internal static float MotionProbeWindowSeconds => 0.75f;

		internal static int MotionProbeSamples => 12;

		internal static float MotionMaxSpeedToFlag => 0.75f;

		internal static int MotionMinDirectionReversalsToFlag => 2;

		internal static float MotionReversalDotThreshold => -0.2f;

		internal static bool FallThroughRecoveryEnabled => true;

		internal static float FallThroughYThreshold => -50f;

		internal static float FallThroughWatchSeconds => 15f;

		internal static float FallThroughPollSeconds => 0.25f;

		internal static int FallThroughMaxRecoveriesPerItem => 2;

		internal static float DamageCheckSeconds => 3f;

		internal static int MaxRelocationsPerItem => 2;

		internal static float MinDamageToTrigger => 1f;
	}
	internal class FallThroughDetector : MonoBehaviour
	{
		private class FallState
		{
			public Vector3 StartPosition;

			public float StartTime;

			public float LastY;

			public bool IsFalling;
		}

		private Dictionary<int, FallState> trackedItems = new Dictionary<int, FallState>();

		private float checkInterval = ConfigEntries.FallThroughPollSeconds;

		private float nextCheckTime;

		private ValuableObject[]? cachedValuables;

		private float cacheExpiry;

		private const float CacheRefreshInterval = 2f;

		private float minFallSpeed = 2f;

		private float minFallDuration = 3f;

		private float maxFallDuration = 10f;

		public void Reset()
		{
			trackedItems.Clear();
			cachedValuables = null;
			cacheExpiry = 0f;
		}

		private ValuableObject[] GetValuables()
		{
			if (cachedValuables == null || Time.time > cacheExpiry)
			{
				cachedValuables = Object.FindObjectsOfType<ValuableObject>();
				cacheExpiry = Time.time + 2f;
			}
			return cachedValuables;
		}

		private void Update()
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: 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)
			if (!ConfigEntries.Enable.Value || !ConfigEntries.FallThroughRecoveryEnabled || Time.time < nextCheckTime)
			{
				return;
			}
			nextCheckTime = Time.time + checkInterval;
			ValuableObject[] valuables = GetValuables();
			HashSet<int> hashSet = new HashSet<int>();
			ValuableObject[] array = valuables;
			foreach (ValuableObject val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				PhysGrabObject physGrabObject = val.physGrabObject;
				if ((Object)(object)physGrabObject == (Object)null || ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart))
				{
					continue;
				}
				int id = ValueDiagnostics.GetId(val);
				if (id == 0)
				{
					continue;
				}
				hashSet.Add(id);
				float y = ((Component)val).transform.position.y;
				if (!trackedItems.TryGetValue(id, out FallState value))
				{
					value = new FallState
					{
						StartPosition = ((Component)val).transform.position,
						StartTime = Time.time,
						LastY = y,
						IsFalling = false
					};
					trackedItems[id] = value;
					continue;
				}
				float num = value.LastY - y;
				_ = Time.time;
				_ = value.StartTime;
				float num2 = num / checkInterval;
				if (num2 >= minFallSpeed)
				{
					if (!value.IsFalling)
					{
						value.IsFalling = true;
						value.StartTime = Time.time;
						value.StartPosition = ((Component)val).transform.position;
					}
					else
					{
						float num3 = Time.time - value.StartTime;
						if (num3 >= minFallDuration)
						{
							ItemUnstuck.Logger.LogWarning((object)("[ItemUnstuck] Fall-through detected for '" + ((Object)val).name + "' " + $"(falling for {num3:F1}s at {num2:F1}m/s, " + $"Y: {value.StartPosition.y:F1} -> {y:F1})"));
							StuckValuableRelocator.TryRecoverFallThrough(val);
							trackedItems.Remove(id);
							continue;
						}
						if (num3 > maxFallDuration)
						{
							trackedItems.Remove(id);
							continue;
						}
					}
				}
				else if (value.IsFalling)
				{
					value.IsFalling = false;
					value.StartTime = Time.time;
					value.StartPosition = ((Component)val).transform.position;
				}
				value.LastY = y;
			}
			List<int> list = new List<int>();
			foreach (int key in trackedItems.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (int item in list)
			{
				trackedItems.Remove(item);
			}
		}
	}
	internal static class FallThroughRecovery
	{
		[CompilerGenerated]
		private sealed class <WatchCoroutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ValuableObject valuable;

			public int id;

			public string label;

			private float <thresholdY>5__2;

			private float <poll>5__3;

			private int <maxRecoveries>5__4;

			private float <end>5__5;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <WatchCoroutine>d__4(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Expected O, but got Unknown
				//IL_0120: Unknown result type (might be due to invalid IL or missing references)
				bool result;
				try
				{
					switch (<>1__state)
					{
					default:
						result = false;
						goto end_IL_0000;
					case 0:
					{
						<>1__state = -1;
						<>1__state = -3;
						<thresholdY>5__2 = ConfigEntries.FallThroughYThreshold;
						float num = Mathf.Max(0.5f, ConfigEntries.FallThroughWatchSeconds);
						<poll>5__3 = Mathf.Clamp(ConfigEntries.FallThroughPollSeconds, 0.05f, 2f);
						<maxRecoveries>5__4 = Mathf.Clamp(ConfigEntries.FallThroughMaxRecoveriesPerItem, 0, 10);
						<end>5__5 = Time.time + num;
						goto IL_029f;
					}
					case 1:
						{
							<>1__state = -3;
							if (!ConfigEntries.Enable.Value || !ConfigEntries.FallThroughRecoveryEnabled)
							{
								result = false;
								break;
							}
							if ((Object)(object)valuable == (Object)null)
							{
								result = false;
								break;
							}
							PhysGrabObject physGrabObject = valuable.physGrabObject;
							if ((Object)(object)physGrabObject == (Object)null)
							{
								result = false;
								break;
							}
							if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
							{
								result = false;
								break;
							}
							float y = ((Component)valuable).transform.position.y;
							if (!(y >= <thresholdY>5__2))
							{
								RecoveryCounts.TryGetValue(id, out var value);
								if (value >= <maxRecoveries>5__4)
								{
									ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Fall-through detected for '{((Object)valuable).name}' (y={y:0.0}) but max recoveries reached ({value}/{<maxRecoveries>5__4}).");
									HudNotifier.ShowFallThrough(label, valuable, y, recovered: false, value, <maxRecoveries>5__4);
									result = false;
									break;
								}
								RecoveryCounts[id] = value + 1;
								if (!StuckValuableRelocator.TryRecoverFallThrough(valuable))
								{
									ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Fall-through detected for '{((Object)valuable).name}' (y={y:0.0}) after {label} relocation, but no safe slot found to recover.");
									HudNotifier.ShowFallThrough(label, valuable, y, recovered: false, value + 1, <maxRecoveries>5__4);
									result = false;
									break;
								}
								InvulnerabilityController.ApplyUntilGrab(physGrabObject);
								ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Recovered fall-through for '{((Object)valuable).name}' (y={y:0.0}) after {label} relocation; recovery #{value + 1}.");
								HudNotifier.ShowFallThrough(label, valuable, y, recovered: true, value + 1, <maxRecoveries>5__4);
							}
							goto IL_029f;
						}
						IL_029f:
						if (Time.time < <end>5__5)
						{
							<>2__current = (object)new WaitForSeconds(<poll>5__3);
							<>1__state = 1;
							result = true;
						}
						else
						{
							<>m__Finally1();
							result = false;
						}
						goto end_IL_0000;
					}
					<>m__Finally1();
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				Watching.Remove(id);
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly HashSet<int> Watching = new HashSet<int>();

		private static readonly Dictionary<int, int> RecoveryCounts = new Dictionary<int, int>();

		public static void Reset()
		{
			Watching.Clear();
			RecoveryCounts.Clear();
		}

		public static void WatchRelocated(ValuableObject valuable, string label)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.FallThroughRecoveryEnabled && !((Object)(object)ItemUnstuck.Instance == (Object)null) && !((Object)(object)valuable == (Object)null))
			{
				int id = ValueDiagnostics.GetId(valuable);
				if (id != 0 && Watching.Add(id))
				{
					((MonoBehaviour)ItemUnstuck.Instance).StartCoroutine(WatchCoroutine(valuable, id, label));
				}
			}
		}

		[IteratorStateMachine(typeof(<WatchCoroutine>d__4))]
		private static IEnumerator WatchCoroutine(ValuableObject valuable, int id, string label)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WatchCoroutine>d__4(0)
			{
				valuable = valuable,
				id = id,
				label = label
			};
		}
	}
	internal static class FreeSpaceProbe
	{
		public static bool HasFreeSpace(ValuableObject valuable, float upwardDot)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00c7: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			PhysGrabObject physGrabObject = valuable.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return false;
			}
			Bounds bounds = default(Bounds);
			((Bounds)(ref bounds))..ctor(physGrabObject.midPoint, Vector3.one * 0.1f);
			Collider[] componentsInChildren = ((Component)valuable).GetComponentsInChildren<Collider>();
			if (componentsInChildren != null && componentsInChildren.Length != 0)
			{
				bounds = componentsInChildren[0].bounds;
				for (int i = 1; i < componentsInChildren.Length; i++)
				{
					if ((Object)(object)componentsInChildren[i] != (Object)null && !componentsInChildren[i].isTrigger)
					{
						((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
					}
				}
			}
			Vector3 center = ((Bounds)(ref bounds)).center;
			Camera main = Camera.main;
			Vector3[] array2;
			if ((Object)(object)main != (Object)null)
			{
				Vector3[] array = (Vector3[])(object)new Vector3[3]
				{
					center,
					center + Vector3.up * ((Bounds)(ref bounds)).extents.y * 0.5f,
					center + Vector3.up * ((Bounds)(ref bounds)).extents.y
				};
				int num = ~LayerMask.GetMask(new string[4] { "Player", "Triggers", "PlayerOnlyCollision", "UI" });
				array2 = array;
				foreach (Vector3 val in array2)
				{
					Vector3 val2 = ((Component)main).transform.position - val;
					float magnitude = ((Vector3)(ref val2)).magnitude;
					if (!Physics.Raycast(val, ((Vector3)(ref val2)).normalized, magnitude - 0.1f, num, (QueryTriggerInteraction)1))
					{
						return true;
					}
				}
			}
			if (upwardDot < -0.3f)
			{
				float num2 = ((Bounds)(ref bounds)).extents.y + 0.15f;
				if (Physics.Raycast(center, Vector3.down, num2, LayerMask.GetMask(new string[2] { "Default", "Environment" }), (QueryTriggerInteraction)1))
				{
					return true;
				}
			}
			float num3 = 0.5f;
			Vector3[] array3 = (Vector3[])(object)new Vector3[5]
			{
				Vector3.forward,
				Vector3.back,
				Vector3.left,
				Vector3.right,
				Vector3.up
			};
			int num4 = 0;
			int mask = LayerMask.GetMask(new string[2] { "Default", "Environment" });
			array2 = array3;
			foreach (Vector3 val3 in array2)
			{
				if (!Physics.Raycast(center, val3, num3, mask, (QueryTriggerInteraction)1))
				{
					num4++;
				}
			}
			if (num4 >= 1)
			{
				return true;
			}
			return false;
		}
	}
	internal static class HudNotifier
	{
		private static string CleanName(string? name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Valuable";
			}
			string text = name;
			if (text.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				text = text.Replace("(Clone)", "", StringComparison.Ordinal);
			}
			return text;
		}

		public static void ShowRelocated(IReadOnlyList<RelocatedValuableInfo> relocated, float totalLostFromBaseline)
		{
			//IL_00b4: 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)
			if (!ConfigEntries.HudNotifications.Value || relocated.Count == 0)
			{
				return;
			}
			int num = 5;
			List<string> values = (from x in relocated.Take(num)
				select $"{x.Name} (${(int)x.ValueCurrentAtRelocation})").ToList();
			string text = ((relocated.Count > num) ? $" +{relocated.Count - num}" : "");
			string text2 = $"ItemUnstuck: relocated {relocated.Count} stuck valuable(s)\n" + $"Lost vs baseline: ${(int)totalLostFromBaseline}\n" + string.Join(", ", values) + text;
			try
			{
				SemiFunc.UIBigMessage(text2, "{check}", 20f, Color.white, Color.white);
			}
			catch (Exception ex)
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogDebug((object)("UIBigMessage failed: " + ex.Message));
				}
			}
		}

		public static void ShowFallThrough(string label, ValuableObject valuable, float y, bool recovered, int recoveryNumber, int maxRecoveries)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.HudNotifications.Value || (Object)(object)valuable == (Object)null)
			{
				return;
			}
			string arg = CleanName(((Object)valuable).name);
			string text = (recovered ? "recovered" : "FAILED (no safe slot)");
			string text2 = "ItemUnstuck: Item fell from map - " + text + "\n" + $"{arg} (${(int)valuable.dollarValueCurrent}) fell to Y={y:0.0}\n" + $"Recovery attempt {recoveryNumber}/{maxRecoveries}";
			if (!recovered)
			{
				_ = Color.red;
			}
			else
			{
				_ = Color.green;
			}
			try
			{
				SemiFunc.UIFocusText(text2, Color.white, Color.white, 3f);
			}
			catch (Exception ex)
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogDebug((object)("UIFocusText failed: " + ex.Message));
				}
			}
		}

		public static void ShowMidGameRelocation(string itemName)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.HudNotifications.Value || !ConfigEntries.MidGameHudNotifications)
			{
				return;
			}
			string text = CleanName(itemName);
			string text2 = "ItemUnstuck: Relocated stuck spawn\n" + text;
			try
			{
				SemiFunc.UIFocusText(text2, Color.white, Color.green, 3f);
			}
			catch (Exception ex)
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogDebug((object)("UIFocusText failed: " + ex.Message));
				}
			}
		}

		public static void Show(string message)
		{
			//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)
			try
			{
				SemiFunc.UIFocusText(message, Color.white, Color.cyan, 4f);
			}
			catch (Exception ex)
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogDebug((object)("UIFocusText failed: " + ex.Message));
				}
			}
		}
	}
	internal static class InvulnerabilityController
	{
		private static readonly FieldInfo? IndestructibleSpawnTimerField = typeof(PhysGrabObjectImpactDetector).GetField("indestructibleSpawnTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static void ApplyUntilGrab(PhysGrabObject physGrabObject)
		{
			if (!ConfigEntries.Enable.Value || !ConfigEntries.InvulnerableUntilGrab.Value || (Object)(object)physGrabObject == (Object)null)
			{
				return;
			}
			PhysGrabObjectImpactDetector component = ((Component)physGrabObject).GetComponent<PhysGrabObjectImpactDetector>();
			if (!((Object)(object)component == (Object)null))
			{
				if (IndestructibleSpawnTimerField == null)
				{
					ItemUnstuck.Logger.LogError((object)"Could not find PhysGrabObjectImpactDetector.indestructibleSpawnTimer; invulnerability not applied.");
					return;
				}
				float num = 99999f;
				IndestructibleSpawnTimerField.SetValue(component, num);
				physGrabObject.OverrideIndestructible(0.5f);
			}
		}

		public static void Clear(PhysGrabObject physGrabObject)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value && !((Object)(object)physGrabObject == (Object)null))
			{
				PhysGrabObjectImpactDetector component = ((Component)physGrabObject).GetComponent<PhysGrabObjectImpactDetector>();
				if ((Object)(object)component != (Object)null && IndestructibleSpawnTimerField != null)
				{
					IndestructibleSpawnTimerField.SetValue(component, 0f);
				}
				physGrabObject.ResetIndestructible();
			}
		}
	}
	[BepInPlugin("Tolga.ItemUnstuck", "ItemUnstuck", "1.0.3")]
	public class ItemUnstuck : BaseUnityPlugin
	{
		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass20_0
		{
			public List<ValuableObject> cachedValuables;
		}

		[CompilerGenerated]
		private sealed class <ProcessMidGameBatch>d__26 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ItemUnstuck <>4__this;

			private int <currentGeneration>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ProcessMidGameBatch>d__26(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Expected O, but got Unknown
				//IL_015c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0166: Expected O, but got Unknown
				int num = <>1__state;
				ItemUnstuck itemUnstuck = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					float num2 = Mathf.Max(0.5f, ConfigEntries.MidGameScanDelaySeconds);
					<>2__current = (object)new WaitForSeconds(num2);
					<>1__state = 1;
					return true;
				}
				case 1:
					<>1__state = -1;
					<currentGeneration>5__2 = itemUnstuck.scanGeneration;
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				while (itemUnstuck.midGameSpawnQueue.Count > 0)
				{
					if (itemUnstuck.scanGeneration != <currentGeneration>5__2)
					{
						itemUnstuck.midGameSpawnQueue.Clear();
						break;
					}
					ValuableObject[] allValuables = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
					int num3 = 0;
					while (itemUnstuck.midGameSpawnQueue.Count > 0 && num3 < 5)
					{
						ValuableObject val = itemUnstuck.midGameSpawnQueue.Dequeue();
						if (!((Object)(object)val == (Object)null))
						{
							PhysGrabObject physGrabObject = val.physGrabObject;
							if (!((Object)(object)physGrabObject == (Object)null) && (!((Object)(object)physGrabObject.impactDetector != (Object)null) || !physGrabObject.impactDetector.inCart) && !StuckValuableRelocator.ShouldSkipEmbedmentCheck(val, out string _))
							{
								StuckValuableRelocator.TryRelocateSingleMidGameSpawn(val, allValuables);
								num3++;
							}
						}
					}
					if (ConfigEntries.VerboseLogging.Value && num3 > 0)
					{
						Logger.LogInfo((object)$"[ItemUnstuck][Batch] Processed {num3} mid-game spawns, {itemUnstuck.midGameSpawnQueue.Count} remaining");
					}
					if (itemUnstuck.midGameSpawnQueue.Count > 0)
					{
						<>2__current = (object)new WaitForSeconds(0.5f);
						<>1__state = 2;
						return true;
					}
				}
				itemUnstuck.midGameBatchCoroutine = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ProcessSingleMidGameSpawn>d__25 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public ValuableObject valuable;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ProcessSingleMidGameSpawn>d__25(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForFixedUpdate();
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					if ((Object)(object)valuable == (Object)null)
					{
						return false;
					}
					ValuableObject[] allValuables = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
					StuckValuableRelocator.TryRelocateSingleMidGameSpawn(valuable, allValuables);
					return false;
				}
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ScanAfterVolumesSetup>d__20 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int generation;

			public ItemUnstuck <>4__this;

			private <>c__DisplayClass20_0 <>8__1;

			private ValuableObject[] <valuablesAtBaseline>5__2;

			private HashSet<int> <affectedIds>5__3;

			private HashSet<int> <relocatedIds>5__4;

			private List<RelocatedValuableInfo> <relocatedInfos>5__5;

			private Dictionary<int, MotionStats> <motionStats>5__6;

			private float <damageWait>5__7;

			private float <minDamage>5__8;

			private int <maxRelocationsPerItem>5__9;

			private float <probeDelay>5__10;

			private float <window>5__11;

			private int <samples>5__12;

			private float <dt>5__13;

			private float <reversalDotThreshold>5__14;

			private List<ValuableObject> <tracked>5__15;

			private List<int> <ids>5__16;

			private List<Vector3> <prevPos>5__17;

			private List<Vector3> <prevDelta>5__18;

			private List<float> <sumDist>5__19;

			private List<float> <maxSpeed>5__20;

			private List<int> <reversals>5__21;

			private int <s>5__22;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ScanAfterVolumesSetup>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = default(<>c__DisplayClass20_0);
				<valuablesAtBaseline>5__2 = null;
				<affectedIds>5__3 = null;
				<relocatedIds>5__4 = null;
				<relocatedInfos>5__5 = null;
				<motionStats>5__6 = null;
				<tracked>5__15 = null;
				<ids>5__16 = null;
				<prevPos>5__17 = null;
				<prevDelta>5__18 = null;
				<sumDist>5__19 = null;
				<maxSpeed>5__20 = null;
				<reversals>5__21 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Expected O, but got Unknown
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0460: Unknown result type (might be due to invalid IL or missing references)
				//IL_0465: Unknown result type (might be due to invalid IL or missing references)
				//IL_0467: Unknown result type (might be due to invalid IL or missing references)
				//IL_0471: Unknown result type (might be due to invalid IL or missing references)
				//IL_0476: Unknown result type (might be due to invalid IL or missing references)
				//IL_047b: Unknown result type (might be due to invalid IL or missing references)
				//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
				//IL_040b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0415: Expected O, but got Unknown
				//IL_0552: Unknown result type (might be due to invalid IL or missing references)
				//IL_0561: Unknown result type (might be due to invalid IL or missing references)
				//IL_04ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0242: Unknown result type (might be due to invalid IL or missing references)
				//IL_024c: Expected O, but got Unknown
				//IL_0512: Unknown result type (might be due to invalid IL or missing references)
				//IL_0519: Unknown result type (might be due to invalid IL or missing references)
				//IL_0703: Unknown result type (might be due to invalid IL or missing references)
				//IL_070d: Expected O, but got Unknown
				int num = <>1__state;
				ItemUnstuck itemUnstuck = <>4__this;
				int count;
				ValuableObject[] array;
				List<ValuableObject> valuablesWithLoss;
				ValuableObject[] array2;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					if (!ConfigEntries.Enable.Value)
					{
						return false;
					}
					float num3 = Mathf.Max(0f, ConfigEntries.ScanDelaySeconds);
					if (num3 > 0f)
					{
						<>2__current = (object)new WaitForSeconds(num3);
						<>1__state = 1;
						return true;
					}
					goto IL_0073;
				}
				case 1:
					<>1__state = -1;
					goto IL_0073;
				case 2:
					<>1__state = -1;
					goto IL_025c;
				case 3:
				{
					<>1__state = -1;
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					for (int i = 0; i < <tracked>5__15.Count; i++)
					{
						ValuableObject val = <tracked>5__15[i];
						if ((Object)(object)val == (Object)null)
						{
							continue;
						}
						Vector3 position = ((Component)val).transform.position;
						Vector3 value = position - <prevPos>5__17[i];
						float magnitude = ((Vector3)(ref value)).magnitude;
						<sumDist>5__19[i] += magnitude;
						float num2 = magnitude / Mathf.Max(0.0001f, <dt>5__13);
						if (num2 > <maxSpeed>5__20[i])
						{
							<maxSpeed>5__20[i] = num2;
						}
						if (<s>5__22 >= 2)
						{
							Vector3 val2 = <prevDelta>5__18[i];
							if (((Vector3)(ref val2)).sqrMagnitude > 1E-06f && ((Vector3)(ref value)).sqrMagnitude > 1E-06f && Vector3.Dot(((Vector3)(ref val2)).normalized, ((Vector3)(ref value)).normalized) <= <reversalDotThreshold>5__14)
							{
								<reversals>5__21[i]++;
							}
						}
						<prevDelta>5__18[i] = value;
						<prevPos>5__17[i] = position;
					}
					<s>5__22++;
					goto IL_0592;
				}
				case 4:
					{
						<>1__state = -1;
						goto IL_071d;
					}
					IL_0073:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					if (SpawnSlotRegistry.Slots.Count == 0)
					{
						if (ConfigEntries.VerboseLogging.Value)
						{
							Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no spawn slots captured yet.");
						}
						return false;
					}
					count = SpawnSlotRegistry.Slots.Count;
					Logger.LogInfo((object)$"[ItemUnstuck] Level loaded: {count} free (spawn) slots.");
					try
					{
						SemiFunc.UIBigMessage($"ItemUnstuck: {count} free slots", "{check}", 4f, Color.white, Color.white);
					}
					catch
					{
					}
					<>8__1.cachedValuables = new List<ValuableObject>(Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>());
					if (<>8__1.cachedValuables.Count == 0)
					{
						if (ConfigEntries.VerboseLogging.Value)
						{
							Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no valuables found.");
						}
						return false;
					}
					<valuablesAtBaseline>5__2 = <ScanAfterVolumesSetup>g__GetLiveSnapshot|20_0(ref <>8__1);
					if (<valuablesAtBaseline>5__2.Length == 0)
					{
						if (ConfigEntries.VerboseLogging.Value)
						{
							Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: all cached valuables were destroyed before baseline capture.");
						}
						return false;
					}
					<affectedIds>5__3 = new HashSet<int>();
					<relocatedIds>5__4 = new HashSet<int>();
					<relocatedInfos>5__5 = new List<RelocatedValuableInfo>();
					ValueDiagnostics.CaptureBaselineIfNeeded(<valuablesAtBaseline>5__2);
					if (ConfigEntries.EnableRelocation.Value)
					{
						foreach (RelocatedValuableInfo item in StuckValuableRelocator.RelocateWorldEmbedded(<valuablesAtBaseline>5__2, null).Relocated)
						{
							<relocatedInfos>5__5.Add(item);
						}
					}
					<motionStats>5__6 = null;
					if (ConfigEntries.MotionProbeEnabled)
					{
						<probeDelay>5__10 = Mathf.Max(0f, ConfigEntries.MotionProbeDelaySeconds);
						if (<probeDelay>5__10 > 0f)
						{
							<>2__current = (object)new WaitForSeconds(<probeDelay>5__10);
							<>1__state = 2;
							return true;
						}
						goto IL_025c;
					}
					goto IL_06a6;
					IL_0592:
					if (<s>5__22 < <samples>5__12)
					{
						<>2__current = (object)new WaitForSeconds(<dt>5__13);
						<>1__state = 3;
						return true;
					}
					for (int j = 0; j < <tracked>5__15.Count; j++)
					{
						float averageSpeed = <sumDist>5__19[j] / <window>5__11;
						<motionStats>5__6[<ids>5__16[j]] = new MotionStats(averageSpeed, <maxSpeed>5__20[j], <reversals>5__21[j], <reversalDotThreshold>5__14);
					}
					if (ConfigEntries.VerboseLogging.Value)
					{
						Logger.LogInfo((object)$"[ItemUnstuck][Debug] Motion probe captured stats for {<motionStats>5__6.Count} valuables (delay={<probeDelay>5__10:0.00}s window={<window>5__11:0.00}s samples={<samples>5__12}).");
					}
					<tracked>5__15 = null;
					<ids>5__16 = null;
					<prevPos>5__17 = null;
					<prevDelta>5__18 = null;
					<sumDist>5__19 = null;
					<maxSpeed>5__20 = null;
					<reversals>5__21 = null;
					goto IL_06a6;
					IL_071d:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					array = <ScanAfterVolumesSetup>g__GetLiveSnapshot|20_0(ref <>8__1);
					valuablesWithLoss = ValueDiagnostics.GetValuablesWithLoss(array, <minDamage>5__8);
					if (valuablesWithLoss.Count == 0)
					{
						if (ConfigEntries.VerboseLogging.Value)
						{
							Logger.LogInfo((object)$"[ItemUnstuck][Debug] Damage check pass {<samples>5__12}: no damaged valuables detected.");
						}
						if (<samples>5__12 == 1)
						{
							break;
						}
					}
					else
					{
						List<ValuableObject> list;
						if (<samples>5__12 == 1)
						{
							list = valuablesWithLoss;
						}
						else
						{
							list = new List<ValuableObject>();
							foreach (ValuableObject item2 in valuablesWithLoss)
							{
								if (!((Object)(object)item2 == (Object)null) && <relocatedIds>5__4.Contains(ValueDiagnostics.GetId(item2)))
								{
									list.Add(item2);
								}
							}
						}
						if (list.Count != 0)
						{
							foreach (ValuableObject item3 in list)
							{
								if (!((Object)(object)item3 == (Object)null))
								{
									<affectedIds>5__3.Add(ValueDiagnostics.GetId(item3));
								}
							}
							foreach (RelocatedValuableInfo item4 in StuckValuableRelocator.RelocateCandidates(list, array, <motionStats>5__6).Relocated)
							{
								<relocatedInfos>5__5.Add(item4);
							}
							foreach (ValuableObject item5 in list)
							{
								if (!((Object)(object)item5 == (Object)null))
								{
									<relocatedIds>5__4.Add(ValueDiagnostics.GetId(item5));
								}
							}
						}
					}
					<samples>5__12++;
					goto IL_08fa;
					IL_08fa:
					if (<samples>5__12 > <maxRelocationsPerItem>5__9)
					{
						break;
					}
					if (<damageWait>5__7 > 0f)
					{
						<>2__current = (object)new WaitForSeconds(<damageWait>5__7);
						<>1__state = 4;
						return true;
					}
					goto IL_071d;
					IL_06a6:
					<damageWait>5__7 = Mathf.Max(0f, ConfigEntries.DamageCheckSeconds);
					<minDamage>5__8 = Mathf.Max(0f, ConfigEntries.MinDamageToTrigger);
					<maxRelocationsPerItem>5__9 = Mathf.Clamp(ConfigEntries.MaxRelocationsPerItem, 0, 10);
					<samples>5__12 = 1;
					goto IL_08fa;
					IL_025c:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					<motionStats>5__6 = new Dictionary<int, MotionStats>();
					<window>5__11 = Mathf.Max(0.05f, ConfigEntries.MotionProbeWindowSeconds);
					<samples>5__12 = Mathf.Clamp(ConfigEntries.MotionProbeSamples, 2, 60);
					<dt>5__13 = <window>5__11 / (float)(<samples>5__12 - 1);
					<reversalDotThreshold>5__14 = Mathf.Clamp(ConfigEntries.MotionReversalDotThreshold, -0.99f, 0.99f);
					<tracked>5__15 = new List<ValuableObject>(<valuablesAtBaseline>5__2.Length);
					<ids>5__16 = new List<int>(<valuablesAtBaseline>5__2.Length);
					<prevPos>5__17 = new List<Vector3>(<valuablesAtBaseline>5__2.Length);
					<prevDelta>5__18 = new List<Vector3>(<valuablesAtBaseline>5__2.Length);
					<sumDist>5__19 = new List<float>(<valuablesAtBaseline>5__2.Length);
					<maxSpeed>5__20 = new List<float>(<valuablesAtBaseline>5__2.Length);
					<reversals>5__21 = new List<int>(<valuablesAtBaseline>5__2.Length);
					array2 = <valuablesAtBaseline>5__2;
					foreach (ValuableObject val3 in array2)
					{
						if (!((Object)(object)val3 == (Object)null))
						{
							<tracked>5__15.Add(val3);
							<ids>5__16.Add(ValueDiagnostics.GetId(val3));
							<prevPos>5__17.Add(((Component)val3).transform.position);
							<prevDelta>5__18.Add(Vector3.zero);
							<sumDist>5__19.Add(0f);
							<maxSpeed>5__20.Add(0f);
							<reversals>5__21.Add(0);
						}
					}
					<s>5__22 = 1;
					goto IL_0592;
				}
				if (ConfigEntries.RestoreValueToInitial.Value && <affectedIds>5__3.Count > 0)
				{
					array2 = <ScanAfterVolumesSetup>g__GetLiveSnapshot|20_0(ref <>8__1);
					foreach (ValuableObject val4 in array2)
					{
						if (!((Object)(object)val4 == (Object)null) && <affectedIds>5__3.Contains(ValueDiagnostics.GetId(val4)))
						{
							ValueDiagnostics.RestoreValueToBaseline(val4);
						}
					}
				}
				ValuableObject[] valuables = <ScanAfterVolumesSetup>g__GetLiveSnapshot|20_0(ref <>8__1);
				(int, float, float, float, float) tuple = ValueDiagnostics.ComputeTotalsWithBaseline(valuables);
				if (ConfigEntries.VerboseLogging.Value)
				{
					Logger.LogInfo((object)$"[ItemUnstuck] Post-flow totals: current=${(int)tuple.Item2}, original=${(int)tuple.Item3}, baselineCurrent=${(int)tuple.Item4}, lost=${(int)tuple.Item5}, count={tuple.Item1}");
				}
				ValueDiagnostics.LogValueLosses(valuables);
				if (<relocatedInfos>5__5.Count > 0)
				{
					HudNotifier.ShowRelocated(<relocatedInfos>5__5, tuple.Item5);
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private Coroutine? scanCoroutine;

		private int scanGeneration;

		private readonly Queue<ValuableObject> midGameSpawnQueue = new Queue<ValuableObject>();

		private Coroutine? midGameBatchCoroutine;

		private const float BATCH_PROCESS_INTERVAL = 0.5f;

		private const int MAX_BATCH_SIZE = 5;

		internal static ItemUnstuck Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			ConfigEntries.Enable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable/disable the ItemUnstuck mod.");
			ConfigEntries.EnableRelocation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableRelocation", true, "If true, detects stuck valuables and relocates them to a free valuable spawn slot.");
			ConfigEntries.HudNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HudNotifications", true, "Show a HUD notification when stuck valuables are relocated.");
			ConfigEntries.ShowInitialValueOnGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowInitialValueOnGrab", false, "Show a HUD message displaying the item's initial (undamaged) value when you grab it.");
			ConfigEntries.InvulnerableUntilGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "InvulnerableUntilGrab", true, "If true, make valuables invulnerable until any player grabs them.");
			ConfigEntries.RestoreValueToInitial = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "RestoreValueToInitial", true, "If true, restore damaged/relocated valuables back to their initial value.");
			ConfigEntries.VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Enable verbose logging for stuck detection and relocation decisions. Set to true for maximum logging during testing.");
			ConfigEntries.EnableMidGameDetection = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "EnableMidGameDetection", true, "Enable detection and relocation of items that get stuck immediately after spawning mid-game (e.g. from vents/monsters).");
			SpawnSlotRegistry.Clear();
			Patch();
			((Component)this).gameObject.AddComponent<FallThroughDetector>();
			if (ConfigEntries.VerboseLogging.Value)
			{
				RelocationAnalytics.Initialize();
				Logger.LogInfo((object)("[Diagnostics] Log file: " + RelocationAnalytics.GetLogFilePath()));
			}
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void NotifyValuableVolumesSetupComplete()
		{
			//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)
			scanGeneration++;
			StuckValuableRelocator.ClearCache();
			Scene activeScene = SceneManager.GetActiveScene();
			RelocationAnalytics.SetLevel(((Scene)(ref activeScene)).name);
			if (scanCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(scanCoroutine);
				scanCoroutine = null;
			}
			scanCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanAfterVolumesSetup(scanGeneration));
		}

		[IteratorStateMachine(typeof(<ScanAfterVolumesSetup>d__20))]
		private IEnumerator ScanAfterVolumesSetup(int generation)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScanAfterVolumesSetup>d__20(0)
			{
				<>4__this = this,
				generation = generation
			};
		}

		internal void Patch()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0025: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnDestroy()
		{
			RelocationAnalytics.Shutdown();
		}

		internal void ScheduleMidGameStuckCheck(ValuableObject valuable)
		{
			if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableMidGameDetection.Value || !ConfigEntries.EnableRelocation.Value || (Object)(object)valuable == (Object)null)
			{
				return;
			}
			if (StuckValuableRelocator.WillEmbedSoon(valuable, out float _, out string reason))
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					Logger.LogInfo((object)("[ItemUnstuck][Predictive] Item '" + ((Object)valuable).name + "' " + reason + " - immediate processing"));
				}
				((MonoBehaviour)this).StartCoroutine(ProcessSingleMidGameSpawn(valuable));
			}
			else
			{
				midGameSpawnQueue.Enqueue(valuable);
				if (midGameBatchCoroutine == null)
				{
					midGameBatchCoroutine = ((MonoBehaviour)this).StartCoroutine(ProcessMidGameBatch());
				}
			}
		}

		[IteratorStateMachine(typeof(<ProcessSingleMidGameSpawn>d__25))]
		private IEnumerator ProcessSingleMidGameSpawn(ValuableObject valuable)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ProcessSingleMidGameSpawn>d__25(0)
			{
				valuable = valuable
			};
		}

		[IteratorStateMachine(typeof(<ProcessMidGameBatch>d__26))]
		private IEnumerator ProcessMidGameBatch()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ProcessMidGameBatch>d__26(0)
			{
				<>4__this = this
			};
		}

		[CompilerGenerated]
		internal static ValuableObject[] <ScanAfterVolumesSetup>g__GetLiveSnapshot|20_0(ref <>c__DisplayClass20_0 P_0)
		{
			List<ValuableObject> list = new List<ValuableObject>(P_0.cachedValuables.Count);
			for (int i = 0; i < P_0.cachedValuables.Count; i++)
			{
				ValuableObject val = P_0.cachedValuables[i];
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
			}
			return list.ToArray();
		}
	}
	public static class ItemUnstuckEvents
	{
		public static event Func<ValuableObject, Vector3, bool>? OnBeforeRelocate;

		public static event Action<ValuableObject, Vector3, Vector3>? OnItemRelocated;

		public static event Action<ValuableObject>? OnMidGameSpawnDetected;

		public static event Action<ValuableObject, string>? OnRelocationFailed;

		internal static bool RaiseBeforeRelocate(ValuableObject valuable, Vector3 targetPosition)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (ItemUnstuckEvents.OnBeforeRelocate == null)
			{
				return true;
			}
			Delegate[] invocationList = ItemUnstuckEvents.OnBeforeRelocate.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Func<ValuableObject, Vector3, bool> func = (Func<ValuableObject, Vector3, bool>)invocationList[i];
				try
				{
					if (!func(valuable, targetPosition))
					{
						return false;
					}
				}
				catch (Exception ex)
				{
					ItemUnstuck.Logger.LogError((object)("[Events] OnBeforeRelocate handler error: " + ex.Message));
				}
			}
			return true;
		}

		internal static void RaiseItemRelocated(ValuableObject valuable, Vector3 from, Vector3 to)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (ItemUnstuckEvents.OnItemRelocated == null)
			{
				return;
			}
			Delegate[] invocationList = ItemUnstuckEvents.OnItemRelocated.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<ValuableObject, Vector3, Vector3> action = (Action<ValuableObject, Vector3, Vector3>)invocationList[i];
				try
				{
					action(valuable, from, to);
				}
				catch (Exception ex)
				{
					ItemUnstuck.Logger.LogError((object)("[Events] OnItemRelocated handler error: " + ex.Message));
				}
			}
		}

		internal static void RaiseMidGameSpawnDetected(ValuableObject valuable)
		{
			if (ItemUnstuckEvents.OnMidGameSpawnDetected == null)
			{
				return;
			}
			Delegate[] invocationList = ItemUnstuckEvents.OnMidGameSpawnDetected.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<ValuableObject> action = (Action<ValuableObject>)invocationList[i];
				try
				{
					action(valuable);
				}
				catch (Exception ex)
				{
					ItemUnstuck.Logger.LogError((object)("[Events] OnMidGameSpawnDetected handler error: " + ex.Message));
				}
			}
		}

		internal static void RaiseRelocationFailed(ValuableObject valuable, string reason)
		{
			if (ItemUnstuckEvents.OnRelocationFailed == null)
			{
				return;
			}
			Delegate[] invocationList = ItemUnstuckEvents.OnRelocationFailed.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<ValuableObject, string> action = (Action<ValuableObject, string>)invocationList[i];
				try
				{
					action(valuable, reason);
				}
				catch (Exception ex)
				{
					ItemUnstuck.Logger.LogError((object)("[Events] OnRelocationFailed handler error: " + ex.Message));
				}
			}
		}
	}
	internal readonly struct MotionStats
	{
		public float AverageSpeed { get; }

		public float MaxSpeed { get; }

		public int DirectionReversals { get; }

		public float ReversalDotThreshold { get; }

		public MotionStats(float averageSpeed, float maxSpeed, int directionReversals, float reversalDotThreshold)
		{
			AverageSpeed = averageSpeed;
			MaxSpeed = maxSpeed;
			DirectionReversals = directionReversals;
			ReversalDotThreshold = reversalDotThreshold;
		}

		public override string ToString()
		{
			return $"avgSpeed={AverageSpeed:0.000}m/s maxSpeed={MaxSpeed:0.000}m/s reversals={DirectionReversals} dotTh={ReversalDotThreshold:0.00}";
		}
	}
	internal readonly struct RelocatedValuableInfo
	{
		public string Name { get; }

		public Type Type { get; }

		public float ValueCurrentAtRelocation { get; }

		public float? BaselineCurrent { get; }

		public Vector3 From { get; }

		public Vector3 To { get; }

		public RelocatedValuableInfo(string name, Type type, float valueCurrentAtRelocation, float? baselineCurrent, Vector3 from, Vector3 to)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Name = name;
			Type = type;
			ValueCurrentAtRelocation = valueCurrentAtRelocation;
			BaselineCurrent = baselineCurrent;
			From = from;
			To = to;
		}
	}
	internal static class RelocationAnalytics
	{
		private static int totalRelocations;

		private static int successfulRelocations;

		private static int fallbackRelocations;

		private static int failedRelocations;

		private static int midGameRelocations;

		private static int predictiveRelocations;

		private static int skippedItems;

		private static DateTime sessionStart = DateTime.Now;

		private static string currentLevel = "Unknown";

		private static int levelLoadCount;

		private static readonly string LogDirectory = Path.Combine(Paths.PluginPath, "ItemUnstuck", "logs");

		private static readonly string LogFilePath = Path.Combine(LogDirectory, "itemunstuck.log");

		private static readonly string OldLogFilePath = Path.Combine(LogDirectory, "itemunstuck.old.log");

		private const long MAX_LOG_SIZE_BYTES = 5242880L;

		private const int MAX_LOG_AGE_HOURS = 24;

		private static StreamWriter? logWriter;

		private static DateTime lastRotationCheck = DateTime.MinValue;

		private static readonly object writeLock = new object();

		private static bool initialized;

		internal static void Initialize()
		{
			if (initialized)
			{
				return;
			}
			try
			{
				if (!Directory.Exists(LogDirectory))
				{
					Directory.CreateDirectory(LogDirectory);
				}
				RotateLogIfNeeded();
				logWriter = new StreamWriter(LogFilePath, append: true, Encoding.UTF8)
				{
					AutoFlush = true
				};
				initialized = true;
				WriteToLog("========================================");
				WriteToLog($"SESSION START: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
				WriteToLog("ItemUnstuck Diagnostics Log");
				WriteToLog("Unity: " + Application.unityVersion + " | Game: " + Application.version);
				WriteToLog("========================================");
				WriteConfig();
			}
			catch (Exception ex)
			{
				ItemUnstuck.Logger.LogError((object)("[Diagnostics] Failed to initialize logging: " + ex.Message));
			}
		}

		internal static void Shutdown()
		{
			try
			{
				if (logWriter != null)
				{
					WriteToLog($"SESSION END: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
					WriteToLog("Stats: " + GetSummary());
					WriteToLog("========================================\n");
					logWriter.Close();
					logWriter = null;
				}
			}
			catch
			{
			}
			initialized = false;
		}

		private static void RotateLogIfNeeded()
		{
			try
			{
				if (!File.Exists(LogFilePath))
				{
					return;
				}
				FileInfo fileInfo = new FileInfo(LogFilePath);
				bool flag = false;
				if (fileInfo.Length > 5242880)
				{
					flag = true;
				}
				if ((DateTime.Now - fileInfo.LastWriteTime).TotalHours > 24.0)
				{
					flag = true;
				}
				if (flag)
				{
					if (File.Exists(OldLogFilePath))
					{
						File.Delete(OldLogFilePath);
					}
					File.Move(LogFilePath, OldLogFilePath);
				}
			}
			catch (Exception ex)
			{
				ItemUnstuck.Logger.LogWarning((object)("[Diagnostics] Log rotation failed: " + ex.Message));
			}
		}

		private static void WriteToLog(string message)
		{
			if (logWriter == null)
			{
				return;
			}
			lock (writeLock)
			{
				try
				{
					logWriter.WriteLine(message);
					if ((DateTime.Now - lastRotationCheck).TotalMinutes > 5.0)
					{
						lastRotationCheck = DateTime.Now;
						if (logWriter.BaseStream.Length > 5242880)
						{
							logWriter.Close();
							RotateLogIfNeeded();
							logWriter = new StreamWriter(LogFilePath, append: true, Encoding.UTF8)
							{
								AutoFlush = true
							};
						}
					}
				}
				catch
				{
				}
			}
		}

		private static void WriteConfig()
		{
			try
			{
				WriteToLog("[CONFIG]");
				WriteToLog($"  Enable={ConfigEntries.Enable.Value}");
				WriteToLog($"  EnableRelocation={ConfigEntries.EnableRelocation.Value}");
				WriteToLog($"  EnableMidGameDetection={ConfigEntries.EnableMidGameDetection}");
				WriteToLog($"  EnableContainerDetection={ConfigEntries.EnableContainerDetection}");
				WriteToLog($"  UseFallbackPositions={ConfigEntries.UseFallbackPositions}");
				WriteToLog($"  InvulnerableUntilGrab={ConfigEntries.InvulnerableUntilGrab.Value}");
				WriteToLog($"  ScanDelaySeconds={ConfigEntries.ScanDelaySeconds}");
				WriteToLog($"  MidGameScanDelaySeconds={ConfigEntries.MidGameScanDelaySeconds}");
				WriteToLog($"  StuckMinPenetrationDistance={ConfigEntries.StuckMinPenetrationDistance}");
				WriteToLog($"  VerboseLogging={ConfigEntries.VerboseLogging.Value}");
			}
			catch
			{
			}
		}

		internal static void LogRelocation(string itemName, Vector3 from, Vector3 to, string reason, bool usedFallback, bool isMidGame, bool isPredictive)
		{
			//IL_00c8: 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)
			totalRelocations++;
			successfulRelocations++;
			if (usedFallback)
			{
				fallbackRelocations++;
			}
			if (isMidGame)
			{
				midGameRelocations++;
			}
			if (isPredictive)
			{
				predictiveRelocations++;
			}
			List<string> list = new List<string>();
			if (usedFallback)
			{
				list.Add("FALLBACK");
			}
			if (isMidGame)
			{
				list.Add("MIDGAME");
			}
			if (isPredictive)
			{
				list.Add("PREDICTIVE");
			}
			string text = ((list.Count > 0) ? (" [" + string.Join(",", list) + "]") : "");
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] RELOCATED{text} '{itemName}' from={from:F2} to={to:F2} reason='{reason}'");
		}

		internal static void LogFailure(string itemName, string reason, Vector3? position = null)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			totalRelocations++;
			failedRelocations++;
			string text = (position.HasValue ? $" pos={position.Value:F2}" : "");
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] FAILED '{itemName}'{text} reason='{reason}'");
		}

		internal static void LogSkipped(string itemName, string reason, Vector3? position = null)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			skippedItems++;
			if (ConfigEntries.VerboseLogging.Value)
			{
				string text = (position.HasValue ? $" pos={position.Value:F2}" : "");
				WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] SKIPPED '{itemName}'{text} reason='{reason}'");
			}
		}

		internal static void LogEmbedment(string itemName, float penetrationDepth, bool isWorldEmbedded, Vector3 position)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigEntries.VerboseLogging.Value)
			{
				WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] EMBEDMENT '{itemName}' pos={position:F2} depth={penetrationDepth:F3}m worldEmbedded={isWorldEmbedded}");
			}
		}

		internal static void SetLevel(string levelName)
		{
			currentLevel = levelName ?? "Unknown";
			levelLoadCount++;
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] LEVEL_CHANGE '{currentLevel}' (load #{levelLoadCount})");
		}

		internal static void LogWarning(string message)
		{
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] WARNING {message}");
		}

		internal static void LogError(string message)
		{
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] ERROR {message}");
		}

		internal static void LogInfo(string message)
		{
			if (ConfigEntries.VerboseLogging.Value)
			{
				WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] INFO {message}");
			}
		}

		internal static void ResetSession()
		{
			totalRelocations = 0;
			successfulRelocations = 0;
			fallbackRelocations = 0;
			failedRelocations = 0;
			midGameRelocations = 0;
			predictiveRelocations = 0;
			skippedItems = 0;
			sessionStart = DateTime.Now;
			levelLoadCount = 0;
			currentLevel = "Unknown";
			WriteToLog($"[{DateTime.Now:HH:mm:ss.fff}] SESSION_RESET");
		}

		internal static string GetSummary()
		{
			return $"Relocations: {successfulRelocations}/{totalRelocations} (fallback:{fallbackRelocations} failed:{failedRelocations} midGame:{midGameRelocations} predictive:{predictiveRelocations} skipped:{skippedItems})";
		}

		internal static string GetLogFilePath()
		{
			return LogFilePath;
		}
	}
	internal class RelocationRecord
	{
		public ValuableObject ValuableRef;

		public string ItemName = "";

		public Vector3 BeforePos;

		public Vector3 AfterPos;

		public Quaternion AfterRot;

		public string Reason = "";

		public string Time = "";

		public bool IsPending;

		public bool IsCompleted;
	}
	internal static class RelocationHistory
	{
		private const int MaxRecords = 20;

		public static readonly List<RelocationRecord> Records = new List<RelocationRecord>();

		public static void Add(ValuableObject val, Vector3 before, Vector3 after, Quaternion rot, string reason, bool pending)
		{
			//IL_001e: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Records.Add(new RelocationRecord
			{
				ValuableRef = val,
				ItemName = ((Object)val).name,
				BeforePos = before,
				AfterPos = after,
				AfterRot = rot,
				Reason = reason,
				Time = DateTime.Now.ToString("HH:mm:ss"),
				IsPending = pending,
				IsCompleted = !pending
			});
			if (Records.Count > 20)
			{
				Records.RemoveAt(0);
			}
		}

		public static void Clear()
		{
			Records.Clear();
		}
	}
	internal static class SpawnModIntegration
	{
		private static bool isBlindBoxDetected;

		private static bool checkedForBlindBox;

		internal static bool IsBlindBoxInstalled()
		{
			if (checkedForBlindBox)
			{
				return isBlindBoxDetected;
			}
			checkedForBlindBox = true;
			try
			{
				isBlindBoxDetected = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.FullName.Contains("BlindBox", StringComparison.OrdinalIgnoreCase));
				if (isBlindBoxDetected)
				{
					ItemUnstuck.Logger.LogInfo((object)"[SpawnModIntegration] BlindBox mod detected - enhanced spawn handling enabled");
				}
			}
			catch (Exception ex)
			{
				ItemUnstuck.Logger.LogWarning((object)("[SpawnModIntegration] Error detecting BlindBox: " + ex.Message));
				isBlindBoxDetected = false;
			}
			return isBlindBoxDetected;
		}

		internal static bool IsModSpawnedItem(ValuableObject valuable)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			string name = ((Object)valuable).name;
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (name.Contains("BlindBox", StringComparison.OrdinalIgnoreCase) || name.Contains("Spawned", StringComparison.OrdinalIgnoreCase) || name.Contains("Custom", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			Transform parent = ((Component)valuable).transform.parent;
			while ((Object)(object)parent != (Object)null)
			{
				string name2 = ((Object)parent).name;
				if (name2.Contains("BlindBox", StringComparison.OrdinalIgnoreCase) || name2.Contains("ModSpawn", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				parent = parent.parent;
			}
			return false;
		}

		internal static float GetRecommendedDelay(ValuableObject valuable)
		{
			if (IsModSpawnedItem(valuable))
			{
				return ConfigEntries.MidGameScanDelaySeconds * 1.5f;
			}
			return ConfigEntries.MidGameScanDelaySeconds;
		}

		internal static void Reset()
		{
			checkedForBlindBox = false;
			isBlindBoxDetected = false;
		}
	}
	internal static class SpawnSlotRegistry
	{
		internal readonly struct Slot
		{
			public Vector3 Position { get; }

			public Quaternion Rotation { get; }

			public Type Type { get; }

			public Transform? Parent { get; }

			public Slot(Vector3 position, Quaternion rotation, Type type, Transform? parent)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				Position = position;
				Rotation = rotation;
				Type = type;
				Parent = parent;
			}
		}

		private static readonly List<Slot> SlotsInternal = new List<Slot>();

		public static IReadOnlyList<Slot> Slots => SlotsInternal;

		public static int GetSlotCount()
		{
			return SlotsInternal.Count;
		}

		public static void Clear()
		{
			SlotsInternal.Clear();
		}

		public static void Register(ValuableVolume volume)
		{
			//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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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)
			Vector3 position = ((Component)volume).transform.position;
			Quaternion rotation = ((Component)volume).transform.rotation;
			for (int i = 0; i < SlotsInternal.Count; i++)
			{
				Slot slot = SlotsInternal[i];
				if (slot.Type == volume.VolumeType && Vector3.SqrMagnitude(slot.Position - position) < 0.0001f)
				{
					return;
				}
			}
			SlotsInternal.Add(new Slot(position, rotation, volume.VolumeType, ((Component)volume).transform.parent));
		}
	}
	internal static class StuckValuableRelocator
	{
		internal readonly struct EmbedmentAssessment
		{
			public float StaticPenetrationSum { get; }

			public float StaticPenetrationMax { get; }

			public float StaticPenetrationMaxUpDot { get; }

			public Vector3 StaticPenetrationMaxDirection { get; }

			public float DynamicPenetrationSum { get; }

			public float DynamicPenetrationMax { get; }

			public float DynamicPenetrationMaxUpDot { get; }

			public bool MidpointInsideStatic { get; }

			public bool MidpointInsideDynamic { get; }

			public bool CenterInsideStatic { get; }

			public float MinDimension { get; }

			public float EmbedRatio { get; }

			public Vector3 BoundsSize { get; }

			public EmbedmentAssessment(float staticPenetrationSum, float staticPenetrationMax, float staticPenetrationMaxUpDot, Vector3 staticPenetrationMaxDirection, float dynamicPenetrationSum, float dynamicPenetrationMax, float dynamicPenetrationMaxUpDot, bool midpointInsideStatic, bool midpointInsideDynamic, bool centerInsideStatic, float minDimension, float embedRatio, Vector3 boundsSize)
			{
				//IL_0016: 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_005e: 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)
				StaticPenetrationSum = staticPenetrationSum;
				StaticPenetrationMax = staticPenetrationMax;
				StaticPenetrationMaxUpDot = staticPenetrationMaxUpDot;
				StaticPenetrationMaxDirection = staticPenetrationMaxDirection;
				DynamicPenetrationSum = dynamicPenetrationSum;
				DynamicPenetrationMax = dynamicPenetrationMax;
				DynamicPenetrationMaxUpDot = dynamicPenetrationMaxUpDot;
				MidpointInsideStatic = midpointInsideStatic;
				MidpointInsideDynamic = midpointInsideDynamic;
				CenterInsideStatic = centerInsideStatic;
				MinDimension = minDimension;
				EmbedRatio = embedRatio;
				BoundsSize = boundsSize;
			}

			public override string ToString()
			{
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				return $"staticMax={StaticPenetrationMax:0.000}m upDot={StaticPenetrationMaxUpDot:0.00} staticSum={StaticPenetrationSum:0.000}m dynamicMax={DynamicPenetrationMax:0.000}m upDotDyn={DynamicPenetrationMaxUpDot:0.00} dynamicSum={DynamicPenetrationSum:0.000}m midInStatic={MidpointInsideStatic} centerInStatic={CenterInsideStatic} midInDyn={MidpointInsideDynamic} minDim={MinDimension:0.000}m ratio={EmbedRatio:P1} size={BoundsSize}";
			}
		}

		public readonly struct ScanResult
		{
			public List<RelocatedValuableInfo> Relocated { get; }

			public ScanResult(List<RelocatedValuableInfo> relocated)
			{
				Relocated = relocated;
			}
		}

		private static readonly Collider[] OverlapBuffer = (Collider[])(object)new Collider[128];

		private static readonly Collider[] CenterCheckBuffer = (Collider[])(object)new Collider[32];

		private static readonly Collider[] SlotCheckBuffer = (Collider[])(object)new Collider[64];

		private static readonly Dictionary<int, Rigidbody> RigidbodyCache = new Dictionary<int, Rigidbody>(64);

		private static Rigidbody? GetCachedRigidbody(ValuableObject valuable)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)valuable).GetInstanceID();
			if (RigidbodyCache.TryGetValue(instanceID, out Rigidbody value))
			{
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
				RigidbodyCache.Remove(instanceID);
			}
			Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				RigidbodyCache[instanceID] = component;
			}
			return component;
		}

		internal static void ClearCache()
		{
			RigidbodyCache.Clear();
		}

		public static ScanResult RelocateWorldEmbedded(ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats)
		{
			return RelocateCandidatesInternal(allValuables, allValuables, motionStats, "world-embedded");
		}

		internal static bool TryRecoverFallThrough(ValuableObject valuable)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value)
			{
				return false;
			}
			if ((Object)(object)GameManager.instance == (Object)null)
			{
				return false;
			}
			if (GameManager.instance.gameMode == 1 && !PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			PhysGrabObject physGrabObject = valuable.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return false;
			}
			if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
			{
				return false;
			}
			IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
			if (slots.Count == 0)
			{
				return false;
			}
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
			float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance);
			float num2 = num * num;
			SpawnSlotRegistry.Slot? slot = FindFreeSlot(valuable.volumeType, array, slots, num2, valuable);
			if (!slot.HasValue)
			{
				for (int i = 0; i < slots.Count; i++)
				{
					SpawnSlotRegistry.Slot slot2 = slots[i];
					if (!IsSlotClearOfWorldGeometry(slot2))
					{
						continue;
					}
					bool flag = false;
					foreach (ValuableObject val in array)
					{
						if (!((Object)(object)val == (Object)null) && Vector3.SqrMagnitude(((Component)val).transform.position - slot2.Position) <= num2)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						slot = slot2;
						break;
					}
				}
			}
			if (!slot.HasValue)
			{
				return false;
			}
			SpawnSlotRegistry.Slot value = slot.Value;
			physGrabObject.Teleport(value.Position, value.Rotation);
			InvulnerabilityController.ApplyUntilGrab(physGrabObject);
			Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				component.velocity = Vector3.zero;
				component.angularVelocity = Vector3.zero;
			}
			if ((Object)(object)value.Parent != (Object)null)
			{
				((Component)valuable).transform.parent = value.Parent;
			}
			RelocationHistory.Add(valuable, ((Component)valuable).transform.position, value.Position, value.Rotation, "fall-through recovery", pending: false);
			ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck] Fall-through detected and recovered for '" + ((Object)valuable).name + "'."));
			return true;
		}

		internal static bool ShouldSkipEmbedmentCheck(ValuableObject valuable, out string skipReason)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			skipReason = "";
			if ((Object)(object)valuable == (Object)null)
			{
				skipReason = "item is null";
				return true;
			}
			if (IsExcluded(valuable))
			{
				skipReason = "explicitly excluded (e.g. enemy drop)";
				return true;
			}
			Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
			if ((Object)(object)component == (Object)null)
			{
				skipReason = "no rigidbody";
				return true;
			}
			Vector3 velocity = component.velocity;
			float magnitude = ((Vector3)(ref velocity)).magnitude;
			if (magnitude > 2f)
			{
				skipReason = $"moving fast ({magnitude:F2} m/s)";
				return true;
			}
			Vector3 position = ((Component)valuable).transform.position;
			int mask = LayerMask.GetMask(new string[1] { "Default" });
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(position, Vector3.down, ref val, 5f, mask))
			{
				skipReason = "far from ground (>5m)";
				return true;
			}
			return false;
		}

		internal static bool IsExcluded(ValuableObject valuable)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return true;
			}
			if (((Object)valuable).name.StartsWith("Enemy Valuable"))
			{
				return true;
			}
			return false;
		}

		private static Vector3? FindFallbackSpawnPosition(ValuableObject valuable)
		{
			//IL_001a: 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_0149: 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_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: 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)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_0118: 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_0130: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)valuable == (Object)null)
			{
				return null;
			}
			Vector3 position = ((Component)valuable).transform.position;
			int mask = LayerMask.GetMask(new string[1] { "Default" });
			PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
			if (array.Length != 0)
			{
				PlayerAvatar val = null;
				float num = float.MaxValue;
				PlayerAvatar[] array2 = array;
				foreach (PlayerAvatar val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						float num2 = Vector3.SqrMagnitude(((Component)val2).transform.position - position);
						if (num2 < num)
						{
							num = num2;
							val = val2;
						}
					}
				}
				if ((Object)(object)val != (Object)null)
				{
					Vector3 position2 = ((Component)val).transform.position;
					Vector3 forward = ((Component)val).transform.forward;
					RaycastHit val3 = default(RaycastHit);
					if (Physics.Raycast(position2 + forward * 2f + Vector3.up * 2f, Vector3.down, ref val3, 5f, mask))
					{
						Vector3 val4 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.5f;
						if (ConfigEntries.VerboseLogging.Value)
						{
							ItemUnstuck.Logger.LogInfo((object)$"[Fallback] Using player-relative position: {val4}");
						}
						return val4;
					}
				}
			}
			Vector3 val5 = position + Vector3.up * 5f;
			int num3 = ~LayerMask.GetMask(new string[6] { "Player", "Valuable", "Enemy", "Triggers", "PlayerOnlyCollision", "UI" });
			if (Physics.OverlapSphereNonAlloc(val5, 0.5f, CenterCheckBuffer, num3, (QueryTriggerInteraction)1) == 0)
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogInfo((object)$"[Fallback] Using vertical offset position: {val5}");
				}
				return val5;
			}
			RaycastHit val6 = default(RaycastHit);
			if (Physics.Raycast(val5, Vector3.down, ref val6, 10f, mask))
			{
				Vector3 val7 = ((RaycastHit)(ref val6)).point + Vector3.up * 0.5f;
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogInfo((object)$"[Fallback] Using ground below vertical: {val7}");
				}
				return val7;
			}
			if (ConfigEntries.VerboseLogging.Value)
			{
				ItemUnstuck.Logger.LogWarning((object)$"[Fallback] Using last-resort vertical position: {val5}");
			}
			return val5;
		}

		internal static bool WillEmbedSoon(ValuableObject valuable, out float predictedDepth, out string reason)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			predictedDepth = 0f;
			reason = "";
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			Vector3 val = component.velocity;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.5f)
			{
				return false;
			}
			Vector3 position = ((Component)valuable).transform.position;
			Vector3 velocity = component.velocity;
			Vector3 val2 = position + velocity * 0.3f;
			int num = ~LayerMask.GetMask(new string[6] { "Player", "Valuable", "Enemy", "Triggers", "PlayerOnlyCollision", "UI" });
			if (Physics.OverlapSphereNonAlloc(val2, 0.3f, CenterCheckBuffer, num, (QueryTriggerInteraction)1) > 0)
			{
				val = val2 - position;
				Vector3 normalized = ((Vector3)(ref val)).normalized;
				float num2 = Vector3.Distance(position, val2);
				RaycastHit val3 = default(RaycastHit);
				if (Physics.Raycast(position, normalized, ref val3, num2, num))
				{
					float num3 = num2 - ((RaycastHit)(ref val3)).distance;
					if (num3 > 0.1f)
					{
						predictedDepth = num3;
						reason = $"trajectory predicts {num3:F2}m embed at {magnitude:F1} m/s";
						return true;
					}
				}
			}
			return false;
		}

		internal static bool TryRelocateSingleMidGameSpawn(ValuableObject valuable, ValuableObject[] allValuables)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: 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_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: 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_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value || !ConfigEntries.EnableMidGameDetection.Value)
			{
				return false;
			}
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			PhysGrabObject physGrabObject = valuable.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return false;
			}
			if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
			{
				return false;
			}
			ComputeEmbedment(valuable);
			if (!ShouldRelocateBasedOnAssessment(valuable, null, out string reason))
			{
				if (ConfigEntries.VerboseLogging.Value)
				{
					ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck][MidGame] '" + ((Object)valuable).name + "' is NOT stuck: " + reason));
				}
				return false;
			}
			if (ConfigEntries.VerboseLogging.Value)
			{
				ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck][MidGame] '" + ((Object)valuable).name + "' is STUCK: " + reason));
			}
			IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
			if (slots.Count == 0)
			{
				ItemUnstuck.Logger.LogWarning((object)("[ItemUnstuck][MidGame] No spawn slots available for relocating '" + ((Object)valuable).name + "'"));
				return false;
			}
			float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance);
			float num2 = num * num;
			SpawnSlotRegistry.Slot? slot = FindFreeSlot(valuable.volumeType, allValuables, slots, num2, valuable);
			if (!slot.HasValue)
			{
				for (int i = 0; i < slots.Count; i++)
				{
					SpawnSlotRegistry.Slot slot2 = slots[i];
					if (!IsSlotClearOfWorldGeometry(slot2))
					{
						continue;
					}
					bool flag = false;
					foreach (ValuableObject val in allValuables)
					{
						if (!((Object)(object)val == (Object)null) && Vector3.SqrMagnitude(((Component)val).transform.position - slot2.Position) <= num2)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						slot = slot2;
						break;
					}
				}
			}
			if (!slot.HasValue)
			{
				if (!ConfigEntries.UseFallbackPositions)
				{
					ItemUnstuck.Logger.LogWarning((object)("[ItemUnstuck][MidGame] No free slot available for relocating stuck item '" + ((Object)valuable).name + "'"));
					return false;
				}
				Vector3? val2 = FindFallbackSpawnPosition(valuable);
				if (!val2.HasValue)
				{
					ItemUnstuck.Logger.LogError((object)("[ItemUnstuck][MidGame] No fallback position found for '" + ((Object)valuable).name + "' - item remains stuck!"));
					return false;
				}
				Vector3 position = ((Component)valuable).transform.position;
				physGrabObject.Teleport(val2.Value, ((Component)valuable).transform.rotation);
				InvulnerabilityController.ApplyUntilGrab(physGrabObject);
				Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.velocity = Vector3.zero;
					component.angularVelocity = Vector3.zero;
				}
				RelocationHistory.Add(valuable, position, val2.Value, ((Component)valuable).transform.rotation, "mid-game stuck (fallback position)", pending: false);
				FallThroughRecovery.WatchRelocated(valuable, "mid-game fallback relocation");
				if (ConfigEntries.MidGameHudNotifications && ConfigEntries.HudNotifications.Value)
				{
					HudNotifier.ShowMidGameRelocation(((Object)valuable).name ?? "Unknown item");
				}
				ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][MidGame] Relocated '{((Object)valuable).name}' to fallback position from {position} to {val2.Value}");
				return true;
			}
			Vector3 position2 = ((Component)valuable).transform.position;
			SpawnSlotRegistry.Slot value = slot.Value;
			physGrabObject.Teleport(value.Position, value.Rotation);
			InvulnerabilityController.ApplyUntilGrab(physGrabObject);
			Rigidbody component2 = ((Component)valuable).GetComponent<Rigidbody>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.velocity = Vector3.zero;
				component2.angularVelocity = Vector3.zero;
			}
			if ((Object)(object)value.Parent != (Object)null)
			{
				((Component)valuable).transform.parent = value.Parent;
			}
			RelocationHistory.Add(valuable, position2, value.Position, value.Rotation, "mid-game stuck: " + reason, pending: false);
			FallThroughRecovery.WatchRelocated(valuable, "mid-game relocation");
			if (ConfigEntries.MidGameHudNotifications && ConfigEntries.HudNotifications.Value)
			{
				HudNotifier.ShowMidGameRelocation(((Object)valuable).name ?? "Unknown item");
			}
			ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][MidGame] Relocated stuck mid-game spawn '{((Object)valuable).name}' from {position2} to {value.Position}");
			return true;
		}

		public static ScanResult RelocateCandidates(IEnumerable<ValuableObject> candidates, ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats)
		{
			return RelocateCandidatesInternal(candidates, allValuables, motionStats, "damaged");
		}

		private static ScanResult RelocateCandidatesInternal(IEnumerable<ValuableObject> candidates, ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats, string label)
		{
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value)
			{
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			if ((Object)(object)GameManager.instance == (Object)null)
			{
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			if (GameManager.instance.gameMode == 1 && !PhotonNetwork.IsMasterClient)
			{
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
			if (slots.Count == 0)
			{
				ItemUnstuck.Logger.LogWarning((object)"No ValuableVolume spawn slots were captured; cannot relocate stuck valuables.");
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			if (candidates == null)
			{
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			if (allValuables == null || allValuables.Length == 0)
			{
				allValuables = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
			}
			float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance);
			float occupiedDistanceSqr = num * num;
			List<RelocatedValuableInfo> list = new List<RelocatedValuableInfo>();
			int num2 = 0;
			HashSet<Rigidbody> hashSet = new HashSet<Rigidbody>();
			foreach (ValuableObject candidate in candidates)
			{
				if ((Object)(object)candidate == (Object)null)
				{
					continue;
				}
				PhysGrabObject physGrabObject = candidate.physGrabObject;
				if ((Object)(object)physGrabObject == (Object)null)
				{
					continue;
				}
				if (IsExcluded(candidate))
				{
					_ = ConfigEntries.VerboseLogging.Value;
					num2++;
				}
				else
				{
					if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
					{
						continue;
					}
					Rigidbody component = ((Component)candidate).GetComponent<Rigidbody>();
					if ((Object)(object)component != (Object)null)
					{
						if (hashSet.Contains(component))
						{
							continue;
						}
						hashSet.Add(component);
					}
					MotionStats? motion = null;
					if (motionStats != null)
					{
						int id = ValueDiagnostics.GetId(candidate);
						if (motionStats.TryGetValue(id, out var value))
						{
							motion = value;
						}
					}
					if (IsReachableBySight(candidate))
					{
						num2++;
						if (!ConfigEntries.VerboseLogging.Value)
						{
						}
						continue;
					}
					if (!ShouldRelocateBasedOnAssessment(candidate, motion, out string reason))
					{
						num2++;
						if (!ConfigEntries.VerboseLogging.Value)
						{
						}
						continue;
					}
					if (ConfigEntries.VerboseLogging.Value)
					{
						ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck] Item FLAGGED AS STUCK '" + ((Object)candidate).name + "': " + reason));
					}
					Type volumeType = candidate.volumeType;
					SpawnSlotRegistry.Slot? slot = FindFreeSlot(volumeType, allValuables, slots, occupiedDistanceSqr, candidate);
					if (!slot.HasValue && (Object)(object)candidate != (Object)null)
					{
						slot = FindSharedSlot(volumeType, allValuables, slots, candidate);
						if (slot.HasValue && ConfigEntries.VerboseLogging.Value)
						{
							ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck] Using slot sharing f