Decompiled source of ShrinkCart v0.2.45

BepInEx/plugins/ShrinkCart/ShrinkCart.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ScalerCore;
using ScalerCore.Handlers;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ShrinkCart;

internal static class Authority
{
	private const float CheckIntervalSeconds = 0.25f;

	private static bool _cachedIsHostOrSingleplayer = true;

	private static float _nextCheckTime;

	internal static bool IsHostOrSingleplayer()
	{
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime < _nextCheckTime)
		{
			return _cachedIsHostOrSingleplayer;
		}
		_nextCheckTime = unscaledTime + 0.25f;
		try
		{
			_cachedIsHostOrSingleplayer = SemiFunc.IsMasterClientOrSingleplayer();
		}
		catch
		{
			_cachedIsHostOrSingleplayer = true;
		}
		return _cachedIsHostOrSingleplayer;
	}

	internal static void Reset()
	{
		_nextCheckTime = 0f;
		_cachedIsHostOrSingleplayer = true;
	}
}
internal static class CartObjectGuard
{
	private static readonly HashSet<int> CartLikeObjectIds = new HashSet<int>();

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

	private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	internal static bool IsCartLike(PhysGrabObject item)
	{
		if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)((Component)item).gameObject).GetInstanceID();
		if (CartLikeObjectIds.Contains(instanceID))
		{
			return true;
		}
		if (NonCartLikeObjectIds.Contains(instanceID))
		{
			return false;
		}
		if (HasCartLikeComponent(item) || HasCartLikeItemType(item))
		{
			CartLikeObjectIds.Add(instanceID);
			return true;
		}
		NonCartLikeObjectIds.Add(instanceID);
		return false;
	}

	internal static void Reset()
	{
		CartLikeObjectIds.Clear();
		NonCartLikeObjectIds.Clear();
	}

	internal static bool ShouldBlockCartInCart(PhysGrabInCart destination, PhysGrabObject item)
	{
		if ((Object)(object)destination == (Object)null || (Object)(object)destination.cart == (Object)null || (Object)(object)item == (Object)null)
		{
			return false;
		}
		if (!IsCartLike(item))
		{
			return false;
		}
		return true;
	}

	private static bool HasCartLikeComponent(PhysGrabObject item)
	{
		if (!((Object)(object)((Component)item).GetComponent<PhysGrabCart>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<PhysGrabCart>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<PhysGrabCart>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemVehicle>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemVehicle>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemVehicle>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartCannon>() != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartCannonMain>() != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartLaser>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartCannon>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartCannonMain>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartLaser>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemCartCannon>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemCartCannonMain>(true) != (Object)null))
		{
			return (Object)(object)((Component)item).GetComponentInChildren<ItemCartLaser>(true) != (Object)null;
		}
		return true;
	}

	private static bool HasCartLikeItemType(PhysGrabObject item)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Invalid comparison between Unknown and I4
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Invalid comparison between Unknown and I4
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Invalid comparison between Unknown and I4
		ItemAttributes component = ((Component)item).GetComponent<ItemAttributes>();
		if ((Object)(object)component == (Object)null || ItemAttributesItemTypeField == null)
		{
			return false;
		}
		if (!(ItemAttributesItemTypeField.GetValue(component) is itemType val))
		{
			return false;
		}
		if ((int)val != 2 && (int)val != 14)
		{
			return (int)val == 12;
		}
		return true;
	}
}
internal static class CartRegistry
{
	private sealed class CartState
	{
		internal PhysGrabCart Cart;
	}

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

	private static readonly List<int> RemoveCartIds = new List<int>(8);

	private static readonly FieldInfo PhysGrabCartItemsInCartField = typeof(PhysGrabCart).GetField("itemsInCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PhysGrabCartItemsInCartCountField = typeof(PhysGrabCart).GetField("itemsInCartCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PhysGrabCartHaulCurrentField = typeof(PhysGrabCart).GetField("haulCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo ValuableObjectDollarValueCurrentField = typeof(ValuableObject).GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	internal static void RegisterCart(PhysGrabCart cart)
	{
		if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null))
		{
			Carts[((Object)cart).GetInstanceID()] = new CartState
			{
				Cart = cart
			};
			DebugLog("Registered cart content guard target: " + ((Object)cart).name);
		}
	}

	internal static void RegisterExistingCarts()
	{
		if (IsHostOrSingleplayer())
		{
			PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
			for (int i = 0; i < array.Length; i++)
			{
				RegisterCart(array[i]);
			}
		}
	}

	internal static void CleanCartContents(PhysGrabCart cart)
	{
		if (IsHostOrSingleplayer())
		{
			CartState orCreateState = GetOrCreateState(cart);
			List<PhysGrabObject> itemsInCart = GetItemsInCart(orCreateState?.Cart);
			if (orCreateState != null && !((Object)(object)orCreateState.Cart == (Object)null) && itemsInCart != null)
			{
				RemoveBlockedItems(orCreateState, itemsInCart);
				DeadHeadCartScaleController.MarkObjectsSeenInCart(orCreateState.Cart, itemsInCart);
				ShrinkerCartController.MarkObjectsSeenInCart(orCreateState.Cart, itemsInCart);
			}
		}
	}

	internal static void Reset()
	{
		Carts.Clear();
		RemoveCartIds.Clear();
		CartObjectGuard.Reset();
	}

	internal static void HandleBlockedCartInCart(PhysGrabInCart destination, PhysGrabObject item)
	{
		if ((Object)(object)destination == (Object)null || (Object)(object)destination.cart == (Object)null || (Object)(object)item == (Object)null)
		{
			return;
		}
		CartState orCreateState = GetOrCreateState(destination.cart);
		if (orCreateState != null)
		{
			List<PhysGrabObject> itemsInCart = GetItemsInCart(orCreateState.Cart);
			if (itemsInCart != null)
			{
				RemoveBlockedItems(orCreateState, itemsInCart);
			}
			DebugLog("Blocked cart-like object from cart Add: " + ((Object)item).name);
		}
	}

	private static CartState GetOrCreateState(PhysGrabCart cart)
	{
		if ((Object)(object)cart == (Object)null)
		{
			return null;
		}
		if (!Carts.TryGetValue(((Object)cart).GetInstanceID(), out var value) || value == null)
		{
			RegisterCart(cart);
			Carts.TryGetValue(((Object)cart).GetInstanceID(), out value);
		}
		return value;
	}

	private static void PruneInvalidCarts()
	{
		RemoveCartIds.Clear();
		foreach (KeyValuePair<int, CartState> cart in Carts)
		{
			if (cart.Value == null || (Object)(object)cart.Value.Cart == (Object)null)
			{
				RemoveCartIds.Add(cart.Key);
			}
		}
		for (int i = 0; i < RemoveCartIds.Count; i++)
		{
			Carts.Remove(RemoveCartIds[i]);
		}
		RemoveCartIds.Clear();
	}

	private static void RemoveBlockedItems(CartState state, List<PhysGrabObject> items)
	{
		if (state == null || (Object)(object)state.Cart == (Object)null || items == null)
		{
			return;
		}
		bool flag = false;
		for (int num = items.Count - 1; num >= 0; num--)
		{
			PhysGrabObject val = items[num];
			if ((Object)(object)val == (Object)null)
			{
				items.RemoveAt(num);
				flag = true;
			}
			else if (CartObjectGuard.IsCartLike(val))
			{
				items.RemoveAt(num);
				flag = true;
			}
		}
		if (flag)
		{
			RecalculateCartCounts(state.Cart);
			DebugLog("Removed blocked or compatibility object from cart contents: " + ((Object)state.Cart).name);
		}
	}

	private static void RecalculateCartCounts(PhysGrabCart cart)
	{
		List<PhysGrabObject> itemsInCart = GetItemsInCart(cart);
		if ((Object)(object)cart == (Object)null || itemsInCart == null)
		{
			return;
		}
		int num = 0;
		int num2 = 0;
		for (int i = 0; i < itemsInCart.Count; i++)
		{
			PhysGrabObject val = itemsInCart[i];
			if (!((Object)(object)val == (Object)null))
			{
				num++;
				ValuableObject val2 = ((Component)val).GetComponent<ValuableObject>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = ((Component)val).GetComponentInParent<ValuableObject>();
				}
				if ((Object)(object)val2 != (Object)null)
				{
					num2 += GetValuableDollarValueCurrent(val2);
				}
			}
		}
		if (PhysGrabCartItemsInCartCountField != null)
		{
			PhysGrabCartItemsInCartCountField.SetValue(cart, num);
		}
		if (PhysGrabCartHaulCurrentField != null)
		{
			PhysGrabCartHaulCurrentField.SetValue(cart, num2);
		}
		if ((Object)(object)cart.valueScreen != (Object)null)
		{
			cart.valueScreen.UpdateValue(num2);
		}
	}

	private static List<PhysGrabObject> GetItemsInCart(PhysGrabCart cart)
	{
		if ((Object)(object)cart == (Object)null || PhysGrabCartItemsInCartField == null)
		{
			return null;
		}
		return PhysGrabCartItemsInCartField.GetValue(cart) as List<PhysGrabObject>;
	}

	private static int GetValuableDollarValueCurrent(ValuableObject valuable)
	{
		if ((Object)(object)valuable == (Object)null || ValuableObjectDollarValueCurrentField == null)
		{
			return 0;
		}
		object value = ValuableObjectDollarValueCurrentField.GetValue(valuable);
		if (value is float)
		{
			return (int)(float)value;
		}
		if (value is int)
		{
			return (int)value;
		}
		return 0;
	}

	private static void DebugLog(string message)
	{
		if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)message);
		}
	}

	private static bool IsHostOrSingleplayer()
	{
		return Authority.IsHostOrSingleplayer();
	}
}
internal static class DeadHeadCartScaleController
{
	private sealed class TrackedHead
	{
		internal GameObject Target;

		internal PlayerDeathHead Head;

		internal float RestoreCheckDueTime;

		internal int LastSeenCartId;
	}

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

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

	private static readonly List<int> RestoreIds = new List<int>(8);

	private static readonly List<GameObject> RestoreTargets = new List<GameObject>(8);

