using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; 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 CartLikeObjectIds = new HashSet(); private static readonly HashSet NonCartLikeObjectIds = new HashSet(); 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() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null)) { return (Object)(object)((Component)item).GetComponentInChildren(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(); 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 Carts = new Dictionary(); private static readonly List RemoveCartIds = new List(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(); for (int i = 0; i < array.Length; i++) { RegisterCart(array[i]); } } } internal static void CleanCartContents(PhysGrabCart cart) { if (IsHostOrSingleplayer()) { CartState orCreateState = GetOrCreateState(cart); List itemsInCart = GetItemsInCart(orCreateState?.Cart); if (orCreateState != null && !((Object)(object)orCreateState.Cart == (Object)null) && itemsInCart != null) { RemoveCartLikeItems(orCreateState, 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 itemsInCart = GetItemsInCart(orCreateState.Cart); if (itemsInCart != null) { RemoveCartLikeItems(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 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 RemoveCartLikeItems(CartState state, List 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 cart-like object from cart contents: " + ((Object)state.Cart).name); } } private static void RecalculateCartCounts(PhysGrabCart cart) { List 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(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).GetComponentInParent(); } 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 GetItemsInCart(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null || PhysGrabCartItemsInCartField == null) { return null; } return PhysGrabCartItemsInCartField.GetValue(cart) as List; } 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 EnemyInCartKillController { private static readonly HashSet ExecutedEnemies = new HashSet(); 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(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.enemy != (Object)null) { return componentInParent.enemy; } return ((Component)item).GetComponentInParent(); } 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 static class HostConfigSync { private sealed class Snapshot { internal bool CartEnabled; internal bool ShrinkShopPlayerItems; internal bool PlayerScalingModuleEnabled; internal bool RestorePlayerOnDamage; internal bool EnemyInCartInstantKill; internal bool PreserveMass; internal bool SuppressValuableDamageRestore; internal float CartLeaveDebounceSeconds; internal float ReshrinkCooldownSeconds; internal float ScaleSpeed; internal float RestoreScaleSpeed; internal float PlayerCartScaleFactor; internal float PlayerCartStandTriggerSeconds; internal float PlayerCartExitGraceSeconds; internal float PlayerCartDetectionIntervalSeconds; internal readonly bool[] Enabled = new bool[14]; internal readonly float[] Factors = new float[14]; } private const string ConfigKey = "ShrinkCart.HostConfig.v2"; private const string PayloadVersion = "SC0232"; private const float SyncIntervalSeconds = 0.5f; private const int CategoryCount = 14; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; private static readonly StringBuilder Builder = new StringBuilder(640); private static Snapshot _remoteSnapshot; private static string _lastPublishedPayload; private static string _lastReadPayload; private static float _nextSyncTime; private static bool _lastWasMasterClient; private static int _lastPublishedConfigVersion = int.MinValue; internal static void Reset() { _remoteSnapshot = null; _lastPublishedPayload = null; _lastReadPayload = null; _nextSyncTime = 0f; _lastWasMasterClient = false; _lastPublishedConfigVersion = int.MinValue; } internal static void Tick() { if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null) { _remoteSnapshot = null; _lastReadPayload = null; _lastWasMasterClient = false; _lastPublishedPayload = null; _lastPublishedConfigVersion = int.MinValue; } else if (PhotonNetwork.IsMasterClient) { if (!_lastWasMasterClient) { _lastPublishedPayload = null; _lastPublishedConfigVersion = int.MinValue; } PublishIfChanged(); _lastWasMasterClient = true; } else { _lastWasMasterClient = false; if (!(Time.time < _nextSyncTime)) { _nextSyncTime = Time.time + 0.5f; ReadRemoteSnapshot(); } } } private static void PublishIfChanged() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (_lastPublishedPayload == null || _lastPublishedConfigVersion != ModConfig.ScalingConfigVersion) { string text = BuildLocalPayload(); if (text == _lastPublishedPayload) { _lastPublishedConfigVersion = ModConfig.ScalingConfigVersion; return; } Hashtable val = new Hashtable(); val[(object)"ShrinkCart.HostConfig.v2"] = text; PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); _lastPublishedPayload = text; _lastPublishedConfigVersion = ModConfig.ScalingConfigVersion; } } private static void ReadRemoteSnapshot() { if (!((Dictionary)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"ShrinkCart.HostConfig.v2", out object value)) { _remoteSnapshot = null; _lastReadPayload = null; return; } string text = value as string; if (!string.IsNullOrEmpty(text) && !(text == _lastReadPayload) && TryParseSnapshot(text, out var snapshot)) { _remoteSnapshot = snapshot; _lastReadPayload = text; } } private static string BuildLocalPayload() { Builder.Length = 0; Builder.Append("SC0232"); Append(ModConfig.CartShrinkingEnabled.Value); Append(ModConfig.ShrinkShopPlayerItems.Value); Append(ModConfig.PlayerScalingModuleEnabled.Value); Append(ModConfig.RestorePlayerOnDamage.Value); Append(ModConfig.EnemyInCartInstantKill.Value); Append(ModConfig.ShouldPreserveMass()); Append(ModConfig.SuppressValuableDamageRestore.Value); Append(ModConfig.SafeCartLeaveDebounceSeconds()); Append(ModConfig.SafeReshrinkCooldownSeconds()); Append(ModConfig.SafeScaleSpeed()); Append(ModConfig.SafeRestoreScaleSpeed()); Append(ModConfig.SafePlayerCartScaleFactor()); Append(ModConfig.SafePlayerCartStandTriggerSeconds()); Append(ModConfig.SafePlayerCartExitGraceSeconds()); Append(ModConfig.SafePlayerCartDetectionIntervalSeconds()); for (int i = 0; i < 14; i++) { GetLocalCategoryConfig((ShrinkCategory)i, out var enabled, out var factor); Append(enabled); Append(factor); } return Builder.ToString(); } private static bool TryParseSnapshot(string payload, out Snapshot snapshot) { snapshot = null; string[] array = payload.Split(new char[1] { '|' }); int num = 44; if (array.Length != num || array[0] != "SC0232") { return false; } Snapshot snapshot2 = new Snapshot(); int num2 = 1; if (!TryParseBool(array[num2++], out snapshot2.CartEnabled) || !TryParseBool(array[num2++], out snapshot2.ShrinkShopPlayerItems) || !TryParseBool(array[num2++], out snapshot2.PlayerScalingModuleEnabled) || !TryParseBool(array[num2++], out snapshot2.RestorePlayerOnDamage) || !TryParseBool(array[num2++], out snapshot2.EnemyInCartInstantKill) || !TryParseBool(array[num2++], out snapshot2.PreserveMass) || !TryParseBool(array[num2++], out snapshot2.SuppressValuableDamageRestore) || !TryParseFloat(array[num2++], out snapshot2.CartLeaveDebounceSeconds) || !TryParseFloat(array[num2++], out snapshot2.ReshrinkCooldownSeconds) || !TryParseFloat(array[num2++], out snapshot2.ScaleSpeed) || !TryParseFloat(array[num2++], out snapshot2.RestoreScaleSpeed) || !TryParseFloat(array[num2++], out snapshot2.PlayerCartScaleFactor) || !TryParseFloat(array[num2++], out snapshot2.PlayerCartStandTriggerSeconds) || !TryParseFloat(array[num2++], out snapshot2.PlayerCartExitGraceSeconds) || !TryParseFloat(array[num2++], out snapshot2.PlayerCartDetectionIntervalSeconds)) { return false; } for (int i = 0; i < 14; i++) { if (!TryParseBool(array[num2++], out snapshot2.Enabled[i]) || !TryParseFloat(array[num2++], out snapshot2.Factors[i])) { return false; } } _remoteSnapshot = snapshot2; snapshot = snapshot2; return true; } private static void GetLocalCategoryConfig(ShrinkCategory category, out bool enabled, out float factor) { switch (category) { case ShrinkCategory.Tiny: enabled = ModConfig.TinyEnabled.Value; factor = ModConfig.TinyScaleFactor.Value; break; case ShrinkCategory.Small: enabled = ModConfig.SmallEnabled.Value; factor = ModConfig.SmallScaleFactor.Value; break; case ShrinkCategory.Medium: enabled = ModConfig.MediumEnabled.Value; factor = ModConfig.MediumScaleFactor.Value; break; case ShrinkCategory.Big: enabled = ModConfig.BigEnabled.Value; factor = ModConfig.BigScaleFactor.Value; break; case ShrinkCategory.Wide: enabled = ModConfig.WideEnabled.Value; factor = ModConfig.WideScaleFactor.Value; break; case ShrinkCategory.Tall: enabled = ModConfig.TallEnabled.Value; factor = ModConfig.TallScaleFactor.Value; break; case ShrinkCategory.VeryTall: enabled = ModConfig.VeryTallEnabled.Value; factor = ModConfig.VeryTallScaleFactor.Value; break; case ShrinkCategory.EnemyOrbSmall: enabled = ModConfig.EnemyOrbEnabled.Value; factor = ModConfig.EnemyOrbSmallScaleFactor.Value; break; case ShrinkCategory.EnemyOrbMedium: enabled = ModConfig.EnemyOrbEnabled.Value; factor = ModConfig.EnemyOrbMediumScaleFactor.Value; break; case ShrinkCategory.EnemyOrbBig: enabled = ModConfig.EnemyOrbEnabled.Value; factor = ModConfig.EnemyOrbBigScaleFactor.Value; break; case ShrinkCategory.EnemyOrbBerserker: enabled = ModConfig.EnemyOrbEnabled.Value; factor = ModConfig.EnemyOrbBerserkerScaleFactor.Value; break; case ShrinkCategory.Surplus: enabled = ModConfig.SurplusEnabled.Value; factor = ModConfig.SurplusScaleFactor.Value; break; case ShrinkCategory.ValuableBox: enabled = ModConfig.ValuableBoxEnabled.Value; factor = ModConfig.ValuableBoxScaleFactor.Value; break; default: enabled = true; factor = ModConfig.FallbackScaleFactor.Value; break; } factor = Mathf.Clamp(factor, 0.05f, 1f); } private static void Append(bool value) { Builder.Append('|'); Builder.Append(value ? "1" : "0"); } private static void Append(float value) { Builder.Append('|'); Builder.Append(value.ToString("R", Invariant)); } private static bool TryParseBool(string value, out bool result) { if (value == "1") { result = true; return true; } if (value == "0") { result = false; return true; } result = false; return false; } private static bool TryParseFloat(string value, out float result) { return float.TryParse(value, NumberStyles.Float, Invariant, out result); } } internal enum ShrinkCategory { Tiny, Small, Medium, Big, Wide, Tall, VeryTall, EnemyOrbSmall, EnemyOrbMedium, EnemyOrbBig, EnemyOrbBerserker, Surplus, ValuableBox, Fallback } internal static class ModConfig { internal static ConfigEntry CartShrinkingEnabled; internal static ConfigEntry CartScaleSpeed; internal static ConfigEntry RestoreScaleSpeed; internal static ConfigEntry CartLeaveDebounceSeconds; internal static ConfigEntry ReshrinkCooldownSeconds; internal static ConfigEntry ScaleMassWithSize; internal static ConfigEntry ShrinkShopPlayerItems; internal static ConfigEntry PlayerScalingModuleEnabled; internal static ConfigEntry PlayerCartScaleFactor; internal static ConfigEntry PlayerCartStandTriggerSeconds; internal static ConfigEntry PlayerCartExitGraceSeconds; internal static ConfigEntry PlayerCartDetectionIntervalSeconds; internal static ConfigEntry RestorePlayerOnDamage; internal static ConfigEntry SuppressValuableDamageRestore; internal static ConfigEntry TinyEnabled; internal static ConfigEntry TinyScaleFactor; internal static ConfigEntry SmallEnabled; internal static ConfigEntry SmallScaleFactor; internal static ConfigEntry MediumEnabled; internal static ConfigEntry MediumScaleFactor; internal static ConfigEntry BigEnabled; internal static ConfigEntry BigScaleFactor; internal static ConfigEntry WideEnabled; internal static ConfigEntry WideScaleFactor; internal static ConfigEntry TallEnabled; internal static ConfigEntry TallScaleFactor; internal static ConfigEntry VeryTallEnabled; internal static ConfigEntry VeryTallScaleFactor; internal static ConfigEntry EnemyOrbEnabled; internal static ConfigEntry EnemyOrbSmallScaleFactor; internal static ConfigEntry EnemyOrbMediumScaleFactor; internal static ConfigEntry EnemyOrbBigScaleFactor; internal static ConfigEntry EnemyOrbBerserkerScaleFactor; internal static ConfigEntry SurplusEnabled; internal static ConfigEntry SurplusScaleFactor; internal static ConfigEntry ValuableBoxEnabled; internal static ConfigEntry ValuableBoxScaleFactor; internal static ConfigEntry FallbackScaleFactor; internal static ConfigEntry EnemyInCartInstantKill; internal static ConfigEntry DynamicItemScanEnabled; internal static ConfigEntry MinimumItemScanIntervalSeconds; internal static ConfigEntry MaximumItemScanIntervalSeconds; internal static ConfigEntry DebugLogging; internal static int ScalingConfigVersion; internal static void Bind(ConfigFile config) { CartShrinkingEnabled = config.Bind("购物车", "启用购物车缩小", true, "启用后,放进 C.A.R.T / 购物车的支持物品会自动缩小,取出后恢复。"); CartScaleSpeed = config.Bind("购物车", "放入时缩小速度", 0.5f, Ranged("ScalerCore 缩小动画速度。数值越大越快。", 0.1f, 20f)); RestoreScaleSpeed = config.Bind("购物车", "取出后放大速度", 0.2f, Ranged("正常取出购物车后的 ScalerCore 放大动画速度。数值越小越慢。", 0.1f, 20f)); CartLeaveDebounceSeconds = config.Bind("购物车", "离车防抖延迟", 0.5f, Ranged("物品触发缩小后,离开购物车检测范围多久才开始恢复原尺寸。调高可减少车边缘抽搐。", 0.1f, 10f)); ReshrinkCooldownSeconds = config.Bind("购物车", "恢复后重新缩小冷却", LegacyFloat(config, 0.5f, "购物车", "取出后恢复延迟", "Cart", "RestoreGraceSeconds"), Ranged("物品开始恢复后,等待多少秒才允许再次被购物车缩小。", 0.05f, 10f)); ScaleMassWithSize = config.Bind("购物车", "启用重量随缩放降低", !LegacyBool(config, true, "购物车", "保持原始重量", "Cart", "PreserveMass"), "启用后通过 ScalerCore 的 PreserveMass 选项允许支持对象按缩放倍率降低质量;关闭则只改变视觉尺寸并保持原始重量。"); ShrinkShopPlayerItems = config.Bind("购物车", "启用商店用品缩小", false, "启用后,枪、血包、近战、手雷、工具、无人机、宝珠、升级、追踪器、地雷等商店购买类实用品会使用默认缩小倍率。大小推车、C.A.R.T. Cannon 和 C.A.R.T. Laser 始终不会缩小。"); PlayerScalingModuleEnabled = config.Bind("玩家缩放", "启用玩家缩放", true, "默认开启。启用后才会运行玩家站车检测和玩家缩放;关闭时 ShrinkCart 不参与任何玩家缩放逻辑。"); PlayerCartScaleFactor = config.Bind("玩家缩放", "玩家进车缩放倍率", 0.55f, Ranged("开启“启用玩家缩放”后,玩家站在购物车中心区域触发缩放时的目标尺寸比例。", 0.05f, 1f)); PlayerCartStandTriggerSeconds = config.Bind("玩家缩放", "玩家站车触发时间", 2f, Ranged("开启“启用玩家缩放”后,玩家站在购物车中心区域多久才切换缩小/恢复。离开中心区域会重置计时。", 0.25f, 10f)); PlayerCartExitGraceSeconds = config.Bind("玩家缩放", "玩家离车判定宽容时间", 0.6f, Ranged("玩家仍在购物车中心区域附近但短暂跳起、踩到车内物品或被货物顶起时,保留车内状态多久后才判定离开。设为 0 可恢复严格判定。", 0f, 2f)); PlayerCartDetectionIntervalSeconds = config.Bind("玩家缩放", "玩家检测间隔", 0.75f, Ranged("玩家缩放开启时,主机多久检测一次玩家是否位于正式购物车底面投影范围内。数值越大越省性能,也越能防误触发。", 0.25f, 2f)); RestorePlayerOnDamage = config.Bind("玩家缩放", "启用玩家受伤后自动恢复", true, "启用后,玩家缩小时使用 ScalerCore 的受伤/碰撞恢复链路;关闭后,玩家只会通过再次站车切换恢复。"); SuppressValuableDamageRestore = config.Bind("购物车", "防止碰撞弹回原尺寸", true, "启用后,贵重物品在购物车里轻微碰撞时不会立刻弹回原尺寸。ScalerCore 的安全恢复仍会保留。"); TinyEnabled = BindCategoryEnabled(config, "Tiny 微型贵重物", defaultValue: true); TinyScaleFactor = BindCategoryFactor(config, "Tiny 微型贵重物", 0.8f); SmallEnabled = BindCategoryEnabled(config, "Small 小贵重物", defaultValue: true); SmallScaleFactor = BindCategoryFactor(config, "Small 小贵重物", 0.6f); MediumEnabled = BindCategoryEnabled(config, "Medium 中贵重物", defaultValue: true); MediumScaleFactor = BindCategoryFactor(config, "Medium 中贵重物", 0.45f); BigEnabled = BindCategoryEnabled(config, "Big 大贵重物", defaultValue: true); BigScaleFactor = BindCategoryFactor(config, "Big 大贵重物", 0.4f); WideEnabled = BindCategoryEnabled(config, "Wide 宽贵重物", defaultValue: true); WideScaleFactor = BindCategoryFactor(config, "Wide 宽贵重物", 0.35f); TallEnabled = BindCategoryEnabled(config, "Tall 高贵重物", defaultValue: true); TallScaleFactor = BindCategoryFactor(config, "Tall 高贵重物", 0.35f); VeryTallEnabled = BindCategoryEnabled(config, "VeryTall 超高贵重物", defaultValue: true); VeryTallScaleFactor = BindCategoryFactor(config, "VeryTall 超高贵重物", 0.25f); EnemyOrbEnabled = config.Bind("敌人球", "启用敌人球缩小", true, "启用后,Enemy - Small/Medium/Big/Berserker 类贵重物会按下方倍率缩小。"); EnemyOrbSmallScaleFactor = config.Bind("敌人球", "Small 敌人球倍率", 0.8f, Ranged("Small 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbMediumScaleFactor = config.Bind("敌人球", "Medium 敌人球倍率", 0.65f, Ranged("Medium 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbBigScaleFactor = config.Bind("敌人球", "Big 敌人球倍率", 0.45f, Ranged("Big 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbBerserkerScaleFactor = config.Bind("敌人球", "Berserker 敌人球倍率", 0.45f, Ranged("Berserker 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); SurplusEnabled = config.Bind("特殊物品", "启用钱袋/Surplus 缩小", LegacyBool(config, true, "特殊物品", "启用 Surplus 缩小"), "启用后,钱袋/SurplusValuable 会使用单独倍率。"); SurplusScaleFactor = config.Bind("特殊物品", "钱袋/Surplus 倍率", LegacyFloat(config, 0.25f, "特殊物品", "Surplus 倍率"), Ranged("钱袋/SurplusValuable 放入购物车后的目标尺寸比例。", 0.05f, 1f)); ValuableBoxEnabled = config.Bind("特殊物品", "启用代币箱缩小", true, "启用后,新版本抽奖用代币箱 ItemValuableBox 会使用单独倍率。"); ValuableBoxScaleFactor = config.Bind("特殊物品", "代币箱倍率", 0.4f, Ranged("抽奖用代币箱 ItemValuableBox 放入购物车后的目标尺寸比例。", 0.05f, 1f)); FallbackScaleFactor = config.Bind("商店用品", "商店用品缩小倍率", LegacyFloat(config, 0.5f, "普通或未知物品", "默认缩小倍率"), Ranged("开启“启用商店用品缩小”后,枪、血包、工具等实用品放入购物车后的目标尺寸比例。也作为未知贵重物分类的兜底倍率。", 0.05f, 1f)); EnemyInCartInstantKill = config.Bind("车辆碾压", "敌人进车秒杀", LegacyBool(config, true, "车辆碾压", "车辆碾压秒杀敌人", "VehicleCrush", "InstantKillEnemies"), "启用后,敌人或敌人刚体进入购物车时会立刻死亡。此功能复刻 ShrinkerCartPlus 的敌人进车秒杀逻辑。"); DynamicItemScanEnabled = config.Bind("性能", "启用动态物品扫描", true, "启用后,ShrinkCart 会根据当前跟踪的缩小物品数量自动拉长状态扫描间隔,减少车内物品很多时的卡顿。"); MinimumItemScanIntervalSeconds = config.Bind("性能", "最小物品扫描间隔", 0.15f, Ranged("少量物品时的最短状态扫描间隔。数值越小,离车恢复越灵敏,但开销更高。", 0.05f, 1f)); MaximumItemScanIntervalSeconds = config.Bind("性能", "最大物品扫描间隔", 1f, Ranged("大量物品时允许使用的最长状态扫描间隔。数值越大越省性能,但离车恢复最多会延后一个扫描间隔。", 0.1f, 2f)); DebugLogging = config.Bind("诊断", "启用调试日志", false, "启用后,在 BepInEx 日志中写入更多缩小、恢复、敌人进车和碾压识别信息。"); WatchScaling(CartShrinkingEnabled); WatchScaling(CartScaleSpeed); WatchScaling(RestoreScaleSpeed); WatchScaling(CartLeaveDebounceSeconds); WatchScaling(ReshrinkCooldownSeconds); WatchScaling(ScaleMassWithSize); WatchScaling(ShrinkShopPlayerItems); WatchScaling(PlayerScalingModuleEnabled); WatchScaling(PlayerCartScaleFactor); WatchScaling(PlayerCartStandTriggerSeconds); WatchScaling(PlayerCartExitGraceSeconds); WatchScaling(PlayerCartDetectionIntervalSeconds); WatchScaling(RestorePlayerOnDamage); WatchScaling(SuppressValuableDamageRestore); WatchScaling(TinyEnabled); WatchScaling(TinyScaleFactor); WatchScaling(SmallEnabled); WatchScaling(SmallScaleFactor); WatchScaling(MediumEnabled); WatchScaling(MediumScaleFactor); WatchScaling(BigEnabled); WatchScaling(BigScaleFactor); WatchScaling(WideEnabled); WatchScaling(WideScaleFactor); WatchScaling(TallEnabled); WatchScaling(TallScaleFactor); WatchScaling(VeryTallEnabled); WatchScaling(VeryTallScaleFactor); WatchScaling(EnemyOrbEnabled); WatchScaling(EnemyOrbSmallScaleFactor); WatchScaling(EnemyOrbMediumScaleFactor); WatchScaling(EnemyOrbBigScaleFactor); WatchScaling(EnemyOrbBerserkerScaleFactor); WatchScaling(SurplusEnabled); WatchScaling(SurplusScaleFactor); WatchScaling(ValuableBoxEnabled); WatchScaling(ValuableBoxScaleFactor); WatchScaling(FallbackScaleFactor); RemoveDeprecatedEntries(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 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 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 BindCategoryEnabled(ConfigFile config, string section, bool defaultValue) { return config.Bind(section, "启用此分类缩小", defaultValue, "启用后,该分类物品放入购物车时会自动缩小。"); } private static ConfigEntry BindCategoryFactor(ConfigFile config, string section, float defaultValue) { return config.Bind(section, "缩小倍率", defaultValue, Ranged("该分类物品放入购物车后的目标尺寸比例。0.4 表示原尺寸的 40%。", 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(min, max), new object[0]); } private static bool TryCategory(ConfigEntry enabled, ConfigEntry factorEntry, out float factor) { factor = SafeFactor(factorEntry.Value); return enabled.Value; } private static bool TryEnemyOrb(ConfigEntry 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 bool LegacyBool(ConfigFile config, bool defaultValue, params string[] sectionKeyPairs) { ConfigEntry val = default(ConfigEntry); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val)) { return val.Value; } } return defaultValue; } private static float LegacyFloat(ConfigFile config, float defaultValue, params string[] sectionKeyPairs) { ConfigEntry val = default(ConfigEntry); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry(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, "购物车", "车辆脱困强度"); if (flag | RemoveDeprecatedEntry(config, "玩家缩放", "旧版玩家缩放开关(已停用)")) { config.Save(); } } private static bool RemoveDeprecatedEntry(ConfigFile config, string section, string key) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown return config.Remove(new ConfigDefinition(section, key)); } private static void WatchScaling(ConfigEntry entry) { entry.SettingChanged += OnScalingSettingChanged; } private static void OnScalingSettingChanged(object sender, EventArgs args) { ScalingConfigVersion++; } } internal static class PlayerCartScaleController { private sealed class PlayerState { internal PlayerAvatar Player; internal bool ShrinkCartScaled; 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 RegisteredCarts = new Dictionary(); private static readonly Dictionary PlayerStates = new Dictionary(); private static readonly HashSet ExcludedCartIds = new HashSet(); private static readonly List RemoveCartIds = new List(8); private static readonly List RemovePlayerIds = new List(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(); 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 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.ShrinkCartScaled && (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; } if (value.ShrinkCartScaled && !ScaleManager.IsScaled(((Component)player).gameObject)) { value.ShrinkCartScaled = false; } 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.ShrinkCartScaled) { if (RestorePlayer(((Component)player).gameObject)) { value.ShrinkCartScaled = false; DebugLog("Restored player after standing in cart center: " + ((Object)player).name); } } else if (ScaleManager.IsScaled(((Component)player).gameObject)) { DebugLog("Skipped player cart shrink because another scale session is active: " + ((Object)player).name); } else if (ShrinkPlayer(((Component)player).gameObject)) { value.ShrinkCartScaled = true; DebugLog("Shrunk player after standing in cart center: " + ((Object)player).name); } } } private static bool ShrinkPlayer(GameObject target) { //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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return false; } ScaleOptions @default = ScaleOptions.Default; @default.Factor = ModConfig.SafePlayerCartScaleFactor(); @default.Speed = ModConfig.SafeScaleSpeed(); @default.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); @default.Duration = 0f; @default.AllowedTargets = (ScaleTargets)1; @default.SuppressImpactFlash = true; @default.SuppressCameraShake = true; @default.IgnoreBonkExpand = ModConfig.RestorePlayerOnDamage != null && !ModConfig.RestorePlayerOnDamage.Value; @default.RejectExternalApply = false; try { bool flag = ScaleManager.ApplyIfNotScaled(target, @default); if (!flag) { DebugLog("ScalerCore rejected player cart shrink: " + ((Object)target).name); } return flag; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to shrink 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 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 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 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 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.33")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "AngelcoMilk.ShrinkCart"; public const string PluginName = "ShrinkCart"; public const string PluginVersion = "0.2.33"; 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(); EnemyInCartKillController.Reset(); HostConfigSync.Reset(); _harmony = new Harmony("AngelcoMilk.ShrinkCart"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ShrinkCart 0.2.33 loaded."); } private void Update() { bool flag = Authority.IsHostOrSingleplayer(); if (flag && !_wasHostOrSingleplayer) { CartRegistry.RegisterExistingCarts(); if (ModConfig.PlayerScalingEnabled()) { PlayerCartScaleController.Reset(); PlayerCartScaleController.RegisterExistingCarts(); _playerScalingWasEnabled = true; } } HostConfigSync.Tick(); ShrinkerCartController.Tick(); bool flag2 = ModConfig.PlayerScalingEnabled(); if (flag2) { if (!_playerScalingWasEnabled) { PlayerCartScaleController.Reset(); PlayerCartScaleController.RegisterExistingCarts(); } PlayerCartScaleController.Tick(); } else if (_playerScalingWasEnabled) { PlayerCartScaleController.Disable(); } _playerScalingWasEnabled = flag2; _wasHostOrSingleplayer = flag; } private void OnDestroy() { ShrinkerCartController.RestoreAll(); PlayerCartScaleController.RestoreAll(); CartRegistry.Reset(); HostConfigSync.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 TrackedObjects = new Dictionary(); private static readonly Dictionary ShrinkDataCache = new Dictionary(); private static readonly Dictionary ReshrinkCooldownUntil = new Dictionary(); private static readonly HashSet PermanentlyExcludedObjectIds = new HashSet(); private static readonly List RestoreIds = new List(16); private static readonly List ExpiredCooldownIds = new List(16); private static readonly List RestoreTargets = new List(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 items) { if (!IsHostOrSingleplayer() || items == null || TrackedObjects.Count == 0) { 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) && TrackedObjects.TryGetValue(((Object)val2).GetInstanceID(), out var value)) { MarkTrackedInCart(value, time, cartId); } } } 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 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_010d: 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) || ScaleManager.IsScaled(val)) { 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 @default = ScaleOptions.Default; @default.Factor = factor; @default.Speed = ModConfig.SafeScaleSpeed(); @default.Duration = 0f; @default.AllowedTargets = (ScaleTargets)((category == ShrinkCategory.ValuableBox) ? 15 : 12); @default.SuppressValueDropExpand = ModConfig.SuppressValuableDamageRestore.Value; @default.PreserveMass = ModConfig.ShouldPreserveMass(); @default.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); @default.SuppressImpactFlash = true; @default.SuppressCameraShake = true; try { if (!ScaleManager.ApplyIfNotScaled(val, @default)) { 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 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(); 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; } string cleanName = CleanName(((Object)item).name); if (IsPermanentlyExcludedCartItem(item, cleanName)) { PermanentlyExcludedObjectIds.Add(instanceID); return false; } ItemEquippable component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null && component.IsEquipped()) { return false; } if ((Object)(object)item.rb != (Object)null && item.rb.isKinematic) { return false; } return true; } private static bool ResolveShrinkData(PhysGrabObject item, out ShrinkCategory category, out float factor) { category = ShrinkCategory.Fallback; factor = 1f; string cleanName = CleanName(((Object)item).name); if (!TryResolveCategory(item, cleanName, out category)) { if (IsShopPlayerItem(item)) { if (!ModConfig.ShrinkShopPlayerItems.Value) { return false; } category = ShrinkCategory.Fallback; return ModConfig.TryGetScaleFactor(category, out factor); } return false; } return ModConfig.TryGetScaleFactor(category, out factor); } private static bool IsPermanentlyExcludedCartItem(PhysGrabObject item, string cleanName) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Invalid comparison between Unknown and I4 PhysGrabCart component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return true; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null && IsCartRootPhysObject(component, item)) { return true; } if ((Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null) { return true; } if (cleanName == "Item Cart Cannon" || cleanName == "Item Cart Laser") { return true; } ItemAttributes component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 == (Object)null) { return false; } if (!TryGetItemType(component2, out var itemType)) { return false; } if ((int)itemType != 2 && (int)itemType != 14) { return (int)itemType == 12; } return true; } private static bool IsCartRootPhysObject(PhysGrabCart cart, PhysGrabObject item) { if ((Object)(object)cart == (Object)null || (Object)(object)item == (Object)null || PhysGrabCartPhysGrabObjectField == null) { return false; } object? value = PhysGrabCartPhysGrabObjectField.GetValue(cart); return (Object)((value is PhysGrabObject) ? value : null) == (Object)(object)item; } private static bool IsShopPlayerItem(PhysGrabObject item) { ItemAttributes component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return true; } ItemEquippable component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 != (Object)null) { return true; } if (TryGetIsGun(item)) { return true; } return (Object)(object)((Component)item).GetComponent() != (Object)null; } private static bool TryGetItemType(ItemAttributes attributes, out itemType itemType) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected I4, but got Unknown itemType = (itemType)0; if ((Object)(object)attributes == (Object)null || ItemAttributesItemTypeField == null) { return false; } object value = ItemAttributesItemTypeField.GetValue(attributes); if (!(value is itemType)) { return false; } itemType = (itemType)(int)(itemType)value; return true; } private static bool TryGetIsGun(PhysGrabObject item) { if ((Object)(object)item == (Object)null || PhysGrabObjectIsGunField == null) { return false; } object value = PhysGrabObjectIsGunField.GetValue(item); if (value is bool) { return (bool)value; } return false; } private static bool TryResolveCategory(PhysGrabObject item, string cleanName, out ShrinkCategory category) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) category = ShrinkCategory.Fallback; if ((Object)(object)((Component)item).GetComponent() != (Object)null) { category = ShrinkCategory.Surplus; return true; } if (ValuableBoxScaleAdapter.IsValuableBox(item)) { category = ShrinkCategory.ValuableBox; DebugLog("Resolved token/cosmetic box category for " + ((Object)item).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item)); return true; } if (TryResolveEnemyOrb(cleanName, out category)) { return true; } ValuableObject component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } category = FromVolumeType(component.volumeType); return true; } private static ShrinkCategory FromVolumeType(Type volumeType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown return (int)volumeType switch { 0 => ShrinkCategory.Tiny, 1 => ShrinkCategory.Small, 2 => ShrinkCategory.Medium, 3 => ShrinkCategory.Big, 4 => ShrinkCategory.Wide, 5 => ShrinkCategory.Tall, 6 => ShrinkCategory.VeryTall, _ => ShrinkCategory.Fallback, }; } private static bool TryResolveEnemyOrb(string cleanName, out ShrinkCategory category) { category = ShrinkCategory.Fallback; if (string.IsNullOrEmpty(cleanName)) { return false; } if (!cleanName.StartsWith("Enemy", StringComparison.OrdinalIgnoreCase)) { return false; } string[] array = cleanName.Split(new char[1] { '-' }); if (array.Length < 2) { return false; } string text = array[1].Trim(); if (text.Equals("Small", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbSmall; return true; } if (text.Equals("Medium", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbMedium; return true; } if (text.Equals("Big", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbBig; return true; } if (text.Equals("Berserker", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbBerserker; return true; } return false; } private static void BeginReshrinkCooldown(int id) { float num = Time.time + ModConfig.SafeReshrinkCooldownSeconds(); ReshrinkCooldownUntil[id] = num; if (num < _nextCooldownCheckTime) { _nextCooldownCheckTime = num; } } private static bool IsInReshrinkCooldown(int id) { if (!ReshrinkCooldownUntil.TryGetValue(id, out var value)) { return false; } if (Time.time < value) { return true; } ReshrinkCooldownUntil.Remove(id); return false; } private static void ClearExpiredCooldowns(float now) { if (ReshrinkCooldownUntil.Count == 0) { _nextCooldownCheckTime = float.PositiveInfinity; } else { if (now < _nextCooldownCheckTime) { return; } ExpiredCooldownIds.Clear(); float num = float.PositiveInfinity; foreach (KeyValuePair item in ReshrinkCooldownUntil) { if (now >= item.Value) { ExpiredCooldownIds.Add(item.Key); } else if (item.Value < num) { num = item.Value; } } for (int i = 0; i < ExpiredCooldownIds.Count; i++) { ReshrinkCooldownUntil.Remove(ExpiredCooldownIds[i]); } ExpiredCooldownIds.Clear(); _nextCooldownCheckTime = ((ReshrinkCooldownUntil.Count == 0) ? float.PositiveInfinity : num); } } private static string CleanName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } return name.Replace("Valuable ", string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [HarmonyPatch(typeof(PhysGrabInCart), "Add")] internal static class PhysGrabInCartAddPatch { private static bool Prefix(PhysGrabInCart __instance, PhysGrabObject _physGrabObject) { if (!Authority.IsHostOrSingleplayer()) { return true; } if (CartObjectGuard.ShouldBlockCartInCart(__instance, _physGrabObject)) { CartRegistry.HandleBlockedCartInCart(__instance, _physGrabObject); return false; } return true; } private static void Postfix(PhysGrabInCart __instance, PhysGrabObject _physGrabObject) { ShrinkerCartController.ProcessCartObject(__instance, _physGrabObject); } } [HarmonyPatch(typeof(PhysGrabCart), "Start")] internal static class PhysGrabCartStartPatch { private static void Postfix(PhysGrabCart __instance) { CartRegistry.RegisterCart(__instance); if (ModConfig.PlayerScalingEnabled()) { PlayerCartScaleController.RegisterCart(__instance); } } } [HarmonyPatch(typeof(PhysGrabCart), "ObjectsInCart")] internal static class PhysGrabCartObjectsInCartPatch { private static void Postfix(PhysGrabCart __instance) { CartRegistry.CleanCartContents(__instance); } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] internal static class RunManagerChangeLevelPatch { private static void Prefix() { ShrinkerCartController.RestoreAll(); PlayerCartScaleController.RestoreAll(); PlayerCartScaleController.Reset(); CartRegistry.Reset(); ValuableBoxScaleAdapter.Reset(); EnemyInCartKillController.Reset(); HostConfigSync.Reset(); } } internal static class ValuableBoxScaleAdapter { private sealed class ValuableBoxHandler : IScaleHandler { public void Setup(ScaleController ctrl) { } public void OnScale(ScaleController ctrl) { } public void OnRestore(ScaleController ctrl, bool isBonk) { } public void OnUpdate(ScaleController ctrl) { } public void OnLateUpdate(ScaleController ctrl) { } public void OnDestroy(ScaleController ctrl) { } } private const int HandlerPriority = -10; private static readonly HashSet ValuableBoxObjectIds = new HashSet(); private static readonly HashSet NonValuableBoxObjectIds = new HashSet(); private static bool _registered; internal static void Reset() { ValuableBoxObjectIds.Clear(); NonValuableBoxObjectIds.Clear(); } internal static void RegisterHandler() { if (!_registered) { ScaleHandlerRegistry.Register((IScaleHandler)(object)new ValuableBoxHandler(), (Func)IsValuableBoxScaleTarget, -10); _registered = true; } } internal static bool IsValuableBox(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 (ValuableBoxObjectIds.Contains(instanceID)) { return true; } if (NonValuableBoxObjectIds.Contains(instanceID)) { return false; } bool flag = (Object)(object)FindCosmeticWorldObject(item) != (Object)null || (Object)(object)FindValuableBox(item) != (Object)null; if (flag) { ValuableBoxObjectIds.Add(instanceID); } else { NonValuableBoxObjectIds.Add(instanceID); } return flag; } internal static bool EnsureController(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } if ((Object)(object)target.GetComponent() != (Object)null) { return true; } if ((Object)(object)target.GetComponent() != (Object)null || (Object)(object)target.GetComponentInParent() != (Object)null || (Object)(object)target.GetComponentInChildren(true) != (Object)null) { DebugLog("Token/cosmetic box is waiting for ScalerCore cosmetic controller: " + ((Object)target).name); return false; } if (!IsValuableBoxScaleTarget(target)) { return false; } target.AddComponent(); DebugLog("Attached ScalerCore controller to ItemValuableBox token box: " + ((Object)target).name); return false; } internal static void EnsureController(ItemValuableBox box) { PhysGrabObject val = FindOwnerPhysGrabObject(box); if ((Object)(object)val != (Object)null) { EnsureController(((Component)val).gameObject); } } private static bool IsValuableBoxScaleTarget(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } PhysGrabObject component = target.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } ItemValuableBox val = FindValuableBox(component); if ((Object)(object)val != (Object)null) { return (Object)(object)FindOwnerPhysGrabObject(val) == (Object)(object)component; } return false; } internal static string DescribeSpecialBox(PhysGrabObject item) { if ((Object)(object)FindCosmeticWorldObject(item) != (Object)null) { return "CosmeticWorldObject"; } if ((Object)(object)FindValuableBox(item) != (Object)null) { return "ItemValuableBox"; } return "none"; } private static CosmeticWorldObject FindCosmeticWorldObject(PhysGrabObject item) { if ((Object)(object)item == (Object)null) { return null; } CosmeticWorldObject component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren(true); } private static ItemValuableBox FindValuableBox(PhysGrabObject item) { if ((Object)(object)item == (Object)null) { return null; } ItemValuableBox component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren(true); } private static PhysGrabObject FindOwnerPhysGrabObject(ItemValuableBox box) { if ((Object)(object)box == (Object)null) { return null; } PhysGrabObject component = ((Component)box).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)box).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)box).GetComponentInChildren(true); } private static void DebugLog(string message) { if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [HarmonyPatch(typeof(ItemValuableBox), "Start")] internal static class ItemValuableBoxStartPatch { private static void Postfix(ItemValuableBox __instance) { if ((Object)(object)__instance != (Object)null) { ValuableBoxScaleAdapter.EnsureController(__instance); } } } [HarmonyPatch(typeof(PhysGrabObject), "Start")] internal static class PhysGrabObjectStartValuableBoxPatch { private static void Postfix(PhysGrabObject __instance) { if ((Object)(object)__instance != (Object)null) { ValuableBoxScaleAdapter.EnsureController(((Component)__instance).gameObject); } } }