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 CartLikeObjectIds = new HashSet(); private static readonly HashSet NonCartLikeObjectIds = new HashSet(); private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static bool IsCartLike(PhysGrabObject item) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (CartLikeObjectIds.Contains(instanceID)) { return true; } if (NonCartLikeObjectIds.Contains(instanceID)) { return false; } if (HasCartLikeComponent(item) || HasCartLikeItemType(item)) { CartLikeObjectIds.Add(instanceID); return true; } NonCartLikeObjectIds.Add(instanceID); return false; } internal static void Reset() { CartLikeObjectIds.Clear(); NonCartLikeObjectIds.Clear(); } internal static bool ShouldBlockCartInCart(PhysGrabInCart destination, PhysGrabObject item) { if ((Object)(object)destination == (Object)null || (Object)(object)destination.cart == (Object)null || (Object)(object)item == (Object)null) { return false; } if (!IsCartLike(item)) { return false; } return true; } private static bool HasCartLikeComponent(PhysGrabObject item) { if (!((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null) && !((Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null)) { return (Object)(object)((Component)item).GetComponentInChildren(true) != (Object)null; } return true; } private static bool HasCartLikeItemType(PhysGrabObject item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 ItemAttributes component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null || ItemAttributesItemTypeField == null) { return false; } if (!(ItemAttributesItemTypeField.GetValue(component) is itemType val)) { return false; } if ((int)val != 2 && (int)val != 14) { return (int)val == 12; } return true; } } internal static class CartRegistry { private sealed class CartState { internal PhysGrabCart Cart; } private static readonly Dictionary Carts = new Dictionary(); private static readonly List RemoveCartIds = new List(8); private static readonly FieldInfo PhysGrabCartItemsInCartField = typeof(PhysGrabCart).GetField("itemsInCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartItemsInCartCountField = typeof(PhysGrabCart).GetField("itemsInCartCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartHaulCurrentField = typeof(PhysGrabCart).GetField("haulCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ValuableObjectDollarValueCurrentField = typeof(ValuableObject).GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static void RegisterCart(PhysGrabCart cart) { if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null)) { Carts[((Object)cart).GetInstanceID()] = new CartState { Cart = cart }; DebugLog("Registered cart content guard target: " + ((Object)cart).name); } } internal static void RegisterExistingCarts() { if (IsHostOrSingleplayer()) { PhysGrabCart[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { RegisterCart(array[i]); } } } internal static void CleanCartContents(PhysGrabCart cart) { if (IsHostOrSingleplayer()) { CartState orCreateState = GetOrCreateState(cart); List itemsInCart = GetItemsInCart(orCreateState?.Cart); if (orCreateState != null && !((Object)(object)orCreateState.Cart == (Object)null) && itemsInCart != null) { 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 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 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 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 itemsInCart = GetItemsInCart(cart); if ((Object)(object)cart == (Object)null || itemsInCart == null) { return; } int num = 0; int num2 = 0; for (int i = 0; i < itemsInCart.Count; i++) { PhysGrabObject val = itemsInCart[i]; if (!((Object)(object)val == (Object)null)) { num++; ValuableObject val2 = ((Component)val).GetComponent(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).GetComponentInParent(); } if ((Object)(object)val2 != (Object)null) { num2 += GetValuableDollarValueCurrent(val2); } } } if (PhysGrabCartItemsInCartCountField != null) { PhysGrabCartItemsInCartCountField.SetValue(cart, num); } if (PhysGrabCartHaulCurrentField != null) { PhysGrabCartHaulCurrentField.SetValue(cart, num2); } if ((Object)(object)cart.valueScreen != (Object)null) { cart.valueScreen.UpdateValue(num2); } } private static List GetItemsInCart(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null || PhysGrabCartItemsInCartField == null) { return null; } return PhysGrabCartItemsInCartField.GetValue(cart) as List; } private static int GetValuableDollarValueCurrent(ValuableObject valuable) { if ((Object)(object)valuable == (Object)null || ValuableObjectDollarValueCurrentField == null) { return 0; } object value = ValuableObjectDollarValueCurrentField.GetValue(valuable); if (value is float) { return (int)(float)value; } if (value is int) { return (int)value; } return 0; } private static void DebugLog(string message) { if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } } internal static class DeadHeadCartScaleController { private sealed class TrackedHead { internal GameObject Target; internal PlayerDeathHead Head; internal float RestoreCheckDueTime; internal int LastSeenCartId; } private static readonly Dictionary TrackedHeads = new Dictionary(); private static readonly HashSet NonHeadObjectIds = new HashSet(); private static readonly List RestoreIds = new List(8); private static readonly List RestoreTargets = new List(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 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 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(); if ((Object)(object)head == (Object)null) { head = ((Component)item).GetComponentInParent(); } if ((Object)(object)head == (Object)null) { head = ((Component)item).GetComponentInChildren(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 ExecutedEnemies = new HashSet(); private static readonly FieldInfo EnemyHealthField = typeof(Enemy).GetField("Health", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo EnemyHealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.NonPublic); internal static void Reset() { ExecutedEnemies.Clear(); } internal static bool TryKill(PhysGrabObject item) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.EnemyInCartInstantKill.Value || (Object)(object)item == (Object)null) { return false; } Enemy val = FindEnemy(item); if ((Object)(object)val == (Object)null) { return false; } int instanceID = ((Object)val).GetInstanceID(); if (ExecutedEnemies.Contains(instanceID)) { return true; } EnemyHealth health = GetHealth(val); if ((Object)(object)health == (Object)null) { return false; } int currentHealth = GetCurrentHealth(health); if (currentHealth <= 0) { ExecutedEnemies.Add(instanceID); return true; } ExecutedEnemies.Add(instanceID); try { int num = Math.Max(currentHealth, health.health) + 1; health.Hurt(num, Vector3.up); DebugLog("Instant killed enemy entering cart: " + ((Object)val).name); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to instant kill enemy entering cart: " + ex.Message)); ExecutedEnemies.Remove(instanceID); return false; } return true; } private static Enemy FindEnemy(PhysGrabObject item) { EnemyRigidbody componentInParent = ((Component)item).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.enemy != (Object)null) { return componentInParent.enemy; } return ((Component)item).GetComponentInParent(); } private static EnemyHealth GetHealth(Enemy enemy) { if (EnemyHealthField == null) { return null; } object? value = EnemyHealthField.GetValue(enemy); return (EnemyHealth)((value is EnemyHealth) ? value : null); } private static int GetCurrentHealth(EnemyHealth health) { if (EnemyHealthCurrentField == null) { return health.health; } object value = EnemyHealthCurrentField.GetValue(health); if (value is int) { return (int)value; } return health.health; } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } internal 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 CartShrinkingEnabled; internal static ConfigEntry CartScaleSpeed; internal static ConfigEntry RestoreScaleSpeed; internal static ConfigEntry CartLeaveDebounceSeconds; internal static ConfigEntry ReshrinkCooldownSeconds; internal static ConfigEntry ScaleMassWithSize; internal static ConfigEntry ShrinkShopPlayerItems; internal static ConfigEntry PlayerScalingModuleEnabled; internal static ConfigEntry PlayerCartScaleFactor; internal static ConfigEntry PlayerCartGrowFactor; internal static ConfigEntry PlayerCartStandTriggerSeconds; internal static ConfigEntry PlayerCartExitGraceSeconds; internal static ConfigEntry PlayerCartDetectionIntervalSeconds; internal static ConfigEntry RestorePlayerOnDamage; internal static ConfigEntry SuppressValuableDamageRestore; internal static ConfigEntry DeadHeadScalingEnabled; internal static ConfigEntry DeadHeadScaleFactor; internal static ConfigEntry TinyEnabled; internal static ConfigEntry TinyScaleFactor; internal static ConfigEntry SmallEnabled; internal static ConfigEntry SmallScaleFactor; internal static ConfigEntry MediumEnabled; internal static ConfigEntry MediumScaleFactor; internal static ConfigEntry BigEnabled; internal static ConfigEntry BigScaleFactor; internal static ConfigEntry WideEnabled; internal static ConfigEntry WideScaleFactor; internal static ConfigEntry TallEnabled; internal static ConfigEntry TallScaleFactor; internal static ConfigEntry VeryTallEnabled; internal static ConfigEntry VeryTallScaleFactor; internal static ConfigEntry EnemyOrbEnabled; internal static ConfigEntry EnemyOrbSmallScaleFactor; internal static ConfigEntry EnemyOrbMediumScaleFactor; internal static ConfigEntry EnemyOrbBigScaleFactor; internal static ConfigEntry EnemyOrbBerserkerScaleFactor; internal static ConfigEntry SurplusEnabled; internal static ConfigEntry SurplusScaleFactor; internal static ConfigEntry ValuableBoxEnabled; internal static ConfigEntry ValuableBoxScaleFactor; internal static ConfigEntry FallbackScaleFactor; internal static ConfigEntry EnemyInCartInstantKill; internal static ConfigEntry DynamicItemScanEnabled; internal static ConfigEntry MinimumItemScanIntervalSeconds; internal static ConfigEntry MaximumItemScanIntervalSeconds; internal static ConfigEntry DebugLogging; internal static ConfigEntry ConfigLanguage; internal static int ScalingConfigVersion; private static bool _useEnglish; private static ConfigFile _boundConfig; private static readonly List ActiveDefinitions = new List(); 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("语言 / 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(new string[2] { "中文", "English" }), new object[0])); WatchScaling(CartShrinkingEnabled); WatchScaling(CartScaleSpeed); WatchScaling(RestoreScaleSpeed); WatchScaling(CartLeaveDebounceSeconds); WatchScaling(ReshrinkCooldownSeconds); WatchScaling(ScaleMassWithSize); WatchScaling(ShrinkShopPlayerItems); WatchScaling(PlayerScalingModuleEnabled); WatchScaling(PlayerCartScaleFactor); WatchScaling(PlayerCartGrowFactor); WatchScaling(PlayerCartStandTriggerSeconds); WatchScaling(PlayerCartExitGraceSeconds); WatchScaling(PlayerCartDetectionIntervalSeconds); WatchScaling(RestorePlayerOnDamage); WatchScaling(SuppressValuableDamageRestore); WatchScaling(DeadHeadScalingEnabled); WatchScaling(DeadHeadScaleFactor); WatchScaling(TinyEnabled); WatchScaling(TinyScaleFactor); WatchScaling(SmallEnabled); WatchScaling(SmallScaleFactor); WatchScaling(MediumEnabled); WatchScaling(MediumScaleFactor); WatchScaling(BigEnabled); WatchScaling(BigScaleFactor); WatchScaling(WideEnabled); WatchScaling(WideScaleFactor); WatchScaling(TallEnabled); WatchScaling(TallScaleFactor); WatchScaling(VeryTallEnabled); WatchScaling(VeryTallScaleFactor); WatchScaling(EnemyOrbEnabled); WatchScaling(EnemyOrbSmallScaleFactor); WatchScaling(EnemyOrbMediumScaleFactor); WatchScaling(EnemyOrbBigScaleFactor); WatchScaling(EnemyOrbBerserkerScaleFactor); WatchScaling(SurplusEnabled); WatchScaling(SurplusScaleFactor); WatchScaling(ValuableBoxEnabled); WatchScaling(ValuableBoxScaleFactor); WatchScaling(FallbackScaleFactor); 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 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 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(min, max), new object[0]); } private static string Text(string chinese, string english) { if (!_useEnglish) { return chinese; } return english; } private static ConfigEntry Bind(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 Bind(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(text, text2, val, description); } private static T ReadLocalizedValue(ConfigFile config, string id, string chineseSection, string englishSection, string chineseKey, string englishKey, T defaultValue) { ConfigEntry val = default(ConfigEntry); T value; T value2; if (_useEnglish) { if (config.TryGetEntry(englishSection, englishKey, ref val)) { return val.Value; } if (config.TryGetEntry(chineseSection, chineseKey, ref val)) { return val.Value; } if (TryGetOrphanedValue(config, englishSection, englishKey, out value)) { return value; } if (TryGetOrphanedValue(config, chineseSection, chineseKey, out value2)) { return value2; } } else { if (config.TryGetEntry(chineseSection, chineseKey, ref val)) { return val.Value; } if (config.TryGetEntry(englishSection, englishKey, ref val)) { return val.Value; } if (TryGetOrphanedValue(config, chineseSection, chineseKey, out value2)) { return value2; } if (TryGetOrphanedValue(config, englishSection, englishKey, out value)) { return value; } } return defaultValue; } private static string DetectConfiguredLanguage(ConfigFile config) { ConfigEntry val = default(ConfigEntry); if (config.TryGetEntry("语言 / Language", "配置语言 / Config Language", ref val)) { return NormalizeLanguage(val.Value); } if (TryGetOrphanedValue(config, "语言 / Language", "配置语言 / Config Language", out var value)) { return NormalizeLanguage(value); } return "中文"; } private static bool TryGetOrphanedValue(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 enabled, ConfigEntry factorEntry, out float factor) { factor = SafeFactor(factorEntry.Value); return enabled.Value; } private static bool TryEnemyOrb(ConfigEntry factorEntry, out float factor) { factor = SafeFactor(factorEntry.Value); return EnemyOrbEnabled.Value; } private static float SafeFactor(float value) { return Mathf.Clamp(value, 0.05f, 1f); } private static 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 val = default(ConfigEntry); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val)) { return val.Value; } } return defaultValue; } private static float LegacyFloat(ConfigFile config, float defaultValue, params string[] sectionKeyPairs) { ConfigEntry val = default(ConfigEntry); for (int i = 0; i + 1 < sectionKeyPairs.Length; i += 2) { if (config.TryGetEntry(sectionKeyPairs[i], sectionKeyPairs[i + 1], ref val)) { return val.Value; } } return defaultValue; } private static void RemoveDeprecatedEntries(ConfigFile config) { bool flag = false; flag |= RemoveDeprecatedEntry(config, "Cart", "Enabled"); flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleFactor"); flag |= RemoveDeprecatedEntry(config, "Cart", "ScaleSpeed"); flag |= RemoveDeprecatedEntry(config, "Cart", "RestoreGraceSeconds"); flag |= RemoveDeprecatedEntry(config, "Cart", "PreserveMass"); flag |= RemoveDeprecatedEntry(config, "Cart", "ShrinkNonValuableItems"); flag |= RemoveDeprecatedEntry(config, "Cart", "SuppressValuableDamageRestore"); flag |= RemoveDeprecatedEntry(config, "Diagnostics", "DebugLogging"); flag |= RemoveDeprecatedEntry(config, "购物车", "保持原始重量"); flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillPlayers"); flag |= RemoveDeprecatedEntry(config, "VehicleCrush", "InstantKillEnemies"); flag |= RemoveDeprecatedEntry(config, "视觉", "隐藏缩放闪光"); flag |= RemoveDeprecatedEntry(config, "车辆碾压", "车辆碾压秒杀玩家"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "启用提取点复活"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活前稳定检测时间"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "复活检测间隔"); flag |= RemoveDeprecatedEntry(config, "提取点复活兼容", "拦截外部立即复活调用"); flag |= RemoveDeprecatedEntry(config, "购物车", "商店用品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "启用实验性玩家缩放"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家死亡前自动恢复"); flag |= RemoveDeprecatedEntry(config, "购物车", "启用玩家缩放"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车缩放倍率"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家站车触发时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家离车判定宽容时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "玩家进车切换间隔"); flag |= RemoveDeprecatedEntry(config, "购物车", "普通物品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "商店/人物用品也缩小"); flag |= RemoveDeprecatedEntry(config, "购物车", "防止车辆互相重叠"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆硬碰撞修正强度"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆最大单帧修正距离"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆挤压速度清除"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间(已废弃)"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆临时忽略碰撞时间"); flag |= RemoveDeprecatedEntry(config, "购物车", "车辆脱困强度"); 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(ConfigEntry 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 RegisteredCarts = new Dictionary(); private static readonly Dictionary PlayerStates = new Dictionary(); private static readonly HashSet ExcludedCartIds = new HashSet(); private static readonly List RemoveCartIds = new List(8); private static readonly List RemovePlayerIds = new List(8); private static readonly FieldInfo PhysGrabCartInCartField = typeof(PhysGrabCart).GetField("inCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PlayerAvatarColliderField = typeof(PlayerAvatar).GetField("collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float _nextTickTime; internal static void Reset() { RegisteredCarts.Clear(); PlayerStates.Clear(); ExcludedCartIds.Clear(); RemoveCartIds.Clear(); RemovePlayerIds.Clear(); _nextTickTime = 0f; } internal static void RegisterCart(PhysGrabCart cart) { if (IsHostOrSingleplayer() && !((Object)(object)cart == (Object)null) && !IsExcludedPlayerScaleCart(cart)) { RegisteredCarts[((Object)cart).GetInstanceID()] = new CartState { Cart = cart, InCart = GetInCartTransform(cart) }; DebugLog("Registered regular cart for player stand-toggle: " + ((Object)cart).name); } } internal static void RegisterExistingCarts() { PhysGrabCart[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { RegisterCart(array[i]); } } internal static void Tick() { if (!IsHostOrSingleplayer()) { return; } float time = Time.time; if (time < _nextTickTime) { return; } _nextTickTime = time + ModConfig.SafePlayerCartDetectionIntervalSeconds(); if (!ModConfig.PlayerScalingEnabled()) { RestoreAll(); } else { if (RegisteredCarts.Count == 0) { return; } PruneInvalidCarts(); List players = GetPlayers(); if (players != null && players.Count != 0) { for (int i = 0; i < players.Count; i++) { ProcessPlayer(players[i], time); } PruneMissingPlayers(); } } } internal static void RestoreAll() { if (PlayerStates.Count == 0) { return; } foreach (PlayerState value in PlayerStates.Values) { if (value != null && value.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 registeredCart in RegisteredCarts) { if (registeredCart.Value == null || (Object)(object)registeredCart.Value.Cart == (Object)null || (Object)(object)registeredCart.Value.InCart == (Object)null || IsExcludedPlayerScaleCart(registeredCart.Value.Cart)) { RemoveCartIds.Add(registeredCart.Key); } } for (int i = 0; i < RemoveCartIds.Count; i++) { RegisteredCarts.Remove(RemoveCartIds[i]); } RemoveCartIds.Clear(); } private static bool IsExcludedPlayerScaleCart(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return true; } int instanceID = ((Object)cart).GetInstanceID(); if (ExcludedCartIds.Contains(instanceID)) { return true; } if (cart.isSmallCart) { ExcludedCartIds.Add(instanceID); DebugLog("Excluded small cart from player stand-toggle: " + ((Object)cart).name); return true; } return false; } private static Vector3 GetPlayerStandPoint(PlayerAvatar player) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) Collider val = null; if ((Object)(object)player != (Object)null && PlayerAvatarColliderField != null) { object? value = PlayerAvatarColliderField.GetValue(player); val = (Collider)((value is Collider) ? value : null); } if ((Object)(object)val != (Object)null) { Bounds bounds = val.bounds; return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + 0.05f, ((Bounds)(ref bounds)).center.z); } Vector3 val2 = (((Object)(object)player.playerTransform != (Object)null) ? player.playerTransform.position : ((Component)player).transform.position); return val2 + Vector3.up * 0.05f; } private static void PruneMissingPlayers() { if (PlayerStates.Count == 0) { return; } RemovePlayerIds.Clear(); foreach (KeyValuePair playerState in PlayerStates) { if (playerState.Value == null || (Object)(object)playerState.Value.Player == (Object)null) { RemovePlayerIds.Add(playerState.Key); } } for (int i = 0; i < RemovePlayerIds.Count; i++) { PlayerStates.Remove(RemovePlayerIds[i]); } RemovePlayerIds.Clear(); } private static List GetPlayers() { try { if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null) { return GameDirector.instance.PlayerList; } } catch { } try { return SemiFunc.PlayerGetAll(); } catch { return null; } } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("AngelcoMilk.ShrinkCart", "ShrinkCart", "0.2.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 TrackedObjects = new Dictionary(); private static readonly Dictionary ShrinkDataCache = new Dictionary(); private static readonly Dictionary ReshrinkCooldownUntil = new Dictionary(); private static readonly HashSet PermanentlyExcludedObjectIds = new HashSet(); private static readonly List RestoreIds = new List(16); private static readonly List ExpiredCooldownIds = new List(16); private static readonly List RestoreTargets = new List(16); private static readonly FieldInfo ItemAttributesItemTypeField = typeof(ItemAttributes).GetField("itemType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabObjectIsGunField = typeof(PhysGrabObject).GetField("isGun", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo PhysGrabCartPhysGrabObjectField = typeof(PhysGrabCart).GetField("physGrabObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static float _nextTickTime; private static float _nextRestoreCheckTime = float.PositiveInfinity; private static float _nextCooldownCheckTime = float.PositiveInfinity; internal static void Reset() { TrackedObjects.Clear(); ShrinkDataCache.Clear(); ReshrinkCooldownUntil.Clear(); PermanentlyExcludedObjectIds.Clear(); RestoreIds.Clear(); ExpiredCooldownIds.Clear(); RestoreTargets.Clear(); _nextTickTime = 0f; _nextRestoreCheckTime = float.PositiveInfinity; _nextCooldownCheckTime = float.PositiveInfinity; } internal static void ProcessCartObject(PhysGrabInCart inCart, PhysGrabObject item) { if (!((Object)(object)inCart == (Object)null) && !((Object)(object)inCart.cart == (Object)null) && !((Object)(object)item == (Object)null) && IsHostOrSingleplayer() && !EnemyInCartKillController.TryKill(item)) { ShrinkCategory category = ShrinkCategory.Fallback; float factor = 1f; if (ModConfig.CartShrinkingEnabled.Value && TryGetShrinkData(item, out category, out factor)) { TrackOrShrink(item, category, factor, inCart.cart); } } } internal static void MarkObjectsSeenInCart(PhysGrabCart cart, List items) { if (!IsHostOrSingleplayer() || items == null) { 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 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(); 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() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInChildren(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(); if ((Object)(object)component != (Object)null && component.IsEquipped()) { return false; } if ((Object)(object)item.rb != (Object)null && item.rb.isKinematic) { return false; } return true; } private static bool ResolveShrinkData(PhysGrabObject item, out ShrinkCategory category, out float factor) { category = ShrinkCategory.Fallback; factor = 1f; string cleanName = CleanName(((Object)item).name); if (!TryResolveCategory(item, cleanName, out category)) { if (IsShopPlayerItem(item)) { if (!ModConfig.ShrinkShopPlayerItems.Value) { return false; } category = ShrinkCategory.Fallback; return ModConfig.TryGetScaleFactor(category, out factor); } return false; } return ModConfig.TryGetScaleFactor(category, out factor); } private static bool IsPermanentlyExcludedCartItem(PhysGrabObject item, string cleanName) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Invalid comparison between Unknown and I4 PhysGrabCart component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return true; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null && IsCartRootPhysObject(component, item)) { return true; } if ((Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null) { return true; } if (cleanName == "Item Cart Cannon" || cleanName == "Item Cart Laser") { return true; } ItemAttributes component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 == (Object)null) { return false; } if (!TryGetItemType(component2, out var itemType)) { return false; } if ((int)itemType != 2 && (int)itemType != 14) { return (int)itemType == 12; } return true; } private static bool IsCartRootPhysObject(PhysGrabCart cart, PhysGrabObject item) { if ((Object)(object)cart == (Object)null || (Object)(object)item == (Object)null || PhysGrabCartPhysGrabObjectField == null) { return false; } object? value = PhysGrabCartPhysGrabObjectField.GetValue(cart); return (Object)((value is PhysGrabObject) ? value : null) == (Object)(object)item; } private static bool IsShopPlayerItem(PhysGrabObject item) { ItemAttributes component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return true; } ItemEquippable component2 = ((Component)item).GetComponent(); if ((Object)(object)component2 != (Object)null) { return true; } if (TryGetIsGun(item)) { return true; } return (Object)(object)((Component)item).GetComponent() != (Object)null; } private static bool TryGetItemType(ItemAttributes attributes, out itemType itemType) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected I4, but got Unknown itemType = (itemType)0; if ((Object)(object)attributes == (Object)null || ItemAttributesItemTypeField == null) { return false; } object value = ItemAttributesItemTypeField.GetValue(attributes); if (!(value is itemType)) { return false; } itemType = (itemType)(int)(itemType)value; return true; } private static bool TryGetIsGun(PhysGrabObject item) { if ((Object)(object)item == (Object)null || PhysGrabObjectIsGunField == null) { return false; } object value = PhysGrabObjectIsGunField.GetValue(item); if (value is bool) { return (bool)value; } return false; } private static bool TryResolveCategory(PhysGrabObject item, string cleanName, out ShrinkCategory category) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) category = ShrinkCategory.Fallback; if ((Object)(object)((Component)item).GetComponent() != (Object)null) { category = ShrinkCategory.Surplus; return true; } if (ValuableBoxScaleAdapter.IsValuableBox(item)) { category = ShrinkCategory.ValuableBox; DebugLog("Resolved token/cosmetic box category for " + ((Object)item).name + " kind=" + ValuableBoxScaleAdapter.DescribeSpecialBox(item)); return true; } if (TryResolveEnemyOrb(cleanName, out category)) { return true; } ValuableObject component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null) { return false; } category = FromVolumeType(component.volumeType); return true; } private static ShrinkCategory FromVolumeType(Type volumeType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown return (int)volumeType switch { 0 => ShrinkCategory.Tiny, 1 => ShrinkCategory.Small, 2 => ShrinkCategory.Medium, 3 => ShrinkCategory.Big, 4 => ShrinkCategory.Wide, 5 => ShrinkCategory.Tall, 6 => ShrinkCategory.VeryTall, _ => ShrinkCategory.Fallback, }; } private static bool TryResolveEnemyOrb(string cleanName, out ShrinkCategory category) { category = ShrinkCategory.Fallback; if (string.IsNullOrEmpty(cleanName)) { return false; } if (!cleanName.StartsWith("Enemy", StringComparison.OrdinalIgnoreCase)) { return false; } string[] array = cleanName.Split(new char[1] { '-' }); if (array.Length < 2) { return false; } string text = array[1].Trim(); if (text.Equals("Small", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbSmall; return true; } if (text.Equals("Medium", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbMedium; return true; } if (text.Equals("Big", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbBig; return true; } if (text.Equals("Berserker", StringComparison.OrdinalIgnoreCase)) { category = ShrinkCategory.EnemyOrbBerserker; return true; } return false; } private static void BeginReshrinkCooldown(int id) { float num = Time.time + ModConfig.SafeReshrinkCooldownSeconds(); ReshrinkCooldownUntil[id] = num; if (num < _nextCooldownCheckTime) { _nextCooldownCheckTime = num; } } private static bool IsInReshrinkCooldown(int id) { if (!ReshrinkCooldownUntil.TryGetValue(id, out var value)) { return false; } if (Time.time < value) { return true; } ReshrinkCooldownUntil.Remove(id); return false; } private static void ClearExpiredCooldowns(float now) { if (ReshrinkCooldownUntil.Count == 0) { _nextCooldownCheckTime = float.PositiveInfinity; } else { if (now < _nextCooldownCheckTime) { return; } ExpiredCooldownIds.Clear(); float num = float.PositiveInfinity; foreach (KeyValuePair item in ReshrinkCooldownUntil) { if (now >= item.Value) { ExpiredCooldownIds.Add(item.Key); } else if (item.Value < num) { num = item.Value; } } for (int i = 0; i < ExpiredCooldownIds.Count; i++) { ReshrinkCooldownUntil.Remove(ExpiredCooldownIds[i]); } ExpiredCooldownIds.Clear(); _nextCooldownCheckTime = ((ReshrinkCooldownUntil.Count == 0) ? float.PositiveInfinity : num); } } private static string CleanName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } return name.Replace("Valuable ", string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static bool IsHostOrSingleplayer() { return Authority.IsHostOrSingleplayer(); } private static void DebugLog(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [HarmonyPatch(typeof(PhysGrabInCart), "Add")] internal static class PhysGrabInCartAddPatch { private static bool Prefix(PhysGrabInCart __instance, PhysGrabObject _physGrabObject) { if (!Authority.IsHostOrSingleplayer()) { return true; } if (CartObjectGuard.ShouldBlockCartInCart(__instance, _physGrabObject)) { CartRegistry.HandleBlockedCartInCart(__instance, _physGrabObject); return false; } return true; } private static void Postfix(PhysGrabInCart __instance, PhysGrabObject _physGrabObject) { ShrinkerCartController.ProcessCartObject(__instance, _physGrabObject); } } [HarmonyPatch(typeof(PhysGrabCart), "Start")] internal static class PhysGrabCartStartPatch { private static void Postfix(PhysGrabCart __instance) { CartRegistry.RegisterCart(__instance); if (ModConfig.PlayerScalingEnabled()) { PlayerCartScaleController.RegisterCart(__instance); } } } [HarmonyPatch(typeof(PhysGrabCart), "ObjectsInCart")] internal static class PhysGrabCartObjectsInCartPatch { private static void Postfix(PhysGrabCart __instance) { CartRegistry.CleanCartContents(__instance); } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] internal static class RunManagerChangeLevelPatch { private static void Prefix() { ShrinkerCartController.RestoreAll(); PlayerCartScaleController.RestoreAll(); DeadHeadCartScaleController.RestoreAll(); DeadHeadCartScaleController.Reset(); PlayerCartScaleController.Reset(); CartRegistry.Reset(); ValuableBoxScaleAdapter.Reset(); EnemyInCartKillController.Reset(); } } [HarmonyPatch(typeof(PlayerDeathHead), "Revive")] internal static class PlayerDeathHeadRevivePatch { private static void Prefix(PlayerDeathHead __instance) { DeadHeadCartScaleController.RestoreBeforeRevive(__instance); } } internal static class ValuableBoxScaleAdapter { private sealed class ValuableBoxHandler : IScaleHandler { public void Setup(ScaleController ctrl) { } public void OnScale(ScaleController ctrl) { } public void OnRestore(ScaleController ctrl, bool isBonk) { } public void OnUpdate(ScaleController ctrl) { } public void OnLateUpdate(ScaleController ctrl) { } public void OnDestroy(ScaleController ctrl) { } } private const int HandlerPriority = -10; private static readonly HashSet ValuableBoxObjectIds = new HashSet(); private static readonly HashSet NonValuableBoxObjectIds = new HashSet(); private static bool _registered; internal static void Reset() { ValuableBoxObjectIds.Clear(); NonValuableBoxObjectIds.Clear(); } internal static void RegisterHandler() { if (!_registered) { ScaleHandlerRegistry.Register((IScaleHandler)(object)new ValuableBoxHandler(), (Func)IsValuableBoxScaleTarget, -10); _registered = true; } } internal static bool IsValuableBox(PhysGrabObject item) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { return false; } int instanceID = ((Object)((Component)item).gameObject).GetInstanceID(); if (ValuableBoxObjectIds.Contains(instanceID)) { return true; } if (NonValuableBoxObjectIds.Contains(instanceID)) { return false; } bool flag = (Object)(object)FindCosmeticWorldObject(item) != (Object)null || (Object)(object)FindValuableBox(item) != (Object)null; if (flag) { ValuableBoxObjectIds.Add(instanceID); } else { NonValuableBoxObjectIds.Add(instanceID); } return flag; } internal static bool EnsureController(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } if ((Object)(object)target.GetComponent() != (Object)null) { return true; } if ((Object)(object)target.GetComponent() != (Object)null || (Object)(object)target.GetComponentInParent() != (Object)null || (Object)(object)target.GetComponentInChildren(true) != (Object)null) { DebugLog("Token/cosmetic box is waiting for ScalerCore cosmetic controller: " + ((Object)target).name); return false; } if (!IsValuableBoxScaleTarget(target)) { return false; } target.AddComponent(); DebugLog("Attached ScalerCore controller to ItemValuableBox token box: " + ((Object)target).name); return false; } internal static void EnsureController(ItemValuableBox box) { PhysGrabObject val = FindOwnerPhysGrabObject(box); if ((Object)(object)val != (Object)null) { EnsureController(((Component)val).gameObject); } } private static bool IsValuableBoxScaleTarget(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } PhysGrabObject component = target.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } ItemValuableBox val = FindValuableBox(component); if ((Object)(object)val != (Object)null) { return (Object)(object)FindOwnerPhysGrabObject(val) == (Object)(object)component; } return false; } internal static string DescribeSpecialBox(PhysGrabObject item) { if ((Object)(object)FindCosmeticWorldObject(item) != (Object)null) { return "CosmeticWorldObject"; } if ((Object)(object)FindValuableBox(item) != (Object)null) { return "ItemValuableBox"; } return "none"; } private static CosmeticWorldObject FindCosmeticWorldObject(PhysGrabObject item) { if ((Object)(object)item == (Object)null) { return null; } CosmeticWorldObject component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren(true); } private static ItemValuableBox FindValuableBox(PhysGrabObject item) { if ((Object)(object)item == (Object)null) { return null; } ItemValuableBox component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)item).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)item).GetComponentInChildren(true); } private static PhysGrabObject FindOwnerPhysGrabObject(ItemValuableBox box) { if ((Object)(object)box == (Object)null) { return null; } PhysGrabObject component = ((Component)box).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)box).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)box).GetComponentInChildren(true); } private static void DebugLog(string message) { if (ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)message); } } } [HarmonyPatch(typeof(ItemValuableBox), "Start")] internal static class ItemValuableBoxStartPatch { private static void Postfix(ItemValuableBox __instance) { if ((Object)(object)__instance != (Object)null) { ValuableBoxScaleAdapter.EnsureController(__instance); } } } [HarmonyPatch(typeof(PhysGrabObject), "Start")] internal static class PhysGrabObjectStartValuableBoxPatch { private static void Postfix(PhysGrabObject __instance) { if ((Object)(object)__instance != (Object)null) { ValuableBoxScaleAdapter.EnsureController(((Component)__instance).gameObject); } } }