	private static readonly FieldInfo PlayerDeathHeadPhysGrabObjectField = typeof(PlayerDeathHead).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerDeathHeadTriggeredField = typeof(PlayerDeathHead).GetField("triggered", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerDeathHeadInExtractionPointField = typeof(PlayerDeathHead).GetField("inExtractionPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerDeathHeadInTruckField = typeof(PlayerDeathHead).GetField("inTruck", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static float _nextRestoreCheckTime = float.PositiveInfinity;

	private static bool _allowDeadHeadsOwned;

	private static bool _previousAllowDeadHeadsValue;

	internal static void Reset()
	{
		TrackedHeads.Clear();
		NonHeadObjectIds.Clear();
		RestoreIds.Clear();
		RestoreTargets.Clear();
		_nextRestoreCheckTime = float.PositiveInfinity;
		SetAllowDeadHeads(enabled: false);
	}

	internal static void Tick()
	{
		bool flag = Authority.IsHostOrSingleplayer() && ModConfig.DeadHeadScalingEnabledValue();
		SetAllowDeadHeads(flag);
		if (!Authority.IsHostOrSingleplayer())
		{
			return;
		}
		if (!flag)
		{
			RestoreAll();
			return;
		}
		float time = Time.time;
		if (TrackedHeads.Count == 0 || time < _nextRestoreCheckTime)
		{
			return;
		}
		RestoreIds.Clear();
		float num = float.PositiveInfinity;
		foreach (KeyValuePair<int, TrackedHead> trackedHead in TrackedHeads)
		{
			TrackedHead value = trackedHead.Value;
			if (value == null || (Object)(object)value.Target == (Object)null || (Object)(object)value.Head == (Object)null || ShouldRestoreImmediately(value.Head) || time >= value.RestoreCheckDueTime)
			{
				RestoreIds.Add(trackedHead.Key);
			}
			else if (value.RestoreCheckDueTime < num)
			{
				num = value.RestoreCheckDueTime;
			}
		}
		for (int i = 0; i < RestoreIds.Count; i++)
		{
			int num2 = RestoreIds[i];
			if (TrackedHeads.TryGetValue(num2, out var value2))
			{
				RestoreTrackedHead(num2, value2, "leave/debounce");
				TrackedHeads.Remove(num2);
			}
		}
		RestoreIds.Clear();
		_nextRestoreCheckTime = ((TrackedHeads.Count == 0) ? float.PositiveInfinity : num);
	}

	internal static void MarkObjectsSeenInCart(PhysGrabCart cart, List<PhysGrabObject> items)
	{
		//IL_00d4: 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_010f: 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)
		if (!Authority.IsHostOrSingleplayer() || !ModConfig.DeadHeadScalingEnabledValue() || items == null)
		{
			return;
		}
		SetAllowDeadHeads(enabled: true);
		float time = Time.time;
		int num = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0);
		for (int i = 0; i < items.Count; i++)
		{
			PhysGrabObject val = items[i];
			if (!TryGetTriggeredDeathHead(val, out var head))
			{
				continue;
			}
			GameObject gameObject = ((Component)val).gameObject;
			int instanceID = ((Object)gameObject).GetInstanceID();
			if (TrackedHeads.TryGetValue(instanceID, out var value))
			{
				value.RestoreCheckDueTime = time + ModConfig.SafeCartLeaveDebounceSeconds();
				value.LastSeenCartId = num;
				value.Head = head;
				ScheduleRestoreCheck(value.RestoreCheckDueTime);
				continue;
			}
			if (ScaleManager.IsScaled(gameObject))
			{
				if (!TryTrackExistingHeadScale(instanceID, gameObject, head, num, time))
				{
					DebugLog("Skipped death head cart shrink because another scale session is active: " + ((Object)gameObject).name);
				}
				continue;
			}
			ScaleOptions val2 = ScaleOptions.Default;
			val2.Factor = ModConfig.SafeDeadHeadScaleFactor();
			val2.Speed = ModConfig.SafeScaleSpeed();
			val2.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
			val2.Duration = 0f;
			val2.AllowedTargets = (ScaleTargets)15;
			val2.SuppressValueDropExpand = true;
			val2.PreserveMass = ModConfig.ShouldPreserveMass();
			val2.SuppressImpactFlash = true;
			val2.SuppressCameraShake = true;
			val2.RejectExternalApply = false;
			try
			{
				if (!ScaleManager.ApplyIfNotScaled(gameObject, val2))
				{
					DebugLog("ScalerCore rejected death head cart shrink: " + ((Object)gameObject).name);
					continue;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to shrink death head in cart: " + ex.Message));
				continue;
			}
			float num2 = time + ModConfig.SafeCartLeaveDebounceSeconds();
			TrackedHeads[instanceID] = new TrackedHead
			{
				Target = gameObject,
				Head = head,
				RestoreCheckDueTime = num2,
				LastSeenCartId = num
			};
			ScheduleRestoreCheck(num2);
			DebugLog("Shrunk death head in cart: " + ((Object)gameObject).name + " factor=" + ModConfig.SafeDeadHeadScaleFactor().ToString("0.###"));
		}
	}

	internal static void RestoreBeforeRevive(PlayerDeathHead head)
	{
		if ((Object)(object)head == (Object)null)
		{
			return;
		}
		PhysGrabObject headPhysGrabObject = GetHeadPhysGrabObject(head);
		GameObject val = (((Object)(object)headPhysGrabObject == (Object)null) ? null : ((Component)headPhysGrabObject).gameObject);
		if (!((Object)(object)val == (Object)null))
		{
			int instanceID = ((Object)val).GetInstanceID();
			if (TrackedHeads.TryGetValue(instanceID, out var value))
			{
				RestoreTrackedHead(instanceID, value, "before revive");
				TrackedHeads.Remove(instanceID);
				_nextRestoreCheckTime = 0f;
			}
		}
	}

	internal static void RestoreAll()
	{
		if (TrackedHeads.Count == 0)
		{
			return;
		}
		RestoreTargets.Clear();
		foreach (TrackedHead value in TrackedHeads.Values)
		{
			if (value != null && (Object)(object)value.Target != (Object)null)
			{
				RestoreTargets.Add(value.Target);
			}
		}
		TrackedHeads.Clear();
		_nextRestoreCheckTime = float.PositiveInfinity;
		for (int i = 0; i < RestoreTargets.Count; i++)
		{
			RestoreTarget(RestoreTargets[i], "restore all");
		}
		RestoreTargets.Clear();
	}

	private static bool TryTrackExistingHeadScale(int id, GameObject target, PlayerDeathHead head, int cartId, float now)
	{
		//IL_001b: 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_0021: Unknown result type (might be due to invalid IL or missing references)
		ScaleController controller = ScaleManager.GetController(target);
		if ((Object)(object)controller == (Object)null || !controller.IsScaled)
		{
			return false;
		}
		ScaleOptions currentOptions = controller.CurrentOptions;
		if (!LooksLikeShrinkCartHeadOptions(currentOptions))
		{
			return false;
		}
		float num = now + ModConfig.SafeCartLeaveDebounceSeconds();
		TrackedHeads[id] = new TrackedHead
		{
			Target = target,
			Head = head,
			RestoreCheckDueTime = num,
			LastSeenCartId = cartId
		};
		ScheduleRestoreCheck(num);
		DebugLog("Adopted existing ShrinkCart-like death head scale: " + ((Object)target).name);
		return true;
	}

	private static bool LooksLikeShrinkCartHeadOptions(ScaleOptions options)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Invalid comparison between Unknown and I4
		if (Mathf.Approximately(options.Factor, ModConfig.SafeDeadHeadScaleFactor()) && (int)options.AllowedTargets == 15 && options.SuppressImpactFlash && options.SuppressCameraShake && options.SuppressValueDropExpand && options.PreserveMass == ModConfig.ShouldPreserveMass())
		{
			return !options.RejectExternalApply;
		}
		return false;
	}

	private static void RestoreTrackedHead(int id, TrackedHead tracked, string reason)
	{
		if (tracked != null)
		{
			RestoreTarget(tracked.Target, reason);
		}
	}

	private static void RestoreTarget(GameObject target, string reason)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target == (Object)null)
		{
			return;
		}
		try
		{
			if (ScaleManager.IsScaled(target))
			{
				ScaleController controller = ScaleManager.GetController(target);
				if ((Object)(object)controller != (Object)null && controller.IsScaled)
				{
					ScaleOptions currentOptions = controller.CurrentOptions;
					currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
					currentOptions.SuppressImpactFlash = true;
					currentOptions.SuppressCameraShake = true;
					ScaleManager.ForceUpdateOptions(target, currentOptions);
				}
				ScaleManager.ForceRestore(target);
				DebugLog("Restored death head " + ((Object)target).name + " reason=" + reason);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to restore death head from cart scale: " + ex.Message));
		}
	}

	private static bool TryGetTriggeredDeathHead(PhysGrabObject item, out PlayerDeathHead head)
	{
		head = null;
		if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)((Component)item).gameObject).GetInstanceID();
		if (NonHeadObjectIds.Contains(instanceID))
		{
			return false;
		}
		head = ((Component)item).GetComponent<PlayerDeathHead>();
		if ((Object)(object)head == (Object)null)
		{
			head = ((Component)item).GetComponentInParent<PlayerDeathHead>();
		}
		if ((Object)(object)head == (Object)null)
		{
			head = ((Component)item).GetComponentInChildren<PlayerDeathHead>(true);
		}
		if ((Object)(object)head == (Object)null)
		{
			NonHeadObjectIds.Add(instanceID);
			head = null;
			return false;
		}
		if (!IsHeadTriggered(head) || (Object)(object)GetHeadPhysGrabObject(head) != (Object)(object)item)
		{
			head = null;
			return false;
		}
		return true;
	}

	private static bool ShouldRestoreImmediately(PlayerDeathHead head)
	{
		if (!((Object)(object)head == (Object)null) && IsHeadTriggered(head) && !IsHeadInExtractionPoint(head))
		{
			return IsHeadInTruck(head);
		}
		return true;
	}

	private static PhysGrabObject GetHeadPhysGrabObject(PlayerDeathHead head)
	{
		if (!((Object)(object)head == (Object)null) && !(PlayerDeathHeadPhysGrabObjectField == null))
		{
			object? value = PlayerDeathHeadPhysGrabObjectField.GetValue(head);
			return (PhysGrabObject)((value is PhysGrabObject) ? value : null);
		}
		return null;
	}

	private static bool IsHeadTriggered(PlayerDeathHead head)
	{
		return GetBoolField(head, PlayerDeathHeadTriggeredField);
	}

	private static bool IsHeadInExtractionPoint(PlayerDeathHead head)
	{
		return GetBoolField(head, PlayerDeathHeadInExtractionPointField);
	}

	private static bool IsHeadInTruck(PlayerDeathHead head)
	{
		return GetBoolField(head, PlayerDeathHeadInTruckField);
	}

	private static bool GetBoolField(PlayerDeathHead head, FieldInfo field)
	{
		if ((Object)(object)head == (Object)null || field == null)
		{
			return false;
		}
		object value = field.GetValue(head);
		if (value is bool)
		{
			return (bool)value;
		}
		return false;
	}

	private static void ScheduleRestoreCheck(float dueTime)
	{
		if (dueTime < _nextRestoreCheckTime)
		{
			_nextRestoreCheckTime = dueTime;
		}
	}

	private static void SetAllowDeadHeads(bool enabled)
	{
		try
		{
			if (enabled)
			{
				if (!_allowDeadHeadsOwned)
				{
					_previousAllowDeadHeadsValue = ScaleManager.AllowDeadHeads;
					_allowDeadHeadsOwned = true;
				}
				if (!ScaleManager.AllowDeadHeads)
				{
					ScaleManager.AllowDeadHeads = true;
				}
			}
			else if (_allowDeadHeadsOwned)
			{
				ScaleManager.AllowDeadHeads = _previousAllowDeadHeadsValue;
				_allowDeadHeadsOwned = false;
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to update ScalerCore dead head scaling permission: " + ex.Message));
		}
	}

	private static void DebugLog(string message)
	{
		if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)message);
		}
	}
}
internal static class EnemyInCartKillController
{
	private static readonly HashSet<int> ExecutedEnemies = new HashSet<int>();

	private static readonly FieldInfo EnemyHealthField = typeof(Enemy).GetField("Health", BindingFlags.Instance | BindingFlags.NonPublic);

	private static readonly FieldInfo EnemyHealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.NonPublic);

	internal static void Reset()
	{
		ExecutedEnemies.Clear();
	}

	internal static bool TryKill(PhysGrabObject item)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		if (!ModConfig.EnemyInCartInstantKill.Value || (Object)(object)item == (Object)null)
		{
			return false;
		}
		Enemy val = FindEnemy(item);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)val).GetInstanceID();
		if (ExecutedEnemies.Contains(instanceID))
		{
			return true;
		}
		EnemyHealth health = GetHealth(val);
		if ((Object)(object)health == (Object)null)
		{
			return false;
		}
		int currentHealth = GetCurrentHealth(health);
		if (currentHealth <= 0)
		{
			ExecutedEnemies.Add(instanceID);
			return true;
		}
		ExecutedEnemies.Add(instanceID);
		try
		{
			int num = Math.Max(currentHealth, health.health) + 1;
			health.Hurt(num, Vector3.up);
			DebugLog("Instant killed enemy entering cart: " + ((Object)val).name);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to instant kill enemy entering cart: " + ex.Message));
			ExecutedEnemies.Remove(instanceID);
			return false;
		}
		return true;
	}

	private static Enemy FindEnemy(PhysGrabObject item)
	{
		EnemyRigidbody componentInParent = ((Component)item).GetComponentInParent<EnemyRigidbody>();
		if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.enemy != (Object)null)
		{
			return componentInParent.enemy;
		}
		return ((Component)item).GetComponentInParent<Enemy>();
	}

	private static EnemyHealth GetHealth(Enemy enemy)
	{
		if (EnemyHealthField == null)
		{
			return null;
		}
		object? value = EnemyHealthField.GetValue(enemy);
		return (EnemyHealth)((value is EnemyHealth) ? value : null);
	}

	private static int GetCurrentHealth(EnemyHealth health)
	{
		if (EnemyHealthCurrentField == null)
		{
			return health.health;
		}
		object value = EnemyHealthCurrentField.GetValue(health);
		if (value is int)
		{
			return (int)value;
		}
		return health.health;
	}

	private static void DebugLog(string message)
	{
		if (ModConfig.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)message);
		}
	}
}
internal enum ShrinkCategory
{
	Tiny,
	Small,
	Medium,
	Big,
	Wide,
	Tall,
	VeryTall,
	EnemyOrbSmall,
	EnemyOrbMedium,
	EnemyOrbBig,
	EnemyOrbBerserker,
	Surplus,
	ValuableBox,
	Fallback
}
internal static class ModConfig
{
	private sealed class LocalizedDefinition
	{
		internal readonly string Id;

