Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ShrinkCart v0.2.33
BepInEx/plugins/ShrinkCart/ShrinkCart.dll
Decompiled 3 days agousing 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<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) { 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<PhysGrabObject> 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<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 RemoveCartLikeItems(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 cart-like 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 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 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, object>)(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<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> 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> 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 int ScalingConfigVersion; internal static void Bind(ConfigFile config) { CartShrinkingEnabled = config.Bind<bool>("购物车", "启用购物车缩小", true, "启用后,放进 C.A.R.T / 购物车的支持物品会自动缩小,取出后恢复。"); CartScaleSpeed = config.Bind<float>("购物车", "放入时缩小速度", 0.5f, Ranged("ScalerCore 缩小动画速度。数值越大越快。", 0.1f, 20f)); RestoreScaleSpeed = config.Bind<float>("购物车", "取出后放大速度", 0.2f, Ranged("正常取出购物车后的 ScalerCore 放大动画速度。数值越小越慢。", 0.1f, 20f)); CartLeaveDebounceSeconds = config.Bind<float>("购物车", "离车防抖延迟", 0.5f, Ranged("物品触发缩小后,离开购物车检测范围多久才开始恢复原尺寸。调高可减少车边缘抽搐。", 0.1f, 10f)); ReshrinkCooldownSeconds = config.Bind<float>("购物车", "恢复后重新缩小冷却", LegacyFloat(config, 0.5f, "购物车", "取出后恢复延迟", "Cart", "RestoreGraceSeconds"), Ranged("物品开始恢复后,等待多少秒才允许再次被购物车缩小。", 0.05f, 10f)); ScaleMassWithSize = config.Bind<bool>("购物车", "启用重量随缩放降低", !LegacyBool(config, true, "购物车", "保持原始重量", "Cart", "PreserveMass"), "启用后通过 ScalerCore 的 PreserveMass 选项允许支持对象按缩放倍率降低质量;关闭则只改变视觉尺寸并保持原始重量。"); ShrinkShopPlayerItems = config.Bind<bool>("购物车", "启用商店用品缩小", false, "启用后,枪、血包、近战、手雷、工具、无人机、宝珠、升级、追踪器、地雷等商店购买类实用品会使用默认缩小倍率。大小推车、C.A.R.T. Cannon 和 C.A.R.T. Laser 始终不会缩小。"); PlayerScalingModuleEnabled = config.Bind<bool>("玩家缩放", "启用玩家缩放", true, "默认开启。启用后才会运行玩家站车检测和玩家缩放;关闭时 ShrinkCart 不参与任何玩家缩放逻辑。"); PlayerCartScaleFactor = config.Bind<float>("玩家缩放", "玩家进车缩放倍率", 0.55f, Ranged("开启“启用玩家缩放”后,玩家站在购物车中心区域触发缩放时的目标尺寸比例。", 0.05f, 1f)); PlayerCartStandTriggerSeconds = config.Bind<float>("玩家缩放", "玩家站车触发时间", 2f, Ranged("开启“启用玩家缩放”后,玩家站在购物车中心区域多久才切换缩小/恢复。离开中心区域会重置计时。", 0.25f, 10f)); PlayerCartExitGraceSeconds = config.Bind<float>("玩家缩放", "玩家离车判定宽容时间", 0.6f, Ranged("玩家仍在购物车中心区域附近但短暂跳起、踩到车内物品或被货物顶起时,保留车内状态多久后才判定离开。设为 0 可恢复严格判定。", 0f, 2f)); PlayerCartDetectionIntervalSeconds = config.Bind<float>("玩家缩放", "玩家检测间隔", 0.75f, Ranged("玩家缩放开启时,主机多久检测一次玩家是否位于正式购物车底面投影范围内。数值越大越省性能,也越能防误触发。", 0.25f, 2f)); RestorePlayerOnDamage = config.Bind<bool>("玩家缩放", "启用玩家受伤后自动恢复", true, "启用后,玩家缩小时使用 ScalerCore 的受伤/碰撞恢复链路;关闭后,玩家只会通过再次站车切换恢复。"); SuppressValuableDamageRestore = config.Bind<bool>("购物车", "防止碰撞弹回原尺寸", 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<bool>("敌人球", "启用敌人球缩小", true, "启用后,Enemy - Small/Medium/Big/Berserker 类贵重物会按下方倍率缩小。"); EnemyOrbSmallScaleFactor = config.Bind<float>("敌人球", "Small 敌人球倍率", 0.8f, Ranged("Small 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbMediumScaleFactor = config.Bind<float>("敌人球", "Medium 敌人球倍率", 0.65f, Ranged("Medium 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbBigScaleFactor = config.Bind<float>("敌人球", "Big 敌人球倍率", 0.45f, Ranged("Big 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); EnemyOrbBerserkerScaleFactor = config.Bind<float>("敌人球", "Berserker 敌人球倍率", 0.45f, Ranged("Berserker 敌人球放入购物车后的目标尺寸比例。", 0.05f, 1f)); SurplusEnabled = config.Bind<bool>("特殊物品", "启用钱袋/Surplus 缩小", LegacyBool(config, true, "特殊物品", "启用 Surplus 缩小"), "启用后,钱袋/SurplusValuable 会使用单独倍率。"); SurplusScaleFactor = config.Bind<float>("特殊物品", "钱袋/Surplus 倍率", LegacyFloat(config, 0.25f, "特殊物品", "Surplus 倍率"), Ranged("钱袋/SurplusValuable 放入购物车后的目标尺寸比例。", 0.05f, 1f)); ValuableBoxEnabled = config.Bind<bool>("特殊物品", "启用代币箱缩小", true, "启用后,新版本抽奖用代币箱 ItemValuableBox 会使用单独倍率。"); ValuableBoxScaleFactor = config.Bind<float>("特殊物品", "代币箱倍率", 0.4f, Ranged("抽奖用代币箱 ItemValuableBox 放入购物车后的目标尺寸比例。", 0.05f, 1f)); FallbackScaleFactor = config.Bind<float>("商店用品", "商店用品缩小倍率", LegacyFloat(config, 0.5f, "普通或未知物品", "默认缩小倍率"), Ranged("开启“启用商店用品缩小”后,枪、血包、工具等实用品放入购物车后的目标尺寸比例。也作为未知贵重物分类的兜底倍率。", 0.05f, 1f)); EnemyInCartInstantKill = config.Bind<bool>("车辆碾压", "敌人进车秒杀", LegacyBool(config, true, "车辆碾压", "车辆碾压秒杀敌人", "VehicleCrush", "InstantKillEnemies"), "启用后,敌人或敌人刚体进入购物车时会立刻死亡。此功能复刻 ShrinkerCartPlus 的敌人进车秒杀逻辑。"); DynamicItemScanEnabled = config.Bind<bool>("性能", "启用动态物品扫描", true, "启用后,ShrinkCart 会根据当前跟踪的缩小物品数量自动拉长状态扫描间隔,减少车内物品很多时的卡顿。"); MinimumItemScanIntervalSeconds = config.Bind<float>("性能", "最小物品扫描间隔", 0.15f, Ranged("少量物品时的最短状态扫描间隔。数值越小,离车恢复越灵敏,但开销更高。", 0.05f, 1f)); MaximumItemScanIntervalSeconds = config.Bind<float>("性能", "最大物品扫描间隔", 1f, Ranged("大量物品时允许使用的最长状态扫描间隔。数值越大越省性能,但离车恢复最多会延后一个扫描间隔。", 0.1f, 2f)); DebugLogging = config.Bind<bool>("诊断", "启用调试日志", false, "启用后,在 BepInEx 日志中写入更多缩小、恢复、敌人进车和碾压识别信息。"); 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>(PlayerCartStandTriggerSeconds); WatchScaling<float>(PlayerCartExitGraceSeconds); WatchScaling<float>(PlayerCartDetectionIntervalSeconds); WatchScaling<bool>(RestorePlayerOnDamage); WatchScaling<bool>(SuppressValuableDamageRestore); 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); 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<bool> BindCategoryEnabled(ConfigFile config, string section, bool defaultValue) { return config.Bind<bool>(section, "启用此分类缩小", defaultValue, "启用后,该分类物品放入购物车时会自动缩小。"); } private static ConfigEntry<float> BindCategoryFactor(ConfigFile config, string section, float defaultValue) { return config.Bind<float>(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<float>(min, max), new object[0]); } 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 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, "购物车", "车辆脱困强度"); 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<T>(ConfigEntry<T> 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<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.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<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.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<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 || 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<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_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<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; } string cleanName = CleanName(((Object)item).name); if (IsPermanentlyExcludedCartItem(item, cleanName)) { PermanentlyExcludedObjectIds.Add(instanceID); return false; } ItemEquippable component = ((Component)item).GetComponent<ItemEquippable>(); 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<PhysGrabCart>(); if ((Object)(object)component != (Object)null) { return true; } component = ((Component)item).GetComponentInParent<PhysGrabCart>(); if ((Object)(object)component != (Object)null && IsCartRootPhysObject(component, item)) { return true; } if ((Object)(object)((Component)item).GetComponent<ItemVehicle>() != (Object)null || (Object)(object)((Component)item).GetComponentInParent<ItemVehicle>() != (Object)null) { return true; } if ((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) { return true; } if (cleanName == "Item Cart Cannon" || cleanName == "Item Cart Laser") { return true; } ItemAttributes component2 = ((Component)item).GetComponent<ItemAttributes>(); 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<ItemAttributes>(); if ((Object)(object)component != (Object)null) { return true; } ItemEquippable component2 = ((Component)item).GetComponent<ItemEquippable>(); if ((Object)(object)component2 != (Object)null) { return true; } if (TryGetIsGun(item)) { return true; } return (Object)(object)((Component)item).GetComponent<ItemBattery>() != (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<SurplusValuable>() != (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<ValuableObject>(); 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<int, float> 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<int> ValuableBoxObjectIds = new HashSet<int>(); private static readonly HashSet<int> NonValuableBoxObjectIds = new HashSet<int>(); 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<GameObject, bool>)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<ScaleController>() != (Object)null) { return true; } if ((Object)(object)target.GetComponent<CosmeticWorldObject>() != (Object)null || (Object)(object)target.GetComponentInParent<CosmeticWorldObject>() != (Object)null || (Object)(object)target.GetComponentInChildren<CosmeticWorldObject>(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<ScaleController>(); 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<PhysGrabObject>(); 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<CosmeticWorldObject>(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent<CosmeticWorldObject>(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren<CosmeticWorldObject>(true); } private static ItemValuableBox FindValuableBox(PhysGrabObject item) { if ((Object)(object)item == (Object)null) { return null; } ItemValuableBox component = ((Component)item).GetComponent<ItemValuableBox>(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent<ItemValuableBox>(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren<ItemValuableBox>(true); } private static PhysGrabObject FindOwnerPhysGrabObject(ItemValuableBox box) { if ((Object)(object)box == (Object)null) { return null; } PhysGrabObject component = ((Component)box).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)box).GetComponentInParent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)box).GetComponentInChildren<PhysGrabObject>(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); } } }