using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Tolga")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ItemUnstuck")]
[assembly: AssemblyTitle("ItemUnstuck")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace ItemUnstuck
{
internal static class ConfigEntries
{
internal static ConfigEntry<bool> Enable { get; set; }
internal static ConfigEntry<bool> HudNotifications { get; set; }
internal static ConfigEntry<bool> ShowInitialValueOnGrab { get; set; }
internal static ConfigEntry<float> InvincibilitySeconds { get; set; }
internal static ConfigEntry<bool> InvulnerableUntilGrab { get; set; }
internal static ConfigEntry<float> InvulnerableUntilGrabSeconds { get; set; }
internal static ConfigEntry<bool> EnableRelocation { get; set; }
internal static ConfigEntry<float> ScanDelaySeconds { get; set; }
internal static ConfigEntry<float> OccupiedDistance { get; set; }
internal static ConfigEntry<float> StuckMinPenetrationDistance { get; set; }
internal static ConfigEntry<float> StuckWeakPenetrationDistance { get; set; }
internal static ConfigEntry<bool> RequireStaticWorldEvidenceForRelocation { get; set; }
internal static ConfigEntry<bool> MotionProbeEnabled { get; set; }
internal static ConfigEntry<float> MotionProbeDelaySeconds { get; set; }
internal static ConfigEntry<float> MotionProbeWindowSeconds { get; set; }
internal static ConfigEntry<int> MotionProbeSamples { get; set; }
internal static ConfigEntry<float> MotionMaxSpeedToFlag { get; set; }
internal static ConfigEntry<int> MotionMinDirectionReversalsToFlag { get; set; }
internal static ConfigEntry<float> MotionReversalDotThreshold { get; set; }
internal static ConfigEntry<bool> FallThroughRecoveryEnabled { get; set; }
internal static ConfigEntry<float> FallThroughYThreshold { get; set; }
internal static ConfigEntry<float> FallThroughWatchSeconds { get; set; }
internal static ConfigEntry<float> FallThroughPollSeconds { get; set; }
internal static ConfigEntry<int> FallThroughMaxRecoveriesPerItem { get; set; }
internal static ConfigEntry<bool> DebugLogging { get; set; }
internal static ConfigEntry<float> DamageCheckSeconds { get; set; }
internal static ConfigEntry<int> MaxRelocationsPerItem { get; set; }
internal static ConfigEntry<bool> RestoreValueToInitial { get; set; }
internal static ConfigEntry<float> MinDamageToTrigger { get; set; }
}
[HarmonyPatch(typeof(PlayerController))]
internal static class ExamplePlayerControllerPatch
{
[HarmonyPrefix]
[HarmonyPatch("Start")]
private static void Start_Prefix(PlayerController __instance)
{
ItemUnstuck.Logger.LogDebug((object)$"{__instance} Start Prefix");
}
[HarmonyPostfix]
[HarmonyPatch("Start")]
private static void Start_Postfix(PlayerController __instance)
{
ItemUnstuck.Logger.LogDebug((object)$"{__instance} Start Postfix");
}
}
internal static class FallThroughRecovery
{
[CompilerGenerated]
private sealed class <WatchCoroutine>d__3 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public ValuableObject valuable;
public int id;
public string label;
private float <thresholdY>5__2;
private float <poll>5__3;
private int <maxRecoveries>5__4;
private float <end>5__5;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WatchCoroutine>d__3(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<>1__state = -2;
}
private bool MoveNext()
{
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Expected O, but got Unknown
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
bool result;
try
{
switch (<>1__state)
{
default:
result = false;
goto end_IL_0000;
case 0:
{
<>1__state = -1;
<>1__state = -3;
<thresholdY>5__2 = ConfigEntries.FallThroughYThreshold.Value;
float num = Mathf.Max(0.5f, ConfigEntries.FallThroughWatchSeconds.Value);
<poll>5__3 = Mathf.Clamp(ConfigEntries.FallThroughPollSeconds.Value, 0.05f, 2f);
<maxRecoveries>5__4 = Mathf.Clamp(ConfigEntries.FallThroughMaxRecoveriesPerItem.Value, 0, 10);
<end>5__5 = Time.time + num;
goto IL_0260;
}
case 1:
{
<>1__state = -3;
if (!ConfigEntries.Enable.Value || !ConfigEntries.FallThroughRecoveryEnabled.Value)
{
result = false;
break;
}
if ((Object)(object)valuable == (Object)null)
{
result = false;
break;
}
PhysGrabObject physGrabObject = valuable.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null)
{
result = false;
break;
}
if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
{
result = false;
break;
}
float y = ((Component)valuable).transform.position.y;
if (!(y >= <thresholdY>5__2))
{
RecoveryCounts.TryGetValue(id, out var value);
if (value >= <maxRecoveries>5__4)
{
ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Fall-through detected for '{((Object)valuable).name}' (y={y:0.0}) but max recoveries reached ({value}/{<maxRecoveries>5__4}).");
result = false;
break;
}
RecoveryCounts[id] = value + 1;
if (!StuckValuableRelocator.TryRecoverFallThrough(valuable))
{
ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Fall-through detected for '{((Object)valuable).name}' (y={y:0.0}) after {label} relocation, but no safe slot found to recover.");
result = false;
break;
}
InvulnerabilityController.ApplyUntilGrab(physGrabObject);
ItemUnstuck.Logger.LogWarning((object)$"[ItemUnstuck] Recovered fall-through for '{((Object)valuable).name}' (y={y:0.0}) after {label} relocation; recovery #{value + 1}.");
}
goto IL_0260;
}
IL_0260:
if (Time.time < <end>5__5)
{
<>2__current = (object)new WaitForSeconds(<poll>5__3);
<>1__state = 1;
result = true;
}
else
{
<>m__Finally1();
result = false;
}
goto end_IL_0000;
}
<>m__Finally1();
end_IL_0000:;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
return result;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
Watching.Remove(id);
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static readonly HashSet<int> Watching = new HashSet<int>();
private static readonly Dictionary<int, int> RecoveryCounts = new Dictionary<int, int>();
public static void WatchRelocated(ValuableObject valuable, string label)
{
if (ConfigEntries.Enable.Value && ConfigEntries.FallThroughRecoveryEnabled.Value && !((Object)(object)ItemUnstuck.Instance == (Object)null) && !((Object)(object)valuable == (Object)null))
{
int id = ValueDiagnostics.GetId(valuable);
if (id != 0 && Watching.Add(id))
{
((MonoBehaviour)ItemUnstuck.Instance).StartCoroutine(WatchCoroutine(valuable, id, label));
}
}
}
[IteratorStateMachine(typeof(<WatchCoroutine>d__3))]
private static IEnumerator WatchCoroutine(ValuableObject valuable, int id, string label)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WatchCoroutine>d__3(0)
{
valuable = valuable,
id = id,
label = label
};
}
}
internal static class HudNotifier
{
public static void ShowRelocated(IReadOnlyList<RelocatedValuableInfo> relocated, float totalLostFromBaseline)
{
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEntries.HudNotifications.Value || relocated.Count == 0)
{
return;
}
int num = 5;
List<string> values = (from x in relocated.Take(num)
select $"{x.Name} (${(int)x.ValueCurrentAtRelocation})").ToList();
string text = ((relocated.Count > num) ? $" +{relocated.Count - num}" : "");
string text2 = $"ItemUnstuck: relocated {relocated.Count} stuck valuable(s)\n" + $"Lost vs baseline: ${(int)totalLostFromBaseline}\n" + string.Join(", ", values) + text;
try
{
SemiFunc.UIBigMessage(text2, "{check}", 20f, Color.white, Color.white);
}
catch
{
}
}
}
internal static class InvulnerabilityController
{
private static readonly FieldInfo? IndestructibleSpawnTimerField = typeof(PhysGrabObjectImpactDetector).GetField("indestructibleSpawnTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
public static void ApplyUntilGrab(PhysGrabObject physGrabObject)
{
if (!ConfigEntries.Enable.Value || !ConfigEntries.InvulnerableUntilGrab.Value || (Object)(object)physGrabObject == (Object)null)
{
return;
}
PhysGrabObjectImpactDetector component = ((Component)physGrabObject).GetComponent<PhysGrabObjectImpactDetector>();
if ((Object)(object)component == (Object)null)
{
return;
}
if (IndestructibleSpawnTimerField == null)
{
ItemUnstuck.Logger.LogError((object)"Could not find PhysGrabObjectImpactDetector.indestructibleSpawnTimer; invulnerability not applied.");
return;
}
float num = ConfigEntries.InvulnerableUntilGrabSeconds.Value;
if (num <= 0f)
{
num = 99999f;
}
IndestructibleSpawnTimerField.SetValue(component, num);
physGrabObject.OverrideIndestructible(0.5f);
}
public static void Clear(PhysGrabObject physGrabObject)
{
if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value && !((Object)(object)physGrabObject == (Object)null))
{
PhysGrabObjectImpactDetector component = ((Component)physGrabObject).GetComponent<PhysGrabObjectImpactDetector>();
if ((Object)(object)component != (Object)null && IndestructibleSpawnTimerField != null)
{
IndestructibleSpawnTimerField.SetValue(component, 0f);
}
physGrabObject.ResetIndestructible();
}
}
}
[BepInPlugin("Tolga.ItemUnstuck", "ItemUnstuck", "1.0.0")]
public class ItemUnstuck : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <ScanAfterVolumesSetup>d__16 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public int generation;
public ItemUnstuck <>4__this;
private ValuableObject[] <valuablesAtBaseline>5__2;
private HashSet<int> <affectedIds>5__3;
private HashSet<int> <relocatedIds>5__4;
private List<RelocatedValuableInfo> <relocatedInfos>5__5;
private Dictionary<int, MotionStats> <motionStats>5__6;
private float <damageWait>5__7;
private float <minDamage>5__8;
private int <maxRelocationsPerItem>5__9;
private float <probeDelay>5__10;
private float <window>5__11;
private int <samples>5__12;
private float <dt>5__13;
private float <reversalDotThreshold>5__14;
private List<ValuableObject> <tracked>5__15;
private List<int> <ids>5__16;
private List<Vector3> <prevPos>5__17;
private List<Vector3> <prevDelta>5__18;
private List<float> <sumDist>5__19;
private List<float> <maxSpeed>5__20;
private List<int> <reversals>5__21;
private int <s>5__22;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ScanAfterVolumesSetup>d__16(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<valuablesAtBaseline>5__2 = null;
<affectedIds>5__3 = null;
<relocatedIds>5__4 = null;
<relocatedInfos>5__5 = null;
<motionStats>5__6 = null;
<tracked>5__15 = null;
<ids>5__16 = null;
<prevPos>5__17 = null;
<prevDelta>5__18 = null;
<sumDist>5__19 = null;
<maxSpeed>5__20 = null;
<reversals>5__21 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Expected O, but got Unknown
//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0321: Unknown result type (might be due to invalid IL or missing references)
//IL_0331: Unknown result type (might be due to invalid IL or missing references)
//IL_038b: Unknown result type (might be due to invalid IL or missing references)
//IL_0395: Expected O, but got Unknown
//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
//IL_046d: Unknown result type (might be due to invalid IL or missing references)
//IL_0472: Unknown result type (might be due to invalid IL or missing references)
//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
//IL_01bd: Expected O, but got Unknown
//IL_0492: Unknown result type (might be due to invalid IL or missing references)
//IL_0499: Unknown result type (might be due to invalid IL or missing references)
//IL_0692: Unknown result type (might be due to invalid IL or missing references)
//IL_069c: Expected O, but got Unknown
int num = <>1__state;
ItemUnstuck itemUnstuck = <>4__this;
ValuableObject[] array;
List<ValuableObject> valuablesWithLoss;
ValuableObject[] array2;
switch (num)
{
default:
return false;
case 0:
{
<>1__state = -1;
if (!ConfigEntries.Enable.Value)
{
return false;
}
float num3 = Mathf.Max(0f, ConfigEntries.ScanDelaySeconds.Value);
if (num3 > 0f)
{
<>2__current = (object)new WaitForSeconds(num3);
<>1__state = 1;
return true;
}
goto IL_0078;
}
case 1:
<>1__state = -1;
goto IL_0078;
case 2:
<>1__state = -1;
goto IL_01cd;
case 3:
{
<>1__state = -1;
if (generation != itemUnstuck.scanGeneration)
{
return false;
}
for (int i = 0; i < <tracked>5__15.Count; i++)
{
ValuableObject val = <tracked>5__15[i];
if ((Object)(object)val == (Object)null)
{
continue;
}
Vector3 position = ((Component)val).transform.position;
Vector3 value = position - <prevPos>5__17[i];
float magnitude = ((Vector3)(ref value)).magnitude;
<sumDist>5__19[i] += magnitude;
float num2 = magnitude / Mathf.Max(0.0001f, <dt>5__13);
if (num2 > <maxSpeed>5__20[i])
{
<maxSpeed>5__20[i] = num2;
}
if (<s>5__22 >= 2)
{
Vector3 val2 = <prevDelta>5__18[i];
if (((Vector3)(ref val2)).sqrMagnitude > 1E-06f && ((Vector3)(ref value)).sqrMagnitude > 1E-06f && Vector3.Dot(((Vector3)(ref val2)).normalized, ((Vector3)(ref value)).normalized) <= <reversalDotThreshold>5__14)
{
<reversals>5__21[i]++;
}
}
<prevDelta>5__18[i] = value;
<prevPos>5__17[i] = position;
}
<s>5__22++;
goto IL_0512;
}
case 4:
{
<>1__state = -1;
goto IL_06ac;
}
IL_0078:
if (generation != itemUnstuck.scanGeneration)
{
return false;
}
if (SpawnSlotRegistry.Slots.Count == 0)
{
if (ConfigEntries.DebugLogging.Value)
{
Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no spawn slots captured yet.");
}
return false;
}
<valuablesAtBaseline>5__2 = Object.FindObjectsOfType<ValuableObject>();
if (<valuablesAtBaseline>5__2 == null || <valuablesAtBaseline>5__2.Length == 0)
{
if (ConfigEntries.DebugLogging.Value)
{
Logger.LogInfo((object)"[ItemUnstuck] Scan skipped: no valuables found.");
}
return false;
}
<affectedIds>5__3 = new HashSet<int>();
<relocatedIds>5__4 = new HashSet<int>();
<relocatedInfos>5__5 = new List<RelocatedValuableInfo>();
ValueDiagnostics.CaptureBaselineIfNeeded(<valuablesAtBaseline>5__2);
if (ConfigEntries.EnableRelocation.Value)
{
foreach (RelocatedValuableInfo item in StuckValuableRelocator.RelocateWorldEmbedded(<valuablesAtBaseline>5__2, null).Relocated)
{
<relocatedInfos>5__5.Add(item);
}
}
<motionStats>5__6 = null;
if (ConfigEntries.MotionProbeEnabled.Value)
{
<probeDelay>5__10 = Mathf.Max(0f, ConfigEntries.MotionProbeDelaySeconds.Value);
if (<probeDelay>5__10 > 0f)
{
<>2__current = (object)new WaitForSeconds(<probeDelay>5__10);
<>1__state = 2;
return true;
}
goto IL_01cd;
}
goto IL_0626;
IL_06ac:
if (generation != itemUnstuck.scanGeneration)
{
return false;
}
array = Object.FindObjectsOfType<ValuableObject>();
valuablesWithLoss = ValueDiagnostics.GetValuablesWithLoss(array, <minDamage>5__8);
if (valuablesWithLoss.Count == 0)
{
if (ConfigEntries.DebugLogging.Value)
{
Logger.LogInfo((object)$"[ItemUnstuck][Debug] Damage check pass {<samples>5__12}: no damaged valuables detected.");
}
if (<samples>5__12 == 1)
{
break;
}
}
else
{
List<ValuableObject> list;
if (<samples>5__12 == 1)
{
list = valuablesWithLoss;
}
else
{
list = new List<ValuableObject>();
foreach (ValuableObject item2 in valuablesWithLoss)
{
if (!((Object)(object)item2 == (Object)null) && <relocatedIds>5__4.Contains(ValueDiagnostics.GetId(item2)))
{
list.Add(item2);
}
}
}
if (list.Count != 0)
{
foreach (ValuableObject item3 in list)
{
if (!((Object)(object)item3 == (Object)null))
{
<affectedIds>5__3.Add(ValueDiagnostics.GetId(item3));
}
}
foreach (RelocatedValuableInfo item4 in StuckValuableRelocator.RelocateCandidates(list, array, <motionStats>5__6).Relocated)
{
<relocatedInfos>5__5.Add(item4);
}
foreach (ValuableObject item5 in list)
{
if (!((Object)(object)item5 == (Object)null))
{
<relocatedIds>5__4.Add(ValueDiagnostics.GetId(item5));
}
}
}
}
<samples>5__12++;
goto IL_0883;
IL_0626:
<damageWait>5__7 = Mathf.Max(0f, ConfigEntries.DamageCheckSeconds.Value);
<minDamage>5__8 = Mathf.Max(0f, ConfigEntries.MinDamageToTrigger.Value);
<maxRelocationsPerItem>5__9 = Mathf.Clamp(ConfigEntries.MaxRelocationsPerItem.Value, 0, 10);
<samples>5__12 = 1;
goto IL_0883;
IL_0883:
if (<samples>5__12 > <maxRelocationsPerItem>5__9)
{
break;
}
if (<damageWait>5__7 > 0f)
{
<>2__current = (object)new WaitForSeconds(<damageWait>5__7);
<>1__state = 4;
return true;
}
goto IL_06ac;
IL_0512:
if (<s>5__22 < <samples>5__12)
{
<>2__current = (object)new WaitForSeconds(<dt>5__13);
<>1__state = 3;
return true;
}
for (int j = 0; j < <tracked>5__15.Count; j++)
{
float averageSpeed = <sumDist>5__19[j] / <window>5__11;
<motionStats>5__6[<ids>5__16[j]] = new MotionStats(averageSpeed, <maxSpeed>5__20[j], <reversals>5__21[j], <reversalDotThreshold>5__14);
}
if (ConfigEntries.DebugLogging.Value)
{
Logger.LogInfo((object)$"[ItemUnstuck][Debug] Motion probe captured stats for {<motionStats>5__6.Count} valuables (delay={<probeDelay>5__10:0.00}s window={<window>5__11:0.00}s samples={<samples>5__12}).");
}
<tracked>5__15 = null;
<ids>5__16 = null;
<prevPos>5__17 = null;
<prevDelta>5__18 = null;
<sumDist>5__19 = null;
<maxSpeed>5__20 = null;
<reversals>5__21 = null;
goto IL_0626;
IL_01cd:
if (generation != itemUnstuck.scanGeneration)
{
return false;
}
<motionStats>5__6 = new Dictionary<int, MotionStats>();
<window>5__11 = Mathf.Max(0.05f, ConfigEntries.MotionProbeWindowSeconds.Value);
<samples>5__12 = Mathf.Clamp(ConfigEntries.MotionProbeSamples.Value, 2, 60);
<dt>5__13 = <window>5__11 / (float)(<samples>5__12 - 1);
<reversalDotThreshold>5__14 = Mathf.Clamp(ConfigEntries.MotionReversalDotThreshold.Value, -0.99f, 0.99f);
<tracked>5__15 = new List<ValuableObject>(<valuablesAtBaseline>5__2.Length);
<ids>5__16 = new List<int>(<valuablesAtBaseline>5__2.Length);
<prevPos>5__17 = new List<Vector3>(<valuablesAtBaseline>5__2.Length);
<prevDelta>5__18 = new List<Vector3>(<valuablesAtBaseline>5__2.Length);
<sumDist>5__19 = new List<float>(<valuablesAtBaseline>5__2.Length);
<maxSpeed>5__20 = new List<float>(<valuablesAtBaseline>5__2.Length);
<reversals>5__21 = new List<int>(<valuablesAtBaseline>5__2.Length);
array2 = <valuablesAtBaseline>5__2;
foreach (ValuableObject val3 in array2)
{
if (!((Object)(object)val3 == (Object)null))
{
<tracked>5__15.Add(val3);
<ids>5__16.Add(ValueDiagnostics.GetId(val3));
<prevPos>5__17.Add(((Component)val3).transform.position);
<prevDelta>5__18.Add(Vector3.zero);
<sumDist>5__19.Add(0f);
<maxSpeed>5__20.Add(0f);
<reversals>5__21.Add(0);
}
}
<s>5__22 = 1;
goto IL_0512;
}
if (ConfigEntries.RestoreValueToInitial.Value && <affectedIds>5__3.Count > 0)
{
array2 = Object.FindObjectsOfType<ValuableObject>();
foreach (ValuableObject val4 in array2)
{
if (!((Object)(object)val4 == (Object)null) && <affectedIds>5__3.Contains(ValueDiagnostics.GetId(val4)))
{
ValueDiagnostics.RestoreValueToBaseline(val4);
}
}
}
ValuableObject[] valuables = Object.FindObjectsOfType<ValuableObject>();
(int, float, float, float, float) tuple = ValueDiagnostics.ComputeTotalsWithBaseline(valuables);
Logger.LogInfo((object)$"[ItemUnstuck] Post-flow totals: current=${(int)tuple.Item2}, original=${(int)tuple.Item3}, baselineCurrent=${(int)tuple.Item4}, lost=${(int)tuple.Item5}, count={tuple.Item1}");
ValueDiagnostics.LogValueLosses(valuables);
if (<relocatedInfos>5__5.Count > 0)
{
HudNotifier.ShowRelocated(<relocatedInfos>5__5, tuple.Item5);
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private Coroutine? scanCoroutine;
private int scanGeneration;
internal static ItemUnstuck Instance { get; private set; }
internal static ManualLogSource Logger => Instance._logger;
private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;
internal Harmony? Harmony { get; set; }
private void Awake()
{
Instance = this;
((Component)this).gameObject.transform.parent = null;
((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
ConfigEntries.Enable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable/disable the ItemUnstuck mod.");
ConfigEntries.InvincibilitySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Invulnerability", "InvincibilitySeconds", 0f, "How long newly spawned valuables stay indestructible (seconds). Set to 0 to disable.");
ConfigEntries.InvulnerableUntilGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "InvulnerableUntilGrab", true, "If true, make valuables invulnerable until any player grabs them (also re-applied after ItemUnstuck teleports a valuable).");
ConfigEntries.InvulnerableUntilGrabSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Invulnerability", "InvulnerableUntilGrabSeconds", 99999f, "Timer used for invulnerable-until-grab. This is a large number; the timer will be cleared automatically on first grab.");
ConfigEntries.HudNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "HudNotifications", true, "Show a HUD notification when stuck valuables are relocated.");
ConfigEntries.ShowInitialValueOnGrab = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowInitialValueOnGrab", true, "Show the grabbed valuable's initial (undamaged) value on the HUD.");
ConfigEntries.EnableRelocation = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "EnableRelocation", true, "If true, detects stuck valuables and relocates them to a free valuable spawn slot.");
ConfigEntries.ScanDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Valuables", "ScanDelaySeconds", 1f, "Delay after level generation before scanning for stuck valuables (seconds).");
ConfigEntries.OccupiedDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.Relocation", "OccupiedDistance", 0.2f, "Distance threshold used to consider a valuable spawn slot occupied.");
ConfigEntries.StuckMinPenetrationDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.StuckDetection", "StuckMinPenetrationDistance", 0.12f, "Minimum penetration distance (meters) required to consider a valuable 'stuck' in world geometry. Increase if it relocates too aggressively.");
ConfigEntries.StuckWeakPenetrationDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.StuckDetection", "StuckWeakPenetrationDistance", 0.03f, "Weaker penetration distance (meters) used with motion jitter to detect embedment without flagging normal floor contact.");
ConfigEntries.RequireStaticWorldEvidenceForRelocation = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced.StuckDetection", "RequireStaticWorldEvidenceForRelocation", true, "If true, only relocate when the item is penetrating static/world colliders (prevents relocating items merely wedged in other physics objects).");
ConfigEntries.MotionProbeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced.MotionProbe", "MotionProbeEnabled", true, "If true, sample valuable motion shortly after level load to detect jittery/rapid motion typical of embedded items.");
ConfigEntries.MotionProbeDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionProbeDelaySeconds", 2f, "Seconds after level setup before starting the motion probe sampling.");
ConfigEntries.MotionProbeWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionProbeWindowSeconds", 0.75f, "How long (seconds) to sample valuable positions to detect rapid/jittery movement.");
ConfigEntries.MotionProbeSamples = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.MotionProbe", "MotionProbeSamples", 12, "How many samples to take during MotionProbeWindowSeconds. Higher values cost more CPU.");
ConfigEntries.MotionMaxSpeedToFlag = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionMaxSpeedToFlag", 0.75f, "If a valuable exceeds this speed (m/s) during motion probing, it is considered rapidly moving.");
ConfigEntries.MotionMinDirectionReversalsToFlag = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.MotionProbe", "MotionMinDirectionReversalsToFlag", 2, "Minimum number of direction reversals during motion probing required to treat motion as 'jittery'.");
ConfigEntries.MotionReversalDotThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.MotionProbe", "MotionReversalDotThreshold", -0.2f, "Dot product threshold to count a direction reversal. More negative = stricter (requires closer to opposite direction).");
ConfigEntries.FallThroughRecoveryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "FallThroughRecoveryEnabled", true, "If true, watch relocated valuables and recover them if they fall far below the map (teleport back to a safe slot and keep invulnerable until grabbed).");
ConfigEntries.FallThroughYThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughYThreshold", -50f, "If a relocated valuable's Y position drops below this threshold, it is considered to have fallen through the map.");
ConfigEntries.FallThroughWatchSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughWatchSeconds", 15f, "How long (seconds) to watch relocated valuables for falling through the map.");
ConfigEntries.FallThroughPollSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.FallThrough", "FallThroughPollSeconds", 0.25f, "How often (seconds) to poll relocated valuables for fall-through detection.");
ConfigEntries.FallThroughMaxRecoveriesPerItem = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.FallThrough", "FallThroughMaxRecoveriesPerItem", 2, "Maximum number of fall-through recoveries per valuable (prevents infinite loops).");
ConfigEntries.DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "DebugLogging", false, "Enable extra debug logs for stuck detection and relocation decisions.");
ConfigEntries.DamageCheckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.DamageFlow", "DamageCheckSeconds", 3f, "How long to wait (seconds) before checking which valuables lost value after spawn/relocation.");
ConfigEntries.MaxRelocationsPerItem = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced.Relocation", "MaxRelocationsPerItem", 2, "Maximum number of relocations per valuable during the damage-check flow (e.g. 2 = relocate once + one re-relocation).");
ConfigEntries.MinDamageToTrigger = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced.DamageFlow", "MinDamageToTrigger", 1f, "Minimum value loss required to treat a valuable as 'damaged' and trigger relocation/restoration.");
ConfigEntries.RestoreValueToInitial = ((BaseUnityPlugin)this).Config.Bind<bool>("Valuables", "RestoreValueToInitial", true, "If true, restore damaged/relocated valuables back to their initial baseline value at the end of the flow.");
SpawnSlotRegistry.Clear();
Patch();
Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
}
internal void NotifyValuableVolumesSetupComplete()
{
scanGeneration++;
if (scanCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(scanCoroutine);
scanCoroutine = null;
}
scanCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanAfterVolumesSetup(scanGeneration));
}
[IteratorStateMachine(typeof(<ScanAfterVolumesSetup>d__16))]
private IEnumerator ScanAfterVolumesSetup(int generation)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ScanAfterVolumesSetup>d__16(0)
{
<>4__this = this,
generation = generation
};
}
internal void Patch()
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
//IL_0025: Expected O, but got Unknown
if (Harmony == null)
{
Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
Harmony val2 = val;
Harmony = val;
}
Harmony.PatchAll();
}
internal void Unpatch()
{
Harmony? harmony = Harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
private void Update()
{
}
}
internal readonly struct MotionStats
{
public float AverageSpeed { get; }
public float MaxSpeed { get; }
public int DirectionReversals { get; }
public float ReversalDotThreshold { get; }
public MotionStats(float averageSpeed, float maxSpeed, int directionReversals, float reversalDotThreshold)
{
AverageSpeed = averageSpeed;
MaxSpeed = maxSpeed;
DirectionReversals = directionReversals;
ReversalDotThreshold = reversalDotThreshold;
}
public override string ToString()
{
return $"avgSpeed={AverageSpeed:0.000}m/s maxSpeed={MaxSpeed:0.000}m/s reversals={DirectionReversals} dotTh={ReversalDotThreshold:0.00}";
}
}
internal readonly struct RelocatedValuableInfo
{
public string Name { get; }
public Type Type { get; }
public float ValueCurrentAtRelocation { get; }
public float? BaselineCurrent { get; }
public Vector3 From { get; }
public Vector3 To { get; }
public RelocatedValuableInfo(string name, Type type, float valueCurrentAtRelocation, float? baselineCurrent, Vector3 from, Vector3 to)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
Name = name;
Type = type;
ValueCurrentAtRelocation = valueCurrentAtRelocation;
BaselineCurrent = baselineCurrent;
From = from;
To = to;
}
}
internal static class SpawnSlotRegistry
{
internal readonly struct Slot
{
public Vector3 Position { get; }
public Quaternion Rotation { get; }
public Type Type { get; }
public Transform? Parent { get; }
public Slot(Vector3 position, Quaternion rotation, Type type, Transform? parent)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
Position = position;
Rotation = rotation;
Type = type;
Parent = parent;
}
}
private static readonly List<Slot> SlotsInternal = new List<Slot>();
public static IReadOnlyList<Slot> Slots => SlotsInternal;
public static void Clear()
{
SlotsInternal.Clear();
}
public static void Register(ValuableVolume volume)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
Vector3 position = ((Component)volume).transform.position;
Quaternion rotation = ((Component)volume).transform.rotation;
for (int i = 0; i < SlotsInternal.Count; i++)
{
Slot slot = SlotsInternal[i];
if (slot.Type == volume.VolumeType && Vector3.SqrMagnitude(slot.Position - position) < 0.0001f)
{
return;
}
}
SlotsInternal.Add(new Slot(position, rotation, volume.VolumeType, ((Component)volume).transform.parent));
}
}
internal static class StuckValuableRelocator
{
private readonly struct EmbedmentAssessment
{
public float StaticPenetrationSum { get; }
public float StaticPenetrationMax { get; }
public float DynamicPenetrationSum { get; }
public float DynamicPenetrationMax { get; }
public bool MidpointInsideStatic { get; }
public bool MidpointInsideDynamic { get; }
public EmbedmentAssessment(float staticPenetrationSum, float staticPenetrationMax, float dynamicPenetrationSum, float dynamicPenetrationMax, bool midpointInsideStatic, bool midpointInsideDynamic)
{
StaticPenetrationSum = staticPenetrationSum;
StaticPenetrationMax = staticPenetrationMax;
DynamicPenetrationSum = dynamicPenetrationSum;
DynamicPenetrationMax = dynamicPenetrationMax;
MidpointInsideStatic = midpointInsideStatic;
MidpointInsideDynamic = midpointInsideDynamic;
}
public override string ToString()
{
return $"staticMax={StaticPenetrationMax:0.000}m staticSum={StaticPenetrationSum:0.000}m dynamicMax={DynamicPenetrationMax:0.000}m dynamicSum={DynamicPenetrationSum:0.000}m midInStatic={MidpointInsideStatic} midInDyn={MidpointInsideDynamic}";
}
}
public readonly struct ScanResult
{
public List<RelocatedValuableInfo> Relocated { get; }
public ScanResult(List<RelocatedValuableInfo> relocated)
{
Relocated = relocated;
}
}
public static ScanResult ScanAndRelocateOnce()
{
//IL_0126: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
//IL_0196: Unknown result type (might be due to invalid IL or missing references)
//IL_019d: Unknown result type (might be due to invalid IL or missing references)
//IL_0153: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01db: Unknown result type (might be due to invalid IL or missing references)
//IL_0210: Unknown result type (might be due to invalid IL or missing references)
//IL_0216: Unknown result type (might be due to invalid IL or missing references)
//IL_021a: Unknown result type (might be due to invalid IL or missing references)
//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
//IL_025f: Unknown result type (might be due to invalid IL or missing references)
//IL_0297: Unknown result type (might be due to invalid IL or missing references)
//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
if ((Object)(object)GameManager.instance == (Object)null)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
if (GameManager.instance.gameMode == 1 && !PhotonNetwork.IsMasterClient)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
if (slots.Count == 0)
{
ItemUnstuck.Logger.LogWarning((object)"No ValuableVolume spawn slots were captured; cannot relocate stuck valuables.");
return new ScanResult(new List<RelocatedValuableInfo>());
}
ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
if (array == null || array.Length == 0)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance.Value);
float occupiedDistanceSqr = num * num;
List<RelocatedValuableInfo> list = new List<RelocatedValuableInfo>();
ValuableObject[] array2 = array;
foreach (ValuableObject val in array2)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
PhysGrabObject physGrabObject = val.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null || ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart) || !ShouldRelocateBasedOnAssessment(val, null, out string _))
{
continue;
}
Type volumeType = val.volumeType;
SpawnSlotRegistry.Slot? slot = FindFreeSlot(volumeType, array, slots, occupiedDistanceSqr);
if (!slot.HasValue)
{
ItemUnstuck.Logger.LogInfo((object)$"Stuck valuable '{((Object)val).name}' found, but no free slot available for type {volumeType}.");
continue;
}
SpawnSlotRegistry.Slot value = slot.Value;
Vector3 position = ((Component)val).transform.position;
float? baselineCurrent = ValueDiagnostics.TryGetBaselineCurrent(val);
float dollarValueCurrent = val.dollarValueCurrent;
physGrabObject.Teleport(value.Position, value.Rotation);
InvulnerabilityController.ApplyUntilGrab(physGrabObject);
FallThroughRecovery.WatchRelocated(val, "stuck");
Rigidbody component = ((Component)val).GetComponent<Rigidbody>();
if ((Object)(object)component != (Object)null)
{
component.velocity = Vector3.zero;
component.angularVelocity = Vector3.zero;
}
if ((Object)(object)value.Parent != (Object)null)
{
((Component)val).transform.parent = value.Parent;
}
RelocatedValuableInfo item = new RelocatedValuableInfo(((Object)val).name, volumeType, dollarValueCurrent, baselineCurrent, position, value.Position);
list.Add(item);
if (baselineCurrent.HasValue && dollarValueCurrent < baselineCurrent.Value)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated stuck valuable '{((Object)val).name}' type={volumeType} value baseline=${(int)baselineCurrent.Value} now=${(int)dollarValueCurrent} lost=${(int)(baselineCurrent.Value - dollarValueCurrent)} from={position} to={value.Position}");
}
else
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated stuck valuable '{((Object)val).name}' type={volumeType} value=${(int)dollarValueCurrent} from={position} to={value.Position}");
}
}
if (list.Count > 0)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {list.Count} stuck valuable(s).");
}
return new ScanResult(list);
}
public static ScanResult RelocateWorldEmbedded(ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats)
{
return RelocateCandidatesInternal(allValuables, allValuables, motionStats, "world-embedded");
}
internal static bool TryRecoverFallThrough(ValuableObject valuable)
{
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_0188: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value)
{
return false;
}
if ((Object)(object)GameManager.instance == (Object)null)
{
return false;
}
if (GameManager.instance.gameMode == 1 && !PhotonNetwork.IsMasterClient)
{
return false;
}
if ((Object)(object)valuable == (Object)null)
{
return false;
}
PhysGrabObject physGrabObject = valuable.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null)
{
return false;
}
if ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart)
{
return false;
}
IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
if (slots.Count == 0)
{
return false;
}
ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance.Value);
float num2 = num * num;
SpawnSlotRegistry.Slot? slot = FindFreeSlot(valuable.volumeType, array, slots, num2);
if (!slot.HasValue)
{
for (int i = 0; i < slots.Count; i++)
{
SpawnSlotRegistry.Slot slot2 = slots[i];
if (!IsSlotClearOfWorldGeometry(slot2))
{
continue;
}
bool flag = false;
foreach (ValuableObject val in array)
{
if (!((Object)(object)val == (Object)null) && Vector3.SqrMagnitude(((Component)val).transform.position - slot2.Position) <= num2)
{
flag = true;
break;
}
}
if (!flag)
{
slot = slot2;
break;
}
}
}
if (!slot.HasValue)
{
return false;
}
SpawnSlotRegistry.Slot value = slot.Value;
physGrabObject.Teleport(value.Position, value.Rotation);
Rigidbody component = ((Component)valuable).GetComponent<Rigidbody>();
if ((Object)(object)component != (Object)null)
{
component.velocity = Vector3.zero;
component.angularVelocity = Vector3.zero;
}
if ((Object)(object)value.Parent != (Object)null)
{
((Component)valuable).transform.parent = value.Parent;
}
InvulnerabilityController.ApplyUntilGrab(physGrabObject);
return true;
}
public static ScanResult RelocateCandidates(IEnumerable<ValuableObject> candidates, ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats)
{
return RelocateCandidatesInternal(candidates, allValuables, motionStats, "damaged");
}
private static ScanResult RelocateCandidatesInternal(IEnumerable<ValuableObject> candidates, ValuableObject[] allValuables, IReadOnlyDictionary<int, MotionStats>? motionStats, string label)
{
//IL_0191: Unknown result type (might be due to invalid IL or missing references)
//IL_0196: Unknown result type (might be due to invalid IL or missing references)
//IL_0198: Unknown result type (might be due to invalid IL or missing references)
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
//IL_0211: Unknown result type (might be due to invalid IL or missing references)
//IL_0218: Unknown result type (might be due to invalid IL or missing references)
//IL_0246: Unknown result type (might be due to invalid IL or missing references)
//IL_0252: Unknown result type (might be due to invalid IL or missing references)
//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
//IL_0286: Unknown result type (might be due to invalid IL or missing references)
//IL_028c: Unknown result type (might be due to invalid IL or missing references)
//IL_0290: Unknown result type (might be due to invalid IL or missing references)
//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEntries.Enable.Value || !ConfigEntries.EnableRelocation.Value)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
if ((Object)(object)GameManager.instance == (Object)null)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
if (GameManager.instance.gameMode == 1 && !PhotonNetwork.IsMasterClient)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
IReadOnlyList<SpawnSlotRegistry.Slot> slots = SpawnSlotRegistry.Slots;
if (slots.Count == 0)
{
ItemUnstuck.Logger.LogWarning((object)"No ValuableVolume spawn slots were captured; cannot relocate stuck valuables.");
return new ScanResult(new List<RelocatedValuableInfo>());
}
if (candidates == null)
{
return new ScanResult(new List<RelocatedValuableInfo>());
}
if (allValuables == null || allValuables.Length == 0)
{
allValuables = Object.FindObjectsOfType<ValuableObject>() ?? Array.Empty<ValuableObject>();
}
float num = Mathf.Max(0.05f, ConfigEntries.OccupiedDistance.Value);
float occupiedDistanceSqr = num * num;
List<RelocatedValuableInfo> list = new List<RelocatedValuableInfo>();
int num2 = 0;
foreach (ValuableObject candidate in candidates)
{
if ((Object)(object)candidate == (Object)null)
{
continue;
}
PhysGrabObject physGrabObject = candidate.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null || ((Object)(object)physGrabObject.impactDetector != (Object)null && physGrabObject.impactDetector.inCart))
{
continue;
}
MotionStats? motion = null;
if (motionStats != null)
{
int id = ValueDiagnostics.GetId(candidate);
if (motionStats.TryGetValue(id, out var value))
{
motion = value;
}
}
if (!ShouldRelocateBasedOnAssessment(candidate, motion, out string reason))
{
num2++;
if (ConfigEntries.DebugLogging.Value)
{
ItemUnstuck.Logger.LogInfo((object)("[ItemUnstuck][Debug] Skip relocation '" + ((Object)candidate).name + "': " + reason));
}
continue;
}
Type volumeType = candidate.volumeType;
SpawnSlotRegistry.Slot? slot = FindFreeSlot(volumeType, allValuables, slots, occupiedDistanceSqr);
if (!slot.HasValue)
{
if (ConfigEntries.DebugLogging.Value)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][Debug] {label} valuable '{((Object)candidate).name}' found, but no free slot available for type {volumeType}.");
}
continue;
}
SpawnSlotRegistry.Slot value2 = slot.Value;
Vector3 position = ((Component)candidate).transform.position;
float? baselineCurrent = ValueDiagnostics.TryGetBaselineCurrent(candidate);
float dollarValueCurrent = candidate.dollarValueCurrent;
physGrabObject.Teleport(value2.Position, value2.Rotation);
InvulnerabilityController.ApplyUntilGrab(physGrabObject);
FallThroughRecovery.WatchRelocated(candidate, label);
Rigidbody component = ((Component)candidate).GetComponent<Rigidbody>();
if ((Object)(object)component != (Object)null)
{
component.velocity = Vector3.zero;
component.angularVelocity = Vector3.zero;
}
if ((Object)(object)value2.Parent != (Object)null)
{
((Component)candidate).transform.parent = value2.Parent;
}
list.Add(new RelocatedValuableInfo(((Object)candidate).name, volumeType, dollarValueCurrent, baselineCurrent, position, value2.Position));
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {label} valuable '{((Object)candidate).name}' type={volumeType} value=${(int)dollarValueCurrent} from={position} to={value2.Position}");
}
if (list.Count > 0)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Relocated {list.Count} {label} valuable(s).");
}
if (num2 > 0 && ConfigEntries.DebugLogging.Value)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck][Debug] Skipped {num2} candidate(s) (not embedded in static/world geometry).");
}
return new ScanResult(list);
}
private static SpawnSlotRegistry.Slot? FindFreeSlot(Type type, ValuableObject[] valuables, IReadOnlyList<SpawnSlotRegistry.Slot> slots, float occupiedDistanceSqr)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < slots.Count; i++)
{
SpawnSlotRegistry.Slot slot = slots[i];
if (slot.Type != type || !IsSlotClearOfWorldGeometry(slot))
{
continue;
}
bool flag = false;
foreach (ValuableObject val in valuables)
{
if (!((Object)(object)val == (Object)null) && Vector3.SqrMagnitude(((Component)val).transform.position - slot.Position) <= occupiedDistanceSqr)
{
flag = true;
break;
}
}
if (!flag)
{
return slot;
}
}
return null;
}
private static bool ShouldRelocateBasedOnAssessment(ValuableObject valuable, MotionStats? motion, out string reason)
{
if ((Object)(object)valuable.physGrabObject == (Object)null)
{
reason = "no PhysGrabObject";
return false;
}
EmbedmentAssessment embedmentAssessment = ComputeEmbedment(valuable);
float num = Mathf.Max(0.01f, ConfigEntries.StuckMinPenetrationDistance.Value);
float num2 = Mathf.Clamp(ConfigEntries.StuckWeakPenetrationDistance.Value, 0.005f, num);
bool flag = embedmentAssessment.MidpointInsideStatic || embedmentAssessment.StaticPenetrationMax >= num;
bool flag2 = embedmentAssessment.StaticPenetrationMax >= num2;
bool flag3 = false;
if (motion.HasValue)
{
float num3 = Mathf.Max(0.05f, ConfigEntries.MotionMaxSpeedToFlag.Value);
int num4 = Mathf.Clamp(ConfigEntries.MotionMinDirectionReversalsToFlag.Value, 0, 50);
flag3 = motion.Value.MaxSpeed >= num3 && motion.Value.DirectionReversals >= num4;
}
if (ConfigEntries.RequireStaticWorldEvidenceForRelocation.Value)
{
if (flag)
{
reason = $"static/world embedment detected ({embedmentAssessment})";
return true;
}
if (flag3 && flag2)
{
reason = $"weak static penetration + rapid jitter ({embedmentAssessment}; motion={motion})";
return true;
}
if (embedmentAssessment.MidpointInsideDynamic || embedmentAssessment.DynamicPenetrationMax >= num)
{
reason = $"dynamic-only embedment (likely inside another object), not relocating ({embedmentAssessment}; motion={motion})";
return false;
}
reason = $"no static/world embedment evidence ({embedmentAssessment}; motion={motion})";
return false;
}
if (flag)
{
reason = $"static/world embedment detected ({embedmentAssessment})";
return true;
}
if (flag3 && (embedmentAssessment.MidpointInsideDynamic || embedmentAssessment.DynamicPenetrationMax >= num || flag2))
{
reason = $"rapid jitter + penetration evidence ({embedmentAssessment}; motion={motion})";
return true;
}
reason = $"insufficient embedment evidence ({embedmentAssessment}; motion={motion})";
return false;
}
private static EmbedmentAssessment ComputeEmbedment(ValuableObject valuable)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_023a: Unknown result type (might be due to invalid IL or missing references)
//IL_023b: Unknown result type (might be due to invalid IL or missing references)
//IL_0134: Unknown result type (might be due to invalid IL or missing references)
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_014c: Unknown result type (might be due to invalid IL or missing references)
//IL_014e: Unknown result type (might be due to invalid IL or missing references)
//IL_0155: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
//IL_018d: Unknown result type (might be due to invalid IL or missing references)
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
//IL_019e: Unknown result type (might be due to invalid IL or missing references)
//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
//IL_01db: Unknown result type (might be due to invalid IL or missing references)
//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_020b: Unknown result type (might be due to invalid IL or missing references)
//IL_0217: Unknown result type (might be due to invalid IL or missing references)
//IL_021f: Unknown result type (might be due to invalid IL or missing references)
//IL_0224: Unknown result type (might be due to invalid IL or missing references)
//IL_0229: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_0266: Unknown result type (might be due to invalid IL or missing references)
//IL_0267: Unknown result type (might be due to invalid IL or missing references)
//IL_026e: Unknown result type (might be due to invalid IL or missing references)
//IL_0350: Unknown result type (might be due to invalid IL or missing references)
//IL_0355: Unknown result type (might be due to invalid IL or missing references)
//IL_0359: Unknown result type (might be due to invalid IL or missing references)
//IL_035b: Unknown result type (might be due to invalid IL or missing references)
//IL_0360: Unknown result type (might be due to invalid IL or missing references)
//IL_0362: Unknown result type (might be due to invalid IL or missing references)
//IL_0367: Unknown result type (might be due to invalid IL or missing references)
//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
//IL_03db: Unknown result type (might be due to invalid IL or missing references)
//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
PhysGrabObject physGrabObject = valuable.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null)
{
return new EmbedmentAssessment(0f, 0f, 0f, 0f, midpointInsideStatic: false, midpointInsideDynamic: false);
}
Vector3 val = physGrabObject.midPoint;
Vector3 val2 = physGrabObject.boundingBox * 0.5f;
val2 -= Vector3.one * 0.02f;
((Vector3)(ref val2))..ctor(Mathf.Max(0.02f, val2.x), Mathf.Max(0.02f, val2.y), Mathf.Max(0.02f, val2.z));
Collider[] componentsInChildren = ((Component)valuable).GetComponentsInChildren<Collider>(true);
if (componentsInChildren == null || componentsInChildren.Length == 0)
{
return new EmbedmentAssessment(0f, 0f, 0f, 0f, midpointInsideStatic: false, midpointInsideDynamic: false);
}
Bounds val3 = default(Bounds);
bool flag = false;
foreach (Collider val4 in componentsInChildren)
{
if (!((Object)(object)val4 == (Object)null))
{
if (!flag)
{
val3 = val4.bounds;
flag = true;
}
else
{
((Bounds)(ref val3)).Encapsulate(val4.bounds);
}
}
}
if (flag && ((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
{
val2 = ((Bounds)(ref val3)).extents;
val = ((Bounds)(ref val3)).center;
}
Vector3[] array;
if (flag)
{
Vector3 center = ((Bounds)(ref val3)).center;
Vector3 extents = ((Bounds)(ref val3)).extents;
array = (Vector3[])(object)new Vector3[7]
{
center,
center + new Vector3(extents.x, 0f, 0f),
center + new Vector3(0f - extents.x, 0f, 0f),
center + new Vector3(0f, extents.y, 0f),
center + new Vector3(0f, 0f - extents.y, 0f),
center + new Vector3(0f, 0f, extents.z),
center + new Vector3(0f, 0f, 0f - extents.z)
};
}
else
{
array = (Vector3[])(object)new Vector3[1] { val };
}
int num = LayerMask.NameToLayer("Triggers");
int num2 = LayerMask.NameToLayer("Player");
int num3 = LayerMask.NameToLayer("PlayerOnlyCollision");
Collider[] array2 = Physics.OverlapBox(val, val2, ((Component)valuable).transform.rotation, -1, (QueryTriggerInteraction)1);
float num4 = 0f;
float num5 = 0f;
float num6 = 0f;
float num7 = 0f;
bool midpointInsideStatic = false;
bool midpointInsideDynamic = false;
Vector3 val7 = default(Vector3);
float num9 = default(float);
foreach (Collider val5 in array2)
{
if ((Object)(object)val5 == (Object)null || val5.isTrigger)
{
continue;
}
int layer = ((Component)val5).gameObject.layer;
if (layer == num || layer == num2 || layer == num3 || (Object)(object)((Component)val5).transform == (Object)null || ((Component)val5).transform.IsChildOf(((Component)valuable).transform))
{
continue;
}
PhysGrabObject componentInParent = ((Component)val5).GetComponentInParent<PhysGrabObject>();
if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent == (Object)(object)physGrabObject)
{
continue;
}
bool flag2 = (Object)(object)val5.attachedRigidbody == (Object)null;
foreach (Vector3 val6 in array)
{
val7 = val5.ClosestPoint(val6) - val6;
if (((Vector3)(ref val7)).sqrMagnitude < 1E-06f)
{
if (flag2)
{
midpointInsideStatic = true;
}
else
{
midpointInsideDynamic = true;
}
break;
}
}
float num8 = 0f;
foreach (Collider val8 in componentsInChildren)
{
if (!((Object)(object)val8 == (Object)null) && !val8.isTrigger && Physics.ComputePenetration(val8, ((Component)val8).transform.position, ((Component)val8).transform.rotation, val5, ((Component)val5).transform.position, ((Component)val5).transform.rotation, ref val7, ref num9) && num9 > num8)
{
num8 = num9;
}
}
if (num8 <= 0f)
{
continue;
}
if (flag2)
{
num4 += num8;
if (num8 > num5)
{
num5 = num8;
}
}
else
{
num6 += num8;
if (num8 > num7)
{
num7 = num8;
}
}
}
return new EmbedmentAssessment(num4, num5, num6, num7, midpointInsideStatic, midpointInsideDynamic);
}
private static bool IsSlotClearOfWorldGeometry(SpawnSlotRegistry.Slot slot)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected I4, but got Unknown
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)AssetManager.instance == (Object)null)
{
return true;
}
Type type = slot.Type;
Mesh val = (Mesh)((int)type switch
{
0 => AssetManager.instance.valuableMeshTiny,
1 => AssetManager.instance.valuableMeshSmall,
2 => AssetManager.instance.valuableMeshMedium,
3 => AssetManager.instance.valuableMeshBig,
4 => AssetManager.instance.valuableMeshWide,
5 => AssetManager.instance.valuableMeshTall,
6 => AssetManager.instance.valuableMeshVeryTall,
_ => null,
});
if ((Object)(object)val == (Object)null)
{
return true;
}
Bounds bounds = val.bounds;
Vector3 size = ((Bounds)(ref bounds)).size;
return Physics.OverlapBox(slot.Position + slot.Rotation * Vector3.forward * (size.z / 2f) + slot.Rotation * Vector3.up * (size.y / 2f) + Vector3.up * 0.01f, size / 2f, slot.Rotation, LayerMask.GetMask(new string[1] { "Default" }), (QueryTriggerInteraction)1).Length == 0;
}
}
internal static class ValueDiagnostics
{
private struct Baseline
{
public float Original;
public float Current;
public string Name;
}
private static readonly Dictionary<int, Baseline> Baselines = new Dictionary<int, Baseline>();
private static bool baselineCaptured;
public static void Reset()
{
Baselines.Clear();
baselineCaptured = false;
}
public static void CaptureBaselineIfNeeded(ValuableObject[] valuables)
{
if (baselineCaptured)
{
return;
}
foreach (ValuableObject val in valuables)
{
if (!((Object)(object)val == (Object)null))
{
int stableId = GetStableId(val);
if (!Baselines.ContainsKey(stableId))
{
Baselines[stableId] = new Baseline
{
Original = val.dollarValueOriginal,
Current = val.dollarValueCurrent,
Name = ((Object)val).name
};
}
}
}
baselineCaptured = true;
(int, float, float) tuple = ComputeTotals(valuables);
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Baseline totals: current=${(int)tuple.Item2}, original=${(int)tuple.Item3}, count={tuple.Item1}");
}
public static (int Count, float TotalCurrent, float TotalOriginal, float TotalBaselineCurrent, float TotalLostFromBaseline) ComputeTotalsWithBaseline(ValuableObject[] valuables)
{
float num = 0f;
foreach (ValuableObject val in valuables)
{
if (!((Object)(object)val == (Object)null))
{
int stableId = GetStableId(val);
if (Baselines.TryGetValue(stableId, out var value))
{
num += value.Current;
}
}
}
(int, float, float) tuple = ComputeTotals(valuables);
float item = num - tuple.Item2;
return (tuple.Item1, tuple.Item2, tuple.Item3, num, item);
}
public static float? TryGetBaselineCurrent(ValuableObject valuable)
{
int stableId = GetStableId(valuable);
if (Baselines.TryGetValue(stableId, out var value))
{
return value.Current;
}
return null;
}
public static bool TryGetBaseline(ValuableObject valuable, out float baselineCurrent)
{
baselineCurrent = 0f;
int stableId = GetStableId(valuable);
if (Baselines.TryGetValue(stableId, out var value))
{
baselineCurrent = value.Current;
return true;
}
return false;
}
public static void EnsureBaselineEntry(ValuableObject valuable)
{
if (!((Object)(object)valuable == (Object)null))
{
int stableId = GetStableId(valuable);
if (!Baselines.ContainsKey(stableId))
{
Baselines[stableId] = new Baseline
{
Original = valuable.dollarValueOriginal,
Current = valuable.dollarValueCurrent,
Name = ((Object)valuable).name
};
}
}
}
public static float GetOrCaptureInitialValue(ValuableObject valuable)
{
EnsureBaselineEntry(valuable);
if (!TryGetBaseline(valuable, out var baselineCurrent))
{
return valuable.dollarValueCurrent;
}
return baselineCurrent;
}
public static float GetOrCaptureInitialUndamagedValue(ValuableObject valuable)
{
if ((Object)(object)valuable == (Object)null)
{
return 0f;
}
EnsureBaselineEntry(valuable);
int stableId = GetStableId(valuable);
if (Baselines.TryGetValue(stableId, out var value))
{
if (!(value.Original > 0f))
{
return value.Current;
}
return value.Original;
}
return valuable.dollarValueOriginal;
}
public static int GetId(ValuableObject valuable)
{
return GetStableId(valuable);
}
public static List<ValuableObject> GetValuablesWithLoss(ValuableObject[] valuables, float minLoss)
{
List<ValuableObject> list = new List<ValuableObject>();
if (valuables == null)
{
return list;
}
float num = Mathf.Max(0f, minLoss);
foreach (ValuableObject val in valuables)
{
if (!((Object)(object)val == (Object)null) && Baselines.TryGetValue(GetStableId(val), out var value))
{
float dollarValueCurrent = val.dollarValueCurrent;
if (value.Current - dollarValueCurrent >= num)
{
list.Add(val);
}
}
}
return list;
}
public static bool RestoreValueToBaseline(ValuableObject valuable)
{
if ((Object)(object)valuable == (Object)null)
{
return false;
}
if (!Baselines.TryGetValue(GetStableId(valuable), out var value))
{
return false;
}
float current = value.Current;
if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode == 1)
{
PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null)
{
component.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { current });
return true;
}
}
valuable.dollarValueOriginal = current;
valuable.dollarValueCurrent = current;
valuable.dollarValueSet = true;
return true;
}
public static void LogValueLosses(ValuableObject[] valuables)
{
foreach (ValuableObject val in valuables)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
float? num = TryGetBaselineCurrent(val);
if (num.HasValue)
{
float dollarValueCurrent = val.dollarValueCurrent;
if (dollarValueCurrent < num.Value)
{
ItemUnstuck.Logger.LogInfo((object)$"[ItemUnstuck] Value loss: '{((Object)val).name}' baseline=${(int)num.Value} now=${(int)dollarValueCurrent} lost=${(int)(num.Value - dollarValueCurrent)}");
}
}
}
}
private static (int Count, float TotalCurrent, float TotalOriginal) ComputeTotals(ValuableObject[] valuables)
{
float num = 0f;
float num2 = 0f;
int num3 = 0;
foreach (ValuableObject val in valuables)
{
if (!((Object)(object)val == (Object)null))
{
num3++;
num += val.dollarValueCurrent;
num2 += val.dollarValueOriginal;
}
}
return (num3, num, num2);
}
private static int GetStableId(ValuableObject valuable)
{
PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.ViewID != 0)
{
return component.ViewID;
}
return ((Object)valuable).GetInstanceID();
}
}
}
namespace ItemUnstuck.Patches
{
[HarmonyPatch(typeof(PhysGrabber), "StartGrabbingPhysObject")]
internal static class PhysGrabberStartGrabbingPhysObjectPatch
{
[HarmonyPostfix]
private static void Postfix(PhysGrabber __instance, RaycastHit hit, PhysGrabObject _physGrabObject)
{
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEntries.Enable.Value || !ConfigEntries.ShowInitialValueOnGrab.Value || (Object)(object)_physGrabObject == (Object)null || (SemiFunc.IsMultiplayer() && ((Object)(object)__instance == (Object)null || (!__instance.isLocal && ((Object)(object)__instance.photonView == (Object)null || !__instance.photonView.IsMine)))))
{
return;
}
ValuableObject val = ((Component)_physGrabObject).GetComponent<ValuableObject>() ?? ((Component)_physGrabObject).GetComponentInParent<ValuableObject>();
if (!((Object)(object)val == (Object)null))
{
float orCaptureInitialUndamagedValue = ValueDiagnostics.GetOrCaptureInitialUndamagedValue(val);
string text = ((Object)val).name ?? "Valuable";
if (text.EndsWith("(Clone)", StringComparison.Ordinal))
{
text = text.Replace("(Clone)", "", StringComparison.Ordinal);
}
Color val2 = (((Object)(object)AssetManager.instance != (Object)null) ? AssetManager.instance.colorYellow : Color.white);
SemiFunc.UIFocusText($"{text} initial: ${(int)orCaptureInitialUndamagedValue}", Color.white, val2, 2.5f);
}
}
}
[HarmonyPatch(typeof(PhysGrabObject))]
[HarmonyPatch("GrabPlayerAddRPC")]
internal static class PhysGrabObjectGrabPlayerAddRPCPatch
{
[HarmonyPostfix]
private static void GrabPlayerAddRPC_Postfix(PhysGrabObject __instance)
{
if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value)
{
InvulnerabilityController.Clear(__instance);
}
}
}
[HarmonyPatch(typeof(PhysGrabObject))]
[HarmonyPatch("GrabStarted")]
internal static class PhysGrabObjectGrabStartedPatch
{
[HarmonyPostfix]
private static void GrabStarted_Postfix(PhysGrabObject __instance)
{
if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value && (Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode == 0)
{
InvulnerabilityController.Clear(__instance);
}
}
}
[HarmonyPatch(typeof(PhysGrabObject))]
[HarmonyPatch("GrabStartedRPC")]
internal static class PhysGrabObjectGrabStartedRPCPatch
{
[HarmonyPostfix]
private static void GrabStartedRPC_Postfix(PhysGrabObject __instance)
{
if (ConfigEntries.Enable.Value && ConfigEntries.InvulnerableUntilGrab.Value)
{
InvulnerabilityController.Clear(__instance);
}
}
}
[HarmonyPatch(typeof(PhysGrabObjectImpactDetector))]
[HarmonyPatch("Update")]
internal static class PhysGrabObjectImpactDetectorUpdateZeroDeltaPatch
{
[HarmonyPrefix]
private static bool Update_Prefix()
{
if (!ConfigEntries.Enable.Value)
{
return true;
}
return Time.deltaTime > 0f;
}
}
[HarmonyPatch(typeof(ValuableDirector), "VolumesAndSwitchSetupRPC")]
internal static class ValuableDirectorVolumesAndSwitchSetupPatch
{
[HarmonyPrefix]
private static void Prefix(PhotonMessageInfo _info)
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
if (ConfigEntries.Enable.Value && SemiFunc.MasterOnlyRPC(_info))
{
SpawnSlotRegistry.Clear();
ValueDiagnostics.Reset();
}
}
[HarmonyPostfix]
private static void Postfix(PhotonMessageInfo _info)
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
if (ConfigEntries.Enable.Value && SemiFunc.MasterOnlyRPC(_info))
{
ItemUnstuck.Instance?.NotifyValuableVolumesSetupComplete();
}
}
}
[HarmonyPatch(typeof(ValuableObject))]
[HarmonyPatch("Start")]
internal static class ValuableObjectStartInvincibilityPatch
{
private static readonly FieldInfo? IndestructibleSpawnTimerField = typeof(PhysGrabObjectImpactDetector).GetField("indestructibleSpawnTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
[HarmonyPostfix]
private static void Start_Postfix(ValuableObject __instance)
{
if (!ConfigEntries.Enable.Value)
{
return;
}
PhysGrabObject physGrabObject = __instance.physGrabObject;
if ((Object)(object)physGrabObject == (Object)null)
{
return;
}
if (ConfigEntries.InvulnerableUntilGrab.Value)
{
InvulnerabilityController.ApplyUntilGrab(physGrabObject);
return;
}
float value = ConfigEntries.InvincibilitySeconds.Value;
if (value <= 0f)
{
return;
}
PhysGrabObjectImpactDetector component = ((Component)physGrabObject).GetComponent<PhysGrabObjectImpactDetector>();
if (!((Object)(object)component == (Object)null))
{
if (IndestructibleSpawnTimerField == null)
{
ItemUnstuck.Logger.LogError((object)"Could not find PhysGrabObjectImpactDetector.indestructibleSpawnTimer; invincibility not applied.");
}
else
{
IndestructibleSpawnTimerField.SetValue(component, value);
}
}
}
}
[HarmonyPatch(typeof(ValuableVolume))]
[HarmonyPatch("Setup")]
internal static class ValuableVolumeSetupPatch
{
[HarmonyPrefix]
private static void Setup_Prefix(ValuableVolume __instance)
{
if (ConfigEntries.Enable.Value)
{
SpawnSlotRegistry.Register(__instance);
}
}
}
}