		internal readonly string ChineseSection;

		internal readonly string EnglishSection;

		internal readonly string ChineseKey;

		internal readonly string EnglishKey;

		internal LocalizedDefinition(string id, string chineseSection, string englishSection, string chineseKey, string englishKey)
		{
			Id = id;
			ChineseSection = chineseSection;
			EnglishSection = englishSection;
			ChineseKey = chineseKey;
			EnglishKey = englishKey;
		}
	}

	private const string ChineseLanguage = "中文";

	private const string EnglishLanguage = "English";

	private const string LanguageSection = "语言 / Language";

	private const string LanguageKey = "配置语言 / Config Language";

	private const string MassDefaultMigrationMarkerName = "ShrinkCart.v0.2.39.mass-default-enabled";

	internal static ConfigEntry<bool> CartShrinkingEnabled;

	internal static ConfigEntry<float> CartScaleSpeed;

	internal static ConfigEntry<float> RestoreScaleSpeed;

	internal static ConfigEntry<float> CartLeaveDebounceSeconds;

	internal static ConfigEntry<float> ReshrinkCooldownSeconds;

	internal static ConfigEntry<bool> ScaleMassWithSize;

	internal static ConfigEntry<bool> ShrinkShopPlayerItems;

	internal static ConfigEntry<bool> PlayerScalingModuleEnabled;

	internal static ConfigEntry<float> PlayerCartScaleFactor;

	internal static ConfigEntry<float> PlayerCartGrowFactor;

	internal static ConfigEntry<float> PlayerCartStandTriggerSeconds;

	internal static ConfigEntry<float> PlayerCartExitGraceSeconds;

	internal static ConfigEntry<float> PlayerCartDetectionIntervalSeconds;

	internal static ConfigEntry<bool> RestorePlayerOnDamage;

	internal static ConfigEntry<bool> SuppressValuableDamageRestore;

	internal static ConfigEntry<bool> DeadHeadScalingEnabled;

	internal static ConfigEntry<float> DeadHeadScaleFactor;

	internal static ConfigEntry<bool> TinyEnabled;

	internal static ConfigEntry<float> TinyScaleFactor;

	internal static ConfigEntry<bool> SmallEnabled;

	internal static ConfigEntry<float> SmallScaleFactor;

	internal static ConfigEntry<bool> MediumEnabled;

	internal static ConfigEntry<float> MediumScaleFactor;

	internal static ConfigEntry<bool> BigEnabled;

	internal static ConfigEntry<float> BigScaleFactor;

	internal static ConfigEntry<bool> WideEnabled;

	internal static ConfigEntry<float> WideScaleFactor;

	internal static ConfigEntry<bool> TallEnabled;

	internal static ConfigEntry<float> TallScaleFactor;

	internal static ConfigEntry<bool> VeryTallEnabled;

	internal static ConfigEntry<float> VeryTallScaleFactor;

	internal static ConfigEntry<bool> EnemyOrbEnabled;

	internal static ConfigEntry<float> EnemyOrbSmallScaleFactor;

	internal static ConfigEntry<float> EnemyOrbMediumScaleFactor;

	internal static ConfigEntry<float> EnemyOrbBigScaleFactor;

	internal static ConfigEntry<float> EnemyOrbBerserkerScaleFactor;

	internal static ConfigEntry<bool> SurplusEnabled;

	internal static ConfigEntry<float> SurplusScaleFactor;

	internal static ConfigEntry<bool> ValuableBoxEnabled;

	internal static ConfigEntry<float> ValuableBoxScaleFactor;

	internal static ConfigEntry<float> FallbackScaleFactor;

	internal static ConfigEntry<bool> EnemyInCartInstantKill;

	internal static ConfigEntry<bool> DynamicItemScanEnabled;

	internal static ConfigEntry<float> MinimumItemScanIntervalSeconds;

	internal static ConfigEntry<float> MaximumItemScanIntervalSeconds;

	internal static ConfigEntry<bool> DebugLogging;

	internal static ConfigEntry<string> ConfigLanguage;

	internal static int ScalingConfigVersion;

	private static bool _useEnglish;

	private static ConfigFile _boundConfig;

	private static readonly List<LocalizedDefinition> ActiveDefinitions = new List<LocalizedDefinition>();

