Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ItemUnstuck v1.0.3
ItemUnstuck.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
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