Decompiled source of ItemUnstuck v1.0.0

ItemUnstuck.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[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.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ItemUnstuck")]
[assembly: AssemblyTitle("ItemUnstuck")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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<float> InvincibilitySeconds { get; set; }

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

		internal static ConfigEntry<float> InvulnerableUntilGrabSeconds { get; set; }

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

		internal static ConfigEntry<float> ScanDelaySeconds { get; set; }

		internal static ConfigEntry<float> OccupiedDistance { get; set; }

		internal static ConfigEntry<float> StuckMinPenetrationDistance { get; set; }

		internal static ConfigEntry<float> StuckWeakPenetrationDistance { get; set; }

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

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

		internal static ConfigEntry<float> MotionProbeDelaySeconds { get; set; }

		internal static ConfigEntry<float> MotionProbeWindowSeconds { get; set; }

		internal static ConfigEntry<int> MotionProbeSamples { get; set; }

		internal static ConfigEntry<float> MotionMaxSpeedToFlag { get; set; }

		internal static ConfigEntry<int> MotionMinDirectionReversalsToFlag { get; set; }

		internal static ConfigEntry<float> MotionReversalDotThreshold { get; set; }

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

		internal static ConfigEntry<float> FallThroughYThreshold { get; set; }

		internal static ConfigEntry<float> FallThroughWatchSeconds { get; set; }

		internal static ConfigEntry<float> FallThroughPollSeconds { get; set; }

		internal static ConfigEntry<int> FallThroughMaxRecoveriesPerItem { get; set; }

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

		internal static ConfigEntry<float> DamageCheckSeconds { get; set; }

		internal static ConfigEntry<int> MaxRelocationsPerItem { get; set; }

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

		internal static ConfigEntry<float> MinDamageToTrigger { get; set; }
	}
	[HarmonyPatch(typeof(PlayerController))]
	internal static class ExamplePlayerControllerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("Start")]
		private static void Start_Prefix(PlayerController __instance)
		{
			ItemUnstuck.Logger.LogDebug((object)$"{__instance} Start Prefix");
		}

		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void Start_Postfix(PlayerController __instance)
		{
			ItemUnstuck.Logger.LogDebug((object)$"{__instance} Start Postfix");
		}
	}
	internal static class FallThroughRecovery
	{
		[CompilerGenerated]
		private sealed class <WatchCoroutine>d__3 : 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__3(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_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Expected O, but got Unknown
				//IL_0139: 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.Value;
						float num = Mathf.Max(0.5f, ConfigEntries.FallThroughWatchSeconds.Value);
						<poll>5__3 = Mathf.Clamp(ConfigEntries.FallThroughPollSeconds.Value, 0.05f, 2f);
						<maxRecoveries>5__4 = Mathf.Clamp(ConfigEntries.FallThroughMaxRecoveriesPerItem.Value, 0, 10);
						<end>5__5 = Time.time + num;
						goto IL_0260;
					}
					case 1:
						{
							<>1__state = -3;
							if (!ConfigEntries.Enable.Value || !ConfigEntries.FallThroughRecoveryEnabled.Value)
							{
								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}).");
									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.");
									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}.");
							}
							goto IL_0260;
						}
						IL_0260:
						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 WatchRelocated(ValuableObject valuable, string label)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.FallThroughRecoveryEnabled.Value && !((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__3))]
		private static IEnumerator WatchCoroutine(ValuableObject valuable, int id, string label)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WatchCoroutine>d__3(0)
			{
				valuable = valuable,
				id = id,
				label = label
			};
		}
	}
	internal static class HudNotifier
	{
		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
			{
			}
		}
	}
	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)
			{
				return;
			}
			if (IndestructibleSpawnTimerField == null)
			{
				ItemUnstuck.Logger.LogError((object)"Could not find PhysGrabObjectImpactDetector.indestructibleSpawnTimer; invulnerability not applied.");
				return;
			}
			float num = ConfigEntries.InvulnerableUntilGrabSeconds.Value;
			if (num <= 0f)
			{
				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.0")]
	public class ItemUnstuck : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <ScanAfterVolumesSetup>d__16 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int generation;

			public ItemUnstuck <>4__this;

			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__16(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<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_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0068: Expected O, but got Unknown
				//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0321: Unknown result type (might be due to invalid IL or missing references)
				//IL_0331: Unknown result type (might be due to invalid IL or missing references)
				//IL_038b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0395: Expected O, but got Unknown
				//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
				//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_046d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0472: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bd: Expected O, but got Unknown
				//IL_0492: Unknown result type (might be due to invalid IL or missing references)
				//IL_0499: Unknown result type (might be due to invalid IL or missing references)
				//IL_0692: Unknown result type (might be due to invalid IL or missing references)
				//IL_069c: Expected O, but got Unknown
				int num = <>1__state;
				ItemUnstuck itemUnstuck = <>4__this;
				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.Value);
					if (num3 > 0f)
					{
						<>2__current = (object)new WaitForSeconds(num3);
						<>1__state = 1;
						return true;
					}
					goto IL_0078;
				}
				case 1:
					<>1__state = -1;
					goto IL_0078;
				case 2:
					<>1__state = -1;
					goto IL_01cd;
				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_0512;
				}
				case 4:
					{
						<>1__state = -1;
						goto IL_06ac;
					}
					IL_0078:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					if (SpawnSlotRegistry.Slots.Count == 0)
					{
						if (ConfigEntries.DebugLogging.Value)
						{
							Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no spawn slots captured yet.");
						}
						return false;
					}
					<valuablesAtBaseline>5__2 = Object.FindObjectsOfType<ValuableObject>();
					if (<valuablesAtBaseline>5__2 == null || <valuablesAtBaseline>5__2.Length == 0)
					{
						if (ConfigEntries.DebugLogging.Value)
						{
							Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no valuables found.");
						}
						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.Value)
					{
						<probeDelay>5__10 = Mathf.Max(0f, ConfigEntries.MotionProbeDelaySeconds.Value);
						if (<probeDelay>5__10 > 0f)
						{
							<>2__current = (object)new WaitForSeconds(<probeDelay>5__10);
							<>1__state = 2;
							return true;
						}
						goto IL_01cd;
					}
					goto IL_0626;
					IL_06ac:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					array = Object.FindObjectsOfType<ValuableObject>();
					valuablesWithLoss = ValueDiagnostics.GetValuablesWithLoss(array, <minDamage>5__8);
					if (valuablesWithLoss.Count == 0)
					{
						if (ConfigEntries.DebugLogging.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_0883;
					IL_0626:
					<damageWait>5__7 = Mathf.Max(0f, ConfigEntries.DamageCheckSeconds.Value);
					<minDamage>5__8 = Mathf.Max(0f, ConfigEntries.MinDamageToTrigger.Value);
					<maxRelocationsPerItem>5__9 = Mathf.Clamp(ConfigEntries.MaxRelocationsPerItem.Value, 0, 10);
					<samples>5__12 = 1;
					goto IL_0883;
					IL_0883:
					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_06ac;
					IL_0512:
					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.DebugLogging.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_0626;
					IL_01cd:
					if (generation != itemUnstuck.scanGeneration)
					{
						return false;
					}
					<motionStats>5__6 = new Dictionary<int, MotionStats>();
					<window>5__11 = Mathf.Max(0.05f, ConfigEntries.MotionProbeWindowSeconds.Value);
					<samples>5__12 = Mathf.Clamp(ConfigEntries.MotionProbeSamples.Value, 2, 60);
					<dt>5__13 = <window>5__11 / (float)(<samples>5__12 - 1);
					<reversalDotThreshold>5__14 = Mathf.Clamp(ConfigEntries.MotionReversalDotThreshold.Value, -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_0512;
				}
				if (ConfigEntries.RestoreValueToInitial.Value && <affectedIds>5__3.Count > 0)
				{
					array2 = Object.FindObjectsOfType<ValuableObject>();
					foreach (ValuableObject val4 in array2)
					{
						if (!((Object)(object)val4 == (Object)null) && <affectedIds>5__3.Contains(ValueDiagnostics.GetId(val4)))
						{
							ValueDiagnostics.RestoreValueToBaseline(val4);
						}
					}
				}
				ValuableObject[] valuables = Object.FindObjectsOfType<ValuableObject>();
				(int, float, float, float, float) tuple = ValueDiagnostics.ComputeTotalsWithBaseline(valuables);
				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;

		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.InvincibilitySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Invulnerability", "InvincibilitySeconds", 0f, "How long newly spawned valuables stay indestructible (seconds). Set to 0 to disable.");
			ConfigEntries.InvulnerableUntilGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "InvulnerableUntilGrab", true, "If true, make valuables invulnerable until any player grabs them (also re-applied after ItemUnstuck teleports a valuable).");
			ConfigEntries.InvulnerableUntilGrabSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Invulnerability", "InvulnerableUntilGrabSeconds", 99999f, "Timer used for invulnerable-until-grab. This is a large number; the timer will be cleared automatically on first grab.");
			ConfigEntries.HudNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "HudNotifications", true, "Show a HUD notification when stuck valuables are relocated.");
			ConfigEntries.ShowInitialValueOnGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowInitialValueOnGrab", true, "Show the grabbed valuable's initial (undamaged) value on the HUD.");
			ConfigEntries.EnableRelocation = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "EnableRelocation", true, "If true, detects stuck valuables and relocates them to a free valuable spawn slot.");
			ConfigEntries.ScanDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Valuables", "ScanDelaySeconds", 1f, "Delay after level generation before scanning for stuck valuables (seconds).");
			ConfigEntries.OccupiedDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Relocation", "OccupiedDistance", 0.2f, "Distance threshold used to consider a valuable spawn slot occupied.");
			ConfigEntries.StuckMinPenetrationDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.StuckDetection", "StuckMinPenetrationDistance", 0.12f, "Minimum penetration distance (meters) required to consider a valuable 'stuck' in world geometry. Increase if it relocates too aggressively.");
			ConfigEntries.StuckWeakPenetrationDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.StuckDetection", "StuckWeakPenetrationDistance", 0.03f, "Weaker penetration distance (meters) used with motion jitter to detect embedment without flagging normal floor contact.");
			ConfigEntries.RequireStaticWorldEvidenceForRelocation = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced.StuckDetection", "RequireStaticWorldEvidenceForRelocation", true, "If true, only relocate when the item is penetrating static/world colliders (prevents relocating items merely wedged in other physics objects).");
			ConfigEntries.MotionProbeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced.MotionProbe", "MotionProbeEnabled", true, "If true, sample valuable motion shortly after level load to detect jittery/rapid motion typical of embedded items.");
			ConfigEntries.MotionProbeDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionProbeDelaySeconds", 2f, "Seconds after level setup before starting the motion probe sampling.");
			ConfigEntries.MotionProbeWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionProbeWindowSeconds", 0.75f, "How long (seconds) to sample valuable positions to detect rapid/jittery movement.");
			ConfigEntries.MotionProbeSamples = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.MotionProbe", "MotionProbeSamples", 12, "How many samples to take during MotionProbeWindowSeconds. Higher values cost more CPU.");
			ConfigEntries.MotionMaxSpeedToFlag = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionMaxSpeedToFlag", 0.75f, "If a valuable exceeds this speed (m/s) during motion probing, it is considered rapidly moving.");
			ConfigEntries.MotionMinDirectionReversalsToFlag = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.MotionProbe", "MotionMinDirectionReversalsToFlag", 2, "Minimum number of direction reversals during motion probing required to treat motion as 'jittery'.");
			ConfigEntries.MotionReversalDotThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionReversalDotThreshold", -0.2f, "Dot product threshold to count a direction reversal. More negative = stricter (requires closer to opposite direction).");
			ConfigEntries.FallThroughRecoveryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "FallThroughRecoveryEnabled", true, "If true, watch relocated valuables and recover them if they fall far below the map (teleport back to a safe slot and keep invulnerable until grabbed).");
			ConfigEntries.FallThroughYThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughYThreshold", -50f, "If a relocated valuable's Y position drops below this threshold, it is considered to have fallen through the map.");
			ConfigEntries.FallThroughWatchSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughWatchSeconds", 15f, "How long (seconds) to watch relocated valuables for falling through the map.");
			ConfigEntries.FallThroughPollSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughPollSeconds", 0.25f, "How often (seconds) to poll relocated valuables for fall-through detection.");
			ConfigEntries.FallThroughMaxRecoveriesPerItem = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.FallThrough", "FallThroughMaxRecoveriesPerItem", 2, "Maximum number of fall-through recoveries per valuable (prevents infinite loops).");
			ConfigEntries.DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "DebugLogging", false, "Enable extra debug logs for stuck detection and relocation decisions.");
			ConfigEntries.DamageCheckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.DamageFlow", "DamageCheckSeconds", 3f, "How long to wait (seconds) before checking which valuables lost value after spawn/relocation.");
			ConfigEntries.MaxRelocationsPerItem = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.Relocation", "MaxRelocationsPerItem", 2, "Maximum number of relocations per valuable during the damage-check flow (e.g. 2 = relocate once + one re-relocation).");
			ConfigEntries.MinDamageToTrigger = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.DamageFlow", "MinDamageToTrigger", 1f, "Minimum value loss required to treat a valuable as 'damaged' and trigger relocation/restoration.");
			ConfigEntries.RestoreValueToInitial = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "RestoreValueToInitial", true, "If true, restore damaged/relocated valuables back to their initial baseline value at the end of the flow.");
			SpawnSlotRegistry.Clear();
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void NotifyValuableVolumesSetupComplete()
		{
			scanGeneration++;
			if (scanCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(scanCoroutine);
				scanCoroutine = null;
			}
			scanCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanAfterVolumesSetup(scanGeneration));
		}

		[IteratorStateMachine(typeof(<ScanAfterVolumesSetup>d__16))]
		private IEnumerator ScanAfterVolumesSetup(int generation)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScanAfterVolumesSetup>d__16(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 Update()
		{
		}
	}
	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 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 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
	{
		private readonly struct EmbedmentAssessment
		{
			public float StaticPenetrationSum { get; }

			public float StaticPenetrationMax { get; }

			public float DynamicPenetrationSum { get; }

			public float DynamicPenetrationMax { get; }

			public bool MidpointInsideStatic { get; }

			public bool MidpointInsideDynamic { get; }

			public EmbedmentAssessment(float staticPenetrationSum, float staticPenetrationMax, float dynamicPenetrationSum, float dynamicPenetrationMax, bool midpointInsideStatic, bool midpointInsideDynamic)
			{
				StaticPenetrationSum = staticPenetrationSum;
				StaticPenetrationMax = staticPenetrationMax;
				DynamicPenetrationSum = dynamicPenetrationSum;
				DynamicPenetrationMax = dynamicPenetrationMax;
				MidpointInsideStatic = midpointInsideStatic;
				MidpointInsideDynamic = midpointInsideDynamic;
			}

			public override string ToString()
			{
				return $"staticMax={StaticPenetrationMax:0.000}m staticSum={StaticPenetrationSum:0.000}m dynamicMax={DynamicPenetrationMax:0.000}m dynamicSum={DynamicPenetrationSum:0.000}m midInStatic={MidpointInsideStatic} midInDyn={MidpointInsideDynamic}";
			}
		}

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

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

		public static ScanResult ScanAndRelocateOnce()
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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_019d: 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_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: 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_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: 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>());
			}
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
			if (array == null || array.Length == 0)
			{
				return new ScanResult(new List<RelocatedValuableInfo>());
			}
			float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance.Value);
			float occupiedDistanceSqr = num * num;
			List<RelocatedValuableInfo> list = new List<RelocatedValuableInfo>();
			ValuableObject[] array2 = array;
			foreach (ValuableObject val in array2)
			{
				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) || !ShouldRelocateBasedOnAssessment(val, null, out string _))
				{
					continue;
				}
				Type volumeType = val.volumeType;
				SpawnSlotRegistry.Slot? slot = FindFreeSlot(volumeType, array, slots, occupiedDistanceSqr);
				if (!slot.HasValue)
				{
					ItemUnstuck.Logger.LogInfo((object)$"Stuck valuable '{((Object)val).name}' found, but no free slot available for type {volumeType}.");
					continue;
				}
				SpawnSlotRegistry.Slot value = slot.Value;
				Vector3 position = ((Component)val).transform.position;
				float? baselineCurrent = ValueDiagnostics.TryGetBaselineCurrent(val);
				float dollarValueCurrent = val.dollarValueCurrent;
				physGrabObject.Teleport(value.Position, value.Rotation);
				InvulnerabilityController.ApplyUntilGrab(physGrabObject);
				FallThroughRecovery.WatchRelocated(val, "stuck");
				Rigidbody component = ((Component)val).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.velocity = Vector3.zero;
					component.angularVelocity = Vector3.zero;
				}
				if ((Object)(object)value.Parent != (Object)null)
				{
					((Component)val).transform.parent = value.Parent;
				}
				RelocatedValuableInfo item = new RelocatedValuableInfo(((Object)val).name, volumeType, dollarValueCurrent, baselineCurrent, position, value.Position);
				list.Add(item);
				if (baselineCurrent.HasValue && dollarValueCurrent < baselineCurrent.Value)
				{
					ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated stuck valuable '{((Object)val).name}' type={volumeType} value baseline=${(int)baselineCurrent.Value} now=${(int)dollarValueCurrent} lost=${(int)(baselineCurrent.Value - dollarValueCurrent)} from={position} to={value.Position}");
				}
				else
				{
					ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated stuck valuable '{((Object)val).name}' type={volumeType} value=${(int)dollarValueCurrent} from={position} to={value.Position}");
				}
			}
			if (list.Count > 0)
			{
				ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {list.Count} stuck valuable(s).");
			}
			return new ScanResult(list);
		}

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

		internal static bool TryRecoverFallThrough(ValuableObject valuable)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: 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_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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)
			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.Value);
			float num2 = num * num;
			SpawnSlotRegistry.Slot? slot = FindFreeSlot(valuable.volumeType, array, slots, num2);
			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);
			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;
			}
			InvulnerabilityController.ApplyUntilGrab(physGrabObject);
			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_0191: 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_0198: 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_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_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: 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.Value);
			float occupiedDistanceSqr = num * num;
			List<RelocatedValuableInfo> list = new List<RelocatedValuableInfo>();
			int num2 = 0;
			foreach (ValuableObject candidate in candidates)
			{
				if ((Object)(object)candidate == (Object)null)
				{
					continue;
				}
				PhysGrabObject physGrabObject = candidate.physGrabObject;
				if ((Object)(object)physGrabObject == (Object)null || ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart))
				{
					continue;
				}
				MotionStats? motion = null;
				if (motionStats != null)
				{
					int id = ValueDiagnostics.GetId(candidate);
					if (motionStats.TryGetValue(id, out var value))
					{
						motion = value;
					}
				}
				if (!ShouldRelocateBasedOnAssessment(candidate, motion, out string reason))
				{
					num2++;
					if (ConfigEntries.DebugLogging.Value)
					{
						ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck][Debug] Skip relocation '" + ((Object)candidate).name + "': " + reason));
					}
					continue;
				}
				Type volumeType = candidate.volumeType;
				SpawnSlotRegistry.Slot? slot = FindFreeSlot(volumeType, allValuables, slots, occupiedDistanceSqr);
				if (!slot.HasValue)
				{
					if (ConfigEntries.DebugLogging.Value)
					{
						ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][Debug] {label} valuable '{((Object)candidate).name}' found, but no free slot available for type {volumeType}.");
					}
					continue;
				}
				SpawnSlotRegistry.Slot value2 = slot.Value;
				Vector3 position = ((Component)candidate).transform.position;
				float? baselineCurrent = ValueDiagnostics.TryGetBaselineCurrent(candidate);
				float dollarValueCurrent = candidate.dollarValueCurrent;
				physGrabObject.Teleport(value2.Position, value2.Rotation);
				InvulnerabilityController.ApplyUntilGrab(physGrabObject);
				FallThroughRecovery.WatchRelocated(candidate, label);
				Rigidbody component = ((Component)candidate).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.velocity = Vector3.zero;
					component.angularVelocity = Vector3.zero;
				}
				if ((Object)(object)value2.Parent != (Object)null)
				{
					((Component)candidate).transform.parent = value2.Parent;
				}
				list.Add(new RelocatedValuableInfo(((Object)candidate).name, volumeType, dollarValueCurrent, baselineCurrent, position, value2.Position));
				ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {label} valuable '{((Object)candidate).name}' type={volumeType} value=${(int)dollarValueCurrent} from={position} to={value2.Position}");
			}
			if (list.Count > 0)
			{
				ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {list.Count} {label} valuable(s).");
			}
			if (num2 > 0 && ConfigEntries.DebugLogging.Value)
			{
				ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][Debug] Skipped {num2} candidate(s) (not embedded in static/world geometry).");
			}
			return new ScanResult(list);
		}

		private static SpawnSlotRegistry.Slot? FindFreeSlot(Type type, ValuableObject[] valuables, IReadOnlyList<SpawnSlotRegistry.Slot> slots, float occupiedDistanceSqr)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < slots.Count; i++)
			{
				SpawnSlotRegistry.Slot slot = slots[i];
				if (slot.Type != type || !IsSlotClearOfWorldGeometry(slot))
				{
					continue;
				}
				bool flag = false;
				foreach (ValuableObject val in valuables)
				{
					if (!((Object)(object)val == (Object)null) && Vector3.SqrMagnitude(((Component)val).transform.position - slot.Position) <= occupiedDistanceSqr)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return slot;
				}
			}
			return null;
		}

		private static bool ShouldRelocateBasedOnAssessment(ValuableObject valuable, MotionStats? motion, out string reason)
		{
			if ((Object)(object)valuable.physGrabObject == (Object)null)
			{
				reason = "no PhysGrabObject";
				return false;
			}
			EmbedmentAssessment embedmentAssessment = ComputeEmbedment(valuable);
			float num = Mathf.Max(0.01f, ConfigEntries.StuckMinPenetrationDistance.Value);
			float num2 = Mathf.Clamp(ConfigEntries.StuckWeakPenetrationDistance.Value, 0.005f, num);
			bool flag = embedmentAssessment.MidpointInsideStatic || embedmentAssessment.StaticPenetrationMax >= num;
			bool flag2 = embedmentAssessment.StaticPenetrationMax >= num2;
			bool flag3 = false;
			if (motion.HasValue)
			{
				float num3 = Mathf.Max(0.05f, ConfigEntries.MotionMaxSpeedToFlag.Value);
				int num4 = Mathf.Clamp(ConfigEntries.MotionMinDirectionReversalsToFlag.Value, 0, 50);
				flag3 = motion.Value.MaxSpeed >= num3 && motion.Value.DirectionReversals >= num4;
			}
			if (ConfigEntries.RequireStaticWorldEvidenceForRelocation.Value)
			{
				if (flag)
				{
					reason = $"static/world embedment detected ({embedmentAssessment})";
					return true;
				}
				if (flag3 && flag2)
				{
					reason = $"weak static penetration + rapid jitter ({embedmentAssessment}; motion={motion})";
					return true;
				}
				if (embedmentAssessment.MidpointInsideDynamic || embedmentAssessment.DynamicPenetrationMax >= num)
				{
					reason = $"dynamic-only embedment (likely inside another object), not relocating ({embedmentAssessment}; motion={motion})";
					return false;
				}
				reason = $"no static/world embedment evidence ({embedmentAssessment}; motion={motion})";
				return false;
			}
			if (flag)
			{
				reason = $"static/world embedment detected ({embedmentAssessment})";
				return true;
			}
			if (flag3 && (embedmentAssessment.MidpointInsideDynamic || embedmentAssessment.DynamicPenetrationMax >= num || flag2))
			{
				reason = $"rapid jitter + penetration evidence ({embedmentAssessment}; motion={motion})";
				return true;
			}
			reason = $"insufficient embedment evidence ({embedmentAssessment}; motion={motion})";
			return false;
		}

		private static EmbedmentAssessment ComputeEmbedment(ValuableObject valuable)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: 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_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: 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_01db: 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_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: 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_03db: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
			PhysGrabObject physGrabObject = valuable.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return new EmbedmentAssessment(0f, 0f, 0f, 0f, midpointInsideStatic: false, midpointInsideDynamic: false);
			}
			Vector3 val = physGrabObject.midPoint;
			Vector3 val2 = physGrabObject.boundingBox * 0.5f;
			val2 -= Vector3.one * 0.02f;
			((Vector3)(ref val2))..ctor(Mathf.Max(0.02f, val2.x), Mathf.Max(0.02f, val2.y), Mathf.Max(0.02f, val2.z));
			Collider[] componentsInChildren = ((Component)valuable).GetComponentsInChildren<Collider>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return new EmbedmentAssessment(0f, 0f, 0f, 0f, midpointInsideStatic: false, midpointInsideDynamic: false);
			}
			Bounds val3 = default(Bounds);
			bool flag = false;
			foreach (Collider val4 in componentsInChildren)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					if (!flag)
					{
						val3 = val4.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref val3)).Encapsulate(val4.bounds);
					}
				}
			}
			if (flag && ((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
			{
				val2 = ((Bounds)(ref val3)).extents;
				val = ((Bounds)(ref val3)).center;
			}
			Vector3[] array;
			if (flag)
			{
				Vector3 center = ((Bounds)(ref val3)).center;
				Vector3 extents = ((Bounds)(ref val3)).extents;
				array = (Vector3[])(object)new Vector3[7]
				{
					center,
					center + new Vector3(extents.x, 0f, 0f),
					center + new Vector3(0f - extents.x, 0f, 0f),
					center + new Vector3(0f, extents.y, 0f),
					center + new Vector3(0f, 0f - extents.y, 0f),
					center + new Vector3(0f, 0f, extents.z),
					center + new Vector3(0f, 0f, 0f - extents.z)
				};
			}
			else
			{
				array = (Vector3[])(object)new Vector3[1] { val };
			}
			int num = LayerMask.NameToLayer("Triggers");
			int num2 = LayerMask.NameToLayer("Player");
			int num3 = LayerMask.NameToLayer("PlayerOnlyCollision");
			Collider[] array2 = Physics.OverlapBox(val, val2, ((Component)valuable).transform.rotation, -1, (QueryTriggerInteraction)1);
			float num4 = 0f;
			float num5 = 0f;
			float num6 = 0f;
			float num7 = 0f;
			bool midpointInsideStatic = false;
			bool midpointInsideDynamic = false;
			Vector3 val7 = default(Vector3);
			float num9 = default(float);
			foreach (Collider val5 in array2)
			{
				if ((Object)(object)val5 == (Object)null || val5.isTrigger)
				{
					continue;
				}
				int layer = ((Component)val5).gameObject.layer;
				if (layer == num || layer == num2 || layer == num3 || (Object)(object)((Component)val5).transform == (Object)null || ((Component)val5).transform.IsChildOf(((Component)valuable).transform))
				{
					continue;
				}
				PhysGrabObject componentInParent = ((Component)val5).GetComponentInParent<PhysGrabObject>();
				if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent == (Object)(object)physGrabObject)
				{
					continue;
				}
				bool flag2 = (Object)(object)val5.attachedRigidbody == (Object)null;
				foreach (Vector3 val6 in array)
				{
					val7 = val5.ClosestPoint(val6) - val6;
					if (((Vector3)(ref val7)).sqrMagnitude < 1E-06f)
					{
						if (flag2)
						{
							midpointInsideStatic = true;
						}
						else
						{
							midpointInsideDynamic = true;
						}
						break;
					}
				}
				float num8 = 0f;
				foreach (Collider val8 in componentsInChildren)
				{
					if (!((Object)(object)val8 == (Object)null) && !val8.isTrigger && Physics.ComputePenetration(val8, ((Component)val8).transform.position, ((Component)val8).transform.rotation, val5, ((Component)val5).transform.position, ((Component)val5).transform.rotation, ref val7, ref num9) && num9 > num8)
					{
						num8 = num9;
					}
				}
				if (num8 <= 0f)
				{
					continue;
				}
				if (flag2)
				{
					num4 += num8;
					if (num8 > num5)
					{
						num5 = num8;
					}
				}
				else
				{
					num6 += num8;
					if (num8 > num7)
					{
						num7 = num8;
					}
				}
			}
			return new EmbedmentAssessment(num4, num5, num6, num7, midpointInsideStatic, midpointInsideDynamic);
		}

		private static bool IsSlotClearOfWorldGeometry(SpawnSlotRegistry.Slot slot)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0039: Expected I4, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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)
			//IL_00e5: 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_00ef: 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_0100: 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)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)AssetManager.instance == (Object)null)
			{
				return true;
			}
			Type type = slot.Type;
			Mesh val = (Mesh)((int)type switch
			{
				0 => AssetManager.instance.valuableMeshTiny, 
				1 => AssetManager.instance.valuableMeshSmall, 
				2 => AssetManager.instance.valuableMeshMedium, 
				3 => AssetManager.instance.valuableMeshBig, 
				4 => AssetManager.instance.valuableMeshWide, 
				5 => AssetManager.instance.valuableMeshTall, 
				6 => AssetManager.instance.valuableMeshVeryTall, 
				_ => null, 
			});
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			Bounds bounds = val.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			return Physics.OverlapBox(slot.Position + slot.Rotation * Vector3.forward * (size.z / 2f) + slot.Rotation * Vector3.up * (size.y / 2f) + Vector3.up * 0.01f, size / 2f, slot.Rotation, LayerMask.GetMask(new string[1] { "Default" }), (QueryTriggerInteraction)1).Length == 0;
		}
	}
	internal static class ValueDiagnostics
	{
		private struct Baseline
		{
			public float Original;

			public float Current;

			public string Name;
		}

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

		private static bool baselineCaptured;

		public static void Reset()
		{
			Baselines.Clear();
			baselineCaptured = false;
		}

		public static void CaptureBaselineIfNeeded(ValuableObject[] valuables)
		{
			if (baselineCaptured)
			{
				return;
			}
			foreach (ValuableObject val in valuables)
			{
				if (!((Object)(object)val == (Object)null))
				{
					int stableId = GetStableId(val);
					if (!Baselines.ContainsKey(stableId))
					{
						Baselines[stableId] = new Baseline
						{
							Original = val.dollarValueOriginal,
							Current = val.dollarValueCurrent,
							Name = ((Object)val).name
						};
					}
				}
			}
			baselineCaptured = true;
			(int, float, float) tuple = ComputeTotals(valuables);
			ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Baseline totals: current=${(int)tuple.Item2}, original=${(int)tuple.Item3}, count={tuple.Item1}");
		}

		public static (int Count, float TotalCurrent, float TotalOriginal, float TotalBaselineCurrent, float TotalLostFromBaseline) ComputeTotalsWithBaseline(ValuableObject[] valuables)
		{
			float num = 0f;
			foreach (ValuableObject val in valuables)
			{
				if (!((Object)(object)val == (Object)null))
				{
					int stableId = GetStableId(val);
					if (Baselines.TryGetValue(stableId, out var value))
					{
						num += value.Current;
					}
				}
			}
			(int, float, float) tuple = ComputeTotals(valuables);
			float item = num - tuple.Item2;
			return (tuple.Item1, tuple.Item2, tuple.Item3, num, item);
		}

		public static float? TryGetBaselineCurrent(ValuableObject valuable)
		{
			int stableId = GetStableId(valuable);
			if (Baselines.TryGetValue(stableId, out var value))
			{
				return value.Current;
			}
			return null;
		}

		public static bool TryGetBaseline(ValuableObject valuable, out float baselineCurrent)
		{
			baselineCurrent = 0f;
			int stableId = GetStableId(valuable);
			if (Baselines.TryGetValue(stableId, out var value))
			{
				baselineCurrent = value.Current;
				return true;
			}
			return false;
		}

		public static void EnsureBaselineEntry(ValuableObject valuable)
		{
			if (!((Object)(object)valuable == (Object)null))
			{
				int stableId = GetStableId(valuable);
				if (!Baselines.ContainsKey(stableId))
				{
					Baselines[stableId] = new Baseline
					{
						Original = valuable.dollarValueOriginal,
						Current = valuable.dollarValueCurrent,
						Name = ((Object)valuable).name
					};
				}
			}
		}

		public static float GetOrCaptureInitialValue(ValuableObject valuable)
		{
			EnsureBaselineEntry(valuable);
			if (!TryGetBaseline(valuable, out var baselineCurrent))
			{
				return valuable.dollarValueCurrent;
			}
			return baselineCurrent;
		}

		public static float GetOrCaptureInitialUndamagedValue(ValuableObject valuable)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return 0f;
			}
			EnsureBaselineEntry(valuable);
			int stableId = GetStableId(valuable);
			if (Baselines.TryGetValue(stableId, out var value))
			{
				if (!(value.Original > 0f))
				{
					return value.Current;
				}
				return value.Original;
			}
			return valuable.dollarValueOriginal;
		}

		public static int GetId(ValuableObject valuable)
		{
			return GetStableId(valuable);
		}

		public static List<ValuableObject> GetValuablesWithLoss(ValuableObject[] valuables, float minLoss)
		{
			List<ValuableObject> list = new List<ValuableObject>();
			if (valuables == null)
			{
				return list;
			}
			float num = Mathf.Max(0f, minLoss);
			foreach (ValuableObject val in valuables)
			{
				if (!((Object)(object)val == (Object)null) && Baselines.TryGetValue(GetStableId(val), out var value))
				{
					float dollarValueCurrent = val.dollarValueCurrent;
					if (value.Current - dollarValueCurrent >= num)
					{
						list.Add(val);
					}
				}
			}
			return list;
		}

		public static bool RestoreValueToBaseline(ValuableObject valuable)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return false;
			}
			if (!Baselines.TryGetValue(GetStableId(valuable), out var value))
			{
				return false;
			}
			float current = value.Current;
			if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode == 1)
			{
				PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null)
				{
					component.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { current });
					return true;
				}
			}
			valuable.dollarValueOriginal = current;
			valuable.dollarValueCurrent = current;
			valuable.dollarValueSet = true;
			return true;
		}

		public static void LogValueLosses(ValuableObject[] valuables)
		{
			foreach (ValuableObject val in valuables)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				float? num = TryGetBaselineCurrent(val);
				if (num.HasValue)
				{
					float dollarValueCurrent = val.dollarValueCurrent;
					if (dollarValueCurrent < num.Value)
					{
						ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Value loss: '{((Object)val).name}' baseline=${(int)num.Value} now=${(int)dollarValueCurrent} lost=${(int)(num.Value - dollarValueCurrent)}");
					}
				}
			}
		}

		private static (int Count, float TotalCurrent, float TotalOriginal) ComputeTotals(ValuableObject[] valuables)
		{
			float num = 0f;
			float num2 = 0f;
			int num3 = 0;
			foreach (ValuableObject val in valuables)
			{
				if (!((Object)(object)val == (Object)null))
				{
					num3++;
					num += val.dollarValueCurrent;
					num2 += val.dollarValueOriginal;
				}
			}
			return (num3, num, num2);
		}

		private static int GetStableId(ValuableObject valuable)
		{
			PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && component.ViewID != 0)
			{
				return component.ViewID;
			}
			return ((Object)valuable).GetInstanceID();
		}
	}
}
namespace ItemUnstuck.Patches
{
	[HarmonyPatch(typeof(PhysGrabber), "StartGrabbingPhysObject")]
	internal static class PhysGrabberStartGrabbingPhysObjectPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PhysGrabber __instance, RaycastHit hit, PhysGrabObject _physGrabObject)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigEntries.Enable.Value || !ConfigEntries.ShowInitialValueOnGrab.Value || (Object)(object)_physGrabObject == (Object)null || (SemiFunc.IsMultiplayer() && ((Object)(object)__instance == (Object)null || (!__instance.isLocal && ((Object)(object)__instance.photonView == (Object)null || !__instance.photonView.IsMine)))))
			{
				return;
			}
			ValuableObject val = ((Component)_physGrabObject).GetComponent<ValuableObject>() ?? ((Component)_physGrabObject).GetComponentInParent<ValuableObject>();
			if (!((Object)(object)val == (Object)null))
			{
				float orCaptureInitialUndamagedValue = ValueDiagnostics.GetOrCaptureInitialUndamagedValue(val);
				string text = ((Object)val).name ?? "Valuable";
				if (text.EndsWith("(Clone)", StringComparison.Ordinal))
				{
					text = text.Replace("(Clone)", "", StringComparison.Ordinal);
				}
				Color val2 = (((Object)(object)AssetManager.instance != (Object)null) ? AssetManager.instance.colorYellow : Color.white);
				SemiFunc.UIFocusText($"{text} initial: ${(int)orCaptureInitialUndamagedValue}", Color.white, val2, 2.5f);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject))]
	[HarmonyPatch("GrabPlayerAddRPC")]
	internal static class PhysGrabObjectGrabPlayerAddRPCPatch
	{
		[HarmonyPostfix]
		private static void GrabPlayerAddRPC_Postfix(PhysGrabObject __instance)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value)
			{
				InvulnerabilityController.Clear(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject))]
	[HarmonyPatch("GrabStarted")]
	internal static class PhysGrabObjectGrabStartedPatch
	{
		[HarmonyPostfix]
		private static void GrabStarted_Postfix(PhysGrabObject __instance)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value && (Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode == 0)
			{
				InvulnerabilityController.Clear(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject))]
	[HarmonyPatch("GrabStartedRPC")]
	internal static class PhysGrabObjectGrabStartedRPCPatch
	{
		[HarmonyPostfix]
		private static void GrabStartedRPC_Postfix(PhysGrabObject __instance)
		{
			if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value)
			{
				InvulnerabilityController.Clear(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector))]
	[HarmonyPatch("Update")]
	internal static class PhysGrabObjectImpactDetectorUpdateZeroDeltaPatch
	{
		[HarmonyPrefix]
		private static bool Update_Prefix()
		{
			if (!ConfigEntries.Enable.Value)
			{
				return true;
			}
			return Time.deltaTime > 0f;
		}
	}
	[HarmonyPatch(typeof(ValuableDirector), "VolumesAndSwitchSetupRPC")]
	internal static class ValuableDirectorVolumesAndSwitchSetupPatch
	{
		[HarmonyPrefix]
		private static void Prefix(PhotonMessageInfo _info)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigEntries.Enable.Value && SemiFunc.MasterOnlyRPC(_info))
			{
				SpawnSlotRegistry.Clear();
				ValueDiagnostics.Reset();
			}
		}

		[HarmonyPostfix]
		private static void Postfix(PhotonMessageInfo _info)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigEntries.Enable.Value && SemiFunc.MasterOnlyRPC(_info))
			{
				ItemUnstuck.Instance?.NotifyValuableVolumesSetupComplete();
			}
		}
	}
	[HarmonyPatch(typeof(ValuableObject))]
	[HarmonyPatch("Start")]
	internal static class ValuableObjectStartInvincibilityPatch
	{
		private static readonly FieldInfo? IndestructibleSpawnTimerField = typeof(PhysGrabObjectImpactDetector).GetField("indestructibleSpawnTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		[HarmonyPostfix]
		private static void Start_Postfix(ValuableObject __instance)
		{
			if (!ConfigEntries.Enable.Value)
			{
				return;
			}
			PhysGrabObject physGrabObject = __instance.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return;
			}
			if (ConfigEntries.InvulnerableUntilGrab.Value)
			{
				InvulnerabilityController.ApplyUntilGrab(physGrabObject);
				return;
			}
			float value = ConfigEntries.InvincibilitySeconds.Value;
			if (value <= 0f)
			{
				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; invincibility not applied.");
				}
				else
				{
					IndestructibleSpawnTimerField.SetValue(component, value);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ValuableVolume))]
	[HarmonyPatch("Setup")]
	internal static class ValuableVolumeSetupPatch
	{
		[HarmonyPrefix]
		private static void Setup_Prefix(ValuableVolume __instance)
		{
			if (ConfigEntries.Enable.Value)
			{
				SpawnSlotRegistry.Register(__instance);
			}
		}
	}
}