	private static readonly PropertyInfo ConfigFileOrphanedEntriesProperty = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	internal static void Bind(ConfigFile config)
	{
		//IL_0aa5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0aaf: Expected O, but got Unknown
		_boundConfig = config;
		ActiveDefinitions.Clear();
		_useEnglish = IsEnglishLanguage(DetectConfiguredLanguage(config));
		CartShrinkingEnabled = Bind(config, "CartShrinkingEnabled", "购物车", "Cart", "启用购物车缩小", "Enable cart shrinking", defaultValue: true, Text("启用后,放进 C.A.R.T / 购物车的支持物品会自动缩小,取出后恢复。", "When enabled, supported objects placed into a C.A.R.T. / shopping cart shrink automatically and restore after leaving."));
		CartScaleSpeed = Bind(config, "CartScaleSpeed", "购物车", "Cart", "放入时缩小速度", "Shrink speed", 0.5f, Ranged(Text("ScalerCore 缩小动画速度。数值越大越快。", "ScalerCore shrink animation speed. Higher values are faster."), 0.1f, 20f));
		RestoreScaleSpeed = Bind(config, "RestoreScaleSpeed", "购物车", "Cart", "取出后放大速度", "Restore speed", 0.2f, Ranged(Text("正常取出购物车后的 ScalerCore 放大动画速度。数值越小越慢。", "ScalerCore restore animation speed after leaving the cart. Lower values are slower."), 0.1f, 20f));
		CartLeaveDebounceSeconds = Bind(config, "CartLeaveDebounceSeconds", "购物车", "Cart", "离车防抖延迟", "Leave-cart debounce", 0.5f, Ranged(Text("物品触发缩小后,离开购物车检测范围多久才开始恢复原尺寸。调高可减少车边缘抽搐。", "How long an item must remain outside the cart before restoring. Higher values reduce edge jitter."), 0.1f, 10f));
		ReshrinkCooldownSeconds = Bind(config, "ReshrinkCooldownSeconds", "购物车", "Cart", "恢复后重新缩小冷却", "Reshrink cooldown", LegacyFloat(config, 0.5f, "购物车", "取出后恢复延迟", "Cart", "RestoreGraceSeconds"), Ranged(Text("物品开始恢复后,等待多少秒才允许再次被购物车缩小。", "How many seconds to wait after restore starts before the cart may shrink the item again."), 0.05f, 10f));
		ScaleMassWithSize = Bind(config, "ScaleMassWithSize", "购物车", "Cart", "启用重量随缩放降低", "Scale mass with size", defaultValue: true, Text("启用后通过 ScalerCore 的 PreserveMass 选项允许支持对象按缩放倍率降低质量;关闭则只改变视觉尺寸并保持原始重量。", "When enabled, ShrinkCart passes ScalerCore options that allow supported objects to reduce mass with scale. When disabled, only visual size changes and original mass is preserved."));
		ShrinkShopPlayerItems = Bind(config, "ShrinkShopPlayerItems", "购物车", "Cart", "启用商店用品缩小", "Enable shop item shrinking", defaultValue: false, Text("启用后,枪、血包、近战、手雷、工具、无人机、宝珠、升级、追踪器、地雷等商店购买类实用品会使用默认缩小倍率。大小推车、C.A.R.T. Cannon 和 C.A.R.T. Laser 始终不会缩小。", "When enabled, shop utility items such as guns, medkits, melee items, grenades, tools, drones, pearls, upgrades, trackers, and mines use the shop-item scale factor. Carts, C.A.R.T. Cannon, and C.A.R.T. Laser never shrink."));
		PlayerScalingModuleEnabled = Bind(config, "PlayerScalingModuleEnabled", "玩家缩放", "Player Scaling", "启用玩家缩放", "Enable player scaling", defaultValue: true, Text("默认开启。启用后才会运行玩家站车检测和玩家缩放;关闭时 ShrinkCart 不参与任何玩家缩放逻辑。", "Enabled by default. When enabled, ShrinkCart runs player cart-floor detection and player scaling. When disabled, ShrinkCart does not run player scaling logic."));
		PlayerCartScaleFactor = Bind(config, "PlayerCartScaleFactor", "玩家缩放", "Player Scaling", "玩家进车缩放倍率", "Player cart scale factor", 0.55f, Ranged(Text("开启“启用玩家缩放”后,玩家站在购物车中心区域触发缩放时的目标尺寸比例。", "Target player size factor when player scaling is enabled and a player stands in the cart center area."), 0.05f, 1f));
		PlayerCartGrowFactor = Bind(config, "PlayerCartGrowFactor", "玩家缩放", "Player Scaling", "玩家进车增大倍率", "Player cart grow factor", 1.5f, Ranged(Text("玩家站车循环中的增大目标比例。循环顺序为:原样 -> 缩小 -> 原样 -> 增大 -> 原样。", "Target player growth factor for the cart cycle: normal -> shrink -> normal -> grow -> normal."), 1f, 2f));
		PlayerCartStandTriggerSeconds = Bind(config, "PlayerCartStandTriggerSeconds", "玩家缩放", "Player Scaling", "玩家站车触发时间", "Player stand trigger time", 2f, Ranged(Text("开启“启用玩家缩放”后,玩家站在购物车中心区域多久才执行一次缩小/恢复/增大循环。离开中心区域会重置计时。", "How long a player must stand in the cart center area before the shrink/restore/grow cycle advances. Leaving the center area resets the timer."), 0.25f, 10f));
		PlayerCartExitGraceSeconds = Bind(config, "PlayerCartExitGraceSeconds", "玩家缩放", "Player Scaling", "玩家离车判定宽容时间", "Player exit grace time", 0.6f, Ranged(Text("玩家仍在购物车中心区域附近但短暂跳起、踩到车内物品或被货物顶起时,保留车内状态多久后才判定离开。设为 0 可恢复严格判定。", "How long to keep cart-inside state when the player briefly jumps, stands on cart cargo, or is pushed up while still near the cart center area. Set to 0 for strict detection."), 0f, 2f));
		PlayerCartDetectionIntervalSeconds = Bind(config, "PlayerCartDetectionIntervalSeconds", "玩家缩放", "Player Scaling", "玩家检测间隔", "Player detection interval", 0.75f, Ranged(Text("玩家缩放开启时,主机多久检测一次玩家是否位于正式购物车底面投影范围内。数值越大越省性能,也越能防误触发。", "When player scaling is enabled, how often the host checks whether players are inside a regular cart floor projection. Higher values cost less and reduce accidental triggers."), 0.25f, 2f));
		RestorePlayerOnDamage = Bind(config, "RestorePlayerOnDamage", "玩家缩放", "Player Scaling", "启用玩家受伤后自动恢复", "Restore player on damage", defaultValue: true, Text("启用后,玩家缩放时使用 ScalerCore 的受伤/碰撞恢复链路;关闭后,玩家只会通过再次站车切换恢复。", "When enabled, ShrinkCart-scaled players use ScalerCore's damage/collision restore path. When disabled, players restore only by standing in the cart again."));
		SuppressValuableDamageRestore = Bind(config, "SuppressValuableDamageRestore", "购物车", "Cart", "防止碰撞弹回原尺寸", "Suppress collision restore", defaultValue: true, Text("启用后,贵重物品在购物车里轻微碰撞时不会立刻弹回原尺寸。ScalerCore 的安全恢复仍会保留。", "When enabled, valuables do not immediately restore from light collision value drops while in the cart. ScalerCore safety restore remains active."));
		DeadHeadScalingEnabled = Bind(config, "DeadHeadScalingEnabled", "死亡头颅", "Death Heads", "启用死亡头颅进车缩小", "Enable dead head cart shrinking", defaultValue: false, Text("默认关闭。启用后,已触发的玩家死亡头颅进入购物车时会缩小,离车、进入提取点、进入卡车或复活前会恢复。", "Disabled by default. When enabled, triggered player death heads shrink in carts and restore when leaving, entering extraction/truck flow, or before revive."));
		DeadHeadScaleFactor = Bind(config, "DeadHeadScaleFactor", "死亡头颅", "Death Heads", "死亡头颅缩小倍率", "Dead head scale factor", 0.5f, Ranged(Text("死亡头颅进入购物车后的目标尺寸比例。", "Target size factor for death heads inside carts."), 0.05f, 1f));
		TinyEnabled = BindCategoryEnabled(config, "Tiny", "Tiny 微型贵重物", "Tiny valuables", defaultValue: true);
		TinyScaleFactor = BindCategoryFactor(config, "Tiny", "Tiny 微型贵重物", "Tiny valuables", 0.8f);
		SmallEnabled = BindCategoryEnabled(config, "Small", "Small 小贵重物", "Small valuables", defaultValue: true);
		SmallScaleFactor = BindCategoryFactor(config, "Small", "Small 小贵重物", "Small valuables", 0.6f);
		MediumEnabled = BindCategoryEnabled(config, "Medium", "Medium 中贵重物", "Medium valuables", defaultValue: true);
		MediumScaleFactor = BindCategoryFactor(config, "Medium", "Medium 中贵重物", "Medium valuables", 0.45f);
		BigEnabled = BindCategoryEnabled(config, "Big", "Big 大贵重物", "Big valuables", defaultValue: true);
		BigScaleFactor = BindCategoryFactor(config, "Big", "Big 大贵重物", "Big valuables", 0.4f);
		WideEnabled = BindCategoryEnabled(config, "Wide", "Wide 宽贵重物", "Wide valuables", defaultValue: true);
		WideScaleFactor = BindCategoryFactor(config, "Wide", "Wide 宽贵重物", "Wide valuables", 0.35f);
		TallEnabled = BindCategoryEnabled(config, "Tall", "Tall 高贵重物", "Tall valuables", defaultValue: true);
		TallScaleFactor = BindCategoryFactor(config, "Tall", "Tall 高贵重物", "Tall valuables", 0.35f);
		VeryTallEnabled = BindCategoryEnabled(config, "VeryTall", "VeryTall 超高贵重物", "VeryTall valuables", defaultValue: true);
		VeryTallScaleFactor = BindCategoryFactor(config, "VeryTall", "VeryTall 超高贵重物", "VeryTall valuables", 0.25f);
		EnemyOrbEnabled = Bind(config, "EnemyOrbEnabled", "敌人球", "Enemy Orbs", "启用敌人球缩小", "Enable enemy orb shrinking", defaultValue: true, Text("启用后,Enemy - Small/Medium/Big/Berserker 类贵重物会按下方倍率缩小。", "When enabled, Enemy - Small/Medium/Big/Berserker valuables shrink using the factors below."));
		EnemyOrbSmallScaleFactor = Bind(config, "EnemyOrbSmallScaleFactor", "敌人球", "Enemy Orbs", "Small 敌人球倍率", "Small enemy orb factor", 0.8f, Ranged(Text("Small 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Small enemy orbs in carts."), 0.05f, 1f));
		EnemyOrbMediumScaleFactor = Bind(config, "EnemyOrbMediumScaleFactor", "敌人球", "Enemy Orbs", "Medium 敌人球倍率", "Medium enemy orb factor", 0.65f, Ranged(Text("Medium 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Medium enemy orbs in carts."), 0.05f, 1f));
		EnemyOrbBigScaleFactor = Bind(config, "EnemyOrbBigScaleFactor", "敌人球", "Enemy Orbs", "Big 敌人球倍率", "Big enemy orb factor", 0.45f, Ranged(Text("Big 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Big enemy orbs in carts."), 0.05f, 1f));
		EnemyOrbBerserkerScaleFactor = Bind(config, "EnemyOrbBerserkerScaleFactor", "敌人球", "Enemy Orbs", "Berserker 敌人球倍率", "Berserker enemy orb factor", 0.45f, Ranged(Text("Berserker 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Berserker enemy orbs in carts."), 0.05f, 1f));
		SurplusEnabled = Bind(config, "SurplusEnabled", "特殊物品", "Special Objects", "启用钱袋/Surplus 缩小", "Enable money bag / Surplus shrinking", LegacyBool(config, true, "特殊物品", "启用 Surplus 缩小"), Text("启用后,钱袋/SurplusValuable 会使用单独倍率。", "When enabled, money bags / SurplusValuable use their own scale factor."));
		SurplusScaleFactor = Bind(config, "SurplusScaleFactor", "特殊物品", "Special Objects", "钱袋/Surplus 倍率", "Money bag / Surplus factor", LegacyFloat(config, 0.25f, "特殊物品", "Surplus 倍率"), Ranged(Text("钱袋/SurplusValuable 放入购物车后的目标尺寸比例。", "Target size factor for money bags / SurplusValuable in carts."), 0.05f, 1f));
		ValuableBoxEnabled = Bind(config, "ValuableBoxEnabled", "特殊物品", "Special Objects", "启用代币箱缩小", "Enable token box shrinking", defaultValue: true, Text("启用后,新版本抽奖用代币箱 ItemValuableBox 会使用单独倍率。", "When enabled, newer token boxes / ItemValuableBox objects use their own scale factor."));
		ValuableBoxScaleFactor = Bind(config, "ValuableBoxScaleFactor", "特殊物品", "Special Objects", "代币箱倍率", "Token box factor", 0.4f, Ranged(Text("抽奖用代币箱 ItemValuableBox 放入购物车后的目标尺寸比例。", "Target size factor for token boxes / ItemValuableBox in carts."), 0.05f, 1f));
		FallbackScaleFactor = Bind(config, "FallbackScaleFactor", "商店用品", "Shop Items", "商店用品缩小倍率", "Shop item scale factor", LegacyFloat(config, 0.5f, "普通或未知物品", "默认缩小倍率"), Ranged(Text("开启“启用商店用品缩小”后,枪、血包、工具等实用品放入购物车后的目标尺寸比例。也作为未知贵重物分类的兜底倍率。", "Target size factor for utility items such as guns, medkits, and tools when shop-item scaling is enabled. Also used as fallback for unknown valuable categories."), 0.05f, 1f));
		EnemyInCartInstantKill = Bind(config, "EnemyInCartInstantKill", "车辆碾压", "Cart Crush", "敌人进车秒杀", "Instant-kill enemies in cart", LegacyBool(config, true, "车辆碾压", "车辆碾压秒杀敌人", "VehicleCrush", "InstantKillEnemies"), Text("启用后,敌人或敌人刚体进入购物车时会立刻死亡。此功能复刻 ShrinkerCartPlus 的敌人进车秒杀逻辑。", "When enabled, enemies or enemy rigidbodies die when entering a cart. This mirrors ShrinkerCartPlus enemy-in-cart instant kill behavior."));
		DynamicItemScanEnabled = Bind(config, "DynamicItemScanEnabled", "性能", "Performance", "启用动态物品扫描", "Enable dynamic item scanning", defaultValue: true, Text("启用后,ShrinkCart 会根据当前跟踪的缩小物品数量自动拉长状态扫描间隔,减少车内物品很多时的卡顿。", "When enabled, ShrinkCart lengthens state scan intervals based on the number of tracked shrunken objects to reduce stutter with many cart items."));
		MinimumItemScanIntervalSeconds = Bind(config, "MinimumItemScanIntervalSeconds", "性能", "Performance", "最小物品扫描间隔", "Minimum item scan interval", 0.15f, Ranged(Text("少量物品时的最短状态扫描间隔。数值越小,离车恢复越灵敏,但开销更高。", "Shortest state scan interval for few items. Lower values make restore more responsive but cost more."), 0.05f, 1f));
		MaximumItemScanIntervalSeconds = Bind(config, "MaximumItemScanIntervalSeconds", "性能", "Performance", "最大物品扫描间隔", "Maximum item scan interval", 1f, Ranged(Text("大量物品时允许使用的最长状态扫描间隔。数值越大越省性能,但离车恢复最多会延后一个扫描间隔。", "Longest state scan interval allowed for many items. Higher values cost less, but leaving-cart restore may be delayed by up to one scan interval."), 0.1f, 2f));
		DebugLogging = Bind(config, "DebugLogging", "诊断", "Diagnostics", "启用调试日志", "Enable debug logging", defaultValue: false, Text("启用后,在 BepInEx 日志中写入更多缩小、恢复、敌人进车和碾压识别信息。", "When enabled, writes additional shrink, restore, enemy-in-cart, map compatibility, and recognition details to BepInEx logs."));
		ConfigLanguage = config.Bind<string>("语言 / Language", "配置语言 / Config Language", _useEnglish ? "English" : "中文", new ConfigDescription("切换 ShrinkCart 配置文件语言。Switch ShrinkCart config file language. Apply and restart/reload to rebuild all config keys.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "中文", "English" }), new object[0]));
		WatchScaling<bool>(CartShrinkingEnabled);
		WatchScaling<float>(CartScaleSpeed);
		WatchScaling<float>(RestoreScaleSpeed);
		WatchScaling<float>(CartLeaveDebounceSeconds);
		WatchScaling<float>(ReshrinkCooldownSeconds);
		WatchScaling<bool>(ScaleMassWithSize);
		WatchScaling<bool>(ShrinkShopPlayerItems);
		WatchScaling<bool>(PlayerScalingModuleEnabled);
		WatchScaling<float>(PlayerCartScaleFactor);
		WatchScaling<float>(PlayerCartGrowFactor);
		WatchScaling<float>(PlayerCartStandTriggerSeconds);
		WatchScaling<float>(PlayerCartExitGraceSeconds);
		WatchScaling<float>(PlayerCartDetectionIntervalSeconds);
		WatchScaling<bool>(RestorePlayerOnDamage);
		WatchScaling<bool>(SuppressValuableDamageRestore);
		WatchScaling<bool>(DeadHeadScalingEnabled);
		WatchScaling<float>(DeadHeadScaleFactor);
		WatchScaling<bool>(TinyEnabled);
		WatchScaling<float>(TinyScaleFactor);
		WatchScaling<bool>(SmallEnabled);
		WatchScaling<float>(SmallScaleFactor);
		WatchScaling<bool>(MediumEnabled);
		WatchScaling<float>(MediumScaleFactor);
		WatchScaling<bool>(BigEnabled);
		WatchScaling<float>(BigScaleFactor);
		WatchScaling<bool>(WideEnabled);
		WatchScaling<float>(WideScaleFactor);
		WatchScaling<bool>(TallEnabled);
		WatchScaling<float>(TallScaleFactor);
		WatchScaling<bool>(VeryTallEnabled);
		WatchScaling<float>(VeryTallScaleFactor);
		WatchScaling<bool>(EnemyOrbEnabled);
		WatchScaling<float>(EnemyOrbSmallScaleFactor);
		WatchScaling<float>(EnemyOrbMediumScaleFactor);
		WatchScaling<float>(EnemyOrbBigScaleFactor);
		WatchScaling<float>(EnemyOrbBerserkerScaleFactor);
		WatchScaling<bool>(SurplusEnabled);
		WatchScaling<float>(SurplusScaleFactor);
		WatchScaling<bool>(ValuableBoxEnabled);
		WatchScaling<float>(ValuableBoxScaleFactor);
		WatchScaling<float>(FallbackScaleFactor);
		MigrateMassScalingDefaultOnce();
		RemoveDeprecatedEntries(config);
		RemoveInactiveLanguageEntries(config);
	}

	internal static float SafeScaleSpeed()
	{
		return Mathf.Clamp(CartScaleSpeed.Value, 0.1f, 20f);
	}

	internal static float SafeRestoreScaleSpeed()
	{
		return Mathf.Clamp(RestoreScaleSpeed.Value, 0.1f, 20f);
	}

	internal static float SafeCartLeaveDebounceSeconds()
	{
		return Mathf.Clamp(CartLeaveDebounceSeconds.Value, 0.1f, 10f);
	}

	internal static float SafeReshrinkCooldownSeconds()
	{
		return Mathf.Clamp(ReshrinkCooldownSeconds.Value, 0.05f, 10f);
	}

	internal static bool PlayerScalingEnabled()
	{
		if (CartShrinkingEnabled != null && PlayerScalingModuleEnabled != null && CartShrinkingEnabled.Value)
		{
			return PlayerScalingModuleEnabled.Value;
		}
		return false;
	}

	internal static bool ShouldPreserveMass()
	{
		if (ScaleMassWithSize != null)
		{
			return !ScaleMassWithSize.Value;
		}
		return true;
	}

	internal static float SafePlayerCartScaleFactor()
	{
		return Mathf.Clamp(PlayerCartScaleFactor.Value, 0.05f, 1f);
	}

	internal static float SafePlayerCartGrowFactor()
	{
		if (PlayerCartGrowFactor != null)
		{
			return Mathf.Clamp(PlayerCartGrowFactor.Value, 1f, 2f);
		}
		return 1.5f;
	}

	internal static float SafePlayerCartStandTriggerSeconds()
	{
		return Mathf.Clamp(PlayerCartStandTriggerSeconds.Value, 0.25f, 10f);
	}

	internal static float SafePlayerCartExitGraceSeconds()
	{
		return Mathf.Clamp(PlayerCartExitGraceSeconds.Value, 0f, 2f);
	}

	internal static float SafePlayerCartDetectionIntervalSeconds()
	{
		return Mathf.Clamp(PlayerCartDetectionIntervalSeconds.Value, 0.25f, 2f);
	}

	internal static bool DynamicItemScanEnabledValue()
	{
		if (DynamicItemScanEnabled != null)
		{
			return DynamicItemScanEnabled.Value;
		}
		return false;
	}

	internal static bool DeadHeadScalingEnabledValue()
	{
		if (CartShrinkingEnabled != null && DeadHeadScalingEnabled != null && CartShrinkingEnabled.Value)
		{
			return DeadHeadScalingEnabled.Value;
		}
		return false;
	}

	internal static float SafeDeadHeadScaleFactor()
	{
		if (DeadHeadScaleFactor != null)
		{
			return Mathf.Clamp(DeadHeadScaleFactor.Value, 0.05f, 1f);
		}
		return 0.5f;
	}

	internal static float SafeMinimumItemScanIntervalSeconds()
	{
		if (MinimumItemScanIntervalSeconds != null)
		{
			return Mathf.Clamp(MinimumItemScanIntervalSeconds.Value, 0.05f, 1f);
		}
		return 0.15f;
	}

	internal static float SafeMaximumItemScanIntervalSeconds()
	{
		float num = SafeMinimumItemScanIntervalSeconds();
		float num2 = ((MaximumItemScanIntervalSeconds == null) ? 1f : Mathf.Clamp(MaximumItemScanIntervalSeconds.Value, 0.1f, 2f));
		return Mathf.Max(num, num2);
	}

	internal static bool TryGetScaleFactor(ShrinkCategory category, out float factor)
	{
		switch (category)
		{
		case ShrinkCategory.Tiny:
			return TryCategory(TinyEnabled, TinyScaleFactor, out factor);
		case ShrinkCategory.Small:
			return TryCategory(SmallEnabled, SmallScaleFactor, out factor);
		case ShrinkCategory.Medium:
			return TryCategory(MediumEnabled, MediumScaleFactor, out factor);
		case ShrinkCategory.Big:
			return TryCategory(BigEnabled, BigScaleFactor, out factor);
		case ShrinkCategory.Wide:
			return TryCategory(WideEnabled, WideScaleFactor, out factor);
		case ShrinkCategory.Tall:
			return TryCategory(TallEnabled, TallScaleFactor, out factor);
		case ShrinkCategory.VeryTall:
			return TryCategory(VeryTallEnabled, VeryTallScaleFactor, out factor);
		case ShrinkCategory.EnemyOrbSmall:
			return TryEnemyOrb(EnemyOrbSmallScaleFactor, out factor);
		case ShrinkCategory.EnemyOrbMedium:
			return TryEnemyOrb(EnemyOrbMediumScaleFactor, out factor);
		case ShrinkCategory.EnemyOrbBig:
			return TryEnemyOrb(EnemyOrbBigScaleFactor, out factor);
		case ShrinkCategory.EnemyOrbBerserker:
			return TryEnemyOrb(EnemyOrbBerserkerScaleFactor, out factor);
		case ShrinkCategory.Surplus:
			return TryCategory(SurplusEnabled, SurplusScaleFactor, out factor);
		case ShrinkCategory.ValuableBox:
			return TryCategory(ValuableBoxEnabled, ValuableBoxScaleFactor, out factor);
		default:
			factor = SafeFactor(FallbackScaleFactor.Value);
			return true;
		}
	}

	private static ConfigEntry<bool> BindCategoryEnabled(ConfigFile config, string id, string chineseSection, string englishSection, bool defaultValue)
	{
		return Bind(config, id + ".Enabled", chineseSection, englishSection, "启用此分类缩小", "Enable this category", defaultValue, Text("启用后,该分类物品放入购物车时会自动缩小。", "When enabled, objects in this category shrink when placed into a cart."));
	}

	private static ConfigEntry<float> BindCategoryFactor(ConfigFile config, string id, string chineseSection, string englishSection, float defaultValue)
	{
		return Bind(config, id + ".Factor", chineseSection, englishSection, "缩小倍率", "Scale factor", defaultValue, Ranged(Text("该分类物品放入购物车后的目标尺寸比例。0.4 表示原尺寸的 40%。", "Target size factor for this category in carts. 0.4 means 40% of original size."), 0.05f, 1f));
	}

	private static ConfigDescription Ranged(string description, float min, float max)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Expected O, but got Unknown
		return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[0]);
	}

