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.45
BepInEx/plugins/ShrinkCart/ShrinkCart.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using ScalerCore; using ScalerCore.Handlers; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace ShrinkCart; internal static class Authority { private const float CheckIntervalSeconds = 0.25f; private static bool _cachedIsHostOrSingleplayer = true; private static float _nextCheckTime; internal static bool IsHostOrSingleplayer() { float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextCheckTime) { return _cachedIsHostOrSingleplayer; } _nextCheckTime = unscaledTime + 0.25f; try { _cachedIsHostOrSingleplayer = SemiFunc.IsMasterClientOrSingleplayer(); } catch { _cachedIsHostOrSingleplayer = true; } return _cachedIsHostOrSingleplayer; } internal static void Reset() { _nextCheckTime = 0f; _cachedIsHostOrSingleplayer = true; } } internal static class CartObjectGuard { private static readonly HashSet<int> CartLikeObjectIds = new HashSet<int>(); private static readonly HashSet<int> NonCartLikeObjectIds = new HashSet<int>(); private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static bool IsCartLike(PhysGrabObject item) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (CartLikeObjectIds.Contains(instanceID)) { return true; } if (NonCartLikeObjectIds.Contains(instanceID)) { return false; } if (HasCartLikeComponent(item) || HasCartLikeItemType(item)) { CartLikeObjectIds.Add(instanceID); return true; } NonCartLikeObjectIds.Add(instanceID); return false; } internal static void Reset() { CartLikeObjectIds.Clear(); NonCartLikeObjectIds.Clear(); } internal static bool ShouldBlockCartInCart(PhysGrabInCart destination, PhysGrabObject item) { if ((Object)(object)destination == (Object)null || (Object)(object)destination.cart == (Object)null || (Object)(object)item == (Object)null) { return false; } if (!IsCartLike(item)) { return false; } return true; } private static bool HasCartLikeComponent(PhysGrabObject item) { if (!((Object)(object)((Component)item).GetComponent<PhysGrabCart>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<PhysGrabCart>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<PhysGrabCart>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemVehicle>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemVehicle>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemVehicle>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartCannon>() != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartCannonMain>() != (Object)null) && !((Object)(object)((Component)item).GetComponent<ItemCartLaser>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartCannon>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartCannonMain>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<ItemCartLaser>() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemCartCannon>(true) != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren<ItemCartCannonMain>(true) != (Object)null)) { return (Object)(object)((Component)item).GetComponentInChildren<ItemCartLaser>(true) != (Object)null; } return true; } private static bool HasCartLikeItemType(PhysGrabObject item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 ItemAttributes component = ((Component)item).GetComponent<ItemAttributes>(); if ((Object)(object)component == (Object)null || ItemAttributesItemTypeField == null) { return false; } if (!(ItemAttributesItemTypeField.GetValue(component) is itemType val)) { return false; } if ((int)val != 2 && (int)val != 14) { return (int)val == 12; } return true; } } internal static class CartRegistry { private sealed class CartState { internal PhysGrabCart Cart; } private static readonly Dictionary<int, CartState> Carts = new Dictionary<int, CartState>(); private static readonly List<int> RemoveCartIds = new List<int>(8); private static readonly FieldInfo PhysGrabCartItemsInCartField = typeof(PhysGrabCart).GetField("itemsInCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartItemsInCartCountField = typeof(PhysGrabCart).GetField("itemsInCartCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartHaulCurrentField = typeof(PhysGrabCart).GetField("haulCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ValuableObjectDollarValueCurrentField = typeof(ValuableObject).GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static void RegisterCart(PhysGrabCart cart) { if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null)) { Carts[((Object)cart).GetInstanceID()] = new CartState { Cart = cart }; DebugLog("Registered cart content guard target: " + ((Object)cart).name); } } internal static void RegisterExistingCarts() { if (IsHostOrSingleplayer()) { PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>(); for (int i = 0; i < array.Length; i++) { RegisterCart(array[i]); } } } internal static void CleanCartContents(PhysGrabCart cart) { if (IsHostOrSingleplayer()) { CartState orCreateState = GetOrCreateState(cart); List<PhysGrabObject> itemsInCart = GetItemsInCart(orCreateState?.Cart); if (orCreateState != null && !((Object)(object)orCreateState.Cart == (Object)null) && itemsInCart != null) { RemoveBlockedItems(orCreateState, itemsInCart); DeadHeadCartScaleController.MarkObjectsSeenInCart(orCreateState.Cart, itemsInCart); ShrinkerCartController.MarkObjectsSeenInCart(orCreateState.Cart, itemsInCart); } } } internal static void Reset() { Carts.Clear(); RemoveCartIds.Clear(); CartObjectGuard.Reset(); } internal static void HandleBlockedCartInCart(PhysGrabInCart destination, PhysGrabObject item) { if ((Object)(object)destination == (Object)null || (Object)(object)destination.cart == (Object)null || (Object)(object)item == (Object)null) { return; } CartState orCreateState = GetOrCreateState(destination.cart); if (orCreateState != null) { List<PhysGrabObject> itemsInCart = GetItemsInCart(orCreateState.Cart); if (itemsInCart != null) { RemoveBlockedItems(orCreateState, itemsInCart); } DebugLog("Blocked cart-like object from cart Add: " + ((Object)item).name); } } private static CartState GetOrCreateState(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return null; } if (!Carts.TryGetValue(((Object)cart).GetInstanceID(), out var value) || value == null) { RegisterCart(cart); Carts.TryGetValue(((Object)cart).GetInstanceID(), out value); } return value; } private static void PruneInvalidCarts() { RemoveCartIds.Clear(); foreach (KeyValuePair<int, CartState> cart in Carts) { if (cart.Value == null || (Object)(object)cart.Value.Cart == (Object)null) { RemoveCartIds.Add(cart.Key); } } for (int i = 0; i < RemoveCartIds.Count; i++) { Carts.Remove(RemoveCartIds[i]); } RemoveCartIds.Clear(); } private static void RemoveBlockedItems(CartState state, List<PhysGrabObject> items) { if (state == null || (Object)(object)state.Cart == (Object)null || items == null) { return; } bool flag = false; for (int num = items.Count - 1; num >= 0; num--) { PhysGrabObject val = items[num]; if ((Object)(object)val == (Object)null) { items.RemoveAt(num); flag = true; } else if (CartObjectGuard.IsCartLike(val)) { items.RemoveAt(num); flag = true; } } if (flag) { RecalculateCartCounts(state.Cart); DebugLog("Removed blocked or compatibility object from cart contents: " + ((Object)state.Cart).name); } } private static void RecalculateCartCounts(PhysGrabCart cart) { List<PhysGrabObject> itemsInCart = GetItemsInCart(cart); if ((Object)(object)cart == (Object)null || itemsInCart == null) { return; } int num = 0; int num2 = 0; for (int i = 0; i < itemsInCart.Count; i++) { PhysGrabObject val = itemsInCart[i]; if (!((Object)(object)val == (Object)null)) { num++; ValuableObject val2 = ((Component)val).GetComponent<ValuableObject>(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).GetComponentInParent<ValuableObject>(); } if ((Object)(object)val2 != (Object)null) { num2 += GetValuableDollarValueCurrent(val2); } } } if (PhysGrabCartItemsInCartCountField != null) { PhysGrabCartItemsInCartCountField.SetValue(cart, num); } if (PhysGrabCartHaulCurrentField != null) { PhysGrabCartHaulCurrentField.SetValue(cart, num2); } if ((Object)(object)cart.valueScreen != (Object)null) { cart.valueScreen.UpdateValue(num2); } } private static List<PhysGrabObject> GetItemsInCart(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null || PhysGrabCartItemsInCartField == null) { return null; } return PhysGrabCartItemsInCartField.GetValue(cart) as List<PhysGrabObject>; } private static int GetValuableDollarValueCurrent(ValuableObject valuable) { if ((Object)(object)valuable == (Object)null || ValuableObjectDollarValueCurrentField == null) { return 0; } object value = ValuableObjectDollarValueCurrentField.GetValue(valuable); if (value is float) { return (int)(float)value; } if (value is int) { return (int)value; } return 0; } private static void DebugLog(string message) { if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } } internal static class DeadHeadCartScaleController { private sealed class TrackedHead { internal GameObject Target; internal PlayerDeathHead Head; internal float RestoreCheckDueTime; internal int LastSeenCartId; } private static readonly Dictionary<int, TrackedHead> TrackedHeads = new Dictionary<int, TrackedHead>(); private static readonly HashSet<int> NonHeadObjectIds = new HashSet<int>(); private static readonly List<int> RestoreIds = new List<int>(8); private static readonly List<GameObject> RestoreTargets = new List<GameObject>(8); private static readonly FieldInfo PlayerDeathHeadPhysGrabObjectField = typeof(PlayerDeathHead).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerDeathHeadTriggeredField = typeof(PlayerDeathHead).GetField("triggered", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerDeathHeadInExtractionPointField = typeof(PlayerDeathHead).GetField("inExtractionPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerDeathHeadInTruckField = typeof(PlayerDeathHead).GetField("inTruck", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float _nextRestoreCheckTime = float.PositiveInfinity; private static bool _allowDeadHeadsOwned; private static bool _previousAllowDeadHeadsValue; internal static void Reset() { TrackedHeads.Clear(); NonHeadObjectIds.Clear(); RestoreIds.Clear(); RestoreTargets.Clear(); _nextRestoreCheckTime = float.PositiveInfinity; SetAllowDeadHeads(enabled: false); } internal static void Tick() { bool flag = Authority.IsHostOrSingleplayer() && ModConfig.DeadHeadScalingEnabledValue(); SetAllowDeadHeads(flag); if (!Authority.IsHostOrSingleplayer()) { return; } if (!flag) { RestoreAll(); return; } float time = Time.time; if (TrackedHeads.Count == 0 || time < _nextRestoreCheckTime) { return; } RestoreIds.Clear(); float num = float.PositiveInfinity; foreach (KeyValuePair<int, TrackedHead> trackedHead in TrackedHeads) { TrackedHead value = trackedHead.Value; if (value == null || (Object)(object)value.Target == (Object)null || (Object)(object)value.Head == (Object)null || ShouldRestoreImmediately(value.Head) || time >= value.RestoreCheckDueTime) { RestoreIds.Add(trackedHead.Key); } else if (value.RestoreCheckDueTime < num) { num = value.RestoreCheckDueTime; } } for (int i = 0; i < RestoreIds.Count; i++) { int num2 = RestoreIds[i]; if (TrackedHeads.TryGetValue(num2, out var value2)) { RestoreTrackedHead(num2, value2, "leave/debounce"); TrackedHeads.Remove(num2); } } RestoreIds.Clear(); _nextRestoreCheckTime = ((TrackedHeads.Count == 0) ? float.PositiveInfinity : num); } internal static void MarkObjectsSeenInCart(PhysGrabCart cart, List<PhysGrabObject> items) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (!Authority.IsHostOrSingleplayer() || !ModConfig.DeadHeadScalingEnabledValue() || items == null) { return; } SetAllowDeadHeads(enabled: true); float time = Time.time; int num = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0); for (int i = 0; i < items.Count; i++) { PhysGrabObject val = items[i]; if (!TryGetTriggeredDeathHead(val, out var head)) { continue; } GameObject gameObject = ((Component)val).gameObject; int instanceID = ((Object)gameObject).GetInstanceID(); if (TrackedHeads.TryGetValue(instanceID, out var value)) { value.RestoreCheckDueTime = time + ModConfig.SafeCartLeaveDebounceSeconds(); value.LastSeenCartId = num; value.Head = head; ScheduleRestoreCheck(value.RestoreCheckDueTime); continue; } if (ScaleManager.IsScaled(gameObject)) { if (!TryTrackExistingHeadScale(instanceID, gameObject, head, num, time)) { DebugLog("Skipped death head cart shrink because another scale session is active: " + ((Object)gameObject).name); } continue; } ScaleOptions val2 = ScaleOptions.Default; val2.Factor = ModConfig.SafeDeadHeadScaleFactor(); val2.Speed = ModConfig.SafeScaleSpeed(); val2.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); val2.Duration = 0f; val2.AllowedTargets = (ScaleTargets)15; val2.SuppressValueDropExpand = true; val2.PreserveMass = ModConfig.ShouldPreserveMass(); val2.SuppressImpactFlash = true; val2.SuppressCameraShake = true; val2.RejectExternalApply = false; try { if (!ScaleManager.ApplyIfNotScaled(gameObject, val2)) { DebugLog("ScalerCore rejected death head cart shrink: " + ((Object)gameObject).name); continue; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to shrink death head in cart: " + ex.Message)); continue; } float num2 = time + ModConfig.SafeCartLeaveDebounceSeconds(); TrackedHeads[instanceID] = new TrackedHead { Target = gameObject, Head = head, RestoreCheckDueTime = num2, LastSeenCartId = num }; ScheduleRestoreCheck(num2); DebugLog("Shrunk death head in cart: " + ((Object)gameObject).name + " factor=" + ModConfig.SafeDeadHeadScaleFactor().ToString("0.###")); } } internal static void RestoreBeforeRevive(PlayerDeathHead head) { if ((Object)(object)head == (Object)null) { return; } PhysGrabObject headPhysGrabObject = GetHeadPhysGrabObject(head); GameObject val = (((Object)(object)headPhysGrabObject == (Object)null) ? null : ((Component)headPhysGrabObject).gameObject); if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); if (TrackedHeads.TryGetValue(instanceID, out var value)) { RestoreTrackedHead(instanceID, value, "before revive"); TrackedHeads.Remove(instanceID); _nextRestoreCheckTime = 0f; } } } internal static void RestoreAll() { if (TrackedHeads.Count == 0) { return; } RestoreTargets.Clear(); foreach (TrackedHead value in TrackedHeads.Values) { if (value != null && (Object)(object)value.Target != (Object)null) { RestoreTargets.Add(value.Target); } } TrackedHeads.Clear(); _nextRestoreCheckTime = float.PositiveInfinity; for (int i = 0; i < RestoreTargets.Count; i++) { RestoreTarget(RestoreTargets[i], "restore all"); } RestoreTargets.Clear(); } private static bool TryTrackExistingHeadScale(int id, GameObject target, PlayerDeathHead head, int cartId, float now) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ScaleController controller = ScaleManager.GetController(target); if ((Object)(object)controller == (Object)null || !controller.IsScaled) { return false; } ScaleOptions currentOptions = controller.CurrentOptions; if (!LooksLikeShrinkCartHeadOptions(currentOptions)) { return false; } float num = now + ModConfig.SafeCartLeaveDebounceSeconds(); TrackedHeads[id] = new TrackedHead { Target = target, Head = head, RestoreCheckDueTime = num, LastSeenCartId = cartId }; ScheduleRestoreCheck(num); DebugLog("Adopted existing ShrinkCart-like death head scale: " + ((Object)target).name); return true; } private static bool LooksLikeShrinkCartHeadOptions(ScaleOptions options) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (Mathf.Approximately(options.Factor, ModConfig.SafeDeadHeadScaleFactor()) && (int)options.AllowedTargets == 15 && options.SuppressImpactFlash && options.SuppressCameraShake && options.SuppressValueDropExpand && options.PreserveMass == ModConfig.ShouldPreserveMass()) { return !options.RejectExternalApply; } return false; } private static void RestoreTrackedHead(int id, TrackedHead tracked, string reason) { if (tracked != null) { RestoreTarget(tracked.Target, reason); } } private static void RestoreTarget(GameObject target, string reason) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return; } try { if (ScaleManager.IsScaled(target)) { ScaleController controller = ScaleManager.GetController(target); if ((Object)(object)controller != (Object)null && controller.IsScaled) { ScaleOptions currentOptions = controller.CurrentOptions; currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); currentOptions.SuppressImpactFlash = true; currentOptions.SuppressCameraShake = true; ScaleManager.ForceUpdateOptions(target, currentOptions); } ScaleManager.ForceRestore(target); DebugLog("Restored death head " + ((Object)target).name + " reason=" + reason); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to restore death head from cart scale: " + ex.Message)); } } private static bool TryGetTriggeredDeathHead(PhysGrabObject item, out PlayerDeathHead head) { head = null; if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (NonHeadObjectIds.Contains(instanceID)) { return false; } head = ((Component)item).GetComponent<PlayerDeathHead>(); if ((Object)(object)head == (Object)null) { head = ((Component)item).GetComponentInParent<PlayerDeathHead>(); } if ((Object)(object)head == (Object)null) { head = ((Component)item).GetComponentInChildren<PlayerDeathHead>(true); } if ((Object)(object)head == (Object)null) { NonHeadObjectIds.Add(instanceID); head = null; return false; } if (!IsHeadTriggered(head) || (Object)(object)GetHeadPhysGrabObject(head) != (Object)(object)item) { head = null; return false; } return true; } private static bool ShouldRestoreImmediately(PlayerDeathHead head) { if (!((Object)(object)head == (Object)null) && IsHeadTriggered(head) && !IsHeadInExtractionPoint(head)) { return IsHeadInTruck(head); } return true; } private static PhysGrabObject GetHeadPhysGrabObject(PlayerDeathHead head) { if (!((Object)(object)head == (Object)null) && !(PlayerDeathHeadPhysGrabObjectField == null)) { object? value = PlayerDeathHeadPhysGrabObjectField.GetValue(head); return (PhysGrabObject)((value is PhysGrabObject) ? value : null); } return null; } private static bool IsHeadTriggered(PlayerDeathHead head) { return GetBoolField(head, PlayerDeathHeadTriggeredField); } private static bool IsHeadInExtractionPoint(PlayerDeathHead head) { return GetBoolField(head, PlayerDeathHeadInExtractionPointField); } private static bool IsHeadInTruck(PlayerDeathHead head) { return GetBoolField(head, PlayerDeathHeadInTruckField); } private static bool GetBoolField(PlayerDeathHead head, FieldInfo field) { if ((Object)(object)head == (Object)null || field == null) { return false; } object value = field.GetValue(head); if (value is bool) { return (bool)value; } return false; } private static void ScheduleRestoreCheck(float dueTime) { if (dueTime < _nextRestoreCheckTime) { _nextRestoreCheckTime = dueTime; } } private static void SetAllowDeadHeads(bool enabled) { try { if (enabled) { if (!_allowDeadHeadsOwned) { _previousAllowDeadHeadsValue = ScaleManager.AllowDeadHeads; _allowDeadHeadsOwned = true; } if (!ScaleManager.AllowDeadHeads) { ScaleManager.AllowDeadHeads = true; } } else if (_allowDeadHeadsOwned) { ScaleManager.AllowDeadHeads = _previousAllowDeadHeadsValue; _allowDeadHeadsOwned = false; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to update ScalerCore dead head scaling permission: " + ex.Message)); } } private static void DebugLog(string message) { if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } internal static class EnemyInCartKillController { private static readonly HashSet<int> ExecutedEnemies = new HashSet<int>(); private static readonly FieldInfo EnemyHealthField = typeof(Enemy).GetField("Health", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo EnemyHealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.NonPublic); internal static void Reset() { ExecutedEnemies.Clear(); } internal static bool TryKill(PhysGrabObject item) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.EnemyInCartInstantKill.Value || (Object)(object)item == (Object)null) { return false; } Enemy val = FindEnemy(item); if ((Object)(object)val == (Object)null) { return false; } int instanceID = ((Object)val).GetInstanceID(); if (ExecutedEnemies.Contains(instanceID)) { return true; } EnemyHealth health = GetHealth(val); if ((Object)(object)health == (Object)null) { return false; } int currentHealth = GetCurrentHealth(health); if (currentHealth <= 0) { ExecutedEnemies.Add(instanceID); return true; } ExecutedEnemies.Add(instanceID); try { int num = Math.Max(currentHealth, health.health) + 1; health.Hurt(num, Vector3.up); DebugLog("Instant killed enemy entering cart: " + ((Object)val).name); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to instant kill enemy entering cart: " + ex.Message)); ExecutedEnemies.Remove(instanceID); return false; } return true; } private static Enemy FindEnemy(PhysGrabObject item) { EnemyRigidbody componentInParent = ((Component)item).GetComponentInParent<EnemyRigidbody>(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.enemy != (Object)null) { return componentInParent.enemy; } return ((Component)item).GetComponentInParent<Enemy>(); } private static EnemyHealth GetHealth(Enemy enemy) { if (EnemyHealthField == null) { return null; } object? value = EnemyHealthField.GetValue(enemy); return (EnemyHealth)((value is EnemyHealth) ? value : null); } private static int GetCurrentHealth(EnemyHealth health) { if (EnemyHealthCurrentField == null) { return health.health; } object value = EnemyHealthCurrentField.GetValue(health); if (value is int) { return (int)value; } return health.health; } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } internal enum ShrinkCategory { Tiny, Small, Medium, Big, Wide, Tall, VeryTall, EnemyOrbSmall, EnemyOrbMedium, EnemyOrbBig, EnemyOrbBerserker, Surplus, ValuableBox, Fallback } internal static class ModConfig { private sealed class LocalizedDefinition { internal readonly string Id; internal readonly string ChineseSection; internal readonly string EnglishSection; internal readonly string ChineseKey; internal readonly string EnglishKey; internal LocalizedDefinition(string id, string chineseSection, string englishSection, string chineseKey, string englishKey) { Id = id; ChineseSection = chineseSection; EnglishSection = englishSection; ChineseKey = chineseKey; EnglishKey = englishKey; } } private const string ChineseLanguage = "中文"; private const string EnglishLanguage = "English"; private const string LanguageSection = "语言 / Language"; private const string LanguageKey = "配置语言 / Config Language"; private const string MassDefaultMigrationMarkerName = "ShrinkCart.v0.2.39.mass-default-enabled"; internal static ConfigEntry<bool> CartShrinkingEnabled; internal static ConfigEntry<float> CartScaleSpeed; internal static ConfigEntry<float> RestoreScaleSpeed; internal static ConfigEntry<float> CartLeaveDebounceSeconds; internal static ConfigEntry<float> ReshrinkCooldownSeconds; internal static ConfigEntry<bool> ScaleMassWithSize; internal static ConfigEntry<bool> ShrinkShopPlayerItems; internal static ConfigEntry<bool> PlayerScalingModuleEnabled; internal static ConfigEntry<float> PlayerCartScaleFactor; internal static ConfigEntry<float> PlayerCartGrowFactor; internal static ConfigEntry<float> PlayerCartStandTriggerSeconds; internal static ConfigEntry<float> PlayerCartExitGraceSeconds; internal static ConfigEntry<float> PlayerCartDetectionIntervalSeconds; internal static ConfigEntry<bool> RestorePlayerOnDamage; internal static ConfigEntry<bool> SuppressValuableDamageRestore; internal static ConfigEntry<bool> DeadHeadScalingEnabled; internal static ConfigEntry<float> DeadHeadScaleFactor; internal static ConfigEntry<bool> TinyEnabled; internal static ConfigEntry<float> TinyScaleFactor; internal static ConfigEntry<bool> SmallEnabled; internal static ConfigEntry<float> SmallScaleFactor; internal static ConfigEntry<bool> MediumEnabled; internal static ConfigEntry<float> MediumScaleFactor; internal static ConfigEntry<bool> BigEnabled; internal static ConfigEntry<float> BigScaleFactor; internal static ConfigEntry<bool> WideEnabled; internal static ConfigEntry<float> WideScaleFactor; internal static ConfigEntry<bool> TallEnabled; internal static ConfigEntry<float> TallScaleFactor; internal static ConfigEntry<bool> VeryTallEnabled; internal static ConfigEntry<float> VeryTallScaleFactor; internal static ConfigEntry<bool> EnemyOrbEnabled; internal static ConfigEntry<float> EnemyOrbSmallScaleFactor; internal static ConfigEntry<float> EnemyOrbMediumScaleFactor; internal static ConfigEntry<float> EnemyOrbBigScaleFactor; internal static ConfigEntry<float> EnemyOrbBerserkerScaleFactor; internal static ConfigEntry<bool> SurplusEnabled; internal static ConfigEntry<float> SurplusScaleFactor; internal static ConfigEntry<bool> ValuableBoxEnabled; internal static ConfigEntry<float> ValuableBoxScaleFactor; internal static ConfigEntry<float> FallbackScaleFactor; internal static ConfigEntry<bool> EnemyInCartInstantKill; internal static ConfigEntry<bool> DynamicItemScanEnabled; internal static ConfigEntry<float> MinimumItemScanIntervalSeconds; internal static ConfigEntry<float> MaximumItemScanIntervalSeconds; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<string> ConfigLanguage; internal static int ScalingConfigVersion; private static bool _useEnglish; private static ConfigFile _boundConfig; private static readonly List<LocalizedDefinition> ActiveDefinitions = new List<LocalizedDefinition>(); private static readonly PropertyInfo ConfigFileOrphanedEntriesProperty = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static void Bind(ConfigFile config) { //IL_0aa5: Unknown result type (might be due to invalid IL or missing references) //IL_0aaf: Expected O, but got Unknown _boundConfig = config; ActiveDefinitions.Clear(); _useEnglish = IsEnglishLanguage(DetectConfiguredLanguage(config)); CartShrinkingEnabled = Bind(config, "CartShrinkingEnabled", "购物车", "Cart", "启用购物车缩小", "Enable cart shrinking", defaultValue: true, Text("启用后,放进 C.A.R.T / 购物车的支持物品会自动缩小,取出后恢复。", "When enabled, supported objects placed into a C.A.R.T. / shopping cart shrink automatically and restore after leaving.")); CartScaleSpeed = Bind(config, "CartScaleSpeed", "购物车", "Cart", "放入时缩小速度", "Shrink speed", 0.5f, Ranged(Text("ScalerCore 缩小动画速度。数值越大越快。", "ScalerCore shrink animation speed. Higher values are faster."), 0.1f, 20f)); RestoreScaleSpeed = Bind(config, "RestoreScaleSpeed", "购物车", "Cart", "取出后放大速度", "Restore speed", 0.2f, Ranged(Text("正常取出购物车后的 ScalerCore 放大动画速度。数值越小越慢。", "ScalerCore restore animation speed after leaving the cart. Lower values are slower."), 0.1f, 20f)); CartLeaveDebounceSeconds = Bind(config, "CartLeaveDebounceSeconds", "购物车", "Cart", "离车防抖延迟", "Leave-cart debounce", 0.5f, Ranged(Text("物品触发缩小后,离开购物车检测范围多久才开始恢复原尺寸。调高可减少车边缘抽搐。", "How long an item must remain outside the cart before restoring. Higher values reduce edge jitter."), 0.1f, 10f)); ReshrinkCooldownSeconds = Bind(config, "ReshrinkCooldownSeconds", "购物车", "Cart", "恢复后重新缩小冷却", "Reshrink cooldown", LegacyFloat(config, 0.5f, "购物车", "取出后恢复延迟", "Cart", "RestoreGraceSeconds"), Ranged(Text("物品开始恢复后,等待多少秒才允许再次被购物车缩小。", "How many seconds to wait after restore starts before the cart may shrink the item again."), 0.05f, 10f)); ScaleMassWithSize = Bind(config, "ScaleMassWithSize", "购物车", "Cart", "启用重量随缩放降低", "Scale mass with size", defaultValue: true, Text("启用后通过 ScalerCore 的 PreserveMass 选项允许支持对象按缩放倍率降低质量;关闭则只改变视觉尺寸并保持原始重量。", "When enabled, ShrinkCart passes ScalerCore options that allow supported objects to reduce mass with scale. When disabled, only visual size changes and original mass is preserved.")); ShrinkShopPlayerItems = Bind(config, "ShrinkShopPlayerItems", "购物车", "Cart", "启用商店用品缩小", "Enable shop item shrinking", defaultValue: false, Text("启用后,枪、血包、近战、手雷、工具、无人机、宝珠、升级、追踪器、地雷等商店购买类实用品会使用默认缩小倍率。大小推车、C.A.R.T. Cannon 和 C.A.R.T. Laser 始终不会缩小。", "When enabled, shop utility items such as guns, medkits, melee items, grenades, tools, drones, pearls, upgrades, trackers, and mines use the shop-item scale factor. Carts, C.A.R.T. Cannon, and C.A.R.T. Laser never shrink.")); PlayerScalingModuleEnabled = Bind(config, "PlayerScalingModuleEnabled", "玩家缩放", "Player Scaling", "启用玩家缩放", "Enable player scaling", defaultValue: true, Text("默认开启。启用后才会运行玩家站车检测和玩家缩放;关闭时 ShrinkCart 不参与任何玩家缩放逻辑。", "Enabled by default. When enabled, ShrinkCart runs player cart-floor detection and player scaling. When disabled, ShrinkCart does not run player scaling logic.")); PlayerCartScaleFactor = Bind(config, "PlayerCartScaleFactor", "玩家缩放", "Player Scaling", "玩家进车缩放倍率", "Player cart scale factor", 0.55f, Ranged(Text("开启“启用玩家缩放”后,玩家站在购物车中心区域触发缩放时的目标尺寸比例。", "Target player size factor when player scaling is enabled and a player stands in the cart center area."), 0.05f, 1f)); PlayerCartGrowFactor = Bind(config, "PlayerCartGrowFactor", "玩家缩放", "Player Scaling", "玩家进车增大倍率", "Player cart grow factor", 1.5f, Ranged(Text("玩家站车循环中的增大目标比例。循环顺序为:原样 -> 缩小 -> 原样 -> 增大 -> 原样。", "Target player growth factor for the cart cycle: normal -> shrink -> normal -> grow -> normal."), 1f, 2f)); PlayerCartStandTriggerSeconds = Bind(config, "PlayerCartStandTriggerSeconds", "玩家缩放", "Player Scaling", "玩家站车触发时间", "Player stand trigger time", 2f, Ranged(Text("开启“启用玩家缩放”后,玩家站在购物车中心区域多久才执行一次缩小/恢复/增大循环。离开中心区域会重置计时。", "How long a player must stand in the cart center area before the shrink/restore/grow cycle advances. Leaving the center area resets the timer."), 0.25f, 10f)); PlayerCartExitGraceSeconds = Bind(config, "PlayerCartExitGraceSeconds", "玩家缩放", "Player Scaling", "玩家离车判定宽容时间", "Player exit grace time", 0.6f, Ranged(Text("玩家仍在购物车中心区域附近但短暂跳起、踩到车内物品或被货物顶起时,保留车内状态多久后才判定离开。设为 0 可恢复严格判定。", "How long to keep cart-inside state when the player briefly jumps, stands on cart cargo, or is pushed up while still near the cart center area. Set to 0 for strict detection."), 0f, 2f)); PlayerCartDetectionIntervalSeconds = Bind(config, "PlayerCartDetectionIntervalSeconds", "玩家缩放", "Player Scaling", "玩家检测间隔", "Player detection interval", 0.75f, Ranged(Text("玩家缩放开启时,主机多久检测一次玩家是否位于正式购物车底面投影范围内。数值越大越省性能,也越能防误触发。", "When player scaling is enabled, how often the host checks whether players are inside a regular cart floor projection. Higher values cost less and reduce accidental triggers."), 0.25f, 2f)); RestorePlayerOnDamage = Bind(config, "RestorePlayerOnDamage", "玩家缩放", "Player Scaling", "启用玩家受伤后自动恢复", "Restore player on damage", defaultValue: true, Text("启用后,玩家缩放时使用 ScalerCore 的受伤/碰撞恢复链路;关闭后,玩家只会通过再次站车切换恢复。", "When enabled, ShrinkCart-scaled players use ScalerCore's damage/collision restore path. When disabled, players restore only by standing in the cart again.")); SuppressValuableDamageRestore = Bind(config, "SuppressValuableDamageRestore", "购物车", "Cart", "防止碰撞弹回原尺寸", "Suppress collision restore", defaultValue: true, Text("启用后,贵重物品在购物车里轻微碰撞时不会立刻弹回原尺寸。ScalerCore 的安全恢复仍会保留。", "When enabled, valuables do not immediately restore from light collision value drops while in the cart. ScalerCore safety restore remains active.")); DeadHeadScalingEnabled = Bind(config, "DeadHeadScalingEnabled", "死亡头颅", "Death Heads", "启用死亡头颅进车缩小", "Enable dead head cart shrinking", defaultValue: false, Text("默认关闭。启用后,已触发的玩家死亡头颅进入购物车时会缩小,离车、进入提取点、进入卡车或复活前会恢复。", "Disabled by default. When enabled, triggered player death heads shrink in carts and restore when leaving, entering extraction/truck flow, or before revive.")); DeadHeadScaleFactor = Bind(config, "DeadHeadScaleFactor", "死亡头颅", "Death Heads", "死亡头颅缩小倍率", "Dead head scale factor", 0.5f, Ranged(Text("死亡头颅进入购物车后的目标尺寸比例。", "Target size factor for death heads inside carts."), 0.05f, 1f)); TinyEnabled = BindCategoryEnabled(config, "Tiny", "Tiny 微型贵重物", "Tiny valuables", defaultValue: true); TinyScaleFactor = BindCategoryFactor(config, "Tiny", "Tiny 微型贵重物", "Tiny valuables", 0.8f); SmallEnabled = BindCategoryEnabled(config, "Small", "Small 小贵重物", "Small valuables", defaultValue: true); SmallScaleFactor = BindCategoryFactor(config, "Small", "Small 小贵重物", "Small valuables", 0.6f); MediumEnabled = BindCategoryEnabled(config, "Medium", "Medium 中贵重物", "Medium valuables", defaultValue: true); MediumScaleFactor = BindCategoryFactor(config, "Medium", "Medium 中贵重物", "Medium valuables", 0.45f); BigEnabled = BindCategoryEnabled(config, "Big", "Big 大贵重物", "Big valuables", defaultValue: true); BigScaleFactor = BindCategoryFactor(config, "Big", "Big 大贵重物", "Big valuables", 0.4f); WideEnabled = BindCategoryEnabled(config, "Wide", "Wide 宽贵重物", "Wide valuables", defaultValue: true); WideScaleFactor = BindCategoryFactor(config, "Wide", "Wide 宽贵重物", "Wide valuables", 0.35f); TallEnabled = BindCategoryEnabled(config, "Tall", "Tall 高贵重物", "Tall valuables", defaultValue: true); TallScaleFactor = BindCategoryFactor(config, "Tall", "Tall 高贵重物", "Tall valuables", 0.35f); VeryTallEnabled = BindCategoryEnabled(config, "VeryTall", "VeryTall 超高贵重物", "VeryTall valuables", defaultValue: true); VeryTallScaleFactor = BindCategoryFactor(config, "VeryTall", "VeryTall 超高贵重物", "VeryTall valuables", 0.25f); EnemyOrbEnabled = Bind(config, "EnemyOrbEnabled", "敌人球", "Enemy Orbs", "启用敌人球缩小", "Enable enemy orb shrinking", defaultValue: true, Text("启用后,Enemy - Small/Medium/Big/Berserker 类贵重物会按下方倍率缩小。", "When enabled, Enemy - Small/Medium/Big/Berserker valuables shrink using the factors below.")); EnemyOrbSmallScaleFactor = Bind(config, "EnemyOrbSmallScaleFactor", "敌人球", "Enemy Orbs", "Small 敌人球倍率", "Small enemy orb factor", 0.8f, Ranged(Text("Small 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Small enemy orbs in carts."), 0.05f, 1f)); EnemyOrbMediumScaleFactor = Bind(config, "EnemyOrbMediumScaleFactor", "敌人球", "Enemy Orbs", "Medium 敌人球倍率", "Medium enemy orb factor", 0.65f, Ranged(Text("Medium 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Medium enemy orbs in carts."), 0.05f, 1f)); EnemyOrbBigScaleFactor = Bind(config, "EnemyOrbBigScaleFactor", "敌人球", "Enemy Orbs", "Big 敌人球倍率", "Big enemy orb factor", 0.45f, Ranged(Text("Big 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Big enemy orbs in carts."), 0.05f, 1f)); EnemyOrbBerserkerScaleFactor = Bind(config, "EnemyOrbBerserkerScaleFactor", "敌人球", "Enemy Orbs", "Berserker 敌人球倍率", "Berserker enemy orb factor", 0.45f, Ranged(Text("Berserker 敌人球放入购物车后的目标尺寸比例。", "Target size factor for Berserker enemy orbs in carts."), 0.05f, 1f)); SurplusEnabled = Bind(config, "SurplusEnabled", "特殊物品", "Special Objects", "启用钱袋/Surplus 缩小", "Enable money bag / Surplus shrinking", LegacyBool(config, true, "特殊物品", "启用 Surplus 缩小"), Text("启用后,钱袋/SurplusValuable 会使用单独倍率。", "When enabled, money bags / SurplusValuable use their own scale factor.")); SurplusScaleFactor = Bind(config, "SurplusScaleFactor", "特殊物品", "Special Objects", "钱袋/Surplus 倍率", "Money bag / Surplus factor", LegacyFloat(config, 0.25f, "特殊物品", "Surplus 倍率"), Ranged(Text("钱袋/SurplusValuable 放入购物车后的目标尺寸比例。", "Target size factor for money bags / SurplusValuable in carts."), 0.05f, 1f)); ValuableBoxEnabled = Bind(config, "ValuableBoxEnabled", "特殊物品", "Special Objects", "启用代币箱缩小", "Enable token box shrinking", defaultValue: true, Text("启用后,新版本抽奖用代币箱 ItemValuableBox 会使用单独倍率。", "When enabled, newer token boxes / ItemValuableBox objects use their own scale factor.")); ValuableBoxScaleFactor = Bind(config, "ValuableBoxScaleFactor", "特殊物品", "Special Objects", "代币箱倍率", "Token box factor", 0.4f, Ranged(Text("抽奖用代币箱 ItemValuableBox 放入购物车后的目标尺寸比例。", "Target size factor for token boxes / ItemValuableBox in carts."), 0.05f, 1f)); FallbackScaleFactor = Bind(config, "FallbackScaleFactor", "商店用品", "Shop Items", "商店用品缩小倍率", "Shop item scale factor", LegacyFloat(config, 0.5f, "普通或未知物品", "默认缩小倍率"), Ranged(Text("开启“启用商店用品缩小”后,枪、血包、工具等实用品放入购物车后的目标尺寸比例。也作为未知贵重物分类的兜底倍率。", "Target size factor for utility items such as guns, medkits, and tools when shop-item scaling is enabled. Also used as fallback for unknown valuable categories."), 0.05f, 1f)); EnemyInCartInstantKill = Bind(config, "EnemyInCartInstantKill", "车辆碾压", "Cart Crush", "敌人进车秒杀", "Instant-kill enemies in cart", LegacyBool(config, true, "车辆碾压", "车辆碾压秒杀敌人", "VehicleCrush", "InstantKillEnemies"), Text("启用后,敌人或敌人刚体进入购物车时会立刻死亡。此功能复刻 ShrinkerCartPlus 的敌人进车秒杀逻辑。", "When enabled, enemies or enemy rigidbodies die when entering a cart. This mirrors ShrinkerCartPlus enemy-in-cart instant kill behavior.")); DynamicItemScanEnabled = Bind(config, "DynamicItemScanEnabled", "性能", "Performance", "启用动态物品扫描", "Enable dynamic item scanning", defaultValue: true, Text("启用后,ShrinkCart 会根据当前跟踪的缩小物品数量自动拉长状态扫描间隔,减少车内物品很多时的卡顿。", "When enabled, ShrinkCart lengthens state scan intervals based on the number of tracked shrunken objects to reduce stutter with many cart items.")); MinimumItemScanIntervalSeconds = Bind(config, "MinimumItemScanIntervalSeconds", "性能", "Performance", "最小物品扫描间隔", "Minimum item scan interval", 0.15f, Ranged(Text("少量物品时的最短状态扫描间隔。数值越小,离车恢复越灵敏,但开销更高。", "Shortest state scan interval for few items. Lower values make restore more responsive but cost more."), 0.05f, 1f)); MaximumItemScanIntervalSeconds = Bind(config, "MaximumItemScanIntervalSeconds", "性能", "Performance", "最大物品扫描间隔", "Maximum item scan interval", 1f, Ranged(Text("大量物品时允许使用的最长状态扫描间隔。数值越大越省性能,但离车恢复最多会延后一个扫描间隔。", "Longest state scan interval allowed for many items. Higher values cost less, but leaving-cart restore may be delayed by up to one scan interval."), 0.1f, 2f)); DebugLogging = Bind(config, "DebugLogging", "诊断", "Diagnostics", "启用调试日志", "Enable debug logging", defaultValue: false, Text("启用后,在 BepInEx 日志中写入更多缩小、恢复、敌人进车和碾压识别信息。", "When enabled, writes additional shrink, restore, enemy-in-cart, map compatibility, and recognition details to BepInEx logs.")); ConfigLanguage = config.Bind<string>("语言 / Language", "配置语言 / Config Language", _useEnglish ? "English" : "中文", new ConfigDescription("切换 ShrinkCart 配置文件语言。Switch ShrinkCart config file language. Apply and restart/reload to rebuild all config keys.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "中文", "English" }), new object[0])); WatchScaling<bool>(CartShrinkingEnabled); WatchScaling<float>(CartScaleSpeed); WatchScaling<float>(RestoreScaleSpeed); WatchScaling<float>(CartLeaveDebounceSeconds); WatchScaling<float>(ReshrinkCooldownSeconds); WatchScaling<bool>(ScaleMassWithSize); WatchScaling<bool>(ShrinkShopPlayerItems); WatchScaling<bool>(PlayerScalingModuleEnabled); WatchScaling<float>(PlayerCartScaleFactor); WatchScaling<float>(PlayerCartGrowFactor); WatchScaling<float>(PlayerCartStandTriggerSeconds); WatchScaling<float>(PlayerCartExitGraceSeconds); WatchScaling<float>(PlayerCartDetectionIntervalSeconds); WatchScaling<bool>(RestorePlayerOnDamage); WatchScaling<bool>(SuppressValuableDamageRestore); WatchScaling<bool>(DeadHeadScalingEnabled); WatchScaling<float>(DeadHeadScaleFactor); WatchScaling<bool>(TinyEnabled); WatchScaling<float>(TinyScaleFactor); WatchScaling<bool>(SmallEnabled); WatchScaling<float>(SmallScaleFactor); WatchScaling<bool>(MediumEnabled); WatchScaling<float>(MediumScaleFactor); WatchScaling<bool>(BigEnabled); WatchScaling<float>(BigScaleFactor); WatchScaling<bool>(WideEnabled); WatchScaling<float>(WideScaleFactor); WatchScaling<bool>(TallEnabled); WatchScaling<float>(TallScaleFactor); WatchScaling<bool>(VeryTallEnabled); WatchScaling<float>(VeryTallScaleFactor); WatchScaling<bool>(EnemyOrbEnabled); WatchScaling<float>(EnemyOrbSmallScaleFactor); WatchScaling<float>(EnemyOrbMediumScaleFactor); WatchScaling<float>(EnemyOrbBigScaleFactor); WatchScaling<float>(EnemyOrbBerserkerScaleFactor); WatchScaling<bool>(SurplusEnabled); WatchScaling<float>(SurplusScaleFactor); WatchScaling<bool>(ValuableBoxEnabled); WatchScaling<float>(ValuableBoxScaleFactor); WatchScaling<float>(FallbackScaleFactor); MigrateMassScalingDefaultOnce(); RemoveDeprecatedEntries(config); RemoveInactiveLanguageEntries(config); } internal static float SafeScaleSpeed() { return Mathf.Clamp(CartScaleSpeed.Value, 0.1f, 20f); } internal static float SafeRestoreScaleSpeed() { return Mathf.Clamp(RestoreScaleSpeed.Value, 0.1f, 20f); } internal static float SafeCartLeaveDebounceSeconds() { return Mathf.Clamp(CartLeaveDebounceSeconds.Value, 0.1f, 10f); } internal static float SafeReshrinkCooldownSeconds() { return Mathf.Clamp(ReshrinkCooldownSeconds.Value, 0.05f, 10f); } internal static bool PlayerScalingEnabled() { if (CartShrinkingEnabled != null && PlayerScalingModuleEnabled != null && CartShrinkingEnabled.Value) { return PlayerScalingModuleEnabled.Value; } return false; } internal static bool ShouldPreserveMass() { if (ScaleMassWithSize != null) { return !ScaleMassWithSize.Value; } return true; } internal static float SafePlayerCartScaleFactor() { return Mathf.Clamp(PlayerCartScaleFactor.Value, 0.05f, 1f); } internal static float SafePlayerCartGrowFactor() { if (PlayerCartGrowFactor != null) { return Mathf.Clamp(PlayerCartGrowFactor.Value, 1f, 2f); } return 1.5f; } internal static float SafePlayerCartStandTriggerSeconds() { return Mathf.Clamp(PlayerCartStandTriggerSeconds.Value, 0.25f, 10f); } internal static float SafePlayerCartExitGraceSeconds() { return Mathf.Clamp(PlayerCartExitGraceSeconds.Value, 0f, 2f); } internal static float SafePlayerCartDetectionIntervalSeconds() { return Mathf.Clamp(PlayerCartDetectionIntervalSeconds.Value, 0.25f, 2f); } internal static bool DynamicItemScanEnabledValue() { if (DynamicItemScanEnabled != null) { return DynamicItemScanEnabled.Value; } return false; } internal static bool DeadHeadScalingEnabledValue() { if (CartShrinkingEnabled != null && DeadHeadScalingEnabled != null && CartShrinkingEnabled.Value) { return DeadHeadScalingEnabled.Value; } return false; } internal static float SafeDeadHeadScaleFactor() { if (DeadHeadScaleFactor != null) { return Mathf.Clamp(DeadHeadScaleFactor.Value, 0.05f, 1f); } return 0.5f; } internal static float SafeMinimumItemScanIntervalSeconds() { if (MinimumItemScanIntervalSeconds != null) { return Mathf.Clamp(MinimumItemScanIntervalSeconds.Value, 0.05f, 1f); } return 0.15f; } internal static float SafeMaximumItemScanIntervalSeconds() { float num = SafeMinimumItemScanIntervalSeconds(); float num2 = ((MaximumItemScanIntervalSeconds == null) ? 1f : Mathf.Clamp(MaximumItemScanIntervalSeconds.Value, 0.1f, 2f)); return Mathf.Max(num, num2); } internal static bool TryGetScaleFactor(ShrinkCategory category, out float factor) { switch (category) { case ShrinkCategory.Tiny: return TryCategory(TinyEnabled, TinyScaleFactor, out factor); case ShrinkCategory.Small: return TryCategory(SmallEnabled, SmallScaleFactor, out factor); case ShrinkCategory.Medium: return TryCategory(MediumEnabled, MediumScaleFactor, out factor); case ShrinkCategory.Big: return TryCategory(BigEnabled, BigScaleFactor, out factor); case ShrinkCategory.Wide: return TryCategory(WideEnabled, WideScaleFactor, out factor); case ShrinkCategory.Tall: return TryCategory(TallEnabled, TallScaleFactor, out factor); case ShrinkCategory.VeryTall: return TryCategory(VeryTallEnabled, VeryTallScaleFactor, out factor); case ShrinkCategory.EnemyOrbSmall: return TryEnemyOrb(EnemyOrbSmallScaleFactor, out factor); case ShrinkCategory.EnemyOrbMedium: return TryEnemyOrb(EnemyOrbMediumScaleFactor, out factor); case ShrinkCategory.EnemyOrbBig: return TryEnemyOrb(EnemyOrbBigScaleFactor, out factor); case ShrinkCategory.EnemyOrbBerserker: return TryEnemyOrb(EnemyOrbBerserkerScaleFactor, out factor); case ShrinkCategory.Surplus: return TryCategory(SurplusEnabled, SurplusScaleFactor, out factor); case ShrinkCategory.ValuableBox: return TryCategory(ValuableBoxEnabled, ValuableBoxScaleFactor, out factor); default: factor = SafeFactor(FallbackScaleFactor.Value); return true; } } private static ConfigEntry<bool> BindCategoryEnabled(ConfigFile config, string id, string chineseSection, string englishSection, bool defaultValue) { return Bind(config, id + ".Enabled", chineseSection, englishSection, "启用此分类缩小", "Enable this category", defaultValue, Text("启用后,该分类物品放入购物车时会自动缩小。", "When enabled, objects in this category shrink when placed into a cart.")); } private static ConfigEntry<float> BindCategoryFactor(ConfigFile config, string id, string chineseSection, string englishSection, float defaultValue) { return Bind(config, id + ".Factor", chineseSection, englishSection, "缩小倍率", "Scale factor", defaultValue, Ranged(Text("该分类物品放入购物车后的目标尺寸比例。0.4 表示原尺寸的 40%。", "Target size factor for this category in carts. 0.4 means 40% of original size."), 0.05f, 1f)); } private static ConfigDescription Ranged(string description, float min, float max) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[0]); } private static string Text(string chinese, string english) { if (!_useEnglish) { return chinese; } return english; } private static ConfigEntry<T> Bind<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue, string description) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return Bind(config, id, chineseSection, englishSection, chineseKey, englishKey, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[0])); } private static ConfigEntry<T> Bind<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue, ConfigDescription description) { T val = ReadLocalizedValue(config, id, chineseSection, englishSection, chineseKey, englishKey, defaultValue); string text = (_useEnglish ? englishSection : chineseSection); string text2 = (_useEnglish ? englishKey : chineseKey); ActiveDefinitions.Add(new LocalizedDefinition(id, chineseSection, englishSection, chineseKey, englishKey)); return config.Bind<T>(text, text2, val, description); } private static T ReadLocalizedValue<T>(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue) { ConfigEntry<T> val = default(ConfigEntry<T>); T value; T value2; if (_useEnglish) { if (config.TryGetEntry<T>(englishSection, englishKey, ref val)) { return val.Value; } if (config.TryGetEntry<T>(chineseSection, chineseKey, ref val)) { return val.Value; } if (TryGetOrphanedValue<T>(config, englishSection, englishKey, out value)) { return value; } if (TryGetOrphanedValue<T>(config, chineseSection, chineseKey, out value2)) { return value2; } } else { if (config.TryGetEntry<T>(chineseSection, chineseKey, ref val)) { return val.Value; } if (config.TryGetEntry<T>(englishSection, englishKey, ref val)) { return val.Value; } if (TryGetOrphanedValue<T>(config, chineseSection, chineseKey, out value2)) { return value2; } if (TryGetOrphanedValue<T>(config, englishSection, englishKey, out value)) { return value; } } return defaultValue; } private static string DetectConfiguredLanguage(ConfigFile config) { ConfigEntry<string> val = default(ConfigEntry<string>); if (config.TryGetEntry<string>("语言 / Language", "配置语言 / Config Language", ref val)) { return NormalizeLanguage(val.Value); } if (TryGetOrphanedValue<string>(config, "语言 / Language", "配置语言 / Config Language", out var value)) { return NormalizeLanguage(value); } return "中文"; } private static bool TryGetOrphanedValue<T>(ConfigFile config, string section, string key, out T value) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown value = default(T); IDictionary dictionary = ((ConfigFileOrphanedEntriesProperty == null) ? null : (ConfigFileOrphanedEntriesProperty.GetValue(config, null) as IDictionary)); if (dictionary == null) { return false; } ConfigDefinition key2 = new ConfigDefinition(section, key); if (!dictionary.Contains(key2)) { return false; } if (!(dictionary[key2] is string text)) { return false; } try { Type typeFromHandle = typeof(T); if (typeFromHandle == typeof(string)) { value = (T)(object)text; return true; } if (typeFromHandle == typeof(bool) && bool.TryParse(text, out var result)) { value = (T)(object)result; return true; } if (typeFromHandle == typeof(float) && (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.TryParse(text, out result2))) { value = (T)(object)result2; return true; } if (typeFromHandle == typeof(int) && int.TryParse(text, out var result3)) { value = (T)(object)result3; return true; } } catch { return false; } return false; } private static bool IsEnglishLanguage(string value) { return NormalizeLanguage(value) == "English"; } private static string NormalizeLanguage(string value) { if (string.Equals(value, "English", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "EN", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "English", StringComparison.OrdinalIgnoreCase)) { return "English"; } return "中文"; } private static bool TryCategory(ConfigEntry<bool> enabled, ConfigEntry<float> factorEntry, out float factor) { factor = SafeFactor(factorEntry.Value); return enabled.Value; } private static bool TryEnemyOrb(ConfigEntry<float> factorEntry, out float factor) { factor = SafeFactor(factorEntry.Value); return EnemyOrbEnabled.Value; } private static float SafeFactor(float value) { return Mathf.Clamp(value, 0.05f, 1f); } private static void MigrateMassScalingDefaultOnce() { if (ScaleMassWithSize == null || _boundConfig == null) { return; } string massDefaultMigrationMarkerPath = GetMassDefaultMigrationMarkerPath(); if (File.Exists(massDefaultMigrationMarkerPath)) { return; } ScaleMassWithSize.Value = true; try { _boundConfig.Save(); File.WriteAllText(massDefaultMigrationMarkerPath, "0.2.39"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to persist mass scaling default migration marker: " + ex.Message)); } } private static string GetMassDefaultMigrationMarkerPath() { string text = ((_boundConfig == null) ? null : _boundConfig.ConfigFilePath); string text2 = (string.IsNullOrEmpty(text) ? Paths.ConfigPath : Path.GetDirectoryName(text)); if (string.IsNullOrEmpty(text2)) { text2 = Paths.ConfigPath; } return Path.Combine(text2, "ShrinkCart.v0.2.39.mass-default-enabled"); } private static bool LegacyBool(ConfigFile config, bool defaultValue, params string[] sectionKeyPairs) { ConfigEntry<bool> val = default(ConfigEntry<bool>); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry<bool>(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val)) { return val.Value; } } return defaultValue; } private static float LegacyFloat(ConfigFile config, float defaultValue, params string[] sectionKeyPairs) { ConfigEntry<float> val = default(ConfigEntry<float>); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry<float>(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val)) { return val.Value; } } return defaultValue; } private static void RemoveDeprecatedEntries(ConfigFile config) { bool flag = false; flag |= RemoveDeprecatedEntry(config, "Cart", "Enabled"); flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleFactor"); flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleSpeed"); flag |= RemoveDeprecatedEntry(config, "Cart", "RestoreGraceSeconds"); flag |= RemoveDeprecatedEntry(config, "Cart", "PreserveMass"); flag |= RemoveDeprecatedEntry(config, "Cart", "ShrinkNonValuableItems"); flag |= RemoveDeprecatedEntry(config, "Cart", "SuppressValuableDamageRestore"); flag |= RemoveDeprecatedEntry(config, "Diagnostics", "DebugLogging"); flag |= RemoveDeprecatedEntry(config, "购物车", "保持原始重量"); flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillPlayers"); flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillEnemies"); flag |= RemoveDeprecatedEntry(config, "视觉", "隐藏缩放闪光"); flag |= RemoveDeprecatedEntry(config, "车辆碾压", "车辆碾压秒杀玩家"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "启用提取点复活"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活前稳定检测时间"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活检测间隔"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "拦截外部立即复活调用"); flag |= RemoveDeprecatedEntry(config, "购物车", "商店用品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "启用实验性玩家缩放"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家死亡前自动恢复"); flag |= RemoveDeprecatedEntry(config, "购物车", "启用玩家缩放"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车缩放倍率"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家站车触发时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家离车判定宽容时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车切换间隔"); flag |= RemoveDeprecatedEntry(config, "购物车", "普通物品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "商店/人物用品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "防止车辆互相重叠"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆硬碰撞修正强度"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆最大单帧修正距离"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆挤压速度清除"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间(已废弃)"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆脱困强度"); flag |= RemoveDeprecatedEntry(config, "玩家缩放", "旧版玩家缩放开关(已停用)"); flag |= RemoveDeprecatedEntry(config, "地图兼容", "Minecraft Stronghold 普通门进车破碎"); flag |= RemoveDeprecatedEntry(config, "地图兼容", "Minecraft Stronghold 门破碎确认时间"); flag |= RemoveDeprecatedEntry(config, "Map Compatibility", "Minecraft Stronghold door shatter in cart"); if (flag | RemoveDeprecatedEntry(config, "Map Compatibility", "Minecraft Stronghold door shatter confirmation time")) { config.Save(); } } private static void RemoveInactiveLanguageEntries(ConfigFile config) { bool flag = false; for (int i = 0; i < ActiveDefinitions.Count; i++) { LocalizedDefinition localizedDefinition = ActiveDefinitions[i]; flag = ((!_useEnglish) ? (flag | RemoveDeprecatedEntry(config, localizedDefinition.EnglishSection, localizedDefinition.EnglishKey)) : (flag | RemoveDeprecatedEntry(config, localizedDefinition.ChineseSection, localizedDefinition.ChineseKey))); } if (flag) { config.Save(); } } private static bool RemoveDeprecatedEntry(ConfigFile config, string section, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); bool result = config.Remove(val); IDictionary dictionary = ((ConfigFileOrphanedEntriesProperty == null) ? null : (ConfigFileOrphanedEntriesProperty.GetValue(config, null) as IDictionary)); if (dictionary != null && dictionary.Contains(val)) { dictionary.Remove(val); result = true; } return result; } private static void WatchScaling<T>(ConfigEntry<T> entry) { entry.SettingChanged += OnScalingSettingChanged; } private static void OnScalingSettingChanged(object sender, EventArgs args) { ScalingConfigVersion++; } } internal static class PlayerCartScaleController { private enum PlayerCartScaleState { Normal, Shrunk, Grown } private enum PlayerCartNextAction { Shrink, Grow } private sealed class PlayerState { internal PlayerAvatar Player; internal PlayerCartScaleState ScaleState; internal PlayerCartNextAction NextAction; internal bool WasInCartRange; internal bool WasInTriggerZone; internal bool TriggeredThisStay; internal float TriggerZoneEnteredTime; internal float LastInsideCenterTime; } private sealed class CartState { internal PhysGrabCart Cart; internal Transform InCart; } private struct CartZoneResult { internal bool InCartRange; internal bool InTriggerZone; } private const float CenterZoneHorizontalScale = 0.45f; private const float FloorProjectionPaddingBelow = 0.25f; private const float FloorProjectionStandingHeightAbove = 1.4f; private const float MinimumCenterHalfExtent = 0.15f; private const float StandPointYOffset = 0.05f; private static readonly Dictionary<int, CartState> RegisteredCarts = new Dictionary<int, CartState>(); private static readonly Dictionary<int, PlayerState> PlayerStates = new Dictionary<int, PlayerState>(); private static readonly HashSet<int> ExcludedCartIds = new HashSet<int>(); private static readonly List<int> RemoveCartIds = new List<int>(8); private static readonly List<int> RemovePlayerIds = new List<int>(8); private static readonly FieldInfo PhysGrabCartInCartField = typeof(PhysGrabCart).GetField("inCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerAvatarColliderField = typeof(PlayerAvatar).GetField("collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float _nextTickTime; internal static void Reset() { RegisteredCarts.Clear(); PlayerStates.Clear(); ExcludedCartIds.Clear(); RemoveCartIds.Clear(); RemovePlayerIds.Clear(); _nextTickTime = 0f; } internal static void RegisterCart(PhysGrabCart cart) { if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null) && !IsExcludedPlayerScaleCart(cart)) { RegisteredCarts[((Object)cart).GetInstanceID()] = new CartState { Cart = cart, InCart = GetInCartTransform(cart) }; DebugLog("Registered regular cart for player stand-toggle: " + ((Object)cart).name); } } internal static void RegisterExistingCarts() { PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>(); for (int i = 0; i < array.Length; i++) { RegisterCart(array[i]); } } internal static void Tick() { if (!IsHostOrSingleplayer()) { return; } float time = Time.time; if (time < _nextTickTime) { return; } _nextTickTime = time + ModConfig.SafePlayerCartDetectionIntervalSeconds(); if (!ModConfig.PlayerScalingEnabled()) { RestoreAll(); } else { if (RegisteredCarts.Count == 0) { return; } PruneInvalidCarts(); List<PlayerAvatar> players = GetPlayers(); if (players != null && players.Count != 0) { for (int i = 0; i < players.Count; i++) { ProcessPlayer(players[i], time); } PruneMissingPlayers(); } } } internal static void RestoreAll() { if (PlayerStates.Count == 0) { return; } foreach (PlayerState value in PlayerStates.Values) { if (value != null && value.ScaleState != PlayerCartScaleState.Normal && (Object)(object)value.Player != (Object)null) { RestorePlayer(((Component)value.Player).gameObject); } } PlayerStates.Clear(); } internal static void Disable() { RestoreAll(); Reset(); } private static void ProcessPlayer(PlayerAvatar player, float now) { if ((Object)(object)player == (Object)null || (Object)(object)((Component)player).gameObject == (Object)null || !((Component)player).gameObject.activeInHierarchy) { return; } int instanceID = ((Object)player).GetInstanceID(); if (!PlayerStates.TryGetValue(instanceID, out var value)) { PlayerState playerState = new PlayerState(); playerState.Player = player; value = playerState; PlayerStates[instanceID] = value; } else { value.Player = player; } PlayerCartScaleState scaleState; if (value.ScaleState != PlayerCartScaleState.Normal && !ScaleManager.IsScaled(((Component)player).gameObject)) { AdvanceCycleAfterRestore(value); value.ScaleState = PlayerCartScaleState.Normal; } else if (value.ScaleState == PlayerCartScaleState.Normal && TryGetShrinkCartPlayerScaleState(((Component)player).gameObject, out scaleState)) { value.ScaleState = scaleState; DebugLog("Adopted existing ShrinkCart-like player scale state: " + ((Object)player).name + " state=" + scaleState); } CartZoneResult playerCartZone = GetPlayerCartZone(player); bool flag = playerCartZone.InCartRange; bool inTriggerZone = playerCartZone.InTriggerZone; if (flag) { value.LastInsideCenterTime = now; } else if (value.WasInCartRange && now - value.LastInsideCenterTime <= ModConfig.SafePlayerCartExitGraceSeconds()) { flag = true; } if (!flag) { if (value.WasInCartRange) { DebugLog("Player left cart floor range: " + ((Object)player).name); } value.WasInCartRange = false; value.WasInTriggerZone = false; value.TriggeredThisStay = false; value.TriggerZoneEnteredTime = 0f; value.LastInsideCenterTime = 0f; return; } value.WasInCartRange = true; if (!inTriggerZone) { value.WasInTriggerZone = false; value.TriggerZoneEnteredTime = 0f; } else if (!value.WasInTriggerZone) { value.WasInTriggerZone = true; value.TriggerZoneEnteredTime = now; value.LastInsideCenterTime = now; DebugLog("Player entered cart floor trigger zone: " + ((Object)player).name); } else { if (value.TriggeredThisStay || now - value.TriggerZoneEnteredTime < ModConfig.SafePlayerCartStandTriggerSeconds()) { return; } value.TriggeredThisStay = true; DebugLog("Player cart floor trigger timer completed: " + ((Object)player).name); if (value.ScaleState == PlayerCartScaleState.Shrunk || value.ScaleState == PlayerCartScaleState.Grown) { PlayerCartScaleState scaleState2 = value.ScaleState; if (RestorePlayer(((Component)player).gameObject)) { value.ScaleState = PlayerCartScaleState.Normal; value.NextAction = ((scaleState2 == PlayerCartScaleState.Shrunk) ? PlayerCartNextAction.Grow : PlayerCartNextAction.Shrink); DebugLog("Restored player after standing in cart center: " + ((Object)player).name + " next=" + value.NextAction); } return; } if (ScaleManager.IsScaled(((Component)player).gameObject)) { DebugLog("Skipped player cart shrink because another scale session is active: " + ((Object)player).name); return; } PlayerCartScaleState playerCartScaleState = ((value.NextAction != PlayerCartNextAction.Grow) ? PlayerCartScaleState.Shrunk : PlayerCartScaleState.Grown); float factor = ((playerCartScaleState == PlayerCartScaleState.Grown) ? ModConfig.SafePlayerCartGrowFactor() : ModConfig.SafePlayerCartScaleFactor()); if (ApplyPlayerScale(((Component)player).gameObject, factor, playerCartScaleState)) { value.ScaleState = playerCartScaleState; DebugLog(string.Concat("Applied player cart scale after standing in cart center: ", ((Object)player).name, " state=", playerCartScaleState, " factor=", factor.ToString("0.###"))); } } } private static void AdvanceCycleAfterRestore(PlayerState state) { if (state != null) { if (state.ScaleState == PlayerCartScaleState.Shrunk) { state.NextAction = PlayerCartNextAction.Grow; } else if (state.ScaleState == PlayerCartScaleState.Grown) { state.NextAction = PlayerCartNextAction.Shrink; } } } private static bool ApplyPlayerScale(GameObject target, float factor, PlayerCartScaleState targetState) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return false; } ScaleOptions val = ScaleOptions.Default; val.Factor = factor; val.Speed = ModConfig.SafeScaleSpeed(); val.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); val.Duration = 0f; val.AllowedTargets = (ScaleTargets)1; val.SuppressImpactFlash = true; val.SuppressCameraShake = true; val.IgnoreBonkExpand = ModConfig.RestorePlayerOnDamage != null && !ModConfig.RestorePlayerOnDamage.Value; val.RejectExternalApply = false; try { bool flag = ScaleManager.ApplyIfNotScaled(target, val); if (!flag) { DebugLog("ScalerCore rejected player cart scale: " + ((Object)target).name + " state=" + targetState); } return flag; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to scale player standing in cart: " + ex.Message)); return false; } } private static bool RestorePlayer(GameObject target) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return false; } try { if (!ScaleManager.IsScaled(target)) { return true; } ScaleController controller = ScaleManager.GetController(target); if ((Object)(object)controller != (Object)null && controller.IsScaled) { ScaleOptions currentOptions = controller.CurrentOptions; currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); currentOptions.SuppressImpactFlash = true; currentOptions.SuppressCameraShake = true; ScaleManager.ForceUpdateOptions(target, currentOptions); } ScaleManager.ForceRestore(target); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to restore player from cart toggle: " + ex.Message)); return false; } } private static bool TryGetShrinkCartPlayerScaleState(GameObject target, out PlayerCartScaleState scaleState) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 scaleState = PlayerCartScaleState.Normal; if ((Object)(object)target == (Object)null || !ScaleManager.IsScaled(target)) { return false; } ScaleController controller = ScaleManager.GetController(target); if ((Object)(object)controller == (Object)null || !controller.IsScaled) { return false; } ScaleOptions currentOptions = controller.CurrentOptions; if ((int)currentOptions.AllowedTargets != 1 || !currentOptions.SuppressImpactFlash || !currentOptions.SuppressCameraShake || currentOptions.RejectExternalApply) { return false; } if (Mathf.Approximately(currentOptions.Factor, ModConfig.SafePlayerCartScaleFactor())) { scaleState = PlayerCartScaleState.Shrunk; return true; } if (Mathf.Approximately(currentOptions.Factor, ModConfig.SafePlayerCartGrowFactor())) { scaleState = PlayerCartScaleState.Grown; return true; } return false; } private static CartZoneResult GetPlayerCartZone(PlayerAvatar player) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) CartZoneResult result = default(CartZoneResult); Vector3 playerStandPoint = GetPlayerStandPoint(player); foreach (CartState value in RegisteredCarts.Values) { if (value != null) { CartZoneResult playerCartZone = GetPlayerCartZone(player, value, playerStandPoint); if (playerCartZone.InCartRange) { result.InCartRange = true; } if (playerCartZone.InTriggerZone) { result.InTriggerZone = true; return result; } } } return result; } private static CartZoneResult GetPlayerCartZone(PlayerAvatar player, CartState cartState, Vector3 standPoint) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) CartZoneResult result = default(CartZoneResult); Transform val = cartState.InCart; if ((Object)(object)val == (Object)null && (Object)(object)cartState.Cart != (Object)null) { val = (cartState.InCart = GetInCartTransform(cartState.Cart)); } if ((Object)(object)player == (Object)null || (Object)(object)val == (Object)null) { return result; } result.InCartRange = IsPointInsideCartFloorProjection(standPoint, val, 1f); result.InTriggerZone = result.InCartRange && IsPointInsideCartFloorProjection(standPoint, val, 0.45f); return result; } private static bool IsPointInsideCartFloorProjection(Vector3 point, Transform inCart, float horizontalScale) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Quaternion.Inverse(inCart.rotation) * (point - inCart.position); Vector3 val2 = inCart.localScale * 0.5f; float num = Mathf.Max(Mathf.Abs(val2.x) * horizontalScale, 0.15f); float num2 = Mathf.Max(Mathf.Abs(val2.z) * horizontalScale, 0.15f); float num3 = 0f - Mathf.Abs(val2.y); if (Mathf.Abs(val.x) <= num && Mathf.Abs(val.z) <= num2 && val.y >= num3 - 0.25f) { return val.y <= num3 + 1.4f; } return false; } private static Transform GetInCartTransform(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null || PhysGrabCartInCartField == null) { return null; } object? value = PhysGrabCartInCartField.GetValue(cart); return (Transform)((value is Transform) ? value : null); } private static void PruneInvalidCarts() { RemoveCartIds.Clear(); foreach (KeyValuePair<int, CartState> registeredCart in RegisteredCarts) { if (registeredCart.Value == null || (Object)(object)registeredCart.Value.Cart == (Object)null || (Object)(object)registeredCart.Value.InCart == (Object)null || IsExcludedPlayerScaleCart(registeredCart.Value.Cart)) { RemoveCartIds.Add(registeredCart.Key); } } for (int i = 0; i < RemoveCartIds.Count; i++) { RegisteredCarts.Remove(RemoveCartIds[i]); } RemoveCartIds.Clear(); } private static bool IsExcludedPlayerScaleCart(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return true; } int instanceID = ((Object)cart).GetInstanceID(); if (ExcludedCartIds.Contains(instanceID)) { return true; } if (cart.isSmallCart) { ExcludedCartIds.Add(instanceID); DebugLog("Excluded small cart from player stand-toggle: " + ((Object)cart).name); return true; } return false; } private static Vector3 GetPlayerStandPoint(PlayerAvatar player) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) Collider val = null; if ((Object)(object)player != (Object)null && PlayerAvatarColliderField != null) { object? value = PlayerAvatarColliderField.GetValue(player); val = (Collider)((value is Collider) ? value : null); } if ((Object)(object)val != (Object)null) { Bounds bounds = val.bounds; return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + 0.05f, ((Bounds)(ref bounds)).center.z); } Vector3 val2 = (((Object)(object)player.playerTransform != (Object)null) ? player.playerTransform.position : ((Component)player).transform.position); return val2 + Vector3.up * 0.05f; } private static void PruneMissingPlayers() { if (PlayerStates.Count == 0) { return; } RemovePlayerIds.Clear(); foreach (KeyValuePair<int, PlayerState> playerState in PlayerStates) { if (playerState.Value == null || (Object)(object)playerState.Value.Player == (Object)null) { RemovePlayerIds.Add(playerState.Key); } } for (int i = 0; i < RemovePlayerIds.Count; i++) { PlayerStates.Remove(RemovePlayerIds[i]); } RemovePlayerIds.Clear(); } private static List<PlayerAvatar> GetPlayers() { try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { return GameDirector.instance.PlayerList; } } catch { } try { return SemiFunc.PlayerGetAll(); } catch { return null; } } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("AngelcoMilk.ShrinkCart", "ShrinkCart", "0.2.45")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "AngelcoMilk.ShrinkCart"; public const string PluginName = "ShrinkCart"; public const string PluginVersion = "0.2.45"; internal static Plugin Instance; internal static ManualLogSource Log; private Harmony _harmony; private bool _playerScalingWasEnabled; private bool _wasHostOrSingleplayer; private void Awake() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Authority.Reset(); ModConfig.Bind(((BaseUnityPlugin)this).Config); ValuableBoxScaleAdapter.Reset(); ValuableBoxScaleAdapter.RegisterHandler(); ShrinkerCartController.Reset(); PlayerCartScaleController.Reset(); DeadHeadCartScaleController.Reset(); EnemyInCartKillController.Reset(); _harmony = new Harmony("AngelcoMilk.ShrinkCart"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ShrinkCart 0.2.45 loaded."); } private void Update() { bool flag = Authority.IsHostOrSingleplayer(); if (flag && !_wasHostOrSingleplayer) { CartRegistry.RegisterExistingCarts(); if (ModConfig.PlayerScalingEnabled()) { PlayerCartScaleController.Reset(); PlayerCartScaleController.RegisterExistingCarts(); _playerScalingWasEnabled = true; } } ShrinkerCartController.Tick(); DeadHeadCartScaleController.Tick(); bool flag2 = flag && ModConfig.PlayerScalingEnabled(); if (flag2) { if (!_playerScalingWasEnabled) { PlayerCartScaleController.Reset(); PlayerCartScaleController.RegisterExistingCarts(); } PlayerCartScaleController.Tick(); } else if (_playerScalingWasEnabled) { if (flag) { PlayerCartScaleController.Disable(); } else { PlayerCartScaleController.Reset(); } } _playerScalingWasEnabled = flag2; _wasHostOrSingleplayer = flag; } private void OnDestroy() { ShrinkerCartController.RestoreAll(); PlayerCartScaleController.RestoreAll(); DeadHeadCartScaleController.RestoreAll(); DeadHeadCartScaleController.Reset(); CartRegistry.Reset(); EnemyInCartKillController.Reset(); Authority.Reset(); if (_harmony != null) { _harmony.UnpatchSelf(); _harmony = null; } } } internal static class ShrinkerCartController { private sealed class TrackedObject { internal GameObject Target; internal float LastSeenInCartTime; internal float RestoreCheckDueTime; internal int LastSeenCartId; internal bool MarkedInCartThisPass; internal ShrinkCategory Category; } private sealed class CachedShrinkData { internal int ConfigVersion; internal bool CanShrink; internal ShrinkCategory Category; internal float Factor; } private const float LowTrackedObjectIntervalSeconds = 0.35f; private const float MediumTrackedObjectIntervalSeconds = 0.75f; private static readonly Dictionary<int, TrackedObject> TrackedObjects = new Dictionary<int, TrackedObject>(); private static readonly Dictionary<int, CachedShrinkData> ShrinkDataCache = new Dictionary<int, CachedShrinkData>(); private static readonly Dictionary<int, float> ReshrinkCooldownUntil = new Dictionary<int, float>(); private static readonly HashSet<int> PermanentlyExcludedObjectIds = new HashSet<int>(); private static readonly List<int> RestoreIds = new List<int>(16); private static readonly List<int> ExpiredCooldownIds = new List<int>(16); private static readonly List<GameObject> RestoreTargets = new List<GameObject>(16); private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabObjectIsGunField = typeof(PhysGrabObject).GetField("isGun", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartPhysGrabObjectField = typeof(PhysGrabCart).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float _nextTickTime; private static float _nextRestoreCheckTime = float.PositiveInfinity; private static float _nextCooldownCheckTime = float.PositiveInfinity; internal static void Reset() { TrackedObjects.Clear(); ShrinkDataCache.Clear(); ReshrinkCooldownUntil.Clear(); PermanentlyExcludedObjectIds.Clear(); RestoreIds.Clear(); ExpiredCooldownIds.Clear(); RestoreTargets.Clear(); _nextTickTime = 0f; _nextRestoreCheckTime = float.PositiveInfinity; _nextCooldownCheckTime = float.PositiveInfinity; } internal static void ProcessCartObject(PhysGrabInCart inCart, PhysGrabObject item) { if (!((Object)(object)inCart == (Object)null) && !((Object)(object)inCart.cart == (Object)null) && !((Object)(object)item == (Object)null) && IsHostOrSingleplayer() && !EnemyInCartKillController.TryKill(item)) { ShrinkCategory category = ShrinkCategory.Fallback; float factor = 1f; if (ModConfig.CartShrinkingEnabled.Value && TryGetShrinkData(item, out category, out factor)) { TrackOrShrink(item, category, factor, inCart.cart); } } } internal static void MarkObjectsSeenInCart(PhysGrabCart cart, List<PhysGrabObject> items) { if (!IsHostOrSingleplayer() || items == null) { return; } float time = Time.time; int cartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0); for (int i = 0; i < items.Count; i++) { PhysGrabObject val = items[i]; GameObject val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).gameObject); if (!((Object)(object)val2 == (Object)null)) { ShrinkCategory category; float factor; if (TrackedObjects.TryGetValue(((Object)val2).GetInstanceID(), out var value)) { MarkTrackedInCart(value, time, cartId); } else if (!EnemyInCartKillController.TryKill(val) && ModConfig.CartShrinkingEnabled.Value && TryGetShrinkData(val, out category, out factor)) { TrackOrShrink(val, category, factor, cart); } } } } internal static void Tick() { if (!IsHostOrSingleplayer()) { return; } float time = Time.time; if (time < _nextTickTime && time < _nextRestoreCheckTime && time < _nextCooldownCheckTime) { return; } _nextTickTime = time + GetCurrentTickInterval(); if (!ModConfig.CartShrinkingEnabled.Value) { RestoreAll(); ClearExpiredCooldowns(time); return; } if (TrackedObjects.Count == 0) { ClearExpiredCooldowns(time); _nextRestoreCheckTime = float.PositiveInfinity; return; } ClearExpiredCooldowns(time); if (time < _nextRestoreCheckTime) { return; } RestoreIds.Clear(); float num = float.PositiveInfinity; foreach (KeyValuePair<int, TrackedObject> trackedObject in TrackedObjects) { TrackedObject value = trackedObject.Value; bool flag = (Object)(object)value.Target != (Object)null && IsTrackedEquipped(value.Target); if ((Object)(object)value.Target == (Object)null || flag || time >= value.RestoreCheckDueTime) { if (flag) { DebugLog("Restoring tracked equippable before equipped use: " + ((Object)value.Target).name); } RestoreIds.Add(trackedObject.Key); } else if (value.RestoreCheckDueTime < num) { num = value.RestoreCheckDueTime; } } for (int i = 0; i < RestoreIds.Count; i++) { int num2 = RestoreIds[i]; if (TrackedObjects.TryGetValue(num2, out var value2)) { RestoreTrackedObject(num2, value2.Target); TrackedObjects.Remove(num2); } } _nextRestoreCheckTime = ((TrackedObjects.Count == 0) ? float.PositiveInfinity : num); } internal static void RestoreAll() { if (TrackedObjects.Count == 0) { return; } RestoreTargets.Clear(); foreach (TrackedObject value in TrackedObjects.Values) { if ((Object)(object)value.Target != (Object)null) { RestoreTargets.Add(value.Target); } } TrackedObjects.Clear(); _nextRestoreCheckTime = float.PositiveInfinity; for (int i = 0; i < RestoreTargets.Count; i++) { RestoreTrackedObject(((Object)RestoreTargets[i]).GetInstanceID(), RestoreTargets[i]); } RestoreTargets.Clear(); } private static float GetCurrentTickInterval() { float num = ModConfig.SafeMinimumItemScanIntervalSeconds(); if (!ModConfig.DynamicItemScanEnabledValue()) { return num; } float num2 = ModConfig.SafeMaximumItemScanIntervalSeconds(); int count = TrackedObjects.Count; float num3 = ((count <= 0) ? num2 : ((count <= 6) ? num : ((count <= 15) ? 0.35f : ((count > 30) ? num2 : 0.75f)))); return Mathf.Clamp(num3, num, num2); } private static void TrackOrShrink(PhysGrabObject item, ShrinkCategory category, float factor, PhysGrabCart cart) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)item == (Object)null) ? null : ((Component)item).gameObject); if ((Object)(object)val == (Object)null) { return; } int instanceID = ((Object)val).GetInstanceID(); float time = Time.time; if (TrackedObjects.TryGetValue(instanceID, out var value)) { MarkTrackedInCart(value, time, (!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0); value.Category = category; } else { if (IsInReshrinkCooldown(instanceID)) { return; } if (ScaleManager.IsScaled(val)) { TryTrackExistingShrinkCartScale(val, category, factor, cart, time); return; } if (category == ShrinkCategory.ValuableBox && !ValuableBoxScaleAdapter.EnsureController(val)) { DebugLog("Prepared token/cosmetic box controller, waiting for ScalerCore initialization: " + ((Object)val).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item)); return; } ScaleOptions val2 = ScaleOptions.Default; val2.Factor = factor; val2.Speed = ModConfig.SafeScaleSpeed(); val2.Duration = 0f; val2.AllowedTargets = (ScaleTargets)((category == ShrinkCategory.ValuableBox) ? 15 : 12); val2.SuppressValueDropExpand = ModConfig.SuppressValuableDamageRestore.Value; val2.PreserveMass = ModConfig.ShouldPreserveMass(); val2.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); val2.SuppressImpactFlash = true; val2.SuppressCameraShake = true; try { if (!ScaleManager.ApplyIfNotScaled(val, val2)) { if (category == ShrinkCategory.ValuableBox) { DebugLog("ScalerCore rejected token/cosmetic box shrink for " + ((Object)val).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item) + " controller=" + ((Object)(object)ScaleManager.GetController(val) != (Object)null)); } return; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to shrink " + ((Object)val).name + ": " + ex.Message)); return; } float num = time + ModConfig.SafeCartLeaveDebounceSeconds(); TrackedObjects[instanceID] = new TrackedObject { Target = val, LastSeenInCartTime = time, RestoreCheckDueTime = num, LastSeenCartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0), MarkedInCartThisPass = true, Category = category }; ScheduleRestoreCheck(num); DebugLog(string.Concat("Shrunk ", ((Object)val).name, " as ", category, " factor=", factor.ToString("0.###"))); } } private static bool TryTrackExistingShrinkCartScale(GameObject target, ShrinkCategory category, float factor, PhysGrabCart cart, float now) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return false; } int instanceID = ((Object)target).GetInstanceID(); if (TrackedObjects.ContainsKey(instanceID)) { return true; } ScaleController controller = ScaleManager.GetController(target); if ((Object)(object)controller == (Object)null || !controller.IsScaled) { return false; } ScaleOptions currentOptions = controller.CurrentOptions; if (!LooksLikeShrinkCartOptions(currentOptions, category, factor)) { return false; } float num = now + ModConfig.SafeCartLeaveDebounceSeconds(); TrackedObjects[instanceID] = new TrackedObject { Target = target, LastSeenInCartTime = now, RestoreCheckDueTime = num, LastSeenCartId = ((!((Object)(object)cart == (Object)null)) ? ((Object)cart).GetInstanceID() : 0), MarkedInCartThisPass = true, Category = category }; ScheduleRestoreCheck(num); DebugLog("Adopted existing ShrinkCart-like scaled object for restore tracking: " + ((Object)target).name); return true; } private static bool LooksLikeShrinkCartOptions(ScaleOptions options, ShrinkCategory category, float factor) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!Mathf.Approximately(options.Factor, factor)) { return false; } ScaleTargets val = (ScaleTargets)((category == ShrinkCategory.ValuableBox) ? 15 : 12); if (options.AllowedTargets == val && options.SuppressImpactFlash && options.SuppressCameraShake && options.SuppressValueDropExpand == ModConfig.SuppressValuableDamageRestore.Value) { return options.PreserveMass == ModConfig.ShouldPreserveMass(); } return false; } private static void MarkTrackedInCart(TrackedObject tracked, float now, int cartId) { if (tracked != null) { tracked.LastSeenInCartTime = now; tracked.RestoreCheckDueTime = now + ModConfig.SafeCartLeaveDebounceSeconds(); tracked.LastSeenCartId = cartId; tracked.MarkedInCartThisPass = true; ScheduleRestoreCheck(tracked.RestoreCheckDueTime); } } private static void ScheduleRestoreCheck(float dueTime) { if (dueTime < _nextRestoreCheckTime) { _nextRestoreCheckTime = dueTime; } } private static void RestoreTrackedObject(int id, GameObject target) { if ((Object)(object)target == (Object)null) { return; } BeginReshrinkCooldown(id); try { if (ScaleManager.IsScaled(target)) { RefreshRestoreOptions(target); ScaleManager.Restore(target); DebugLog("Restored " + ((Object)target).name + " speed=" + ModConfig.SafeRestoreScaleSpeed().ToString("0.###")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to restore " + ((Object)target).name + ": " + ex.Message)); } } private static void RefreshRestoreOptions(GameObject target) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) ScaleController controller = ScaleManager.GetController(target); if (!((Object)(object)controller == (Object)null) && controller.IsScaled) { ScaleOptions currentOptions = controller.CurrentOptions; currentOptions.RestoreSpeed = ModConfig.SafeRestoreScaleSpeed(); currentOptions.SuppressImpactFlash = true; currentOptions.SuppressCameraShake = true; ScaleManager.UpdateOptions(target, currentOptions); } } private static bool IsTrackedEquipped(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } ItemEquippable component = target.GetComponent<ItemEquippable>(); if ((Object)(object)component != (Object)null) { return component.IsEquipped(); } return false; } private static bool TryGetShrinkData(PhysGrabObject item, out ShrinkCategory category, out float factor) { category = ShrinkCategory.Fallback; factor = 1f; if (!PassesFastCandidateChecks(item)) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (IsInReshrinkCooldown(instanceID)) { return false; } if (ShrinkDataCache.TryGetValue(instanceID, out var value) && value.ConfigVersion == ModConfig.ScalingConfigVersion) { category = value.Category; factor = value.Factor; return value.CanShrink; } bool flag = ResolveShrinkData(item, out category, out factor); ShrinkDataCache[instanceID] = new CachedShrinkData { ConfigVersion = ModConfig.ScalingConfigVersion, CanShrink = flag, Category = category, Factor = factor }; return flag; } private static bool PassesFastCandidateChecks(PhysGrabObject item) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { return false; } if (item.dead) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (PermanentlyExcludedObjectIds.Contains(instanceID)) { return false; } if (CartObjectGuard.IsCartLike(item)) { PermanentlyExcludedObjectIds.Add(instanceID); return false; } if ((Object)(object)((Component)item).GetComponent<PlayerDeathHead>() != (Object)null || (Object)(object)((Component)item).GetComponentInParent<PlayerDeathHead>() != (Object)null || (Object)(object)((Component)item).GetComponentInChildren<PlayerDeathHead>(true) != (Object)null) { return false; } string cleanName = CleanName(((Object)item).name); if (IsPermanentlyExcludedCartItem(item, cleanName)) { PermanentlyExcludedObjectIds.Add(instanceID); return false; } ItemEquippable component = ((Component)item).GetComponent<It