	private static string Text(string chinese, string english)
	{
		if (!_useEnglish)
		{
			return chinese;
		}
		return english;
	}

	private static ConfigEntry<T> Bind<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue, string description)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		return Bind(config, id, chineseSection, englishSection, chineseKey, englishKey, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[0]));
	}

	private static ConfigEntry<T> Bind<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue, ConfigDescription description)
	{
		T val = ReadLocalizedValue(config, id, chineseSection, englishSection, chineseKey, englishKey, defaultValue);
		string text = (_useEnglish ? englishSection : chineseSection);
		string text2 = (_useEnglish ? englishKey : chineseKey);
		ActiveDefinitions.Add(new LocalizedDefinition(id, chineseSection, englishSection, chineseKey, englishKey));
		return config.Bind<T>(text, text2, val, description);
	}

	private static T ReadLocalizedValue<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue)
	{
		ConfigEntry<T> val = default(ConfigEntry<T>);
		T value;
		T value2;
		if (_useEnglish)
		{
			if (config.TryGetEntry<T>(englishSection, englishKey, ref val))
			{
				return val.Value;
			}
			if (config.TryGetEntry<T>(chineseSection, chineseKey, ref val))
			{
				return val.Value;
			}
			if (TryGetOrphanedValue<T>(config, englishSection, englishKey, out value))
			{
				return value;
			}
			if (TryGetOrphanedValue<T>(config, chineseSection, chineseKey, out value2))
			{
				return value2;
			}
		}
		else
		{
			if (config.TryGetEntry<T>(chineseSection, chineseKey, ref val))
			{
				return val.Value;
			}
			if (config.TryGetEntry<T>(englishSection, englishKey, ref val))
			{
				return val.Value;
			}
			if (TryGetOrphanedValue<T>(config, chineseSection, chineseKey, out value2))
			{
				return value2;
			}
			if (TryGetOrphanedValue<T>(config, englishSection, englishKey, out value))
			{
				return value;
			}
		}
		return defaultValue;
	}

	private static string DetectConfiguredLanguage(ConfigFile config)
	{
		ConfigEntry<string> val = default(ConfigEntry<string>);
		if (config.TryGetEntry<string>("语言 / Language", "配置语言 / Config Language", ref val))
		{
			return NormalizeLanguage(val.Value);
		}
		if (TryGetOrphanedValue<string>(config, "语言 / Language", "配置语言 / Config Language", out var value))
		{
			return NormalizeLanguage(value);
		}
		return "中文";
	}

	private static bool TryGetOrphanedValue<T>(ConfigFile config, string section, string key, out T value)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Expected O, but got Unknown
		value = default(T);
		IDictionary dictionary = ((ConfigFileOrphanedEntriesProperty == null) ? null : (ConfigFileOrphanedEntriesProperty.GetValue(config, null) as IDictionary));
		if (dictionary == null)
		{
			return false;
		}
		ConfigDefinition key2 = new ConfigDefinition(section, key);
		if (!dictionary.Contains(key2))
		{
			return false;
		}
		if (!(dictionary[key2] is string text))
		{
			return false;
		}
		try
		{
			Type typeFromHandle = typeof(T);
			if (typeFromHandle == typeof(string))
			{
				value = (T)(object)text;
				return true;
			}
			if (typeFromHandle == typeof(bool) && bool.TryParse(text, out var result))
			{
				value = (T)(object)result;
				return true;
			}
			if (typeFromHandle == typeof(float) && (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.TryParse(text, out result2)))
			{
				value = (T)(object)result2;
				return true;
			}
			if (typeFromHandle == typeof(int) && int.TryParse(text, out var result3))
			{
				value = (T)(object)result3;
				return true;
			}
		}
		catch
		{
			return false;
		}
		return false;
	}

	private static bool IsEnglishLanguage(string value)
	{
		return NormalizeLanguage(value) == "English";
	}

	private static string NormalizeLanguage(string value)
	{
		if (string.Equals(value, "English", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "EN", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "English", StringComparison.OrdinalIgnoreCase))
		{
			return "English";
		}
		return "中文";
	}

	private static bool TryCategory(ConfigEntry<bool> enabled, ConfigEntry<float> factorEntry, out float factor)
	{
		factor = SafeFactor(factorEntry.Value);
		return enabled.Value;
	}

	private static bool TryEnemyOrb(ConfigEntry<float> factorEntry, out float factor)
	{
		factor = SafeFactor(factorEntry.Value);
		return EnemyOrbEnabled.Value;
	}

	private static float SafeFactor(float value)
	{
		return Mathf.Clamp(value, 0.05f, 1f);
	}

	private static void MigrateMassScalingDefaultOnce()
	{
		if (ScaleMassWithSize == null || _boundConfig == null)
		{
			return;
		}
		string massDefaultMigrationMarkerPath = GetMassDefaultMigrationMarkerPath();
		if (File.Exists(massDefaultMigrationMarkerPath))
		{
			return;
		}
		ScaleMassWithSize.Value = true;
		try
		{
			_boundConfig.Save();
			File.WriteAllText(massDefaultMigrationMarkerPath, "0.2.39");
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to persist mass scaling default migration marker: " + ex.Message));
		}
	}

	private static string GetMassDefaultMigrationMarkerPath()
	{
		string text = ((_boundConfig == null) ? null : _boundConfig.ConfigFilePath);
		string text2 = (string.IsNullOrEmpty(text) ? Paths.ConfigPath : Path.GetDirectoryName(text));
		if (string.IsNullOrEmpty(text2))
		{
			text2 = Paths.ConfigPath;
		}
		return Path.Combine(text2, "ShrinkCart.v0.2.39.mass-default-enabled");
	}

	private static bool LegacyBool(ConfigFile config, bool defaultValue, params string[] sectionKeyPairs)
	{
		ConfigEntry<bool> val = default(ConfigEntry<bool>);
		for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2)
		{
			if (config.TryGetEntry<bool>(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val))
			{
				return val.Value;
			}
		}
		return defaultValue;
	}

	private static float LegacyFloat(ConfigFile config, float defaultValue, params string[] sectionKeyPairs)
	{
		ConfigEntry<float> val = default(ConfigEntry<float>);
		for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2)
		{
			if (config.TryGetEntry<float>(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val))
			{
				return val.Value;
			}
		}
		return defaultValue;
	}

	private static void RemoveDeprecatedEntries(ConfigFile config)
	{
		bool flag = false;
		flag |= RemoveDeprecatedEntry(config, "Cart", "Enabled");
		flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleFactor");
		flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleSpeed");
		flag |= RemoveDeprecatedEntry(config, "Cart", "RestoreGraceSeconds");
		flag |= RemoveDeprecatedEntry(config, "Cart", "PreserveMass");
		flag |= RemoveDeprecatedEntry(config, "Cart", "ShrinkNonValuableItems");
		flag |= RemoveDeprecatedEntry(config, "Cart", "SuppressValuableDamageRestore");
		flag |= RemoveDeprecatedEntry(config, "Diagnostics", "DebugLogging");
		flag |= RemoveDeprecatedEntry(config, "购物车", "保持原始重量");
		flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillPlayers");
		flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillEnemies");
		flag |= RemoveDeprecatedEntry(config, "视觉", "隐藏缩放闪光");
		flag |= RemoveDeprecatedEntry(config, "车辆碾压", "车辆碾压秒杀玩家");
		flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "启用提取点复活");
		flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活前稳定检测时间");
		flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活检测间隔");
		flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "拦截外部立即复活调用");
		flag |= RemoveDeprecatedEntry(config, "购物车", "商店用品也缩小");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家也缩小");
		flag |= RemoveDeprecatedEntry(config, "购物车", "启用实验性玩家缩放");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家死亡前自动恢复");
		flag |= RemoveDeprecatedEntry(config, "购物车", "启用玩家缩放");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车缩放倍率");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家站车触发时间");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家离车判定宽容时间");
		flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车切换间隔");
		flag |= RemoveDeprecatedEntry(config, "购物车", "普通物品也缩小");
		flag |= RemoveDeprecatedEntry(config, "购物车", "商店/人物用品也缩小");
		flag |= RemoveDeprecatedEntry(config, "购物车", "防止车辆互相重叠");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆硬碰撞修正强度");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆最大单帧修正距离");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆挤压速度清除");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间(已废弃)");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间");
		flag |= RemoveDeprecatedEntry(config, "购物车", "车辆脱困强度");
		flag |= RemoveDeprecatedEntry(config, "玩家缩放", "旧版玩家缩放开关(已停用)");
		flag |= RemoveDeprecatedEntry(config, "地图兼容", "Minecraft Stronghold 普通门进车破碎");
		flag |= RemoveDeprecatedEntry(config, "地图兼容", "Minecraft Stronghold 门破碎确认时间");
		flag |= RemoveDeprecatedEntry(config, "Map Compatibility", "Minecraft Stronghold door shatter in cart");
		if (flag | RemoveDeprecatedEntry(config, "Map Compatibility", "Minecraft Stronghold door shatter confirmation time"))
		{
			config.Save();
		}
	}

	private static void RemoveInactiveLanguageEntries(ConfigFile config)
	{
		bool flag = false;
		for (int i = 0; i < ActiveDefinitions.Count; i++)
		{
			LocalizedDefinition localizedDefinition = ActiveDefinitions[i];
			flag = ((!_useEnglish) ? (flag | RemoveDeprecatedEntry(config, localizedDefinition.EnglishSection, localizedDefinition.EnglishKey)) : (flag | RemoveDeprecatedEntry(config, localizedDefinition.ChineseSection, localizedDefinition.ChineseKey)));
		}
		if (flag)
		{
			config.Save();
		}
	}

	private static bool RemoveDeprecatedEntry(ConfigFile config, string section, string key)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		ConfigDefinition val = new ConfigDefinition(section, key);
		bool result = config.Remove(val);
		IDictionary dictionary = ((ConfigFileOrphanedEntriesProperty == null) ? null : (ConfigFileOrphanedEntriesProperty.GetValue(config, null) as IDictionary));
		if (dictionary != null && dictionary.Contains(val))
		{
			dictionary.Remove(val);
			result = true;
		}
		return result;
	}

	private static void WatchScaling<T>(ConfigEntry<T> entry)
	{
		entry.SettingChanged += OnScalingSettingChanged;
	}

	private static void OnScalingSettingChanged(object sender, EventArgs args)
	{
		ScalingConfigVersion++;
	}
}
internal static class PlayerCartScaleController
{
	private enum PlayerCartScaleState
	{
		Normal,
		Shrunk,
		Grown
	}

	private enum PlayerCartNextAction
	{
		Shrink,
		Grow
	}

	private sealed class PlayerState
	{
		internal PlayerAvatar Player;

		internal PlayerCartScaleState ScaleState;

		internal PlayerCartNextAction NextAction;

		internal bool WasInCartRange;

		internal bool WasInTriggerZone;

		internal bool TriggeredThisStay;

		internal float TriggerZoneEnteredTime;

		internal float LastInsideCenterTime;
	}

	private sealed class CartState
	{
		internal PhysGrabCart Cart;

		internal Transform InCart;
	}

	private struct CartZoneResult
	{
		internal bool InCartRange;

		internal bool InTriggerZone;
	}

	private const float CenterZoneHorizontalScale = 0.45f;

	private const float FloorProjectionPaddingBelow = 0.25f;

	private const float FloorProjectionStandingHeightAbove = 1.4f;

	private const float MinimumCenterHalfExtent = 0.15f;

	private const float StandPointYOffset = 0.05f;

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

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

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

	private static readonly List<int> RemoveCartIds = new List<int>(8);

	private static readonly List<int> RemovePlayerIds = new List<int>(8);

	private static readonly FieldInfo PhysGrabCartInCartField = typeof(PhysGrabCart).GetField("inCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerAvatarColliderField = typeof(PlayerAvatar).GetField("collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static float _nextTickTime;

	internal static void Reset()
	{
		RegisteredCarts.Clear();
		PlayerStates.Clear();
		ExcludedCartIds.Clear();
		RemoveCartIds.Clear();
		RemovePlayerIds.Clear();
		_nextTickTime = 0f;
	}

	internal static void RegisterCart(PhysGrabCart cart)
	{
		if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null) && !IsExcludedPlayerScaleCart(cart))
		{
			RegisteredCarts[((Object)cart).GetInstanceID()] = new CartState
			{
				Cart = cart,
				InCart = GetInCartTransform(cart)
			};
			DebugLog("Registered regular cart for player stand-toggle: " + ((Object)cart).name);
		}
	}

	internal static void RegisterExistingCarts()
	{
		PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
		for (int i = 0; i < array.Length; i++)
		{
			RegisterCart(array[i]);
		}
	}

	internal static void Tick()
	{
		if (!IsHostOrSingleplayer())
		{
			return;
		}
		float time = Time.time;
		if (time < _nextTickTime)
		{
			return;
		}
		_nextTickTime = time + ModConfig.SafePlayerCartDetectionIntervalSeconds();
		if (!ModConfig.PlayerScalingEnabled())
		{
			RestoreAll();
		}
		else
		{
			if (RegisteredCarts.Count == 0)
			{
				return;
			}
			PruneInvalidCarts();
			List<PlayerAvatar> players = GetPlayers();
			if (players != null && players.Count != 0)
			{
				for (int i = 0; i < players.Count; i++)
				{
					ProcessPlayer(players[i], time);
				}
				PruneMissingPlayers();
			}
		}
	}

	internal static void RestoreAll()
	{
		if (PlayerStates.Count == 0)
		{
			return;
		}
		foreach (PlayerState value in PlayerStates.Values)
		{
			if (value != null && value.ScaleState != PlayerCartScaleState.Normal && (Object)(object)value.Player != (Object)null)
			{
				RestorePlayer(((Component)value.Player).gameObject);
			}
		}
		PlayerStates.Clear();
	}

	internal static void Disable()
	{
		RestoreAll();
		Reset();
	}

	private static void ProcessPlayer(PlayerAvatar player, float now)
	{
		if ((Object)(object)player == (Object)null || (Object)(object)((Component)player).gameObject == (Object)null || !((Component)player).gameObject.activeInHierarchy)
		{
			return;
		}
		int instanceID = ((Object)player).GetInstanceID();
		if (!PlayerStates.TryGetValue(instanceID, out var value))
		{
			PlayerState playerState = new PlayerState();
			playerState.Player = player;
			value = playerState;
			PlayerStates[instanceID] = value;
		}
		else
		{
			value.Player = player;
		}
		PlayerCartScaleState scaleState;
		if (value.ScaleState != PlayerCartScaleState.Normal && !ScaleManager.IsScaled(((Component)player).gameObject))
		{
			AdvanceCycleAfterRestore(value);
			value.ScaleState = PlayerCartScaleState.Normal;
		}
		else if (value.ScaleState == PlayerCartScaleState.Normal && TryGetShrinkCartPlayerScaleState(((Component)player).gameObject, out scaleState))
		{
			value.ScaleState = scaleState;
			DebugLog("Adopted existing ShrinkCart-like player scale state: " + ((Object)player).name + " state=" + scaleState);
		}
		CartZoneResult playerCartZone = GetPlayerCartZone(player);
		bool flag = playerCartZone.InCartRange;
		bool inTriggerZone = playerCartZone.InTriggerZone;
		if (flag)
		{
			value.LastInsideCenterTime = now;
		}
		else if (value.WasInCartRange && now - value.LastInsideCenterTime <= ModConfig.SafePlayerCartExitGraceSeconds())
		{
			flag = true;
		}
		if (!flag)
		{
			if (value.WasInCartRange)
			{
				DebugLog("Player left cart floor range: " + ((Object)player).name);
			}
			value.WasInCartRange = false;
			value.WasInTriggerZone = false;
			value.TriggeredThisStay = false;
			value.TriggerZoneEnteredTime = 0f;
			value.LastInsideCenterTime = 0f;
			return;
		}
		value.WasInCartRange = true;
		if (!inTriggerZone)
		{
			value.WasInTriggerZone = false;
			value.TriggerZoneEnteredTime = 0f;
		}
		else if (!value.WasInTriggerZone)
		{
			value.WasInTriggerZone = true;
			value.TriggerZoneEnteredTime = now;
			value.LastInsideCenterTime = now;
			DebugLog("Player entered cart floor trigger zone: " + ((Object)player).name);
		}
		else
		{
			if (value.TriggeredThisStay || now - value.TriggerZoneEnteredTime < ModConfig.SafePlayerCartStandTriggerSeconds())
			{
				return;
			}
			value.TriggeredThisStay = true;
			DebugLog("Player cart floor trigger timer completed: " + ((Object)player).name);
			if (value.ScaleState == PlayerCartScaleState.Shrunk || value.ScaleState == PlayerCartScaleState.Grown)
			{
				PlayerCartScaleState scaleState2 = value.ScaleState;
				if (RestorePlayer(((Component)player).gameObject))
				{
					value.ScaleState = PlayerCartScaleState.Normal;
					value.NextAction = ((scaleState2 == PlayerCartScaleState.Shrunk) ? PlayerCartNextAction.Grow : PlayerCartNextAction.Shrink);
					DebugLog("Restored player after standing in cart center: " + ((Object)player).name + " next=" + value.NextAction);
				}
				return;
			}
			if (ScaleManager.IsScaled(((Component)player).gameObject))
			{
				DebugLog("Skipped player cart shrink because another scale session is active: " + ((Object)player).name);
				return;
			}
			PlayerCartScaleState playerCartScaleState = ((value.NextAction != PlayerCartNextAction.Grow) ? PlayerCartScaleState.Shrunk : PlayerCartScaleState.Grown);
			float factor = ((playerCartScaleState == PlayerCartScaleState.Grown) ? ModConfig.SafePlayerCartGrowFactor() : ModConfig.SafePlayerCartScaleFactor());
			if (ApplyPlayerScale(((Component)player).gameObject, factor, playerCartScaleState))
			{
				value.ScaleState = playerCartScaleState;
				DebugLog(string.Concat("Applied player cart scale after standing in cart center: ", ((Object)player).name, " state=", playerCartScaleState, " factor=", factor.ToString("0.###")));
			}
		}
	}

	private static void AdvanceCycleAfterRestore(PlayerState state)
	{
		if (state != null)
		{
			if (state.ScaleState == PlayerCartScaleState.Shrunk)
			{
				state.NextAction = PlayerCartNextAction.Grow;
			}
			else if (state.ScaleState == PlayerCartScaleState.Grown)
			{
				state.NextAction = PlayerCartNextAction.Shrink;
			}
		}
	}

	private static bool ApplyPlayerScale(GameObject target, float factor, PlayerCartScaleState targetState)
	{
		//IL_000b: 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)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		ScaleOptions val = ScaleOptions.Default;
		val.Factor = factor;
		val.Speed = ModConfig.SafeScaleSpeed();
		val.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
		val.Duration = 0f;
		val.AllowedTargets = (ScaleTargets)1;
		val.SuppressImpactFlash = true;
		val.SuppressCameraShake = true;
		val.IgnoreBonkExpand = ModConfig.RestorePlayerOnDamage != null && !ModConfig.RestorePlayerOnDamage.Value;
		val.RejectExternalApply = false;
		try
		{
			bool flag = ScaleManager.ApplyIfNotScaled(target, val);
			if (!flag)
			{
				DebugLog("ScalerCore rejected player cart scale: " + ((Object)target).name + " state=" + targetState);
			}
			return flag;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to scale player standing in cart: " + ex.Message));
			return false;
		}
	}

	private static bool RestorePlayer(GameObject target)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		try
		{
			if (!ScaleManager.IsScaled(target))
			{
				return true;
			}
			ScaleController controller = ScaleManager.GetController(target);
			if ((Object)(object)controller != (Object)null && controller.IsScaled)
			{
				ScaleOptions currentOptions = controller.CurrentOptions;
				currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
				currentOptions.SuppressImpactFlash = true;
				currentOptions.SuppressCameraShake = true;
				ScaleManager.ForceUpdateOptions(target, currentOptions);
			}
			ScaleManager.ForceRestore(target);
			return true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to restore player from cart toggle: " + ex.Message));
			return false;
		}
	}

	private static bool TryGetShrinkCartPlayerScaleState(GameObject target, out PlayerCartScaleState scaleState)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: 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_003f: Invalid comparison between Unknown and I4
		scaleState = PlayerCartScaleState.Normal;
		if ((Object)(object)target == (Object)null || !ScaleManager.IsScaled(target))
		{
			return false;
		}
		ScaleController controller = ScaleManager.GetController(target);
		if ((Object)(object)controller == (Object)null || !controller.IsScaled)
		{
			return false;
		}
		ScaleOptions currentOptions = controller.CurrentOptions;
		if ((int)currentOptions.AllowedTargets != 1 || !currentOptions.SuppressImpactFlash || !currentOptions.SuppressCameraShake || currentOptions.RejectExternalApply)
		{
			return false;
		}
		if (Mathf.Approximately(currentOptions.Factor, ModConfig.SafePlayerCartScaleFactor()))
		{
			scaleState = PlayerCartScaleState.Shrunk;
			return true;
		}
		if (Mathf.Approximately(currentOptions.Factor, ModConfig.SafePlayerCartGrowFactor()))
		{
			scaleState = PlayerCartScaleState.Grown;
			return true;
		}
		return false;
	}

	private static CartZoneResult GetPlayerCartZone(PlayerAvatar player)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		CartZoneResult result = default(CartZoneResult);
		Vector3 playerStandPoint = GetPlayerStandPoint(player);
		foreach (CartState value in RegisteredCarts.Values)
		{
			if (value != null)
			{
				CartZoneResult playerCartZone = GetPlayerCartZone(player, value, playerStandPoint);
				if (playerCartZone.InCartRange)
				{
					result.InCartRange = true;
				}
				if (playerCartZone.InTriggerZone)
				{
					result.InTriggerZone = true;
					return result;
				}
			}
		}
		return result;
	}

	private static CartZoneResult GetPlayerCartZone(PlayerAvatar player, CartState cartState, Vector3 standPoint)
	{
		//IL_004f: 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)
		CartZoneResult result = default(CartZoneResult);
		Transform val = cartState.InCart;
		if ((Object)(object)val == (Object)null && (Object)(object)cartState.Cart != (Object)null)
		{
			val = (cartState.InCart = GetInCartTransform(cartState.Cart));
		}
		if ((Object)(object)player == (Object)null || (Object)(object)val == (Object)null)
		{
			return result;
		}
		result.InCartRange = IsPointInsideCartFloorProjection(standPoint, val, 1f);
		result.InTriggerZone = result.InCartRange && IsPointInsideCartFloorProjection(standPoint, val, 0.45f);
		return result;
	}

	private static bool IsPointInsideCartFloorProjection(Vector3 point, Transform inCart, float horizontalScale)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_001c: 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_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)
		Vector3 val = Quaternion.Inverse(inCart.rotation) * (point - inCart.position);
		Vector3 val2 = inCart.localScale * 0.5f;
		float num = Mathf.Max(Mathf.Abs(val2.x) * horizontalScale, 0.15f);
		float num2 = Mathf.Max(Mathf.Abs(val2.z) * horizontalScale, 0.15f);
		float num3 = 0f - Mathf.Abs(val2.y);
		if (Mathf.Abs(val.x) <= num && Mathf.Abs(val.z) <= num2 && val.y >= num3 - 0.25f)
		{
			return val.y <= num3 + 1.4f;
		}
		return false;
	}

	private static Transform GetInCartTransform(PhysGrabCart cart)
	{
		if ((Object)(object)cart == (Object)null || PhysGrabCartInCartField == null)
		{
			return null;
		}
		object? value = PhysGrabCartInCartField.GetValue(cart);
		return (Transform)((value is Transform) ? value : null);
	}

	private static void PruneInvalidCarts()
	{
		RemoveCartIds.Clear();
		foreach (KeyValuePair<int, CartState> registeredCart in RegisteredCarts)
		{
			if (registeredCart.Value == null || (Object)(object)registeredCart.Value.Cart == (Object)null || (Object)(object)registeredCart.Value.InCart == (Object)null || IsExcludedPlayerScaleCart(registeredCart.Value.Cart))
			{
				RemoveCartIds.Add(registeredCart.Key);
			}
		}
		for (int i = 0; i < RemoveCartIds.Count; i++)
		{
			RegisteredCarts.Remove(RemoveCartIds[i]);
		}
		RemoveCartIds.Clear();
	}

	private static bool IsExcludedPlayerScaleCart(PhysGrabCart cart)
	{
		if ((Object)(object)cart == (Object)null)
		{
			return true;
		}
		int instanceID = ((Object)cart).GetInstanceID();
		if (ExcludedCartIds.Contains(instanceID))
		{
			return true;
		}
		if (cart.isSmallCart)
		{
			ExcludedCartIds.Add(instanceID);
			DebugLog("Excluded small cart from player stand-toggle: " + ((Object)cart).name);
			return true;
		}
		return false;
	}

	private static Vector3 GetPlayerStandPoint(PlayerAvatar player)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: 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)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		Collider val = null;
		if ((Object)(object)player != (Object)null && PlayerAvatarColliderField != null)
		{
			object? value = PlayerAvatarColliderField.GetValue(player);
			val = (Collider)((value is Collider) ? value : null);
		}
		if ((Object)(object)val != (Object)null)
		{
			Bounds bounds = val.bounds;
			return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + 0.05f, ((Bounds)(ref bounds)).center.z);
		}
		Vector3 val2 = (((Object)(object)player.playerTransform != (Object)null) ? player.playerTransform.position : ((Component)player).transform.position);
		return val2 + Vector3.up * 0.05f;
	}

	private static void PruneMissingPlayers()
	{
		if (PlayerStates.Count == 0)
		{
			return;
		}
		RemovePlayerIds.Clear();
		foreach (KeyValuePair<int, PlayerState> playerState in PlayerStates)
		{
			if (playerState.Value == null || (Object)(object)playerState.Value.Player == (Object)null)
			{
				RemovePlayerIds.Add(playerState.Key);
			}
		}
		for (int i = 0; i < RemovePlayerIds.Count; i++)
		{
			PlayerStates.Remove(RemovePlayerIds[i]);
		}
		RemovePlayerIds.Clear();
	}

	private static List<PlayerAvatar> GetPlayers()
	{
		try
		{
			if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null)
			{
				return GameDirector.instance.PlayerList;
			}
		}
		catch
		{
		}
		try
		{
			return SemiFunc.PlayerGetAll();
		}
		catch
		{
			return null;
		}
	}

	private static bool IsHostOrSingleplayer()
	{
		return Authority.IsHostOrSingleplayer();
	}

	private static void DebugLog(string message)
	{
		if (ModConfig.DebugLogging.Value)
		{
			Plugin.Log.LogInfo((object)message);
		}
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("AngelcoMilk.ShrinkCart", "ShrinkCart", "0.2.45")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "AngelcoMilk.ShrinkCart";

	public const string PluginName = "ShrinkCart";

	public const string PluginVersion = "0.2.45";

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private bool _playerScalingWasEnabled;

	private bool _wasHostOrSingleplayer;

	private void Awake()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Authority.Reset();
		ModConfig.Bind(((BaseUnityPlugin)this).Config);
		ValuableBoxScaleAdapter.Reset();
		ValuableBoxScaleAdapter.RegisterHandler();
		ShrinkerCartController.Reset();
		PlayerCartScaleController.Reset();
		DeadHeadCartScaleController.Reset();
		EnemyInCartKillController.Reset();
		_harmony = new Harmony("AngelcoMilk.ShrinkCart");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"ShrinkCart 0.2.45 loaded.");
	}

	private void Update()
	{
		bool flag = Authority.IsHostOrSingleplayer();
		if (flag && !_wasHostOrSingleplayer)
		{
			CartRegistry.RegisterExistingCarts();
			if (ModConfig.PlayerScalingEnabled())
			{
				PlayerCartScaleController.Reset();
				PlayerCartScaleController.RegisterExistingCarts();
				_playerScalingWasEnabled = true;
			}
		}
		ShrinkerCartController.Tick();
		DeadHeadCartScaleController.Tick();
		bool flag2 = flag && ModConfig.PlayerScalingEnabled();
		if (flag2)
		{
			if (!_playerScalingWasEnabled)
			{
				PlayerCartScaleController.Reset();
				PlayerCartScaleController.RegisterExistingCarts();
			}
			PlayerCartScaleController.Tick();
		}
		else if (_playerScalingWasEnabled)
		{
			if (flag)
			{
				PlayerCartScaleController.Disable();
			}
			else
			{
				PlayerCartScaleController.Reset();
			}
		}
		_playerScalingWasEnabled = flag2;
		_wasHostOrSingleplayer = flag;
	}

	private void OnDestroy()
	{
		ShrinkerCartController.RestoreAll();
		PlayerCartScaleController.RestoreAll();
		DeadHeadCartScaleController.RestoreAll();
		DeadHeadCartScaleController.Reset();
		CartRegistry.Reset();
		EnemyInCartKillController.Reset();
		Authority.Reset();
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
			_harmony = null;
		}
	}
}
internal static class ShrinkerCartController
{
	private sealed class TrackedObject
	{
		internal GameObject Target;

		internal float LastSeenInCartTime;

		internal float RestoreCheckDueTime;

		internal int LastSeenCartId;

		internal bool MarkedInCartThisPass;

		internal ShrinkCategory Category;
	}

	private sealed class CachedShrinkData
	{
		internal int ConfigVersion;

		internal bool CanShrink;

		internal ShrinkCategory Category;

		internal float Factor;
	}

	private const float LowTrackedObjectIntervalSeconds = 0.35f;

	private const float MediumTrackedObjectIntervalSeconds = 0.75f;

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

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

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

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

	private static readonly List<int> RestoreIds = new List<int>(16);

	private static readonly List<int> ExpiredCooldownIds = new List<int>(16);

	private static readonly List<GameObject> RestoreTargets = new List<GameObject>(16);

	private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PhysGrabObjectIsGunField = typeof(PhysGrabObject).GetField("isGun", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PhysGrabCartPhysGrabObjectField = typeof(PhysGrabCart).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static float _nextTickTime;

	private static float _nextRestoreCheckTime = float.PositiveInfinity;

	private static float _nextCooldownCheckTime = float.PositiveInfinity;

	internal static void Reset()
	{
		TrackedObjects.Clear();
		ShrinkDataCache.Clear();
		ReshrinkCooldownUntil.Clear();
		PermanentlyExcludedObjectIds.Clear();
		RestoreIds.Clear();
		ExpiredCooldownIds.Clear();
		RestoreTargets.Clear();
		_nextTickTime = 0f;
		_nextRestoreCheckTime = float.PositiveInfinity;
		_nextCooldownCheckTime = float.PositiveInfinity;
	}

	internal static void ProcessCartObject(PhysGrabInCart inCart, PhysGrabObject item)
	{
		if (!((Object)(object)inCart == (Object)null) && !((Object)(object)inCart.cart == (Object)null) && !((Object)(object)item == (Object)null) && IsHostOrSingleplayer() && !EnemyInCartKillController.TryKill(item))
		{
			ShrinkCategory category = ShrinkCategory.Fallback;
			float factor = 1f;
			if (ModConfig.CartShrinkingEnabled.Value && TryGetShrinkData(item, out category, out factor))
			{
				TrackOrShrink(item, category, factor, inCart.cart);
			}
		}
	}

	internal static void MarkObjectsSeenInCart(PhysGrabCart cart, List<PhysGrabObject> items)
	{
		if (!IsHostOrSingleplayer() || items == null)
		{
			return;
		}
		float time = Time.time;
		int cartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0);
		for (int i = 0; i < items.Count; i++)
		{
			PhysGrabObject val = items[i];
			GameObject val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).gameObject);
			if (!((Object)(object)val2 == (Object)null))
			{
				ShrinkCategory category;
				float factor;
				if (TrackedObjects.TryGetValue(((Object)val2).GetInstanceID(), out var value))
				{
					MarkTrackedInCart(value, time, cartId);
				}
				else if (!EnemyInCartKillController.TryKill(val) && ModConfig.CartShrinkingEnabled.Value && TryGetShrinkData(val, out category, out factor))
				{
					TrackOrShrink(val, category, factor, cart);
				}
			}
		}
	}

	internal static void Tick()
	{
		if (!IsHostOrSingleplayer())
		{
			return;
		}
		float time = Time.time;
		if (time < _nextTickTime && time < _nextRestoreCheckTime && time < _nextCooldownCheckTime)
		{
			return;
		}
		_nextTickTime = time + GetCurrentTickInterval();
		if (!ModConfig.CartShrinkingEnabled.Value)
		{
			RestoreAll();
			ClearExpiredCooldowns(time);
			return;
		}
		if (TrackedObjects.Count == 0)
		{
			ClearExpiredCooldowns(time);
			_nextRestoreCheckTime = float.PositiveInfinity;
			return;
		}
		ClearExpiredCooldowns(time);
		if (time < _nextRestoreCheckTime)
		{
			return;
		}
		RestoreIds.Clear();
		float num = float.PositiveInfinity;
		foreach (KeyValuePair<int, TrackedObject> trackedObject in TrackedObjects)
		{
			TrackedObject value = trackedObject.Value;
			bool flag = (Object)(object)value.Target != (Object)null && IsTrackedEquipped(value.Target);
			if ((Object)(object)value.Target == (Object)null || flag || time >= value.RestoreCheckDueTime)
			{
				if (flag)
				{
					DebugLog("Restoring tracked equippable before equipped use: " + ((Object)value.Target).name);
				}
				RestoreIds.Add(trackedObject.Key);
			}
			else if (value.RestoreCheckDueTime < num)
			{
				num = value.RestoreCheckDueTime;
			}
		}
		for (int i = 0; i < RestoreIds.Count; i++)
		{
			int num2 = RestoreIds[i];
			if (TrackedObjects.TryGetValue(num2, out var value2))
			{
				RestoreTrackedObject(num2, value2.Target);
				TrackedObjects.Remove(num2);
			}
		}
		_nextRestoreCheckTime = ((TrackedObjects.Count == 0) ? float.PositiveInfinity : num);
	}

	internal static void RestoreAll()
	{
		if (TrackedObjects.Count == 0)
		{
			return;
		}
		RestoreTargets.Clear();
		foreach (TrackedObject value in TrackedObjects.Values)
		{
			if ((Object)(object)value.Target != (Object)null)
			{
				RestoreTargets.Add(value.Target);
			}
		}
		TrackedObjects.Clear();
		_nextRestoreCheckTime = float.PositiveInfinity;
		for (int i = 0; i < RestoreTargets.Count; i++)
		{
			RestoreTrackedObject(((Object)RestoreTargets[i]).GetInstanceID(), RestoreTargets[i]);
		}
		RestoreTargets.Clear();
	}

	private static float GetCurrentTickInterval()
	{
		float num = ModConfig.SafeMinimumItemScanIntervalSeconds();
		if (!ModConfig.DynamicItemScanEnabledValue())
		{
			return num;
		}
		float num2 = ModConfig.SafeMaximumItemScanIntervalSeconds();
		int count = TrackedObjects.Count;
		float num3 = ((count <= 0) ? num2 : ((count <= 6) ? num : ((count <= 15) ? 0.35f : ((count > 30) ? num2 : 0.75f))));
		return Mathf.Clamp(num3, num, num2);
	}

	private static void TrackOrShrink(PhysGrabObject item, ShrinkCategory category, float factor, PhysGrabCart cart)
	{
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: 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)
		GameObject val = (((Object)(object)item == (Object)null) ? null : ((Component)item).gameObject);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)val).GetInstanceID();
		float time = Time.time;
		if (TrackedObjects.TryGetValue(instanceID, out var value))
		{
			MarkTrackedInCart(value, time, (!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0);
			value.Category = category;
		}
		else
		{
			if (IsInReshrinkCooldown(instanceID))
			{
				return;
			}
			if (ScaleManager.IsScaled(val))
			{
				TryTrackExistingShrinkCartScale(val, category, factor, cart, time);
				return;
			}
			if (category == ShrinkCategory.ValuableBox && !ValuableBoxScaleAdapter.EnsureController(val))
			{
				DebugLog("Prepared token/cosmetic box controller, waiting for ScalerCore initialization: " + ((Object)val).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item));
				return;
			}
			ScaleOptions val2 = ScaleOptions.Default;
			val2.Factor = factor;
			val2.Speed = ModConfig.SafeScaleSpeed();
			val2.Duration = 0f;
			val2.AllowedTargets = (ScaleTargets)((category == ShrinkCategory.ValuableBox) ? 15 : 12);
			val2.SuppressValueDropExpand = ModConfig.SuppressValuableDamageRestore.Value;
			val2.PreserveMass = ModConfig.ShouldPreserveMass();
			val2.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
			val2.SuppressImpactFlash = true;
			val2.SuppressCameraShake = true;
			try
			{
				if (!ScaleManager.ApplyIfNotScaled(val, val2))
				{
					if (category == ShrinkCategory.ValuableBox)
					{
						DebugLog("ScalerCore rejected token/cosmetic box shrink for " + ((Object)val).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item) + " controller=" + ((Object)(object)ScaleManager.GetController(val) != (Object)null));
					}
					return;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to shrink " + ((Object)val).name + ": " + ex.Message));
				return;
			}
			float num = time + ModConfig.SafeCartLeaveDebounceSeconds();
			TrackedObjects[instanceID] = new TrackedObject
			{
				Target = val,
				LastSeenInCartTime = time,
				RestoreCheckDueTime = num,
				LastSeenCartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0),
				MarkedInCartThisPass = true,
				Category = category
			};
			ScheduleRestoreCheck(num);
			DebugLog(string.Concat("Shrunk ", ((Object)val).name, " as ", category, " factor=", factor.ToString("0.###")));
		}
	}

	private static bool TryTrackExistingShrinkCartScale(GameObject target, ShrinkCategory category, float factor, PhysGrabCart cart, float now)
	{
		//IL_003c: 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_0042: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)target).GetInstanceID();
		if (TrackedObjects.ContainsKey(instanceID))
		{
			return true;
		}
		ScaleController controller = ScaleManager.GetController(target);
		if ((Object)(object)controller == (Object)null || !controller.IsScaled)
		{
			return false;
		}
		ScaleOptions currentOptions = controller.CurrentOptions;
		if (!LooksLikeShrinkCartOptions(currentOptions, category, factor))
		{
			return false;
		}
		float num = now + ModConfig.SafeCartLeaveDebounceSeconds();
		TrackedObjects[instanceID] = new TrackedObject
		{
			Target = target,
			LastSeenInCartTime = now,
			RestoreCheckDueTime = num,
			LastSeenCartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0),
			MarkedInCartThisPass = true,
			Category = category
		};
		ScheduleRestoreCheck(num);
		DebugLog("Adopted existing ShrinkCart-like scaled object for restore tracking: " + ((Object)target).name);
		return true;
	}

	private static bool LooksLikeShrinkCartOptions(ScaleOptions options, ShrinkCategory category, float factor)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		if (!Mathf.Approximately(options.Factor, factor))
		{
			return false;
		}
		ScaleTargets val = (ScaleTargets)((category == ShrinkCategory.ValuableBox) ? 15 : 12);
		if (options.AllowedTargets == val && options.SuppressImpactFlash && options.SuppressCameraShake && options.SuppressValueDropExpand == ModConfig.SuppressValuableDamageRestore.Value)
		{
			return options.PreserveMass == ModConfig.ShouldPreserveMass();
		}
		return false;
	}

	private static void MarkTrackedInCart(TrackedObject tracked, float now, int cartId)
	{
		if (tracked != null)
		{
			tracked.LastSeenInCartTime = now;
			tracked.RestoreCheckDueTime = now + ModConfig.SafeCartLeaveDebounceSeconds();
			tracked.LastSeenCartId = cartId;
			tracked.MarkedInCartThisPass = true;
			ScheduleRestoreCheck(tracked.RestoreCheckDueTime);
		}
	}

	private static void ScheduleRestoreCheck(float dueTime)
	{
		if (dueTime < _nextRestoreCheckTime)
		{
			_nextRestoreCheckTime = dueTime;
		}
	}

	private static void RestoreTrackedObject(int id, GameObject target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return;
		}
		BeginReshrinkCooldown(id);
		try
		{
			if (ScaleManager.IsScaled(target))
			{
				RefreshRestoreOptions(target);
				ScaleManager.Restore(target);
				DebugLog("Restored " + ((Object)target).name + " speed=" + ModConfig.SafeRestoreScaleSpeed().ToString("0.###"));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to restore " + ((Object)target).name + ": " + ex.Message));
		}
	}

	private static void RefreshRestoreOptions(GameObject target)
	{
		//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_003d: Unknown result type (might be due to invalid IL or missing references)
		ScaleController controller = ScaleManager.GetController(target);
		if (!((Object)(object)controller == (Object)null) && controller.IsScaled)
		{
			ScaleOptions currentOptions = controller.CurrentOptions;
			currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed();
			currentOptions.SuppressImpactFlash = true;
			currentOptions.SuppressCameraShake = true;
			ScaleManager.UpdateOptions(target, currentOptions);
		}
	}

	private static bool IsTrackedEquipped(GameObject target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		ItemEquippable component = target.GetComponent<ItemEquippable>();
		if ((Object)(object)component != (Object)null)
		{
			return component.IsEquipped();
		}
		return false;
	}

	private static bool TryGetShrinkData(PhysGrabObject item, out ShrinkCategory category, out float factor)
	{
		category = ShrinkCategory.Fallback;
		factor = 1f;
		if (!PassesFastCandidateChecks(item))
		{
			return false;
		}
		int instanceID = ((Object)((Component)item).gameObject).GetInstanceID();
		if (IsInReshrinkCooldown(instanceID))
		{
			return false;
		}
		if (ShrinkDataCache.TryGetValue(instanceID, out var value) && value.ConfigVersion == ModConfig.ScalingConfigVersion)
		{
			category = value.Category;
			factor = value.Factor;
			return value.CanShrink;
		}
		bool flag = ResolveShrinkData(item, out category, out factor);
		ShrinkDataCache[instanceID] = new CachedShrinkData
		{
			ConfigVersion = ModConfig.ScalingConfigVersion,
			CanShrink = flag,
			Category = category,
			Factor = factor
		};
		return flag;
	}

	private static bool PassesFastCandidateChecks(PhysGrabObject item)
	{
		if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null)
		{
			return false;
		}
		if (item.dead)
		{
			return false;
		}
		int instanceID = ((Object)((Component)item).gameObject).GetInstanceID();
		if (PermanentlyExcludedObjectIds.Contains(instanceID))
		{
			return false;
		}
		if (CartObjectGuard.IsCartLike(item))
		{
			PermanentlyExcludedObjectIds.Add(instanceID);
			return false;
		}
		if ((Object)(object)((Component)item).GetComponent<PlayerDeathHead>() != (Object)null || (Object)(object)((Component)item).GetComponentInParent<PlayerDeathHead>() != (Object)null || (Object)(object)((Component)item).GetComponentInChildren<PlayerDeathHead>(true) != (Object)null)
		{
			return false;
		}
		string cleanName = CleanName(((Object)item).name);
		if (IsPermanentlyExcludedCartItem(item, cleanName))
		{
			PermanentlyExcludedObjectIds.Add(instanceID);
			return false;
		}
		ItemEquippable component = ((Component)item).GetComponent<It