Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of PackRat v2.1.0
PackRat-IL2CPP.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Threading; using HarmonyLib; using Il2CppFishNet; using Il2CppFishNet.Component.Spawning; using Il2CppFishNet.Connection; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppNewtonsoft.Json; using Il2CppScheduleOne; using Il2CppScheduleOne.AvatarFramework; using Il2CppScheduleOne.AvatarFramework.Customization; using Il2CppScheduleOne.Core.Items.Framework; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Equipping; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Levelling; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.Networking; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.Persistence.Datas; using Il2CppScheduleOne.Persistence.Loaders; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.Product.Packaging; using Il2CppScheduleOne.Quests; using Il2CppScheduleOne.StationFramework; using Il2CppScheduleOne.Storage; using Il2CppScheduleOne.TV; using Il2CppScheduleOne.Tools; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Handover; using Il2CppScheduleOne.UI.Items; using Il2CppScheduleOne.UI.Phone; using Il2CppScheduleOne.UI.Shop; using Il2CppScheduleOne.Variables; using Il2CppScheduleOne.Vehicles; using Il2CppSteamworks; using Il2CppSystem; using Il2CppSystem.Collections; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Linq; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using PackRat; using PackRat.Config; using PackRat.Extensions; using PackRat.Helpers; using PackRat.Logic; using PackRat.Networking; using PackRat.Patches; using PackRat.Profiling; using PackRat.Routing; using PackRat.Shops; using PackRat.Storage; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(global::PackRat.PackRat), "PackRat", "2.1.0", "SirTidez", null)] [assembly: MelonColor(1, 255, 165, 0)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("PackRat-IL2CPP")] [assembly: AssemblyConfiguration("Release IL2CPP")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0+eab397543e055d1e65172f408b02a67dfe507980")] [assembly: AssemblyProduct("PackRat-IL2CPP")] [assembly: AssemblyTitle("PackRat-IL2CPP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PackRat { public static class BuildInfo { public const string Name = "PackRat"; public const string Description = "Portable backpack storage for Schedule One"; public const string Author = "SirTidez"; public const string Version = "2.1.0"; } public class PackRat : MelonMod { public override void OnInitializeMelon() { Configuration.Instance.Load(); EditorUiAssetBundle.Prewarm(); UiProfiler.ApplyEnabledState(Configuration.Instance.EnableDeveloperProfiler); UiProfiler.Event("lifecycle", "initialize"); ModLogger.Info("PackRat initialized."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { using (UiProfiler.Measure("lifecycle", "scene_loaded", $"index={buildIndex};scene={sceneName}")) { UiProfiler.Event("lifecycle", "scene_change", $"index={buildIndex};scene={sceneName}"); Configuration.Instance.Reset(); UiProfiler.ApplyEnabledState(Configuration.Instance.EnableDeveloperProfiler); if (sceneName != "Main") { CameraLockedStateHelper.ResetSceneCache(); return; } CameraLockedStateHelper.PrepareForMainSceneLoad(); ConfigSyncManager.StartSync(); BackpackShopIntegration.RunWhenReady(); } } public override void OnUpdate() { if (UiProfiler.IsEnabled) { UiProfiler.FlushIfDue(); } } public override void OnDeinitializeMelon() { UiProfiler.Event("lifecycle", "deinitialize"); EditorUiAssetBundle.Unload(); UiProfiler.Shutdown(); } public override void OnApplicationQuit() { UiProfiler.Event("lifecycle", "application_quit"); EditorUiAssetBundle.Unload(); UiProfiler.Shutdown(); } } [RegisterTypeInIl2Cpp] public class PlayerBackpack : MonoBehaviour { public const string StorageName = "Backpack"; public const int MinimumStorageSlots = 1; private bool _backpackEnabled = true; private StorageEntity _storage; private int _lastTierIndex = -2; private string _openTitle; private int _equippedTierIndex = -1; private const int TierCheckIntervalFrames = 60; private static Type _cachedStorageMenuType; private static MethodInfo[] _cachedStorageMenuOpenMethods = Array.Empty<MethodInfo>(); private static readonly string[] SelectedHotbarIndexMemberNames = new string[16] { "selectedSlotIndex", "SelectedSlotIndex", "selectedIndex", "SelectedIndex", "currentSlotIndex", "CurrentSlotIndex", "activeSlotIndex", "ActiveSlotIndex", "activeIndex", "ActiveIndex", "equippedSlotIndex", "EquippedSlotIndex", "SelectedSlot", "selectedSlot", "slotIndex", "SlotIndex" }; public static PlayerBackpack Instance { get; private set; } public int EquippedTierIndex => _equippedTierIndex; public int HighestPurchasedTierIndex => EquippedTierIndex; public int CurrentTierIndex { get { Configuration instance = Configuration.Instance; if (_equippedTierIndex < 0) { return -1; } for (int num = _equippedTierIndex; num >= 0; num--) { if (num < instance.TierEnabled.Length && instance.TierEnabled[num]) { return num; } } return -1; } } [HideFromIl2Cpp] public BackpackTierDefinition CurrentTier { get { int currentTierIndex = CurrentTierIndex; return (currentTierIndex >= 0) ? Configuration.BackpackTiers[currentTierIndex] : null; } } public bool IsUnlocked => CurrentTierIndex >= 0; public bool IsPoliceSearchable => CurrentTierIndex >= 2; public bool IsOpen { get { StorageMenu instance = Singleton<StorageMenu>.Instance; return (Object)(object)instance != (Object)null && instance.IsOpen && (Object)(object)instance.TitleLabel != (Object)null && ((TMP_Text)instance.TitleLabel).text == _openTitle; } } public List<ItemSlot> ItemSlots => Enumerable.ToList<ItemSlot>(((Il2CppObjectBase)_storage.ItemSlots).Cast<IEnumerable<ItemSlot>>()); public PlayerBackpack(IntPtr ptr) : base(ptr) { } public void SetEquippedTierIndex(int tierIndex) { _equippedTierIndex = ((tierIndex < 0) ? (-1) : Math.Min(tierIndex, Configuration.BackpackTiers.Length - 1)); } public void SetHighestPurchasedTierIndex(int tierIndex) { SetEquippedTierIndex(tierIndex); } public void RestorePurchasedTier(int tierIndex) { SetEquippedTierIndex(tierIndex); EnsureCorrectTierApplied(); } private void Awake() { _storage = ((Component)this).gameObject.GetComponentInParent<StorageEntity>(); if ((Object)(object)_storage == (Object)null) { ModLogger.Error("Player does not have a BackpackStorage component!"); return; } ModLogger.Info("Configuring backpack storage..."); ModLogger.Debug($"[BackpackUI] Awake: object='{((Object)this).name}', storage='{((Object)_storage).name}'."); MelonCoroutines.Start(DeferredConfigureStorage(this)); } private static IEnumerator DeferredConfigureStorage(PlayerBackpack instance) { yield return null; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance._storage == (Object)null)) { int tierIdx = instance.CurrentTierIndex; int slotCount = ((tierIdx >= 0) ? Configuration.Instance.TierSlotCounts[tierIdx] : Configuration.BackpackTiers[0].DefaultSlotCount); instance.UpdateSize(slotCount); instance.OnStartClient(instance.IsOwnedByLocalPlayer()); } } private void Update() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { StorageMenuPatch.ClearStandaloneBackpackKeyboardFocusOnPointerInput(); } if (IsOpen && StorageMenuPatch.HandleStandaloneBackpackSettingsInput()) { return; } bool keyDown = Input.GetKeyDown(Configuration.Instance.ToggleKey); if (keyDown) { UiProfiler.Event("hotkey", "pressed", $"open={IsOpen};unlocked={IsUnlocked};tier={CurrentTierIndex}"); } if (keyDown || Time.frameCount % 60 == 0) { int currentTierIndex = CurrentTierIndex; if (currentTierIndex != _lastTierIndex) { _lastTierIndex = currentTierIndex; ApplyCurrentTier(currentTierIndex); } } if (!_backpackEnabled) { return; } try { if ((IsOpen && StorageMenuPatch.HandleStandaloneBackpackKeyboardNavigation()) || !keyDown) { return; } int appliedTier; if (IsOpen && StorageMenuPatch.IsStandaloneBackpackSearchFocused()) { UiProfiler.Event("hotkey", "consumed_by_search"); } else if (TryConsumeSelectedHotbarBackpackItem(out appliedTier)) { if (appliedTier >= 0) { ModLogger.Info($"Backpack tier {appliedTier} ({Configuration.BackpackTiers[appliedTier].Name}) applied; opening backpack."); } if (IsOpen) { Close(); } else { Open(); } } else if (!IsUnlocked) { ModLogger.Debug($"[BackpackUI] Hotkey ignored: no enabled backpack tier (equipped={_equippedTierIndex}, current={CurrentTierIndex})."); } else if (IsOpen) { Close(); } else { Open(); } } catch (Exception exception) { ModLogger.Error("Error toggling backpack", exception); } } private bool TryConsumeSelectedHotbarBackpackItem(out int appliedTier) { appliedTier = -1; try { PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { return false; } object obj = ReflectionUtils.TryGetFieldOrProperty(instance, "hotbarSlots"); if (obj == null) { return false; } int num = ReflectionUtils.TryGetListCount(obj); if (num <= 0) { return false; } int selectedHotbarIndex = GetSelectedHotbarIndex(instance); if (selectedHotbarIndex >= 0 && selectedHotbarIndex < num && TryConsumeBackpackItemFromSlot(instance, obj, selectedHotbarIndex, out appliedTier)) { return true; } for (int i = 0; i < num; i++) { if (TryConsumeBackpackItemFromSlot(instance, obj, i, out appliedTier)) { return true; } } List<object> list = ReflectionUtils.TryGetAllListLikeMembers(instance); foreach (object item in list) { if (item == obj) { continue; } int num2 = ReflectionUtils.TryGetListCount(item); for (int j = 0; j < num2; j++) { if (TryConsumeBackpackItemFromSlot(instance, item, j, out appliedTier)) { return true; } } } return false; } catch (Exception exception) { ModLogger.Error("TryConsumeSelectedHotbarBackpackItem", exception); return false; } } [HideFromIl2Cpp] private bool TryConsumeBackpackItemFromSlot(object playerInventory, object slotsList, int index, out int appliedTier) { appliedTier = -1; object obj = ReflectionUtils.TryGetListItem(slotsList, index); if (obj == null) { return false; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "ItemInstance"); if (obj2 == null) { return false; } object obj3 = ReflectionUtils.TryGetFieldOrProperty(obj2, "Definition"); if (obj3 == null) { return false; } object obj4 = ReflectionUtils.TryGetFieldOrProperty(obj3, "ID") ?? ReflectionUtils.TryGetFieldOrProperty(obj3, "id"); string text = (obj4 as string) ?? obj4?.ToString(); if (string.IsNullOrEmpty(text) || !text.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return false; } if (!BackpackShopIntegration.IsBackpackTierPurchase(text, out var tierIndex) || tierIndex < 0) { return false; } ClearSlotItem(obj); RefreshInventoryUIAfterSlotChange(playerInventory, obj); if (tierIndex != _equippedTierIndex) { SetEquippedTierIndex(tierIndex); ApplyTierAfterPurchase(tierIndex); BackpackShopIntegration.RefreshBackpackListingsInAllShops(); appliedTier = tierIndex; } return true; } private static int GetSelectedHotbarIndex(object playerInventory) { if (playerInventory == null) { return -1; } string[] selectedHotbarIndexMemberNames = SelectedHotbarIndexMemberNames; foreach (string memberName in selectedHotbarIndexMemberNames) { object obj = ReflectionUtils.TryGetFieldOrProperty(playerInventory, memberName); if (obj != null) { if (obj is int num && num >= 0) { return num; } if (obj is byte result) { return result; } if (obj is short num2 && num2 >= 0) { return num2; } if (obj is long num3 && num3 >= 0 && num3 <= int.MaxValue) { return (int)num3; } } } return -1; } private static void ClearSlotItem(object slot) { if (slot == null) { return; } Type type = slot.GetType(); MethodInfo methodInfo = ReflectionUtils.GetMethod(type, "ClearStoredInstance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ReflectionUtils.GetMethod(type, "Clear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ReflectionUtils.GetMethod(type, "ClearSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo != null) { try { methodInfo.Invoke(slot, null); return; } catch { } } ReflectionUtils.TrySetFieldOrProperty(slot, "ItemInstance", null); } private static void RefreshInventoryUIAfterSlotChange(object playerInventory, object slotThatChanged) { if (slotThatChanged != null) { ReflectionUtils.TryInvokeParameterlessCallback(slotThatChanged, "onItemDataChanged", "OnItemDataChanged", "ItemDataChanged"); } if (playerInventory != null) { ReflectionUtils.TryInvokeParameterlessCallback(playerInventory, "Refresh", "RefreshUI", "UpdateDisplay", "OnInventoryChanged", "NotifySlotsChanged", "Rebuild"); } } private void ApplyCurrentTier(int tierIdx) { if (tierIdx >= 0) { int num = Configuration.Instance.TierSlotCounts[tierIdx]; if (_storage.SlotCount != num || _storage.ItemSlots.Count != num) { ModLogger.Info($"Backpack upgraded to {Configuration.BackpackTiers[tierIdx].Name} ({num} slots)."); UpdateSize(num); } } } public void ApplyTierAfterPurchase(int tierIdx) { if (tierIdx >= 0) { _lastTierIndex = tierIdx; ApplyCurrentTier(tierIdx); } } public void EnsureCorrectTierApplied() { _lastTierIndex = CurrentTierIndex; if (!((Object)(object)_storage == (Object)null) && _lastTierIndex >= 0) { ApplyCurrentTier(_lastTierIndex); } } public void SetBackpackEnabled(bool state) { if (!state) { Close(); } _backpackEnabled = state; } public void Open() { using (UiProfiler.Measure("standalone", "open_request", $"enabled={_backpackEnabled};unlocked={IsUnlocked};tier={CurrentTierIndex}")) { if (!_backpackEnabled) { UiProfiler.Event("standalone", "open_blocked", "reason=disabled"); ModLogger.Debug("Backpack open blocked: backpack disabled."); return; } if (!IsUnlocked) { UiProfiler.Event("standalone", "open_blocked", "reason=locked"); ModLogger.Debug($"Backpack open blocked: not unlocked (CurrentTierIndex={CurrentTierIndex}, EquippedTier={_equippedTierIndex}). Purchase a tier at the Hardware Store."); return; } if ((Object)(object)_storage == (Object)null) { UiProfiler.Event("standalone", "open_blocked", "reason=no_storage"); ModLogger.Warn("Backpack open blocked: no storage entity."); return; } ManagementClipboard instance = Singleton<ManagementClipboard>.Instance; if ((Object)(object)instance != (Object)null && instance.IsEquipped) { UiProfiler.Event("standalone", "open_blocked", "reason=clipboard"); ModLogger.Debug("[BackpackUI] Open blocked: management clipboard is equipped."); return; } StorageMenu instance2 = Singleton<StorageMenu>.Instance; if ((Object)(object)instance2 == (Object)null) { UiProfiler.Event("standalone", "open_blocked", "reason=no_storage_menu"); ModLogger.Warn("[BackpackUI] Open blocked: StorageMenu is not available yet."); return; } if (instance2.IsOpen) { UiProfiler.Event("standalone", "open_blocked", "reason=other_storage_open"); ModLogger.Debug("[BackpackUI] Open blocked: another storage menu is already open."); return; } if ((Object)(object)PlayerSingleton<Phone>.Instance != (Object)null && PlayerSingleton<Phone>.Instance.IsOpen) { UiProfiler.Event("standalone", "open_blocked", "reason=phone"); ModLogger.Debug("[BackpackUI] Open blocked: phone is open."); return; } if (CameraLockedStateHelper.IsCameraLockedByUI()) { UiProfiler.Event("standalone", "open_blocked", "reason=camera_locked_ui"); ModLogger.Debug("Backpack blocked: player is in camera-locked UI (TV, ATM, dialogue, vehicle, etc.)."); return; } _openTitle = CurrentTier?.Name ?? "Backpack"; UiProfiler.Event("standalone", "opening", $"title={_openTitle};slots={_storage.ItemSlots.Count}"); ModLogger.Info($"[BackpackUI] PlayerBackpack.Open -> StorageMenu.Open: title='{_openTitle}', slots={_storage.ItemSlots.Count}."); BackpackStateSyncManager.BeginLocalBackpackEdit(); OpenStorageMenu(instance2, ((Il2CppObjectBase)_storage).Cast<IItemSlotOwner>(), _openTitle, string.Empty); _storage.SendAccessor(((NetworkBehaviour)Player.Local).NetworkObject); } } private static void OpenStorageMenu(StorageMenu storageMenu, IItemSlotOwner owner, string title, string subtitle) { if ((Object)(object)storageMenu == (Object)null || owner == null) { return; } PrewarmStorageMenuOpenMethods(storageMenu); MethodInfo[] cachedStorageMenuOpenMethods = _cachedStorageMenuOpenMethods; foreach (MethodInfo methodInfo in cachedStorageMenuOpenMethods) { if (methodInfo.Name != "Open") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); try { if (parameters.Length == 4 && parameters[0].ParameterType.IsInstanceOfType(owner) && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(string)) { methodInfo.Invoke(storageMenu, new object[4] { owner, title, subtitle, null }); return; } if (parameters.Length == 3 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType.IsInstanceOfType(owner)) { methodInfo.Invoke(storageMenu, new object[3] { title, subtitle, owner }); return; } } catch (Exception ex) { Exception exception = ((ex is TargetInvocationException { InnerException: not null } ex2) ? ex2.InnerException : ex); ModLogger.Error("Failed to open backpack storage menu", exception); return; } } ModLogger.Error("Failed to find a compatible StorageMenu.Open overload for the backpack."); } private static void PrewarmStorageMenuOpenMethods(StorageMenu storageMenu) { using (UiProfiler.Measure("prewarm", "storage_menu_open_reflection", "type=" + ((object)storageMenu)?.GetType().FullName)) { if ((Object)(object)storageMenu == (Object)null) { return; } Type type = ((object)storageMenu).GetType(); if (_cachedStorageMenuType == type && _cachedStorageMenuOpenMethods.Length != 0) { return; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); List<MethodInfo> list = new List<MethodInfo>(); for (int i = 0; i < methods.Length; i++) { if (methods[i].Name == "Open") { list.Add(methods[i]); } } _cachedStorageMenuType = type; _cachedStorageMenuOpenMethods = list.ToArray(); } } private static void PrewarmPlayerInventoryReflection() { using (UiProfiler.Measure("prewarm", "player_inventory_reflection")) { PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { return; } Type type = ((object)instance).GetType(); ReflectionUtils.PrewarmReadableMembers(type, "hotbarSlots"); ReflectionUtils.PrewarmReadableMembers(type, SelectedHotbarIndexMemberNames); ReflectionUtils.PrewarmListLikeMembers(type); object list = ReflectionUtils.TryGetFieldOrProperty(instance, "hotbarSlots"); int num = ReflectionUtils.TryGetListCount(list); for (int i = 0; i < num; i++) { object obj = ReflectionUtils.TryGetListItem(list, i); if (obj == null) { continue; } ReflectionUtils.PrewarmReadableMembers(obj.GetType(), "ItemInstance"); object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "ItemInstance"); if (obj2 != null) { ReflectionUtils.PrewarmReadableMembers(obj2.GetType(), "Definition"); object obj3 = ReflectionUtils.TryGetFieldOrProperty(obj2, "Definition"); if (obj3 != null) { ReflectionUtils.PrewarmReadableMembers(obj3.GetType(), "ID", "id"); } } } } } public void Close() { using (UiProfiler.Measure("standalone", "close_request", $"open={IsOpen}")) { if (_backpackEnabled && IsOpen) { UiProfiler.Event("standalone", "closing"); Singleton<StorageMenu>.Instance.Close(); _storage.SendAccessor((NetworkObject)null); } } } public bool ContainsItemsOfInterest(EStealthLevel maxStealthLevel) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _storage.ItemSlots.Count; i++) { List<ItemSlot> itemSlots = _storage.ItemSlots; ItemSlot val = ((Il2CppObjectBase)itemSlots[new Index(i).GetOffset(itemSlots.Count)]).Cast<ItemSlot>(); if (((val != null) ? val.ItemInstance : null) == null) { continue; } ItemInstance itemInstance = val.ItemInstance; ProductItemInstance val2 = (ProductItemInstance)(object)((itemInstance is ProductItemInstance) ? itemInstance : null); if (val2 == null) { string a = (ReflectionUtils.TryGetFieldOrProperty(val.ItemInstance.Definition, "legalStatus") ?? ReflectionUtils.TryGetFieldOrProperty(val.ItemInstance.Definition, "LegalStatus"))?.ToString(); if (!string.Equals(a, "Legal", StringComparison.Ordinal)) { return true; } } else if ((Object)(object)val2.AppliedPackaging == (Object)null || val2.AppliedPackaging.StealthLevel <= maxStealthLevel) { return true; } } return false; } public void Upgrade(int slotCount) { if (slotCount >= 1 && !((Object)(object)_storage == (Object)null)) { if (_storage.SlotCount > int.MaxValue - slotCount) { ModLogger.Warn("Cannot upgrade backpack: requested capacity exceeds the supported integer range."); } else { UpdateSize(_storage.SlotCount + slotCount); } } } public void Downgrade(int slotCount, bool force = false) { if (slotCount < 1) { return; } if (!force && slotCount >= _storage.SlotCount) { ModLogger.Warn("Cannot downgrade backpack to zero slots. A minimum of one must remain."); return; } int num = _storage.SlotCount - slotCount; if (num < 1) { num = 1; } if (force) { UpdateSize(num); return; } bool flag = true; List<ItemSlot> range = _storage.ItemSlots.GetRange(num, _storage.SlotCount - num); for (int i = 0; i < range.Count; i++) { List<ItemSlot> val = range; ItemSlot val2 = ((Il2CppObjectBase)val[new Index(i).GetOffset(val.Count)]).Cast<ItemSlot>(); if (((val2 != null) ? val2.ItemInstance : null) != null) { ModLogger.Warn("Downgrading backpack will remove item: " + ((Object)val2.ItemInstance.Definition).name); flag = false; } } if (!flag) { ModLogger.Warn("Cannot downgrade backpack due to items present in removed slots."); } else { UpdateSize(num); } } private void UpdateSize(int newSize) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown newSize = Math.Max(1, newSize); _storage.SlotCount = newSize; StorageEntity storage = _storage; if (1 == 0) { } int displayRowCount = ((newSize <= 20) ? ((int)Math.Ceiling((double)newSize / 5.0)) : ((newSize > 80) ? ((int)Math.Ceiling((double)newSize / 16.0)) : ((int)Math.Ceiling((double)newSize / 10.0)))); if (1 == 0) { } storage.DisplayRowCount = displayRowCount; if (_storage.ItemSlots.Count > newSize) { _storage.ItemSlots.RemoveRange(newSize, _storage.ItemSlots.Count - newSize); return; } for (int i = _storage.ItemSlots.Count; i < newSize; i++) { ItemSlot val = new ItemSlot(); int count = _storage.ItemSlots.Count; if ((Delegate)(object)val.onItemDataChanged == (Delegate)null) { val.onItemDataChanged = Action.op_Implicit((Action)_storage.ContentsChanged); } else { ((Delegate)val.onItemDataChanged).CombineImpl((Delegate)(object)Action.op_Implicit((Action)_storage.ContentsChanged)); } val.SetSlotOwner(((Il2CppObjectBase)_storage).Cast<IItemSlotOwner>()); if (_storage.ItemSlots.Count == count) { _storage.ItemSlots.Add(val); } } } private void OnStartClient(bool isOwner) { if (!isOwner) { ModLogger.Info("Destroying non-local player backpack on: " + ((Object)this).name); Object.Destroy((Object)(object)this); return; } if ((Object)(object)Instance != (Object)null) { ModLogger.Warn("Multiple instances of " + ((Object)this).name + " exist. Keeping prior instance reference."); Object.Destroy((Object)(object)this); return; } Instance = this; CameraLockedStateHelper.RefreshSceneCache(); PrewarmStorageMenuOpenMethods(Singleton<StorageMenu>.Instance); PrewarmPlayerInventoryReflection(); StorageMenuPatch.PrewarmStandaloneAssets(); StorageMenuPatch.PrewarmStandaloneSurfaces(Singleton<StorageMenu>.Instance); } private bool IsOwnedByLocalPlayer() { Player componentInParent = ((Component)this).gameObject.GetComponentInParent<Player>(); if ((Object)(object)componentInParent == (Object)null) { ModLogger.Debug("[BackpackUI] Ownership check could not find Player; retaining local component fallback."); return true; } return ((NetworkBehaviour)componentInParent).IsOwner; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } } namespace PackRat.Storage { public static class BackpackFavorites { private static readonly HashSet<string> FavoriteDefinitionIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static bool IsFavorite(string definitionId) { return !string.IsNullOrWhiteSpace(definitionId) && FavoriteDefinitionIds.Contains(definitionId.Trim()); } public static bool Toggle(string definitionId) { if (string.IsNullOrWhiteSpace(definitionId)) { return false; } string item = definitionId.Trim(); if (!FavoriteDefinitionIds.Add(item)) { FavoriteDefinitionIds.Remove(item); return false; } return true; } public static void SetFavorites(IEnumerable<string> definitionIds) { FavoriteDefinitionIds.Clear(); if (definitionIds == null) { return; } foreach (string definitionId in definitionIds) { if (!string.IsNullOrWhiteSpace(definitionId)) { FavoriteDefinitionIds.Add(definitionId.Trim()); } } } public static List<string> GetSavedFavoriteIds() { return FavoriteDefinitionIds.OrderBy<string, string>((string id) => id, StringComparer.OrdinalIgnoreCase).ToList(); } } public sealed class BackpackSaveData { public string Contents { get; set; } public int EquippedTierIndex { get; set; } = -1; public int HighestPurchasedTierIndex { get; set; } = -1; public List<string> FavoriteDefinitionIds { get; set; } = new List<string>(); } } namespace PackRat.Shops { public static class BackpackShopIntegration { public const string BackpackItemIdPrefix = "PackRat_Backpack_Tier_"; private const string HardwareStoreName = "Hardware Store"; private static readonly string[] TierDescriptions = new string[5] { "A basic 8-slot backpack. Good for starting out.", "A compact 16-slot pack. Stays under the radar.", "24 slots. Police may search this size and above.", "32 slots. Sturdy and roomy; draws more attention.", "The largest default option starts at 40 slots. Capacity can be configured." }; private static readonly HashSet<int> _shopsIntegrated = new HashSet<int>(); private static StorableItemDefinition _safeBackpackTemplate; private static bool _loggedSafeTemplateUnavailable; private static bool _loggedBackpackDefinitionCreationFailure; private static bool _loggedListingIntegrationFailureSummary; private static bool _waitForIntegrationRunning; public static void RunWhenReady() { if (!_waitForIntegrationRunning) { _waitForIntegrationRunning = true; MelonCoroutines.Start(WaitAndIntegrate()); } } public static bool TryAddBackpackListingsToShop(ShopInterface shop) { if ((Object)(object)shop == (Object)null) { return false; } if (!TryMatchHardwareStore(shop, out var _)) { return false; } int instanceID = ((Object)shop).GetInstanceID(); bool flag = _shopsIntegrated.Add(instanceID); if (AddBackpackListings(shop)) { if (flag) { ModLogger.Info($"BackpackShopIntegration: Added backpack tier listings to Hardware Store (instance {instanceID})."); } return true; } ModLogger.Warn("BackpackShopIntegration: Hardware Store found but adding listings failed (see tier warnings above)."); return false; } private static IEnumerator WaitAndIntegrate() { try { int i = 0; while (true) { if (i < 60) { yield return (object)new WaitForSeconds(0.5f); if (!AddToAllHardwareStoresInScene()) { i++; continue; } break; } LogHardwareStoreNotFound(); break; } } finally { _waitForIntegrationRunning = false; } } private static bool AddToAllHardwareStoresInScene() { bool flag = false; try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null && TryMatchHardwareStore(item, out var shop)) { flag |= TryAddBackpackListingsToShop(shop); } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && TryMatchHardwareStore(array[i], out var shop2)) { flag |= TryAddBackpackListingsToShop(shop2); } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddToAllHardwareStoresInScene", exception); } return flag; } private static void LogHardwareStoreNotFound() { List<string> list = new List<string>(); try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null && value is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null) { string shopDisplayName = GetShopDisplayName(item); if (!string.IsNullOrEmpty(shopDisplayName) && !list.Contains(shopDisplayName)) { list.Add(shopDisplayName); } } } } } if (list.Count == 0) { ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { string shopDisplayName2 = GetShopDisplayName(array[i]); if (!string.IsNullOrEmpty(shopDisplayName2) && !list.Contains(shopDisplayName2)) { list.Add(shopDisplayName2); } } } } } catch { } if (list.Count > 0) { ModLogger.Warn("BackpackShopIntegration: Hardware Store not found after 30s. Shop names in game: [" + string.Join(", ", list) + "]. If your hardware store uses a different name, we can add a match for it."); } else { ModLogger.Warn("BackpackShopIntegration: Hardware Store not found after 30s (no ShopInterface instances with a name found). Shops may load later when you open them."); } } private static bool TryGetHardwareStore(out ShopInterface shop) { shop = null; try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null && value is IEnumerable enumerable) { int num = 0; foreach (object item in enumerable) { if (item != null) { num++; if (TryMatchHardwareStore(item, out shop)) { return true; } } } if (num == 0) { ModLogger.Debug("BackpackShopIntegration: AllShops list is empty."); } else { ModLogger.Debug($"BackpackShopIntegration: AllShops has {num} entries; no Hardware Store name matched."); } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null && array.Length != 0) { foreach (ShopInterface val in array) { if (!((Object)(object)val == (Object)null) && TryMatchHardwareStore(val, out shop)) { return true; } } ModLogger.Debug($"BackpackShopIntegration: Found {array.Length} ShopInterface in scene; none matched Hardware Store."); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: Error finding Hardware Store", exception); } return false; } private static bool TryMatchHardwareStore(object shopObj, out ShopInterface shop) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown shop = null; if (shopObj == null) { return false; } string shopDisplayName = GetShopDisplayName(shopObj); if (string.IsNullOrEmpty(shopDisplayName)) { return false; } if (!string.Equals(shopDisplayName, "Hardware Store", StringComparison.OrdinalIgnoreCase) && shopDisplayName.IndexOf("Hardware", StringComparison.OrdinalIgnoreCase) < 0) { return false; } try { shop = (ShopInterface)shopObj; return true; } catch { return false; } } private static string GetShopDisplayName(object shopObj) { if (shopObj == null) { return null; } string[] array = new string[5] { "ShopName", "shopName", "Name", "name", "DisplayName" }; foreach (string memberName in array) { object obj = ReflectionUtils.TryGetFieldOrProperty(shopObj, memberName); if (obj is string text && !string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } return null; } private static bool AddBackpackListings(ShopInterface shop) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)shop == (Object)null) { return false; } if (shop.Listings == null) { ModLogger.Warn("BackpackShopIntegration: Shop.Listings is null; cannot add backpack listings."); return false; } Configuration instance = Configuration.Instance; if (!LevelManagerPatch.TryGetFallbackIcon(out var texture, out var sprite)) { texture = new Texture2D(2, 2); texture.SetPixel(0, 0, Color.white); texture.SetPixel(1, 0, Color.white); texture.SetPixel(0, 1, Color.white); texture.SetPixel(1, 1, Color.white); texture.Apply(); sprite = Sprite.Create(texture, new Rect(0f, 0f, 2f, 2f), new Vector2(0.5f, 0.5f)); ModLogger.Warn("BackpackShopIntegration: Fallback icon resource missing; using generated placeholder icon."); } int num = (((Object)(object)PlayerBackpack.Instance != (Object)null) ? PlayerBackpack.Instance.EquippedTierIndex : (-1)); bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; int num2 = 0; int num3 = 0; int num4 = 0; for (int i = 0; i < Configuration.BackpackTiers.Length; i++) { if (!instance.TierEnabled[i]) { RemoveTierListingFromShop(shop, "PackRat_Backpack_Tier_" + i); continue; } flag2 = true; string itemId = "PackRat_Backpack_Tier_" + i; Sprite tierSprite = LevelManagerPatch.GetTierSprite(i, sprite, texture); if (i <= num) { RemoveTierListingFromShop(shop, itemId); continue; } flag3 = true; if (ShopHasItem(shop, itemId)) { StorableItemDefinition registeredBackpackDefinition = GetRegisteredBackpackDefinition(itemId); if ((Object)(object)registeredBackpackDefinition != (Object)null) { ConfigureBackpackTierDefinition(registeredBackpackDefinition, i, itemId, instance, tierSprite); } flag4 = true; continue; } StorableItemDefinition val = GetRegisteredBackpackDefinition(itemId); if ((Object)(object)val != (Object)null) { ConfigureBackpackTierDefinition(val, i, itemId, instance, tierSprite); } else { val = CreateBackpackTierDefinition(shop, i, itemId, instance, tierSprite); } if ((Object)(object)val == (Object)null) { num2++; } else if ((Object)(object)GetRegisteredBackpackDefinition(itemId) == (Object)null && !RegisterDefinition(val)) { num3++; ModLogger.Warn($"BackpackShopIntegration: Could not register definition for tier {i}."); } else if (!AddListingToShop(shop, val, sprite, texture)) { num4++; ModLogger.Warn($"BackpackShopIntegration: Could not add listing for tier {i}."); } else { flag = true; } } if (flag) { _loggedListingIntegrationFailureSummary = false; ModLogger.Info("BackpackShopIntegration: Added backpack tier listings to Hardware Store."); } if (!flag2 || !flag3 || flag4 || flag) { _loggedListingIntegrationFailureSummary = false; return true; } if (!_loggedListingIntegrationFailureSummary) { _loggedListingIntegrationFailureSummary = true; ModLogger.Warn($"BackpackShopIntegration: Eligible tiers found but none added. missingDef={num2}, registerFailed={num3}, listingFailed={num4}"); } return false; } public static void RefreshBackpackListingsInAllShops() { AddToAllHardwareStoresInScene(); } private static bool ShopHasItem(ShopInterface shop, string itemId) { try { if (shop.Listings == null) { return false; } Enumerator<ShopListing> enumerator = shop.Listings.GetEnumerator(); while (enumerator.MoveNext()) { ShopListing current = enumerator.Current; if ((Object)(object)((current != null) ? current.Item : null) != (Object)null && ((BaseItemDefinition)current.Item).ID == itemId) { return true; } } } catch { } return false; } private static StorableItemDefinition CreateBackpackTierDefinition(ShopInterface shop, int tierIndex, string itemId, Configuration cfg, Sprite iconSprite) { try { StorableItemDefinition val = CreateEmptyStorableItemDefinition(); if ((Object)(object)val == (Object)null) { val = CloneSafeBackpackTemplate(shop); } if ((Object)(object)val == (Object)null && !_loggedBackpackDefinitionCreationFailure) { _loggedBackpackDefinitionCreationFailure = true; ModLogger.Warn("BackpackShopIntegration: Could not create a backpack item definition. Skipping backpack listings until a valid template can be created."); } if ((Object)(object)val == (Object)null) { return null; } ConfigureBackpackTierDefinition(val, tierIndex, itemId, cfg, iconSprite); return val; } catch (Exception exception) { ModLogger.Error($"BackpackShopIntegration: CreateBackpackTierDefinition tier {tierIndex}", exception); return null; } } private static void ConfigureBackpackTierDefinition(StorableItemDefinition def, int tierIndex, string itemId, Configuration cfg, Sprite iconSprite) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)def == (Object)null) { return; } ApplyBackpackDefinitionDefaults(def); if (!ReflectionUtils.TrySetFieldOrProperty(def, "ID", itemId)) { ReflectionUtils.TrySetFieldOrProperty(def, "id", itemId); } string name = Configuration.BackpackTiers[tierIndex].Name; if (!ReflectionUtils.TrySetFieldOrProperty(def, "Name", name)) { ReflectionUtils.TrySetFieldOrProperty(def, "name", name); } if ((Object)(object)iconSprite != (Object)null) { string[] array = new string[7] { "Icon", "icon", "Sprite", "sprite", "ItemIcon", "itemIcon", "DisplayIcon" }; foreach (string memberName in array) { if (ReflectionUtils.TrySetFieldOrProperty(def, memberName, iconSprite)) { break; } } } float num = ((tierIndex < cfg.TierPrices.Length) ? cfg.TierPrices[tierIndex] : (25f + (float)tierIndex * 50f)); ReflectionUtils.TrySetFieldOrProperty(def, "BasePurchasePrice", num); FullRank requiredRank = ((tierIndex < cfg.TierUnlockRanks.Length) ? cfg.TierUnlockRanks[tierIndex] : Configuration.BackpackTiers[tierIndex].DefaultUnlockRank); def.BasePurchasePrice = num; def.RequiredRank = requiredRank; def.RequiresLevelToPurchase = true; string value = ((tierIndex < TierDescriptions.Length) ? TierDescriptions[tierIndex] : null); if (string.IsNullOrEmpty(value)) { return; } string[] array2 = new string[8] { "Description", "description", "TooltipText", "tooltipText", "FlavorText", "flavorText", "ItemDescription", "itemDescription" }; foreach (string memberName2 in array2) { if (ReflectionUtils.TrySetFieldOrProperty(def, memberName2, value)) { break; } } } private static StorableItemDefinition GetRegisteredBackpackDefinition(string itemId) { if (string.IsNullOrWhiteSpace(itemId)) { return null; } try { ItemDefinition registeredItemDefinition = GetRegisteredItemDefinition(itemId); if ((Object)(object)registeredItemDefinition == (Object)null) { return null; } return ((Il2CppObjectBase)registeredItemDefinition).TryCast<StorableItemDefinition>(); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: GetRegisteredBackpackDefinition", exception); return null; } } private static ItemDefinition GetRegisteredItemDefinition(string itemId) { if (string.IsNullOrWhiteSpace(itemId)) { return null; } try { if (Registry.ItemExists(itemId)) { return Registry.GetItem(itemId); } } catch { } try { Registry instance = Singleton<Registry>.Instance; if ((Object)(object)instance == (Object)null) { return null; } ItemDefinition val = FindDefinitionInRegistryList(instance.ItemRegistry, itemId); if ((Object)(object)val != (Object)null) { return val; } return FindDefinitionInRegistryList(instance.ItemsAddedAtRuntime, itemId); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: GetRegisteredItemDefinition", exception); return null; } } private static ItemDefinition FindDefinitionInRegistryList(List<ItemRegister> registryList, string itemId) { if (registryList == null || string.IsNullOrWhiteSpace(itemId)) { return null; } for (int i = 0; i < registryList.Count; i++) { ItemRegister val = registryList[i]; if (val != null && string.Equals(val.ID, itemId, StringComparison.OrdinalIgnoreCase)) { return val.Definition; } } return null; } private static StorableItemDefinition CloneTemplateStorableItemDefinition(ShopInterface shop) { if (((shop != null) ? shop.Listings : null) == null) { return null; } try { Enumerator<ShopListing> enumerator = shop.Listings.GetEnumerator(); while (enumerator.MoveNext()) { ShopListing current = enumerator.Current; if (!((Object)(object)((current != null) ? current.Item : null) == (Object)null)) { StorableItemDefinition val = ((Il2CppObjectBase)current.Item).TryCast<StorableItemDefinition>(); if (!((Object)(object)val == (Object)null)) { StorableItemDefinition val2 = Object.Instantiate<StorableItemDefinition>(val); return ((Il2CppObjectBase)val2).TryCast<StorableItemDefinition>(); } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CloneTemplateStorableItemDefinition", exception); } return null; } private static StorableItemDefinition CloneSafeBackpackTemplate(ShopInterface shop) { if ((Object)(object)_safeBackpackTemplate == (Object)null) { _safeBackpackTemplate = CreateSafeBackpackTemplate(shop); if ((Object)(object)_safeBackpackTemplate == (Object)null) { if (!_loggedSafeTemplateUnavailable) { _loggedSafeTemplateUnavailable = true; ModLogger.Warn("BackpackShopIntegration: Safe backpack template is unavailable in IL2CPP. Backpack listings will retry once the shop can provide a valid storable-item template."); } return null; } } try { StorableItemDefinition val = Object.Instantiate<StorableItemDefinition>(_safeBackpackTemplate); return ((Il2CppObjectBase)val).TryCast<StorableItemDefinition>(); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CloneSafeBackpackTemplate", exception); return null; } } public static StorableItemDefinition CreateSafeBackpackTemplate(ShopInterface shop = null) { if ((Object)(object)_safeBackpackTemplate != (Object)null) { return _safeBackpackTemplate; } try { _safeBackpackTemplate = CreateEmptyStorableItemDefinition(); if ((Object)(object)_safeBackpackTemplate == (Object)null && (Object)(object)shop != (Object)null) { _safeBackpackTemplate = CloneTemplateStorableItemDefinition(shop); } if ((Object)(object)_safeBackpackTemplate == (Object)null) { if (!_loggedSafeTemplateUnavailable) { _loggedSafeTemplateUnavailable = true; ModLogger.Warn("BackpackShopIntegration: Could not create safe backpack template instance yet."); } return null; } ApplyBackpackDefinitionDefaults(_safeBackpackTemplate); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "ID", "PackRat_Template"); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "Name", "Backpack"); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "BasePurchasePrice", 0f); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "RequiredRank", 0); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "RequiresLevelToPurchase", false); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "Description", string.Empty); _loggedSafeTemplateUnavailable = false; _loggedBackpackDefinitionCreationFailure = false; _loggedListingIntegrationFailureSummary = false; ModLogger.Info("BackpackShopIntegration: Safe backpack template created successfully."); return _safeBackpackTemplate; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateSafeBackpackTemplate", exception); return null; } } private static void ApplyBackpackDefinitionDefaults(StorableItemDefinition def) { if (!((Object)(object)def == (Object)null)) { ReflectionUtils.TrySetFieldOrProperty(def, "Description", string.Empty); ReflectionUtils.TrySetEnumFieldOrProperty(def, "Category", "Storage"); ReflectionUtils.TrySetFieldOrProperty(def, "StoredItem", null); ReflectionUtils.TrySetFieldOrProperty(def, "StationItem", null); ReflectionUtils.TrySetFieldOrProperty(def, "Equippable", null); ReflectionUtils.TrySetFieldOrProperty(def, "CustomItemUI", null); ReflectionUtils.TrySetFieldOrProperty(def, "CustomInfoContent", null); ReflectionUtils.TrySetEnumFieldOrProperty(def, "legalStatus", "Legal"); ReflectionUtils.TrySetFieldOrProperty(def, "ResellMultiplier", 0.5f); ReflectionUtils.TrySetFieldOrProperty(def, "PickpocketDifficultyMultiplier", 1f); ReflectionUtils.TrySetFieldOrProperty(def, "CombatUtility", 0f); } } private static StorableItemDefinition CreateEmptyStorableItemDefinition() { try { Type val = Il2CppType.Of<StorableItemDefinition>(); if (val != (Type)null) { ScriptableObject val2 = ScriptableObject.CreateInstance(val); StorableItemDefinition val3 = ((Il2CppObjectBase)val2).TryCast<StorableItemDefinition>(); if ((Object)(object)val3 != (Object)null) { return val3; } } ScriptableObject val4 = ScriptableObject.CreateInstance("ScheduleOne.ItemFramework.StorableItemDefinition") ?? ScriptableObject.CreateInstance("Il2CppScheduleOne.ItemFramework.StorableItemDefinition") ?? ScriptableObject.CreateInstance("StorableItemDefinition"); return (val4 != null) ? ((Il2CppObjectBase)val4).TryCast<StorableItemDefinition>() : null; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateEmptyStorableItemDefinition", exception); return null; } } private static bool RegisterDefinition(StorableItemDefinition def) { try { if ((Object)(object)def == (Object)null || string.IsNullOrWhiteSpace(((BaseItemDefinition)def).ID)) { return false; } if ((Object)(object)GetRegisteredItemDefinition(((BaseItemDefinition)def).ID) != (Object)null) { ModLogger.Debug("BackpackShopIntegration: Definition '" + ((BaseItemDefinition)def).ID + "' is already registered; reusing existing definition."); return true; } Registry instance = Singleton<Registry>.Instance; if ((Object)(object)instance == (Object)null) { return false; } instance.AddToRegistry((ItemDefinition)(object)def); return true; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RegisterDefinition", exception); return false; } } private static bool AddListingToShop(ShopInterface shop, StorableItemDefinition def, Sprite fallbackSprite, Texture2D fallbackTexture) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown try { ShopListing val = new ShopListing { Item = def, name = ((BaseItemDefinition)def).Name }; shop.Listings.Add(val); val.Initialize(shop); if (!TryInvokeShopMethod(shop, "CreateListingUI", val)) { Sprite tierSprite = LevelManagerPatch.GetTierSprite(ParseTierFromItemId(((BaseItemDefinition)def).ID), fallbackSprite, fallbackTexture); CreateListingUI(shop, val, tierSprite); } TryInvokeShopMethod(shop, "RefreshUnlockStatus"); TryInvokeShopMethod(shop, "RefreshShownItems"); return true; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddListingToShop", exception); return false; } } private static bool TryInvokeShopMethod(ShopInterface shop, string methodName, params object[] args) { try { if ((Object)(object)shop == (Object)null || string.IsNullOrEmpty(methodName)) { return false; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo[] methods = ((object)shop).GetType().GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal)) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (((args != null) ? args.Length : 0) == parameters.Length) { methodInfo.Invoke(shop, args); return true; } } } } catch (Exception ex) { ModLogger.Debug("BackpackShopIntegration: " + methodName + " invoke failed: " + ex.Message); } return false; } private static int ParseTierFromItemId(string itemId) { if (string.IsNullOrEmpty(itemId) || !itemId.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return -1; } string s = itemId.Substring("PackRat_Backpack_Tier_".Length); int result; return int.TryParse(s, out result) ? result : (-1); } private static void CreateListingUI(ShopInterface shop, ShopListing listing, Sprite iconSprite) { try { object obj = ReflectionUtils.TryGetFieldOrProperty(shop, "ListingUIPrefab"); object obj2 = ReflectionUtils.TryGetFieldOrProperty(shop, "ListingContainer"); if (obj == null || obj2 == null) { ModLogger.Warn("BackpackShopIntegration: Shop missing ListingUIPrefab or ListingContainer."); return; } object obj3 = ((obj is Component) ? obj : null); GameObject val = ((obj3 != null) ? ((Component)obj3).gameObject : null); Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } GameObject val3 = Object.Instantiate<GameObject>(val, val2); ListingUI component = val3.GetComponent<ListingUI>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val3); return; } component.Initialize(listing); if ((Object)(object)iconSprite != (Object)null && (Object)(object)component.Icon != (Object)null) { component.Icon.sprite = iconSprite; } BindListingUIEvents(shop, component); AddToListingUICollection(shop, component); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateListingUI", exception); } } private static void BindListingUIEvents(ShopInterface shop, ListingUI listingUI) { try { if (!TryBindIl2CppListingEvent(listingUI, "onAddItem", delegate { InvokeListingAdd(shop, listingUI); })) { TryBindIl2CppListingEvent(listingUI, "onClicked", delegate { InvokeListingAdd(shop, listingUI); }); } TryBindIl2CppListingEvent(listingUI, "onRemoveItem", delegate { TryInvokeShopMethod(shop, "RemoveItem", listingUI); }); TryBindIl2CppListingEvent(listingUI, "onDropdownClicked", delegate { TryInvokeShopMethod(shop, "DropdownClicked", listingUI); }); TryBindIl2CppListingEvent(listingUI, "hoverStart", delegate { TryInvokeShopMethod(shop, "EntryHovered", listingUI); }); TryBindIl2CppListingEvent(listingUI, "hoverEnd", delegate { TryInvokeShopMethod(shop, "EntryUnhovered"); }); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: BindListingUIEvents", exception); } } private static void InvokeListingAdd(ShopInterface shop, ListingUI listingUI) { if (!TryInvokeShopMethod(shop, "AddItem", listingUI) && !TryInvokeShopMethod(shop, "ListingClicked", listingUI)) { ModLogger.Warn("BackpackShopIntegration: Shop listing add handler was not found."); } } private static bool TryBindIl2CppListingEvent(ListingUI listingUI, string memberName, Action callback) { Action value = DelegateSupport.ConvertDelegate<Action>((Delegate)callback); return ReflectionUtils.TrySetFieldOrProperty(listingUI, memberName, value); } private static void AddToListingUICollection(ShopInterface shop, ListingUI listingUI) { try { FieldInfo field = typeof(ShopInterface).GetField("listingUI", BindingFlags.Instance | BindingFlags.NonPublic); if (!(field == null)) { object value = field.GetValue(shop); if (value != null && value is List<ListingUI> val) { val.Add(listingUI); } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddToListingUICollection", exception); } } public static bool IsBackpackTierPurchase(string itemId, out int tierIndex) { tierIndex = ParseTierFromItemId(itemId ?? ""); return tierIndex >= 0; } public static void RemoveTierListingFromAllShops(int tierIndex) { if (tierIndex < 0 || tierIndex >= Configuration.BackpackTiers.Length) { return; } string itemId = "PackRat_Backpack_Tier_" + tierIndex; List<int> list = new List<int>(_shopsIntegrated); foreach (int item in list) { ShopInterface val = FindShopByInstanceId(item); if ((Object)(object)val != (Object)null) { RemoveTierListingFromShop(val, itemId); } } } private static ShopInterface FindShopByInstanceId(int instanceId) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null) { object obj = ((item is Object) ? item : null); if (obj != null && ((Object)obj).GetInstanceID() == instanceId) { return (ShopInterface)item; } } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && ((Object)array[i]).GetInstanceID() == instanceId) { return array[i]; } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: FindShopByInstanceId", exception); } return null; } private static void RemoveTierListingFromShop(ShopInterface shop, string itemId) { if (((shop != null) ? shop.Listings : null) == null) { return; } try { object obj = null; Enumerator<ShopListing> enumerator = shop.Listings.GetEnumerator(); while (enumerator.MoveNext()) { ShopListing current = enumerator.Current; if ((Object)(object)((current != null) ? current.Item : null) != (Object)null && ((BaseItemDefinition)current.Item).ID == itemId) { obj = current; break; } } if (obj != null) { RemoveListingFromList(shop.Listings, obj); RemoveAndDestroyListingUI(shop, obj); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveTierListingFromShop", exception); } } private static int GetListCount(object list) { if (list == null) { return 0; } object obj = ReflectionUtils.TryGetFieldOrProperty(list, "Count") ?? ReflectionUtils.TryGetFieldOrProperty(list, "count"); if (obj is int result) { return result; } if (obj is long num) { return (int)num; } return 0; } private static void RemoveListingFromList(object list, object listing) { if (list == null || listing == null) { return; } try { IList val = (IList)((list is IList) ? list : null); if (val == null) { return; } int listCount = GetListCount(val); for (int i = 0; i < listCount; i++) { Object val2 = val[i]; if (val2 != null && (((object)val2).Equals(listing) || val2 == listing)) { val.RemoveAt(i); break; } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveListingFromList", exception); } } private static void RemoveAndDestroyListingUI(ShopInterface shop, object listing) { if ((Object)(object)shop == (Object)null || listing == null) { return; } try { object obj = typeof(ShopInterface).GetField("listingUI", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(shop); if (obj == null) { return; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "Count") ?? ReflectionUtils.TryGetFieldOrProperty(obj, "count"); int num = ((obj2 is int num2) ? num2 : 0); if (obj2 is long num3) { num = (int)num3; } for (int num4 = num - 1; num4 >= 0; num4--) { object obj3 = (obj.GetType().GetMethod("get_Item", new Type[1] { typeof(int) }) ?? obj.GetType().GetMethod("Get", new Type[1] { typeof(int) }))?.Invoke(obj, new object[1] { num4 }); if (obj3 != null) { object obj4 = ReflectionUtils.TryGetFieldOrProperty(obj3, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(obj3, "listing"); if (obj4 == listing) { object obj5 = ((obj3 is Component) ? obj3 : null); GameObject val = ((obj5 != null) ? ((Component)obj5).gameObject : null); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } IList val2 = (IList)((obj is IList) ? obj : null); if (val2 != null) { val2.RemoveAt(num4); } break; } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveAndDestroyListingUI", exception); } } } } namespace PackRat.Routing { public static class SmartRoutingManager { public static bool IsEnabled => Configuration.Instance.EnableSmartRouting; public static bool ShouldPreferBackpack(ItemInstance item) { if (item == null || !IsEnabled) { return false; } string itemCategory = StorageMenuPatch.GetItemCategory(item); if (1 == 0) { } bool result = itemCategory switch { "Products" => Configuration.Instance.RouteProducts, "Seeds" => Configuration.Instance.RouteSeeds, "Mixers" => Configuration.Instance.RouteMixers, "Reagents" => Configuration.Instance.RouteReagents, _ => false, }; if (1 == 0) { } return result; } } } namespace PackRat.Profiling { internal static class UiProfiler { private const int FlushEveryEvents = 64; private static object _sync; private static Stopwatch _sessionClock; private static StreamWriter _writer; private static bool _enabled; private static bool _initializationFailed; private static int _eventsSinceFlush; private static long _nextFlushMilliseconds; internal static bool IsEnabled => _enabled; internal static string OutputPath { get; private set; } internal static void ApplyEnabledState(bool enabled) { if (enabled) { if (!_enabled && !_initializationFailed) { Initialize(); } } else if (_enabled || _initializationFailed) { Shutdown(); } } internal static void Initialize() { if (_enabled || _initializationFailed) { return; } lock (GetSync()) { if (_enabled || _initializationFailed) { return; } try { string text = Path.Combine(Environment.CurrentDirectory, "UserData", "PackRatProfiler"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, $"packrat-ui-{DateTime.UtcNow:yyyyMMdd-HHmmssfff}-{"il2cpp"}.log"); StreamWriter streamWriter = new StreamWriter(new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite, 32768, FileOptions.SequentialScan), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 32768); streamWriter.WriteLine("utc|session_us|frame|thread|kind|feature|operation|duration_us|gc0|gc1|gc2|details"); _sessionClock = Stopwatch.StartNew(); _writer = streamWriter; OutputPath = text2; _eventsSinceFlush = 0; _nextFlushMilliseconds = 1000L; _enabled = true; WriteLocked("state", "session", "start", 0L, 0, 0, 0, $"version={"2.1.0"};runtime={"il2cpp"};os={Environment.OSVersion};cpu={Environment.ProcessorCount}"); streamWriter.Flush(); ModLogger.Info("PackRat developer profiler trace: " + OutputPath); } catch (Exception exception) { _writer?.Dispose(); _writer = null; _sessionClock?.Stop(); _sessionClock = null; _enabled = false; _initializationFailed = true; ModLogger.Error("PackRat developer profiler could not create its trace file", exception); } } } internal static void Event(string feature, string operation, string details = null) { if (_enabled) { Write("state", feature, operation, 0L, 0, 0, 0, details); } } internal static UiProfileScope Measure(string feature, string operation, string details = null) { return _enabled ? new UiProfileScope(feature, operation, details) : default(UiProfileScope); } internal static void Shutdown() { if (!_enabled && !_initializationFailed) { return; } lock (GetSync()) { if (_writer != null) { WriteLocked("state", "session", "stop", 0L, 0, 0, 0, null); _writer.Flush(); _writer.Dispose(); _writer = null; } _enabled = false; _eventsSinceFlush = 0; _nextFlushMilliseconds = 0L; _sessionClock?.Stop(); _sessionClock = null; _initializationFailed = false; } } internal static void FlushIfDue() { if (!_enabled || _sessionClock == null) { return; } long elapsedMilliseconds = _sessionClock.ElapsedMilliseconds; if (elapsedMilliseconds < _nextFlushMilliseconds) { return; } lock (GetSync()) { if (_enabled && _sessionClock != null && _sessionClock.ElapsedMilliseconds >= _nextFlushMilliseconds) { _writer?.Flush(); _eventsSinceFlush = 0; _nextFlushMilliseconds = _sessionClock.ElapsedMilliseconds + 1000; } } } internal static void Complete(string feature, string operation, long startedTimestamp, int gc0, int gc1, int gc2, string details) { if (_enabled && startedTimestamp != 0) { long durationUs = (Stopwatch.GetTimestamp() - startedTimestamp) * 1000000 / Stopwatch.Frequency; Write("span", feature, operation, durationUs, GC.CollectionCount(0) - gc0, GC.CollectionCount(1) - gc1, GC.CollectionCount(2) - gc2, details); } } private static object GetSync() { if (_sync != null) { return _sync; } object value = new object(); return Interlocked.CompareExchange(ref _sync, value, null) ?? _sync; } private static void Write(string kind, string feature, string operation, long durationUs, int gc0, int gc1, int gc2, string details) { if (!_enabled) { return; } lock (GetSync()) { if (_enabled) { WriteLocked(kind, feature, operation, durationUs, gc0, gc1, gc2, details); if (++_eventsSinceFlush >= 64) { _writer?.Flush(); _eventsSinceFlush = 0; } } } } private static void WriteLocked(string kind, string feature, string operation, long durationUs, int gc0, int gc1, int gc2, string details) { if (_writer != null && _sessionClock != null) { _writer.Write(DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); _writer.Write('|'); _writer.Write(_sessionClock.ElapsedTicks * 1000000 / Stopwatch.Frequency); _writer.Write('|'); _writer.Write(SafeFrameCount()); _writer.Write('|'); _writer.Write(Thread.CurrentThread.ManagedThreadId); _writer.Write('|'); _writer.Write(Sanitize(kind)); _writer.Write('|'); _writer.Write(Sanitize(feature)); _writer.Write('|'); _writer.Write(Sanitize(operation)); _writer.Write('|'); _writer.Write(durationUs); _writer.Write('|'); _writer.Write(gc0); _writer.Write('|'); _writer.Write(gc1); _writer.Write('|'); _writer.Write(gc2); _writer.Write('|'); _writer.WriteLine(Sanitize(details)); } } private static int SafeFrameCount() { try { return Time.frameCount; } catch { return -1; } } private static string Sanitize(string value) { return string.IsNullOrEmpty(value) ? string.Empty : value.Replace('|', '/').Replace('\r', ' ').Replace('\n', ' '); } } internal readonly struct UiProfileScope : IDisposable { private readonly string _feature; private readonly string _operation; private readonly string _details; private readonly long _startedTimestamp; private readonly int _gc0; private readonly int _gc1; private readonly int _gc2; internal UiProfileScope(string feature, string operation, string details) { _feature = feature; _operation = operation; _details = details; _startedTimestamp = Stopwatch.GetTimestamp(); _gc0 = GC.CollectionCount(0); _gc1 = GC.CollectionCount(1); _gc2 = GC.CollectionCount(2); } public void Dispose() { if (_startedTimestamp != 0) { UiProfiler.Complete(_feature, _operation, _startedTimestamp, _gc0, _gc1, _gc2, _details); } } } } namespace PackRat.Patches { [HarmonyPatch(typeof(ShopInterface))] public static class BackpackPurchasePatch { [HarmonyPatch("AddItem", new Type[] { typeof(ListingUI) })] [HarmonyPrefix] public static bool AddItem_Prefix(ShopInterface __instance, ListingUI ui) { try { if ((Object)(object)ui == (Object)null) { return true; } object obj = ReflectionUtils.TryGetFieldOrProperty(ui, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(ui, "listing"); if (obj == null) { return true; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "Item"); StorableItemDefinition val = null; Object val2 = (Object)((obj2 is Object) ? obj2 : null); val = (StorableItemDefinition)((val2 == null) ? ((obj2 is StorableItemDefinition) ? obj2 : null) : ((Il2CppObjectBase)val2).TryCast<StorableItemDefinition>()); if (val == null || ((BaseItemDefinition)val).ID == null || !((BaseItemDefinition)val).ID.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return true; } if (!BackpackShopIntegration.IsBackpackTierPurchase(((BaseItemDefinition)val).ID, out var tierIndex)) { return true; } PlayerBackpack instance = PlayerBackpack.Instance; if ((Object)(object)instance != (Object)null && tierIndex <= instance.EquippedTierIndex) { ModLogger.Info($"Blocked purchase selection for backpack tier {tierIndex}; player already owns tier {instance.EquippedTierIndex} or better."); return false; } if ((ReflectionUtils.TryGetFieldOrProperty(obj, "QuantityInCart") ?? ReflectionUtils.TryGetFieldOrProperty(obj, "quantityInCart")) is int num && num >= 1) { ModLogger.Debug($"[Shop] Blocked duplicate cart quantity for backpack tier {tierIndex}."); return false; } return true; } catch (Exception exception) { ModLogger.Error("BackpackPurchasePatch: AddItem prefix error", exception); return true; } } [HarmonyPatch("SetAmount", new Type[] { typeof(ListingUI), typeof(int) })] [HarmonyPrefix] public static void SetAmount_Prefix(ListingUI ui, ref int amount) { try { if (amount > 1 && !((Object)(object)ui == (Object)null)) { object target = ReflectionUtils.TryGetFieldOrProperty(ui, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(ui, "listing"); object obj = ReflectionUtils.TryGetFieldOrProperty(target, "Item"); Object val = (Object)((obj is Object) ? obj : null); StorableItemDefinition val2 = (StorableItemDefinition)((val != null) ? ((Il2CppObjectBase)val).TryCast<StorableItemDefinition>() : ((obj is StorableItemDefinition) ? obj : null)); if (val2 != null && ((BaseItemDefinition)val2).ID != null && BackpackShopIntegration.IsBackpackTierPurchase(((BaseItemDefinition)val2).ID, out var _)) { amount = 1; ModLogger.Debug("[Shop] Clamped backpack tier quantity to one."); } } } catch (Exception exception) { ModLogger.Error("BackpackPurchasePatch: SetAmount prefix error", exception); } } } [HarmonyPatch(typeof(BodySearchBehaviour))] public static class BodySearchBehaviourPatch { [HarmonyPatch("SearchClean")] [HarmonyPrefix] public static bool SearchClean(BodySearchBehaviour __instance) { if ((Object)(object)PlayerBackpack.Instance == (Object)null || !Configuration.Instance.EnableSearch || !PlayerBackpack.Instance.IsUnlocked || !PlayerBackpack.Instance.IsPoliceSearchable) { return true; } Singleton<BodySearchScreen>.Instance.onSearchClear.RemoveListener(UnityAction.op_Implicit((Action)__instance.SearchClean)); Singleton<BodySearchScreen>.Instance.onSearchFail.RemoveListener(UnityAction.op_Implicit((Action)__instance.SearchFail)); Singleton<BodySearchScreen>.Instance.IsOpen = true; MelonCoroutines.Start(CheckForItems(__instance)); return false; } private static IEnumerator CheckForItems(BodySearchBehaviour behaviour) { ((NPC)behaviour.officer).DialogueHandler.ShowWorldspaceDialogue("Hold on, let me see your backpack as well.", 5f); yield return (object)new WaitForSeconds(3f); Singleton<BodySearchScreen>.Instance.IsOpen = false; behaviour.ConcludeSearch(!PlayerBackpack.Instance.ContainsItemsOfInterest(behaviour.MaxStealthLevel)); } } [HarmonyPatch(typeof(Cart))] public static class CartPatch { [HarmonyPatch("GetWarning")] [HarmonyPostfix] public static void GetWarning(Cart __instance, ref bool __result, ref string warning) { PlayerBackpack instance = PlayerBackpack.Instance; if (!((Object)(object)instance == (Object)null) && instance.IsUnlocked && !warning.StartsWith("Vehicle") && __result) { List<ItemSlot> itemSlots = instance.ItemSlots; itemSlots.InsertRange(0, ((Il2CppObjectBase)PlayerSingleton<PlayerInventory>.Instance.hotbarSlots).Cast<IEnumerable<ItemSlot>>()); if (__instance.Shop.WillCartFit(itemSlots)) { warning = "Inventory won't fit everything. Some items will be placed in your backpack."; } } } } [HarmonyPatch(typeof(HandoverScreen))] public static class HandoverScreenPatch { private sealed class PanelState { public RectTransform BackpackContainer; public RectTransform BackpackSlotContainer; public RectTransform BackpackHeaderRoot; public RectTransform BackpackVisualRoot; public Canvas DedicatedCanvas; public EditorUiDedicatedCanvasBinding EditorDedicatedCanvas; public RectTransform DedicatedCard; public RectTransform DedicatedBrowserHost; public RectTransform DedicatedGrid; public ItemSlotUI SlotPrefab; public RectTransform PagingRoot; public RectTransform VehicleContainer; public Component SourceTitleLabel; public Component SourceSubtitleLabel; public Component ClonedTitleLabel; public Component ClonedSubtitleLabel; public Component OverlayTitleLabel; public Component OverlaySubtitleLabel; public ItemSlotUI[] SlotUIs; public Button PrevButton; public Button NextButton; public Button ToggleButton; public Button DedicatedToggleButton; public RectTransform DedicatedToggleRoot; public RectTransform TransferRoot; public Button TransferButton; public Button BoundTransferButton; public Button EditorBackpackModeButton; public Button EditorVehicleModeButton; public Text TransferStatusLabel; public Text PageLabel; public Text VisualTitleLabel; public Text VisualMetaLabel; public Action PrevAction; public Action NextAction; public Action ToggleAction; public Action TransferAction; public Action ShowBackpackAction; public Action ShowVehicleAction; public LandVehicle NearbyVehicle; public Vector2 VehicleOriginalAnchoredPos; public int CurrentPage; public int SlotsPerPage; public bool ShowingVehicle; public bool IsOpen; public int LastPageInputFrame; public int NextVehicleProbeFrame; public bool Initialized; public string TransferStatus; } private sealed class HandoverRequirement { public string ProductId; public string Quality; public int QualityRank; public int Remaining; } private sealed class HandoverTransferSource { public string Name; public List<ItemSlot> Slots; public int MovedUnits; } private sealed class HeaderCandidate { public Component Label; public float LocalY; public float FontSize; } private const float VehicleMaxDistance = 20f; private const float BackpackGridScale = 0.74f; private const float BackpackContentCenterY = 125f; private static readonly Vector2 BackpackCardSize = new Vector2(420f, 660f); private const string VehicleHeaderTitle = "Vehicle"; private const string VehicleHeaderSubtitle = "This is the vehicle you last drove.\nMust be within 20 meters."; private static readonly Dictionary<int, PanelState> States = new Dictionary<int, PanelState>(); private const int HeaderReapplyFrameCount = 3; private static BackpackUiThemePalette GetCurrentBackpackThemePalette() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) Configuration instance = Configuration.Instance; return BackpackUiThemes.Get(instance.BackpackUiTheme, instance.CustomBackpackUiPrimaryColor); } public static void RefreshActiveLayouts() { try { foreach (PanelState value in States.Values) { if (value == null) { continue; } bool flag = (Object)(object)value.DedicatedCanvas != (Object)null && ((Component)value.DedicatedCanvas).gameObject.activeSelf; bool flag2 = (Object)(object)value.BackpackContainer != (Object)null && ((Component)value.BackpackContainer).gameObject.activeSelf; if (flag || flag2) { if ((Object)(object)value.DedicatedCard != (Object)null) { UpdateDedicatedOverlayLayout(FindOwningScreen(value), value); } else { ConfigureCompactBackpackLayout(FindOwningScreen(value), value); } UpdatePagingLayout(value); } } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.RefreshActiveLayouts", exception); } } [HarmonyPatch("Start")] [HarmonyPostfix] public static void Start(HandoverScreen __instance) { try { ModLogger.Info($"[HandoverUI] Start receipt: id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}"); PruneDeadStates(); EnsurePanel(__instance); } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Start", exception); } } [HarmonyPatch("Open")] [HarmonyPostfix] public static void Open(HandoverScreen __instance) { using (UiProfiler.Measure("handover", "open", $"id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}")) { try { ModLogger.Info($"[HandoverUI] Open receipt: id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}, hasBackpack={HasBackpack()}"); PruneDeadStates(); if (!HasBackpack()) { HidePanelAndRestoreVehicle(__instance); return; } PanelState panelState = EnsurePanel(__instance); if (panelState != null && (!((Object)(object)panelState.BackpackContainer == (Object)null) || !((Object)(object)panelState.DedicatedCanvas == (Object)null))) { LandVehicle val = ResolveNearbyVehicleStorage(panelState, forceRefresh: true); bool hasVehicle = (Object)(object)val != (Object)null; LogVehicleSelector(val); EnsureDedicatedVehicleToggle(__instance, panelState, hasVehicle); panelState.CurrentPage = 0; panelState.SlotsPerPage = ((panelState.SlotUIs != null) ? panelState.SlotUIs.Length : 0); panelState.ShowingVehicle = false; panelState.TransferStatus = null; panelState.IsOpen = true; if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackContainer != (Object)null) { ((Component)panelState.BackpackContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.PagingRoot != (Object)null) { ((Component)panelState.PagingRoot).gameObject.SetActive((Object)(object)panelState.DedicatedCanvas == (Object)null); } UpdateBackpackHeaderTexts(panelState); ApplyVisibleStorageMode(panelState, hasVehicle); if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackContainer != (Object)null) { ((Component)panelState.BackpackContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackSlotContainer != (Object)null) { ((Component)panelState.BackpackSlotContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.VehicleContainer != (Object)null) { ((Component)panelState.VehicleContainer).gameObject.SetActive(false); } ApplyPrimaryHeaderForMode(__instance, panelState, panelState.ShowingVehicle); MelonCoroutines.Start(ReapplyHeaderNextFrame(__instance, panelState)); if ((Object)(object)panelState.DedicatedCanvas == (Object)null) { ApplyBackpackPage(panelState); } else { UpdateDedicatedOverlayLayout(__instance, panelState); EnsureDedicatedVehicleToggle(__instance, panelState, hasVehicle); UpdateDedicatedVehicleToggle(__instance, panelState, hasVehicle); } RebuildQuickMove(__instance, val); } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Open", exception); } } } private static IEnumerator ReapplyHeaderNextFrame(HandoverScreen screen, PanelState panel) { if ((Object)(object)screen == (Object)null || panel == null) { yield break; } for (int i = 0; i < 3; i++) { yield return null; if ((Object)(object)screen == (Object)null || panel == null || (Object)(object)panel.BackpackContainer == (Object)null || !((Component)panel.BackpackContainer).gameObject.activeSelf || panel.ShowingVehicle) { break; } if ((Object)(object)panel.VehicleContainer != (Object)null && ((Component)panel.VehicleContainer).gameObject.activeSelf) { ((Component)panel.VehicleContainer).gameObject.SetActive(false); } ApplyPrimaryHeaderForMode(screen, panel, showingVehicle: false); } } [HarmonyPatch("Update")] [HarmonyPostfix] public static void Update(HandoverScreen __instance) { PruneDeadStates(); if ((Object)(object)__instance == (Object)null || !States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { return; } if (value.IsOpen && !__instance.IsOpen) { ModLogger.Info("[HandoverUI] Owner screen closed outside Close(); dismissing dedicated backpack overlay."); HidePanelAndRestoreVehicle(__instance); } else { if (!value.IsOpen) { return; } if ((Object)(object)value.DedicatedCanvas != (Object)null) { if (!value.ShowingVehicle && (Object)(object)value.VehicleContainer != (Object)null && ((Component)value.VehicleContainer).gameObject.activeSelf) { ((Component)value.VehicleContainer).gameObject.SetActive(false); } UpdateDedicatedVehicleToggle(__instance, value, (Object)(object)ResolveNearbyVehicleStorage(value) != (Object)null); } else { if ((Object)(object)value.BackpackContainer == (Object)null || !((Component)value.BackpackContainer).gameObject.activeSelf || value.ShowingVehicle) { return; } if ((Object)(object)value.VehicleContainer != (Object)null && ((Component)value.VehicleContainer).gameObject.activeSelf) { ((Component)value.VehicleContainer).gameObject.SetActive(false); } ConfigureCompactBackpackLayout(__instance, value); ApplyPrimaryHeaderForMode(__instance, value, showingVehicle: false); if ((Object)(object)value.BackpackHeaderRoot != (Object)null && ((Component)value.BackpackHeaderRoot).gameObject.activeSelf) { ((Transform)value.BackpackHeaderRoot).SetAsLastSibling(); Canvas component = ((Component)value.BackpackHeaderRoot).GetComponent<Canvas>(); if ((Object)(object)component != (Object)null && component.overrideSorting && component.sortingOrder != 9999) { component.sortingOrder = 9999; } } } } } [HarmonyPatch("Close")] [HarmonyPostfix] public static void Close(HandoverScreen __instance) { //IL_016d: Unknown result type (might be due to invalid IL or missing references) using (UiProfiler.Measure("handover", "close", $"id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}")) { try { if (States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { ClearSlotAssignments(value); value.IsOpen = false; if ((Object)(object)value.BackpackContainer != (Object)null) { ((Component)value.BackpackContainer).gameObject.SetActive(false); } if ((Object)(object)value.PagingRoot != (Object)null) { ((Component)value.PagingRoot).gameObject.SetActive(false); } if ((Object)(object)value.DedicatedToggleRoot != (Object)null) { ((Component)value.DedicatedToggleRoot).gameObject.SetActive(false); } if ((Object)(object)value.TransferRoot != (Object)null) { ((Component)value.TransferRoot).gameObject.SetActive(false); } if ((Object)(object)value.DedicatedCanvas != (Object)null) { ((Component)value.DedicatedCanvas).gameObject.SetActive(false); } SetBackpackVisualVisible(value, visible: false); HideOverlayHeader(value); SetHeaderPairActive(value.SourceTitleLabel, value.SourceSubtitleLabel, active: true); if ((Object)(object)value.VehicleContainer != (Object)null) { value.VehicleContainer.anchoredPosition = value.VehicleOriginalAnchoredPos; } } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Close", exception); } } } private static PanelState EnsurePanel(HandoverScreen screen) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) using (UiProfiler.Measure("handover", "ensure_panel", $"id={((screen != null) ? new int?(((Object)screen).GetInstanceID()) : ((int?)null))}")) { PruneDeadStates(); if ((Object)(object)screen == (Object)null) { return null; } int instanceID = ((Object)screen).GetInstanceID(); if (States.TryGetValue(instanceID, out var value) && value.Initialized && IsComponentAlive((Component)(object)value.BackpackContainer) && IsComponentAlive((Component)(object)value.VehicleContainer) && ((Object)(object)value.BackpackHeaderRoot == (Object)null || IsComponentAlive((Component)(object)value.BackpackHeaderRoot)) && IsComponentAlive((Component)(object)value.PagingRoot) && IsComponentAlive((Component)(object)value.PrevButton) && IsComponentAlive((Component)(object)value.NextButton) && IsComponentAlive((Component)(object)value.ToggleButton) && IsComponentAlive((Component)(object)value.PageLabel)) { RefreshHeaderBindings(value, screen); EnsureDedicatedBackpackOverlay(screen, value); return value; } PanelState panelState = value ?? new PanelState(); panelState.VehicleContainer = screen.VehicleContainer; if ((Object)(object)panelState.VehicleContainer == (Object)null) { return null; } panelState.VehicleOriginalAnchoredPos = panelState.VehicleContainer.anchoredPosition; if (!IsComponentAlive((Component)(object)panelState.BackpackContainer)) { panelState.BackpackContainer = null; } if ((Object)(object)panelState.BackpackContainer == (Object)null) { RectTransform val = Object.Instantiate<RectTransform>(panelState.VehicleContainer, ((Transform)panelState.VehicleContainer).parent); ((Object)val).name = "BackpackContainer"; ((Component)val).gameObject.SetActive(false); panelState.BackpackContainer = val; } CenterBackpackContainer(panelState); panelState.BackpackSlotContainer = FindMatchingRectTransform(panelState.BackpackContainer, screen.VehicleSlotContainer); RectTransform val2 = (((Object)(object)panelState.BackpackSlotContainer != (Object)null) ? panelState.BackpackSlotContainer : panelState.BackpackContainer); Il2CppArrayBase<ItemSlotUI> componentsInChildren = ((Component)val2).GetComponentsInChildren<ItemSlotUI>(false); if (componentsInChildren == null || componentsInChildren.Length == 0) { componentsInChildren = ((Component)val2).GetComponentsInChildren<ItemSlotUI>(true); } ItemSlotUI dedicatedSlotTemplate = GetDedicatedSlotTemplate((componentsInChildren != null && componentsInChildren.Length > 0) ? componentsInChildren[0] : null); if ((Object)(object)panelState.DedicatedCanvas == (Object)null) { panelState.SlotUIs = Il2CppArrayBase<ItemSlotUI>.op_Implicit(componentsInChildren); panelState.SlotPrefab = dedicatedSlotTemplate; } else if ((Object)(object)panelState.SlotPrefab == (Object)null) { panelState.SlotPrefab = dedicatedSlotTemplate; } RefreshHeaderBindings(panelState, screen); EnsureDedicatedBackpackOverlay(screen, panelState); if ((Object)(object)panelState.DedicatedCard == (Object)null) { EnsureBackpackVisuals(panelState); ConfigureCompactBackpackLayout(screen, panelState); } EnsurePagingControls(panelState); panelState.Initialized = true; States[instanceID] = panelState; return panelState; } } private static void PruneDeadStates() { if (States.Count == 0) { return; } List<int> list = new List<int>(); foreach (KeyValuePair<int, PanelState> state in States) { PanelState value = state.Value; if (value == null) { list.Add(state.Key); } else if (!IsComponentAlive((Component)(object)value.VehicleContainer) && !IsComponentAlive((Component)(object)value.BackpackContainer) && !IsComponentAlive((Component)(object)value.PagingRoot) && !IsComponentAlive((Component)(object)value.BackpackHeaderRoot)) { list.Add(state.Key); } } for (int i = 0; i < list.Count; i++) { if (States.TryGetValue(list[i], out var value2) && (Object)(object)value2?.DedicatedCanvas != (Object)null) { Object.Destroy((Object)(object)((Component)value2.DedicatedCanvas).gameObject); } States.Remove(list[i]); } } private static void RefreshHeaderBindings(PanelState state, HandoverScreen screen) { if (state != null) { ResolveLabels(state, screen); EnsureBackpackHeader(state); } } private static void ResolveLabels(PanelState state, HandoverScreen screen) { ResolveHeaderPairInContainer(state.VehicleContainer, (Transform)(object)((screen != null) ? screen.VehicleSlotContainer : null), null, null, out state.SourceTitleLabel, out state.SourceSubtitleLabel); ResolveHeaderPairInContainer(state.BackpackContainer, (Transform)(object)state.BackpackSlotContainer, (Transform)(object)state.PagingRoot, (Transform)(object)state.BackpackHeaderRoot, out state.ClonedTitleLabel, out state.ClonedSubtitleLabel); if (IsUnderTransform(state.ClonedTitleLabel, (Transform)(object)state.BackpackVisualRoot)) { state.ClonedTitleLabel = null; } if (IsUnderTransform(state.ClonedSubtitleLabel, (Transform)(object)state.BackpackVisualRoot)) { state.ClonedSubtitleLabel = null; } } private static void ResolveHeaderPairInContainer(RectTransform container, Transform slotContainer, Transform pagingRoot, Transform overlayRoot, out Component titleLabel, out Component subtitleLabel) { titleLabel = null; subtitleLabel = null; if ((Object)(object)container == (Object)null) { return; } Il2CppArrayBase<Component> componentsInChildren = ((Component)container).GetComponentsInChildren<Component>(true); List<Component> list = new List<Component>(); for (int i = 0; i < componentsInChildren.Length; i++) { Component val = componentsInChildren[i]; if (!((Object)(object)val == (Object)null) && IsTextLikeComponent(val) && !IsUnderTransform(val, slotContainer) && !IsUnderTransform(val, pagingRoot) && !IsUnderTransform(val, overlayRoot)) { list.Add(val); } } if (list.Count == 0) { return; } titleLabel = FindNamedHeaderLabel(container, slotContainer, pagingRoot, overlayRoot, "Title"); subtitleLabel = FindNamedHeaderLabel(container, slotContainer, pagingRoot, overlayRoot, "Subtitle"); foreach (Component item in list) { string text = NormalizeLabelText(GetLabelText(item)); if ((Object)(object)titleLabel == (Object)null && text.Equals("Vehicle", StringComparison.OrdinalIgnoreCase)) { titleLabel = item; } else if ((Object)(object)subtitleLabel == (Object)null && (text.Contains("vehicle you last drove", StringComparison.OrdinalIgnoreCase) || text.Contains("within 20 meters", StringComparison.OrdinalIgnoreCase))) { subtitleLabel = item; } } if ((Object)(object)titleLabel != (Object)null && (Object)(object)subtitleLabel != (Object)null) { return; } List<HeaderCandidate> list2 = new List<HeaderCandidate>(list.Count); for (int j = 0; j < list.Count; j++) { Component val2 = list[j]; list2.Add(new HeaderCandidate { Label = val2, LocalY = GetLocalY(container, val2), FontSize = GetFontSize(val2) }); } list2.Sort(CompareHeaderCandidates); if ((Object)(object)titleLabel == (Object)null && list2.Count > 0) { titleLabel = list2[0].Label; } if (!((Object)(object)subtitleLabel == (Object)null)) { return; } for (int k = 0; k < list2.Count; k++) { Component label = list2[k].Label; if (!((Object)(object)label == (Object)null) && !((Object)(object)label == (Object)(object)titleLabel)) { subtitleLabel = label; break; } } } private static Component FindNamedHeaderLabel(RectTransform container, Transform slotContainer, Transform pagingRoot, Transform overlayRoot, string targetName) { if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(targetName)) { return null; } Il2CppArrayBase<Transform> componentsInChildren = ((Component)container).GetComponentsInChildren<Transform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Transform val = componentsInChildren[i]; if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val).name, targetName, StringComparison.OrdinalIgnoreCase) && (!((Object)(object)slotContainer != (Object)null) || (!((Object)(object)val == (Object)(object)slotContainer) && !val.IsChildOf(slotContainer))) && (!((Object)(object)pagingRoot != (Object)null) || (!((Object)(object)val == (Object)(object)pagingRoot) && !val.IsChildOf(pagingRoot))) && (!((Object)(object)overlayRoot != (Object)null) || (!((Object)(object)val == (Object)(object)overlayRoot) && !val.IsChildOf(overlayRoot)))) { Component textLikeComponent = GetTextLikeComponent(((Component)val).gameObject); if ((Object)(object)textLikeComponent != (Object)null) { return textLikeComponent; } Component val2 = FindFirstTextLikeInSubtree(val); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } private static Component FindFirstTextLikeInSubtree(Transform root) { if ((Object)(object)root == (Object)null) { return null; } Il2CppArrayBase<Component> componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Component val = componentsInChildren[i]; if ((Object)(object)val != (Object)null && IsTextLikeComponent(val)) { return val; } } return null; } private static int CompareHeaderCandidates(HeaderCandidate left, HeaderCandidate right) { if (left == right) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int num = right.LocalY.CompareTo(left.LocalY); if (num != 0) { return num; } return right.FontSize.CompareTo(left.FontSize); } private static float GetLocalY(RectTransform container, Component component) { //IL_0037: 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_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)container == (Object)null || (Object)(object)component == (Object)null || (Object)(object)component.transform == (Object)null) { return float.MinValue; } try { Vector3 val = ((Transform)container).InverseTransformPoint(component.transform.position); return val.y; } catch { return float.MinValue; } } private static float GetFontSize(Component label) { if ((Object)(object)label == (Object)null) { return 0f; } TextMeshProUGUI val = (TextMeshProUGUI)(object)((label is TextMeshProUGUI) ? label : null); if (val != null) { try { return ((TMP_Text)val).fontSize; } catch { } } Text val2 = (Text)(object)((label is Text) ? label : null); if (val2 != null) { return val2.fontSize; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(label, "fontSize"); if (obj2 is float result) { return result; } if (obj2 is int num) { return num; } return 0f; } private static Component GetTextLikeComponent(GameObje
PackRat-Mono.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using FishNet; using FishNet.Component.Spawning; using FishNet.Connection; using FishNet.Object; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using PackRat; using PackRat.Config; using PackRat.Extensions; using PackRat.Helpers; using PackRat.Logic; using PackRat.Networking; using PackRat.Patches; using PackRat.Profiling; using PackRat.Routing; using PackRat.Shops; using PackRat.Storage; using ScheduleOne; using ScheduleOne.AvatarFramework; using ScheduleOne.AvatarFramework.Customization; using ScheduleOne.Core.Items.Framework; using ScheduleOne.DevUtilities; using ScheduleOne.Equipping; using ScheduleOne.ItemFramework; using ScheduleOne.Levelling; using ScheduleOne.Money; using ScheduleOne.NPCs; using ScheduleOne.NPCs.Behaviour; using ScheduleOne.Networking; using ScheduleOne.Persistence; using ScheduleOne.Persistence.Datas; using ScheduleOne.Persistence.Loaders; using ScheduleOne.PlayerScripts; using ScheduleOne.Product; using ScheduleOne.Product.Packaging; using ScheduleOne.Quests; using ScheduleOne.StationFramework; using ScheduleOne.Storage; using ScheduleOne.TV; using ScheduleOne.Tools; using ScheduleOne.UI; using ScheduleOne.UI.Handover; using ScheduleOne.UI.Items; using ScheduleOne.UI.Phone; using ScheduleOne.UI.Shop; using ScheduleOne.Variables; using ScheduleOne.Vehicles; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(global::PackRat.PackRat), "PackRat", "2.1.0", "SirTidez", null)] [assembly: MelonColor(1, 255, 165, 0)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PackRat-Mono")] [assembly: AssemblyConfiguration("Release Mono")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0+eab397543e055d1e65172f408b02a67dfe507980")] [assembly: AssemblyProduct("PackRat-Mono")] [assembly: AssemblyTitle("PackRat-Mono")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PackRat { public static class BuildInfo { public const string Name = "PackRat"; public const string Description = "Portable backpack storage for Schedule One"; public const string Author = "SirTidez"; public const string Version = "2.1.0"; } public class PackRat : MelonMod { public override void OnInitializeMelon() { Configuration.Instance.Load(); EditorUiAssetBundle.Prewarm(); UiProfiler.ApplyEnabledState(Configuration.Instance.EnableDeveloperProfiler); UiProfiler.Event("lifecycle", "initialize"); ModLogger.Info("PackRat initialized."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { using (UiProfiler.Measure("lifecycle", "scene_loaded", $"index={buildIndex};scene={sceneName}")) { UiProfiler.Event("lifecycle", "scene_change", $"index={buildIndex};scene={sceneName}"); Configuration.Instance.Reset(); UiProfiler.ApplyEnabledState(Configuration.Instance.EnableDeveloperProfiler); if (sceneName != "Main") { CameraLockedStateHelper.ResetSceneCache(); return; } CameraLockedStateHelper.PrepareForMainSceneLoad(); ConfigSyncManager.StartSync(); BackpackShopIntegration.RunWhenReady(); } } public override void OnUpdate() { if (UiProfiler.IsEnabled) { UiProfiler.FlushIfDue(); } } public override void OnDeinitializeMelon() { UiProfiler.Event("lifecycle", "deinitialize"); EditorUiAssetBundle.Unload(); UiProfiler.Shutdown(); } public override void OnApplicationQuit() { UiProfiler.Event("lifecycle", "application_quit"); EditorUiAssetBundle.Unload(); UiProfiler.Shutdown(); } } public class PlayerBackpack : MonoBehaviour { public const string StorageName = "Backpack"; public const int MinimumStorageSlots = 1; private bool _backpackEnabled = true; private StorageEntity _storage; private int _lastTierIndex = -2; private string _openTitle; private int _equippedTierIndex = -1; private const int TierCheckIntervalFrames = 60; private static Type _cachedStorageMenuType; private static MethodInfo[] _cachedStorageMenuOpenMethods = Array.Empty<MethodInfo>(); private bool _monoCameraLockFallbackActive; private static readonly string[] SelectedHotbarIndexMemberNames = new string[16] { "selectedSlotIndex", "SelectedSlotIndex", "selectedIndex", "SelectedIndex", "currentSlotIndex", "CurrentSlotIndex", "activeSlotIndex", "ActiveSlotIndex", "activeIndex", "ActiveIndex", "equippedSlotIndex", "EquippedSlotIndex", "SelectedSlot", "selectedSlot", "slotIndex", "SlotIndex" }; public static PlayerBackpack Instance { get; private set; } public int EquippedTierIndex => _equippedTierIndex; public int HighestPurchasedTierIndex => EquippedTierIndex; public int CurrentTierIndex { get { Configuration instance = Configuration.Instance; if (_equippedTierIndex < 0) { return -1; } for (int num = _equippedTierIndex; num >= 0; num--) { if (num < instance.TierEnabled.Length && instance.TierEnabled[num]) { return num; } } return -1; } } public BackpackTierDefinition CurrentTier { get { int currentTierIndex = CurrentTierIndex; return (currentTierIndex >= 0) ? Configuration.BackpackTiers[currentTierIndex] : null; } } public bool IsUnlocked => CurrentTierIndex >= 0; public bool IsPoliceSearchable => CurrentTierIndex >= 2; public bool IsOpen { get { StorageMenu instance = Singleton<StorageMenu>.Instance; return (Object)(object)instance != (Object)null && instance.IsOpen && (Object)(object)instance.TitleLabel != (Object)null && ((TMP_Text)instance.TitleLabel).text == _openTitle; } } public List<ItemSlot> ItemSlots => _storage.ItemSlots.ToList(); public void SetEquippedTierIndex(int tierIndex) { _equippedTierIndex = ((tierIndex < 0) ? (-1) : Math.Min(tierIndex, Configuration.BackpackTiers.Length - 1)); } public void SetHighestPurchasedTierIndex(int tierIndex) { SetEquippedTierIndex(tierIndex); } public void RestorePurchasedTier(int tierIndex) { SetEquippedTierIndex(tierIndex); EnsureCorrectTierApplied(); } private void Awake() { _storage = ((Component)this).gameObject.GetComponentInParent<StorageEntity>(); if ((Object)(object)_storage == (Object)null) { ModLogger.Error("Player does not have a BackpackStorage component!"); return; } ModLogger.Info("Configuring backpack storage..."); ModLogger.Debug("[BackpackUI] Awake: object='" + ((Object)this).name + "', storage='" + ((Object)_storage).name + "'."); MelonCoroutines.Start(DeferredConfigureStorage(this)); } private static IEnumerator DeferredConfigureStorage(PlayerBackpack instance) { yield return null; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance._storage == (Object)null)) { int tierIdx = instance.CurrentTierIndex; int slotCount = ((tierIdx >= 0) ? Configuration.Instance.TierSlotCounts[tierIdx] : Configuration.BackpackTiers[0].DefaultSlotCount); instance.UpdateSize(slotCount); instance.OnStartClient(instance.IsOwnedByLocalPlayer()); } } private void Update() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { StorageMenuPatch.ClearStandaloneBackpackKeyboardFocusOnPointerInput(); } if (IsOpen && StorageMenuPatch.HandleStandaloneBackpackSettingsInput()) { return; } bool keyDown = Input.GetKeyDown(Configuration.Instance.ToggleKey); if (keyDown) { UiProfiler.Event("hotkey", "pressed", $"open={IsOpen};unlocked={IsUnlocked};tier={CurrentTierIndex}"); } if (keyDown || Time.frameCount % 60 == 0) { int currentTierIndex = CurrentTierIndex; if (currentTierIndex != _lastTierIndex) { _lastTierIndex = currentTierIndex; ApplyCurrentTier(currentTierIndex); } } if (!_backpackEnabled) { return; } try { if ((IsOpen && StorageMenuPatch.HandleStandaloneBackpackKeyboardNavigation()) || !keyDown) { return; } int appliedTier; if (IsOpen && StorageMenuPatch.IsStandaloneBackpackSearchFocused()) { UiProfiler.Event("hotkey", "consumed_by_search"); } else if (TryConsumeSelectedHotbarBackpackItem(out appliedTier)) { if (appliedTier >= 0) { ModLogger.Info($"Backpack tier {appliedTier} ({Configuration.BackpackTiers[appliedTier].Name}) applied; opening backpack."); } if (IsOpen) { Close(); } else { Open(); } } else if (!IsUnlocked) { ModLogger.Debug($"[BackpackUI] Hotkey ignored: no enabled backpack tier (equipped={_equippedTierIndex}, current={CurrentTierIndex})."); } else if (IsOpen) { Close(); } else { Open(); } } catch (Exception exception) { ModLogger.Error("Error toggling backpack", exception); } } private bool TryConsumeSelectedHotbarBackpackItem(out int appliedTier) { appliedTier = -1; try { PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { return false; } object obj = ReflectionUtils.TryGetFieldOrProperty(instance, "hotbarSlots"); if (obj == null) { return false; } int num = ReflectionUtils.TryGetListCount(obj); if (num <= 0) { return false; } int selectedHotbarIndex = GetSelectedHotbarIndex(instance); if (selectedHotbarIndex >= 0 && selectedHotbarIndex < num && TryConsumeBackpackItemFromSlot(instance, obj, selectedHotbarIndex, out appliedTier)) { return true; } for (int i = 0; i < num; i++) { if (TryConsumeBackpackItemFromSlot(instance, obj, i, out appliedTier)) { return true; } } List<object> list = ReflectionUtils.TryGetAllListLikeMembers(instance); foreach (object item in list) { if (item == obj) { continue; } int num2 = ReflectionUtils.TryGetListCount(item); for (int j = 0; j < num2; j++) { if (TryConsumeBackpackItemFromSlot(instance, item, j, out appliedTier)) { return true; } } } return false; } catch (Exception exception) { ModLogger.Error("TryConsumeSelectedHotbarBackpackItem", exception); return false; } } private bool TryConsumeBackpackItemFromSlot(object playerInventory, object slotsList, int index, out int appliedTier) { appliedTier = -1; object obj = ReflectionUtils.TryGetListItem(slotsList, index); if (obj == null) { return false; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "ItemInstance"); if (obj2 == null) { return false; } object obj3 = ReflectionUtils.TryGetFieldOrProperty(obj2, "Definition"); if (obj3 == null) { return false; } object obj4 = ReflectionUtils.TryGetFieldOrProperty(obj3, "ID") ?? ReflectionUtils.TryGetFieldOrProperty(obj3, "id"); string text = (obj4 as string) ?? obj4?.ToString(); if (string.IsNullOrEmpty(text) || !text.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return false; } if (!BackpackShopIntegration.IsBackpackTierPurchase(text, out var tierIndex) || tierIndex < 0) { return false; } ClearSlotItem(obj); RefreshInventoryUIAfterSlotChange(playerInventory, obj); if (tierIndex != _equippedTierIndex) { SetEquippedTierIndex(tierIndex); ApplyTierAfterPurchase(tierIndex); BackpackShopIntegration.RefreshBackpackListingsInAllShops(); appliedTier = tierIndex; } return true; } private static int GetSelectedHotbarIndex(object playerInventory) { if (playerInventory == null) { return -1; } string[] selectedHotbarIndexMemberNames = SelectedHotbarIndexMemberNames; foreach (string memberName in selectedHotbarIndexMemberNames) { object obj = ReflectionUtils.TryGetFieldOrProperty(playerInventory, memberName); if (obj != null) { if (obj is int num && num >= 0) { return num; } if (obj is byte result) { return result; } if (obj is short num2 && num2 >= 0) { return num2; } if (obj is long num3 && num3 >= 0 && num3 <= int.MaxValue) { return (int)num3; } } } return -1; } private static void ClearSlotItem(object slot) { if (slot == null) { return; } Type type = slot.GetType(); MethodInfo methodInfo = ReflectionUtils.GetMethod(type, "ClearStoredInstance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ReflectionUtils.GetMethod(type, "Clear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ReflectionUtils.GetMethod(type, "ClearSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo != null) { try { methodInfo.Invoke(slot, null); return; } catch { } } ReflectionUtils.TrySetFieldOrProperty(slot, "ItemInstance", null); } private static void RefreshInventoryUIAfterSlotChange(object playerInventory, object slotThatChanged) { if (slotThatChanged != null) { ReflectionUtils.TryInvokeParameterlessCallback(slotThatChanged, "onItemDataChanged", "OnItemDataChanged", "ItemDataChanged"); } if (playerInventory != null) { ReflectionUtils.TryInvokeParameterlessCallback(playerInventory, "Refresh", "RefreshUI", "UpdateDisplay", "OnInventoryChanged", "NotifySlotsChanged", "Rebuild"); } } private void ApplyCurrentTier(int tierIdx) { if (tierIdx >= 0) { int num = Configuration.Instance.TierSlotCounts[tierIdx]; if (_storage.SlotCount != num || _storage.ItemSlots.Count != num) { ModLogger.Info($"Backpack upgraded to {Configuration.BackpackTiers[tierIdx].Name} ({num} slots)."); UpdateSize(num); } } } public void ApplyTierAfterPurchase(int tierIdx) { if (tierIdx >= 0) { _lastTierIndex = tierIdx; ApplyCurrentTier(tierIdx); } } public void EnsureCorrectTierApplied() { _lastTierIndex = CurrentTierIndex; if (!((Object)(object)_storage == (Object)null) && _lastTierIndex >= 0) { ApplyCurrentTier(_lastTierIndex); } } public void SetBackpackEnabled(bool state) { if (!state) { Close(); } _backpackEnabled = state; } public void Open() { using (UiProfiler.Measure("standalone", "open_request", $"enabled={_backpackEnabled};unlocked={IsUnlocked};tier={CurrentTierIndex}")) { if (!_backpackEnabled) { UiProfiler.Event("standalone", "open_blocked", "reason=disabled"); ModLogger.Debug("Backpack open blocked: backpack disabled."); return; } if (!IsUnlocked) { UiProfiler.Event("standalone", "open_blocked", "reason=locked"); ModLogger.Debug($"Backpack open blocked: not unlocked (CurrentTierIndex={CurrentTierIndex}, EquippedTier={_equippedTierIndex}). Purchase a tier at the Hardware Store."); return; } if ((Object)(object)_storage == (Object)null) { UiProfiler.Event("standalone", "open_blocked", "reason=no_storage"); ModLogger.Warn("Backpack open blocked: no storage entity."); return; } ManagementClipboard instance = Singleton<ManagementClipboard>.Instance; if ((Object)(object)instance != (Object)null && instance.IsEquipped) { UiProfiler.Event("standalone", "open_blocked", "reason=clipboard"); ModLogger.Debug("[BackpackUI] Open blocked: management clipboard is equipped."); return; } StorageMenu instance2 = Singleton<StorageMenu>.Instance; if ((Object)(object)instance2 == (Object)null) { UiProfiler.Event("standalone", "open_blocked", "reason=no_storage_menu"); ModLogger.Warn("[BackpackUI] Open blocked: StorageMenu is not available yet."); return; } if (instance2.IsOpen) { UiProfiler.Event("standalone", "open_blocked", "reason=other_storage_open"); ModLogger.Debug("[BackpackUI] Open blocked: another storage menu is already open."); return; } if ((Object)(object)PlayerSingleton<Phone>.Instance != (Object)null && PlayerSingleton<Phone>.Instance.IsOpen) { UiProfiler.Event("standalone", "open_blocked", "reason=phone"); ModLogger.Debug("[BackpackUI] Open blocked: phone is open."); return; } if (CameraLockedStateHelper.IsCameraLockedByUI()) { UiProfiler.Event("standalone", "open_blocked", "reason=camera_locked_ui"); ModLogger.Debug("Backpack blocked: player is in camera-locked UI (TV, ATM, dialogue, vehicle, etc.)."); return; } _openTitle = CurrentTier?.Name ?? "Backpack"; UiProfiler.Event("standalone", "opening", $"title={_openTitle};slots={_storage.ItemSlots.Count}"); ModLogger.Info($"[BackpackUI] PlayerBackpack.Open -> StorageMenu.Open: title='{_openTitle}', slots={_storage.ItemSlots.Count}."); BackpackStateSyncManager.BeginLocalBackpackEdit(); OpenStorageMenu(instance2, (IItemSlotOwner)(object)_storage, _openTitle, string.Empty); EnsureMonoStandaloneCameraLock(); _storage.SendAccessor(((NetworkBehaviour)Player.Local).NetworkObject); } } private static void OpenStorageMenu(StorageMenu storageMenu, IItemSlotOwner owner, string title, string subtitle) { if ((Object)(object)storageMenu == (Object)null || owner == null) { return; } PrewarmStorageMenuOpenMethods(storageMenu); MethodInfo[] cachedStorageMenuOpenMethods = _cachedStorageMenuOpenMethods; foreach (MethodInfo methodInfo in cachedStorageMenuOpenMethods) { if (methodInfo.Name != "Open") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); try { if (parameters.Length == 4 && parameters[0].ParameterType.IsInstanceOfType(owner) && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(string)) { methodInfo.Invoke(storageMenu, new object[4] { owner, title, subtitle, null }); return; } if (parameters.Length == 3 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType.IsInstanceOfType(owner)) { methodInfo.Invoke(storageMenu, new object[3] { title, subtitle, owner }); return; } } catch (Exception ex) { Exception exception = ((ex is TargetInvocationException { InnerException: not null } ex2) ? ex2.InnerException : ex); ModLogger.Error("Failed to open backpack storage menu", exception); return; } } ModLogger.Error("Failed to find a compatible StorageMenu.Open overload for the backpack."); } private static void PrewarmStorageMenuOpenMethods(StorageMenu storageMenu) { using (UiProfiler.Measure("prewarm", "storage_menu_open_reflection", "type=" + ((object)storageMenu)?.GetType().FullName)) { if ((Object)(object)storageMenu == (Object)null) { return; } Type type = ((object)storageMenu).GetType(); if (_cachedStorageMenuType == type && _cachedStorageMenuOpenMethods.Length != 0) { return; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); List<MethodInfo> list = new List<MethodInfo>(); for (int i = 0; i < methods.Length; i++) { if (methods[i].Name == "Open") { list.Add(methods[i]); } } _cachedStorageMenuType = type; _cachedStorageMenuOpenMethods = list.ToArray(); } } private static void PrewarmPlayerInventoryReflection() { using (UiProfiler.Measure("prewarm", "player_inventory_reflection")) { PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { return; } Type type = ((object)instance).GetType(); ReflectionUtils.PrewarmReadableMembers(type, "hotbarSlots"); ReflectionUtils.PrewarmReadableMembers(type, SelectedHotbarIndexMemberNames); ReflectionUtils.PrewarmListLikeMembers(type); object list = ReflectionUtils.TryGetFieldOrProperty(instance, "hotbarSlots"); int num = ReflectionUtils.TryGetListCount(list); for (int i = 0; i < num; i++) { object obj = ReflectionUtils.TryGetListItem(list, i); if (obj == null) { continue; } ReflectionUtils.PrewarmReadableMembers(obj.GetType(), "ItemInstance"); object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "ItemInstance"); if (obj2 != null) { ReflectionUtils.PrewarmReadableMembers(obj2.GetType(), "Definition"); object obj3 = ReflectionUtils.TryGetFieldOrProperty(obj2, "Definition"); if (obj3 != null) { ReflectionUtils.PrewarmReadableMembers(obj3.GetType(), "ID", "id"); } } } } } private void EnsureMonoStandaloneCameraLock() { if (IsOpen && PlayerSingleton<PlayerCamera>.InstanceExists) { PlayerCamera instance = PlayerSingleton<PlayerCamera>.Instance; if (!((Object)(object)instance == (Object)null) && instance.CanLook) { instance.SetCanLook(false); _monoCameraLockFallbackActive = true; UiProfiler.Event("standalone", "mono_camera_lock_fallback", "phase=acquired"); ModLogger.Debug("[BackpackUI] Applied Mono camera-look fallback for the standalone backpack."); } } } internal void ReleaseMonoStandaloneCameraLock() { if (!_monoCameraLockFallbackActive) { return; } _monoCameraLockFallbackActive = false; if (PlayerSingleton<PlayerCamera>.InstanceExists) { PlayerCamera instance = PlayerSingleton<PlayerCamera>.Instance; if ((Object)(object)instance != (Object)null && !instance.CanLook) { instance.SetCanLook(true); } } UiProfiler.Event("standalone", "mono_camera_lock_fallback", "phase=released"); ModLogger.Debug("[BackpackUI] Released Mono camera-look fallback for the standalone backpack."); } public void Close() { using (UiProfiler.Measure("standalone", "close_request", $"open={IsOpen}")) { if (_backpackEnabled && IsOpen) { UiProfiler.Event("standalone", "closing"); Singleton<StorageMenu>.Instance.Close(); _storage.SendAccessor((NetworkObject)null); } } } public bool ContainsItemsOfInterest(EStealthLevel maxStealthLevel) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _storage.ItemSlots.Count; i++) { ItemSlot val = _storage.ItemSlots[i]; if (((val != null) ? val.ItemInstance : null) == null) { continue; } ItemInstance itemInstance = val.ItemInstance; ProductItemInstance val2 = (ProductItemInstance)(object)((itemInstance is ProductItemInstance) ? itemInstance : null); if (val2 == null) { string a = (ReflectionUtils.TryGetFieldOrProperty(val.ItemInstance.Definition, "legalStatus") ?? ReflectionUtils.TryGetFieldOrProperty(val.ItemInstance.Definition, "LegalStatus"))?.ToString(); if (!string.Equals(a, "Legal", StringComparison.Ordinal)) { return true; } } else if ((Object)(object)val2.AppliedPackaging == (Object)null || val2.AppliedPackaging.StealthLevel <= maxStealthLevel) { return true; } } return false; } public void Upgrade(int slotCount) { if (slotCount >= 1 && !((Object)(object)_storage == (Object)null)) { if (_storage.SlotCount > int.MaxValue - slotCount) { ModLogger.Warn("Cannot upgrade backpack: requested capacity exceeds the supported integer range."); } else { UpdateSize(_storage.SlotCount + slotCount); } } } public void Downgrade(int slotCount, bool force = false) { if (slotCount < 1) { return; } if (!force && slotCount >= _storage.SlotCount) { ModLogger.Warn("Cannot downgrade backpack to zero slots. A minimum of one must remain."); return; } int num = _storage.SlotCount - slotCount; if (num < 1) { num = 1; } if (force) { UpdateSize(num); return; } bool flag = true; List<ItemSlot> range = _storage.ItemSlots.GetRange(num, _storage.SlotCount - num); for (int i = 0; i < range.Count; i++) { List<ItemSlot> list = range; ItemSlot val = list[new Index(i).GetOffset(list.Count)]; if (((val != null) ? val.ItemInstance : null) != null) { ModLogger.Warn("Downgrading backpack will remove item: " + ((Object)val.ItemInstance.Definition).name); flag = false; } } if (!flag) { ModLogger.Warn("Cannot downgrade backpack due to items present in removed slots."); } else { UpdateSize(num); } } private void UpdateSize(int newSize) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown newSize = Math.Max(1, newSize); _storage.SlotCount = newSize; StorageEntity storage = _storage; if (1 == 0) { } int displayRowCount = ((newSize <= 20) ? ((int)Math.Ceiling((double)newSize / 5.0)) : ((newSize > 80) ? ((int)Math.Ceiling((double)newSize / 16.0)) : ((int)Math.Ceiling((double)newSize / 10.0)))); if (1 == 0) { } storage.DisplayRowCount = displayRowCount; if (_storage.ItemSlots.Count > newSize) { _storage.ItemSlots.RemoveRange(newSize, _storage.ItemSlots.Count - newSize); return; } for (int i = _storage.ItemSlots.Count; i < newSize; i++) { ItemSlot val = new ItemSlot(); int count = _storage.ItemSlots.Count; val.onItemDataChanged = (Action)Delegate.Combine(val.onItemDataChanged, new Action(_storage.ContentsChanged)); val.SetSlotOwner((IItemSlotOwner)(object)_storage); if (_storage.ItemSlots.Count == count) { _storage.ItemSlots.Add(val); } } } private void OnStartClient(bool isOwner) { if (!isOwner) { ModLogger.Info("Destroying non-local player backpack on: " + ((Object)this).name); Object.Destroy((Object)(object)this); return; } if ((Object)(object)Instance != (Object)null) { ModLogger.Warn("Multiple instances of " + ((Object)this).name + " exist. Keeping prior instance reference."); Object.Destroy((Object)(object)this); return; } Instance = this; CameraLockedStateHelper.RefreshSceneCache(); PrewarmStorageMenuOpenMethods(Singleton<StorageMenu>.Instance); PrewarmPlayerInventoryReflection(); StorageMenuPatch.PrewarmStandaloneAssets(); StorageMenuPatch.PrewarmStandaloneSurfaces(Singleton<StorageMenu>.Instance); } private bool IsOwnedByLocalPlayer() { Player componentInParent = ((Component)this).gameObject.GetComponentInParent<Player>(); if ((Object)(object)componentInParent == (Object)null) { ModLogger.Debug("[BackpackUI] Ownership check could not find Player; retaining local component fallback."); return true; } return ((NetworkBehaviour)componentInParent).IsOwner; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } } namespace PackRat.Storage { public static class BackpackFavorites { private static readonly HashSet<string> FavoriteDefinitionIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static bool IsFavorite(string definitionId) { return !string.IsNullOrWhiteSpace(definitionId) && FavoriteDefinitionIds.Contains(definitionId.Trim()); } public static bool Toggle(string definitionId) { if (string.IsNullOrWhiteSpace(definitionId)) { return false; } string item = definitionId.Trim(); if (!FavoriteDefinitionIds.Add(item)) { FavoriteDefinitionIds.Remove(item); return false; } return true; } public static void SetFavorites(IEnumerable<string> definitionIds) { FavoriteDefinitionIds.Clear(); if (definitionIds == null) { return; } foreach (string definitionId in definitionIds) { if (!string.IsNullOrWhiteSpace(definitionId)) { FavoriteDefinitionIds.Add(definitionId.Trim()); } } } public static List<string> GetSavedFavoriteIds() { return FavoriteDefinitionIds.OrderBy<string, string>((string id) => id, StringComparer.OrdinalIgnoreCase).ToList(); } } public sealed class BackpackSaveData { public string Contents { get; set; } public int EquippedTierIndex { get; set; } = -1; public int HighestPurchasedTierIndex { get; set; } = -1; public List<string> FavoriteDefinitionIds { get; set; } = new List<string>(); } } namespace PackRat.Shops { public static class BackpackShopIntegration { public const string BackpackItemIdPrefix = "PackRat_Backpack_Tier_"; private const string HardwareStoreName = "Hardware Store"; private static readonly string[] TierDescriptions = new string[5] { "A basic 8-slot backpack. Good for starting out.", "A compact 16-slot pack. Stays under the radar.", "24 slots. Police may search this size and above.", "32 slots. Sturdy and roomy; draws more attention.", "The largest default option starts at 40 slots. Capacity can be configured." }; private static readonly HashSet<int> _shopsIntegrated = new HashSet<int>(); private static StorableItemDefinition _safeBackpackTemplate; private static bool _loggedSafeTemplateUnavailable; private static bool _loggedBackpackDefinitionCreationFailure; private static bool _loggedListingIntegrationFailureSummary; private static bool _waitForIntegrationRunning; public static void RunWhenReady() { if (!_waitForIntegrationRunning) { _waitForIntegrationRunning = true; MelonCoroutines.Start(WaitAndIntegrate()); } } public static bool TryAddBackpackListingsToShop(ShopInterface shop) { if ((Object)(object)shop == (Object)null) { return false; } if (!TryMatchHardwareStore(shop, out var _)) { return false; } int instanceID = ((Object)shop).GetInstanceID(); bool flag = _shopsIntegrated.Add(instanceID); if (AddBackpackListings(shop)) { if (flag) { ModLogger.Info($"BackpackShopIntegration: Added backpack tier listings to Hardware Store (instance {instanceID})."); } return true; } ModLogger.Warn("BackpackShopIntegration: Hardware Store found but adding listings failed (see tier warnings above)."); return false; } private static IEnumerator WaitAndIntegrate() { try { int i = 0; while (true) { if (i < 60) { yield return (object)new WaitForSeconds(0.5f); if (!AddToAllHardwareStoresInScene()) { i++; continue; } break; } LogHardwareStoreNotFound(); break; } } finally { _waitForIntegrationRunning = false; } } private static bool AddToAllHardwareStoresInScene() { bool flag = false; try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null && TryMatchHardwareStore(item, out var shop)) { flag |= TryAddBackpackListingsToShop(shop); } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && TryMatchHardwareStore(array[i], out var shop2)) { flag |= TryAddBackpackListingsToShop(shop2); } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddToAllHardwareStoresInScene", exception); } return flag; } private static void LogHardwareStoreNotFound() { List<string> list = new List<string>(); try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null && value is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null) { string shopDisplayName = GetShopDisplayName(item); if (!string.IsNullOrEmpty(shopDisplayName) && !list.Contains(shopDisplayName)) { list.Add(shopDisplayName); } } } } } if (list.Count == 0) { ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { string shopDisplayName2 = GetShopDisplayName(array[i]); if (!string.IsNullOrEmpty(shopDisplayName2) && !list.Contains(shopDisplayName2)) { list.Add(shopDisplayName2); } } } } } catch { } if (list.Count > 0) { ModLogger.Warn("BackpackShopIntegration: Hardware Store not found after 30s. Shop names in game: [" + string.Join(", ", list) + "]. If your hardware store uses a different name, we can add a match for it."); } else { ModLogger.Warn("BackpackShopIntegration: Hardware Store not found after 30s (no ShopInterface instances with a name found). Shops may load later when you open them."); } } private static bool TryGetHardwareStore(out ShopInterface shop) { shop = null; try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null && value is IEnumerable enumerable) { int num = 0; foreach (object item in enumerable) { if (item != null) { num++; if (TryMatchHardwareStore(item, out shop)) { return true; } } } if (num == 0) { ModLogger.Debug("BackpackShopIntegration: AllShops list is empty."); } else { ModLogger.Debug($"BackpackShopIntegration: AllShops has {num} entries; no Hardware Store name matched."); } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null && array.Length != 0) { foreach (ShopInterface val in array) { if (!((Object)(object)val == (Object)null) && TryMatchHardwareStore(val, out shop)) { return true; } } ModLogger.Debug($"BackpackShopIntegration: Found {array.Length} ShopInterface in scene; none matched Hardware Store."); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: Error finding Hardware Store", exception); } return false; } private static bool TryMatchHardwareStore(object shopObj, out ShopInterface shop) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown shop = null; if (shopObj == null) { return false; } string shopDisplayName = GetShopDisplayName(shopObj); if (string.IsNullOrEmpty(shopDisplayName)) { return false; } if (!string.Equals(shopDisplayName, "Hardware Store", StringComparison.OrdinalIgnoreCase) && shopDisplayName.IndexOf("Hardware", StringComparison.OrdinalIgnoreCase) < 0) { return false; } try { shop = (ShopInterface)shopObj; return true; } catch { return false; } } private static string GetShopDisplayName(object shopObj) { if (shopObj == null) { return null; } string[] array = new string[5] { "ShopName", "shopName", "Name", "name", "DisplayName" }; foreach (string memberName in array) { object obj = ReflectionUtils.TryGetFieldOrProperty(shopObj, memberName); if (obj is string text && !string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } return null; } private static bool AddBackpackListings(ShopInterface shop) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)shop == (Object)null) { return false; } if (shop.Listings == null) { ModLogger.Warn("BackpackShopIntegration: Shop.Listings is null; cannot add backpack listings."); return false; } Configuration instance = Configuration.Instance; if (!LevelManagerPatch.TryGetFallbackIcon(out var texture, out var sprite)) { texture = new Texture2D(2, 2); texture.SetPixel(0, 0, Color.white); texture.SetPixel(1, 0, Color.white); texture.SetPixel(0, 1, Color.white); texture.SetPixel(1, 1, Color.white); texture.Apply(); sprite = Sprite.Create(texture, new Rect(0f, 0f, 2f, 2f), new Vector2(0.5f, 0.5f)); ModLogger.Warn("BackpackShopIntegration: Fallback icon resource missing; using generated placeholder icon."); } int num = (((Object)(object)PlayerBackpack.Instance != (Object)null) ? PlayerBackpack.Instance.EquippedTierIndex : (-1)); bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; int num2 = 0; int num3 = 0; int num4 = 0; for (int i = 0; i < Configuration.BackpackTiers.Length; i++) { if (!instance.TierEnabled[i]) { RemoveTierListingFromShop(shop, "PackRat_Backpack_Tier_" + i); continue; } flag2 = true; string itemId = "PackRat_Backpack_Tier_" + i; Sprite tierSprite = LevelManagerPatch.GetTierSprite(i, sprite, texture); if (i <= num) { RemoveTierListingFromShop(shop, itemId); continue; } flag3 = true; if (ShopHasItem(shop, itemId)) { StorableItemDefinition registeredBackpackDefinition = GetRegisteredBackpackDefinition(itemId); if ((Object)(object)registeredBackpackDefinition != (Object)null) { ConfigureBackpackTierDefinition(registeredBackpackDefinition, i, itemId, instance, tierSprite); } flag4 = true; continue; } StorableItemDefinition val = GetRegisteredBackpackDefinition(itemId); if ((Object)(object)val != (Object)null) { ConfigureBackpackTierDefinition(val, i, itemId, instance, tierSprite); } else { val = CreateBackpackTierDefinition(shop, i, itemId, instance, tierSprite); } if ((Object)(object)val == (Object)null) { num2++; } else if ((Object)(object)GetRegisteredBackpackDefinition(itemId) == (Object)null && !RegisterDefinition(val)) { num3++; ModLogger.Warn($"BackpackShopIntegration: Could not register definition for tier {i}."); } else if (!AddListingToShop(shop, val, sprite, texture)) { num4++; ModLogger.Warn($"BackpackShopIntegration: Could not add listing for tier {i}."); } else { flag = true; } } if (flag) { _loggedListingIntegrationFailureSummary = false; ModLogger.Info("BackpackShopIntegration: Added backpack tier listings to Hardware Store."); } if (!flag2 || !flag3 || flag4 || flag) { _loggedListingIntegrationFailureSummary = false; return true; } if (!_loggedListingIntegrationFailureSummary) { _loggedListingIntegrationFailureSummary = true; ModLogger.Warn($"BackpackShopIntegration: Eligible tiers found but none added. missingDef={num2}, registerFailed={num3}, listingFailed={num4}"); } return false; } public static void RefreshBackpackListingsInAllShops() { AddToAllHardwareStoresInScene(); } private static bool ShopHasItem(ShopInterface shop, string itemId) { try { if (shop.Listings == null) { return false; } foreach (ShopListing listing in shop.Listings) { if ((Object)(object)listing?.Item != (Object)null && ((BaseItemDefinition)listing.Item).ID == itemId) { return true; } } } catch { } return false; } private static StorableItemDefinition CreateBackpackTierDefinition(ShopInterface shop, int tierIndex, string itemId, Configuration cfg, Sprite iconSprite) { try { StorableItemDefinition val = CreateEmptyStorableItemDefinition(); if ((Object)(object)val == (Object)null) { val = CloneSafeBackpackTemplate(shop); } if ((Object)(object)val == (Object)null && !_loggedBackpackDefinitionCreationFailure) { _loggedBackpackDefinitionCreationFailure = true; ModLogger.Warn("BackpackShopIntegration: Could not create a backpack item definition. Skipping backpack listings until a valid template can be created."); } if ((Object)(object)val == (Object)null) { return null; } ConfigureBackpackTierDefinition(val, tierIndex, itemId, cfg, iconSprite); return val; } catch (Exception exception) { ModLogger.Error($"BackpackShopIntegration: CreateBackpackTierDefinition tier {tierIndex}", exception); return null; } } private static void ConfigureBackpackTierDefinition(StorableItemDefinition def, int tierIndex, string itemId, Configuration cfg, Sprite iconSprite) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)def == (Object)null) { return; } ApplyBackpackDefinitionDefaults(def); if (!ReflectionUtils.TrySetFieldOrProperty(def, "ID", itemId)) { ReflectionUtils.TrySetFieldOrProperty(def, "id", itemId); } string name = Configuration.BackpackTiers[tierIndex].Name; if (!ReflectionUtils.TrySetFieldOrProperty(def, "Name", name)) { ReflectionUtils.TrySetFieldOrProperty(def, "name", name); } if ((Object)(object)iconSprite != (Object)null) { string[] array = new string[7] { "Icon", "icon", "Sprite", "sprite", "ItemIcon", "itemIcon", "DisplayIcon" }; foreach (string memberName in array) { if (ReflectionUtils.TrySetFieldOrProperty(def, memberName, iconSprite)) { break; } } } float num = ((tierIndex < cfg.TierPrices.Length) ? cfg.TierPrices[tierIndex] : (25f + (float)tierIndex * 50f)); ReflectionUtils.TrySetFieldOrProperty(def, "BasePurchasePrice", num); FullRank val = ((tierIndex < cfg.TierUnlockRanks.Length) ? cfg.TierUnlockRanks[tierIndex] : Configuration.BackpackTiers[tierIndex].DefaultUnlockRank); ReflectionUtils.TrySetFieldOrProperty(def, "RequiredRank", val); ReflectionUtils.TrySetFieldOrProperty(def, "RequiresLevelToPurchase", true); string value = ((tierIndex < TierDescriptions.Length) ? TierDescriptions[tierIndex] : null); if (string.IsNullOrEmpty(value)) { return; } string[] array2 = new string[8] { "Description", "description", "TooltipText", "tooltipText", "FlavorText", "flavorText", "ItemDescription", "itemDescription" }; foreach (string memberName2 in array2) { if (ReflectionUtils.TrySetFieldOrProperty(def, memberName2, value)) { break; } } } private static StorableItemDefinition GetRegisteredBackpackDefinition(string itemId) { if (string.IsNullOrWhiteSpace(itemId)) { return null; } try { ItemDefinition registeredItemDefinition = GetRegisteredItemDefinition(itemId); if ((Object)(object)registeredItemDefinition == (Object)null) { return null; } return (StorableItemDefinition)(object)((registeredItemDefinition is StorableItemDefinition) ? registeredItemDefinition : null); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: GetRegisteredBackpackDefinition", exception); return null; } } private static ItemDefinition GetRegisteredItemDefinition(string itemId) { if (string.IsNullOrWhiteSpace(itemId)) { return null; } try { if (Registry.ItemExists(itemId)) { return Registry.GetItem(itemId); } } catch { } try { Registry instance = Singleton<Registry>.Instance; if ((Object)(object)instance == (Object)null) { return null; } ItemDefinition val = FindDefinitionInRegistryList(instance.ItemRegistry, itemId); if ((Object)(object)val != (Object)null) { return val; } return FindDefinitionInRegistryList(instance.ItemsAddedAtRuntime, itemId); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: GetRegisteredItemDefinition", exception); return null; } } private static ItemDefinition FindDefinitionInRegistryList(IEnumerable registryList, string itemId) { if (registryList == null || string.IsNullOrWhiteSpace(itemId)) { return null; } foreach (object registry in registryList) { if (registry != null) { string a = ReflectionUtils.TryGetFieldOrProperty(registry, "ID") as string; if (string.Equals(a, itemId, StringComparison.OrdinalIgnoreCase)) { object obj = ReflectionUtils.TryGetFieldOrProperty(registry, "Definition"); return (ItemDefinition)((obj is ItemDefinition) ? obj : null); } } } return null; } private static StorableItemDefinition CloneTemplateStorableItemDefinition(ShopInterface shop) { if (shop?.Listings == null) { return null; } try { foreach (ShopListing listing in shop.Listings) { if (!((Object)(object)listing?.Item == (Object)null)) { StorableItemDefinition item = listing.Item; if (!((Object)(object)item == (Object)null)) { return Object.Instantiate<StorableItemDefinition>(item); } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CloneTemplateStorableItemDefinition", exception); } return null; } private static StorableItemDefinition CloneSafeBackpackTemplate(ShopInterface shop) { if ((Object)(object)_safeBackpackTemplate == (Object)null) { _safeBackpackTemplate = CreateSafeBackpackTemplate(shop); if ((Object)(object)_safeBackpackTemplate == (Object)null) { if (!_loggedSafeTemplateUnavailable) { _loggedSafeTemplateUnavailable = true; ModLogger.Warn("BackpackShopIntegration: Safe backpack template is unavailable in IL2CPP. Backpack listings will retry once the shop can provide a valid storable-item template."); } return null; } } try { return Object.Instantiate<StorableItemDefinition>(_safeBackpackTemplate); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CloneSafeBackpackTemplate", exception); return null; } } public static StorableItemDefinition CreateSafeBackpackTemplate(ShopInterface shop = null) { if ((Object)(object)_safeBackpackTemplate != (Object)null) { return _safeBackpackTemplate; } try { _safeBackpackTemplate = CreateEmptyStorableItemDefinition(); if ((Object)(object)_safeBackpackTemplate == (Object)null && (Object)(object)shop != (Object)null) { _safeBackpackTemplate = CloneTemplateStorableItemDefinition(shop); } if ((Object)(object)_safeBackpackTemplate == (Object)null) { if (!_loggedSafeTemplateUnavailable) { _loggedSafeTemplateUnavailable = true; ModLogger.Warn("BackpackShopIntegration: Could not create safe backpack template instance yet."); } return null; } ApplyBackpackDefinitionDefaults(_safeBackpackTemplate); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "ID", "PackRat_Template"); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "Name", "Backpack"); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "BasePurchasePrice", 0f); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "RequiredRank", 0); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "RequiresLevelToPurchase", false); ReflectionUtils.TrySetFieldOrProperty(_safeBackpackTemplate, "Description", string.Empty); _loggedSafeTemplateUnavailable = false; _loggedBackpackDefinitionCreationFailure = false; _loggedListingIntegrationFailureSummary = false; ModLogger.Info("BackpackShopIntegration: Safe backpack template created successfully."); return _safeBackpackTemplate; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateSafeBackpackTemplate", exception); return null; } } private static void ApplyBackpackDefinitionDefaults(StorableItemDefinition def) { if (!((Object)(object)def == (Object)null)) { ReflectionUtils.TrySetFieldOrProperty(def, "Description", string.Empty); ReflectionUtils.TrySetEnumFieldOrProperty(def, "Category", "Storage"); ReflectionUtils.TrySetFieldOrProperty(def, "StoredItem", null); ReflectionUtils.TrySetFieldOrProperty(def, "StationItem", null); ReflectionUtils.TrySetFieldOrProperty(def, "Equippable", null); ReflectionUtils.TrySetFieldOrProperty(def, "CustomItemUI", null); ReflectionUtils.TrySetFieldOrProperty(def, "CustomInfoContent", null); ReflectionUtils.TrySetEnumFieldOrProperty(def, "legalStatus", "Legal"); ReflectionUtils.TrySetFieldOrProperty(def, "ResellMultiplier", 0.5f); ReflectionUtils.TrySetFieldOrProperty(def, "PickpocketDifficultyMultiplier", 1f); ReflectionUtils.TrySetFieldOrProperty(def, "CombatUtility", 0f); } } private static StorableItemDefinition CreateEmptyStorableItemDefinition() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown try { return (StorableItemDefinition)ScriptableObject.CreateInstance(typeof(StorableItemDefinition)); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateEmptyStorableItemDefinition", exception); return null; } } private static bool RegisterDefinition(StorableItemDefinition def) { try { if ((Object)(object)def == (Object)null || string.IsNullOrWhiteSpace(((BaseItemDefinition)def).ID)) { return false; } if ((Object)(object)GetRegisteredItemDefinition(((BaseItemDefinition)def).ID) != (Object)null) { ModLogger.Debug("BackpackShopIntegration: Definition '" + ((BaseItemDefinition)def).ID + "' is already registered; reusing existing definition."); return true; } Registry instance = Singleton<Registry>.Instance; if ((Object)(object)instance == (Object)null) { return false; } MethodInfo method = ((object)instance).GetType().GetMethod("AddToRegistry", BindingFlags.Instance | BindingFlags.Public); if (method == null) { return false; } method.Invoke(instance, new object[1] { def }); return true; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RegisterDefinition", exception); return false; } } private static bool AddListingToShop(ShopInterface shop, StorableItemDefinition def, Sprite fallbackSprite, Texture2D fallbackTexture) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_001b: Expected O, but got Unknown try { ShopListing val = new ShopListing { Item = def, name = ((BaseItemDefinition)def).Name }; shop.Listings.Add(val); val.Initialize(shop); if (!TryInvokeShopMethod(shop, "CreateListingUI", val)) { Sprite tierSprite = LevelManagerPatch.GetTierSprite(ParseTierFromItemId(((BaseItemDefinition)def).ID), fallbackSprite, fallbackTexture); CreateListingUI(shop, val, tierSprite); } TryInvokeShopMethod(shop, "RefreshUnlockStatus"); TryInvokeShopMethod(shop, "RefreshShownItems"); return true; } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddListingToShop", exception); return false; } } private static bool TryInvokeShopMethod(ShopInterface shop, string methodName, params object[] args) { try { if ((Object)(object)shop == (Object)null || string.IsNullOrEmpty(methodName)) { return false; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo[] methods = ((object)shop).GetType().GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal)) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (((args != null) ? args.Length : 0) == parameters.Length) { methodInfo.Invoke(shop, args); return true; } } } } catch (Exception ex) { ModLogger.Debug("BackpackShopIntegration: " + methodName + " invoke failed: " + ex.Message); } return false; } private static int ParseTierFromItemId(string itemId) { if (string.IsNullOrEmpty(itemId) || !itemId.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return -1; } string s = itemId.Substring("PackRat_Backpack_Tier_".Length); int result; return int.TryParse(s, out result) ? result : (-1); } private static void CreateListingUI(ShopInterface shop, ShopListing listing, Sprite iconSprite) { try { object obj = ReflectionUtils.TryGetFieldOrProperty(shop, "ListingUIPrefab"); object obj2 = ReflectionUtils.TryGetFieldOrProperty(shop, "ListingContainer"); if (obj == null || obj2 == null) { ModLogger.Warn("BackpackShopIntegration: Shop missing ListingUIPrefab or ListingContainer."); return; } object obj3 = ((obj is Component) ? obj : null); GameObject val = ((obj3 != null) ? ((Component)obj3).gameObject : null); Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } GameObject val3 = Object.Instantiate<GameObject>(val, val2); ListingUI component = val3.GetComponent<ListingUI>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val3); return; } component.Initialize(listing); if ((Object)(object)iconSprite != (Object)null && (Object)(object)component.Icon != (Object)null) { component.Icon.sprite = iconSprite; } BindListingUIEvents(shop, component); AddToListingUICollection(shop, component); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: CreateListingUI", exception); } } private static void BindListingUIEvents(ShopInterface shop, ListingUI listingUI) { try { Type typeFromHandle = typeof(ShopInterface); MethodInfo dropdownClicked = typeFromHandle.GetMethod("DropdownClicked", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo entryHovered = typeFromHandle.GetMethod("EntryHovered", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo entryUnhovered = typeFromHandle.GetMethod("EntryUnhovered", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); listingUI.onAddItem = (Action)Delegate.Combine(listingUI.onAddItem, (Action)delegate { shop.AddItem(listingUI); }); listingUI.onRemoveItem = (Action)Delegate.Combine(listingUI.onRemoveItem, (Action)delegate { shop.RemoveItem(listingUI); }); if (dropdownClicked != null) { listingUI.onDropdownClicked = (Action)Delegate.Combine(listingUI.onDropdownClicked, (Action)delegate { dropdownClicked.Invoke(shop, new object[1] { listingUI }); }); } if (entryHovered != null) { listingUI.hoverStart = (Action)Delegate.Combine(listingUI.hoverStart, (Action)delegate { entryHovered.Invoke(shop, new object[1] { listingUI }); }); } if (entryUnhovered != null) { listingUI.hoverEnd = (Action)Delegate.Combine(listingUI.hoverEnd, (Action)delegate { entryUnhovered.Invoke(shop, null); }); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: BindListingUIEvents", exception); } } private static void InvokeListingAdd(ShopInterface shop, ListingUI listingUI) { if (!TryInvokeShopMethod(shop, "AddItem", listingUI) && !TryInvokeShopMethod(shop, "ListingClicked", listingUI)) { ModLogger.Warn("BackpackShopIntegration: Shop listing add handler was not found."); } } private static void AddToListingUICollection(ShopInterface shop, ListingUI listingUI) { try { FieldInfo field = typeof(ShopInterface).GetField("listingUI", BindingFlags.Instance | BindingFlags.NonPublic); if (!(field == null)) { object value = field.GetValue(shop); value?.GetType().GetMethod("Add", new Type[1] { typeof(ListingUI) })?.Invoke(value, new object[1] { listingUI }); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: AddToListingUICollection", exception); } } public static bool IsBackpackTierPurchase(string itemId, out int tierIndex) { tierIndex = ParseTierFromItemId(itemId ?? ""); return tierIndex >= 0; } public static void RemoveTierListingFromAllShops(int tierIndex) { if (tierIndex < 0 || tierIndex >= Configuration.BackpackTiers.Length) { return; } string itemId = "PackRat_Backpack_Tier_" + tierIndex; List<int> list = new List<int>(_shopsIntegrated); foreach (int item in list) { ShopInterface val = FindShopByInstanceId(item); if ((Object)(object)val != (Object)null) { RemoveTierListingFromShop(val, itemId); } } } private static ShopInterface FindShopByInstanceId(int instanceId) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown try { Type typeFromHandle = typeof(ShopInterface); FieldInfo field = typeFromHandle.GetField("AllShops", BindingFlags.Static | BindingFlags.Public); if (field != null && field.GetValue(null) is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null) { object obj = ((item is Object) ? item : null); if (obj != null && ((Object)obj).GetInstanceID() == instanceId) { return (ShopInterface)item; } } } } ShopInterface[] array = Utils.FindObjectsOfTypeSafe<ShopInterface>(); if (array != null) { for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && ((Object)array[i]).GetInstanceID() == instanceId) { return array[i]; } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: FindShopByInstanceId", exception); } return null; } private static void RemoveTierListingFromShop(ShopInterface shop, string itemId) { if (shop?.Listings == null) { return; } try { object obj = null; foreach (ShopListing listing in shop.Listings) { if ((Object)(object)listing?.Item != (Object)null && ((BaseItemDefinition)listing.Item).ID == itemId) { obj = listing; break; } } if (obj != null) { RemoveListingFromList(shop.Listings, obj); RemoveAndDestroyListingUI(shop, obj); } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveTierListingFromShop", exception); } } private static int GetListCount(object list) { if (list == null) { return 0; } object obj = ReflectionUtils.TryGetFieldOrProperty(list, "Count") ?? ReflectionUtils.TryGetFieldOrProperty(list, "count"); if (obj is int result) { return result; } if (obj is long num) { return (int)num; } return 0; } private static void RemoveListingFromList(object list, object listing) { if (list == null || listing == null) { return; } try { list.GetType().GetMethod("Remove", new Type[1] { listing.GetType() })?.Invoke(list, new object[1] { listing }); } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveListingFromList", exception); } } private static void RemoveAndDestroyListingUI(ShopInterface shop, object listing) { if ((Object)(object)shop == (Object)null || listing == null) { return; } try { object obj = typeof(ShopInterface).GetField("listingUI", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(shop); if (obj == null) { return; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "Count") ?? ReflectionUtils.TryGetFieldOrProperty(obj, "count"); int num = ((obj2 is int num2) ? num2 : 0); if (obj2 is long num3) { num = (int)num3; } for (int num4 = num - 1; num4 >= 0; num4--) { object obj3 = (obj.GetType().GetMethod("get_Item", new Type[1] { typeof(int) }) ?? obj.GetType().GetMethod("Get", new Type[1] { typeof(int) }))?.Invoke(obj, new object[1] { num4 }); if (obj3 != null) { object obj4 = ReflectionUtils.TryGetFieldOrProperty(obj3, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(obj3, "listing"); if (obj4 == listing) { object obj5 = ((obj3 is Component) ? obj3 : null); GameObject val = ((obj5 != null) ? ((Component)obj5).gameObject : null); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } obj.GetType().GetMethod("RemoveAt", new Type[1] { typeof(int) })?.Invoke(obj, new object[1] { num4 }); break; } } } } catch (Exception exception) { ModLogger.Error("BackpackShopIntegration: RemoveAndDestroyListingUI", exception); } } } } namespace PackRat.Routing { public static class SmartRoutingManager { public static bool IsEnabled => Configuration.Instance.EnableSmartRouting; public static bool ShouldPreferBackpack(ItemInstance item) { if (item == null || !IsEnabled) { return false; } string itemCategory = StorageMenuPatch.GetItemCategory(item); if (1 == 0) { } bool result = itemCategory switch { "Products" => Configuration.Instance.RouteProducts, "Seeds" => Configuration.Instance.RouteSeeds, "Mixers" => Configuration.Instance.RouteMixers, "Reagents" => Configuration.Instance.RouteReagents, _ => false, }; if (1 == 0) { } return result; } } } namespace PackRat.Profiling { internal static class UiProfiler { private const int FlushEveryEvents = 64; private static object _sync; private static Stopwatch _sessionClock; private static StreamWriter _writer; private static bool _enabled; private static bool _initializationFailed; private static int _eventsSinceFlush; private static long _nextFlushMilliseconds; internal static bool IsEnabled => _enabled; internal static string OutputPath { get; private set; } internal static void ApplyEnabledState(bool enabled) { if (enabled) { if (!_enabled && !_initializationFailed) { Initialize(); } } else if (_enabled || _initializationFailed) { Shutdown(); } } internal static void Initialize() { if (_enabled || _initializationFailed) { return; } lock (GetSync()) { if (_enabled || _initializationFailed) { return; } try { string text = Path.Combine(Environment.CurrentDirectory, "UserData", "PackRatProfiler"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, string.Format("packrat-ui-{0:yyyyMMdd-HHmmssfff}-{1}.log", DateTime.UtcNow, "mono")); StreamWriter streamWriter = new StreamWriter(new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite, 32768, FileOptions.SequentialScan), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 32768); streamWriter.WriteLine("utc|session_us|frame|thread|kind|feature|operation|duration_us|gc0|gc1|gc2|details"); _sessionClock = Stopwatch.StartNew(); _writer = streamWriter; OutputPath = text2; _eventsSinceFlush = 0; _nextFlushMilliseconds = 1000L; _enabled = true; WriteLocked("state", "session", "start", 0L, 0, 0, 0, string.Format("version={0};runtime={1};os={2};cpu={3}", "2.1.0", "mono", Environment.OSVersion, Environment.ProcessorCount)); streamWriter.Flush(); ModLogger.Info("PackRat developer profiler trace: " + OutputPath); } catch (Exception exception) { _writer?.Dispose(); _writer = null; _sessionClock?.Stop(); _sessionClock = null; _enabled = false; _initializationFailed = true; ModLogger.Error("PackRat developer profiler could not create its trace file", exception); } } } internal static void Event(string feature, string operation, string details = null) { if (_enabled) { Write("state", feature, operation, 0L, 0, 0, 0, details); } } internal static UiProfileScope Measure(string feature, string operation, string details = null) { return _enabled ? new UiProfileScope(feature, operation, details) : default(UiProfileScope); } internal static void Shutdown() { if (!_enabled && !_initializationFailed) { return; } lock (GetSync()) { if (_writer != null) { WriteLocked("state", "session", "stop", 0L, 0, 0, 0, null); _writer.Flush(); _writer.Dispose(); _writer = null; } _enabled = false; _eventsSinceFlush = 0; _nextFlushMilliseconds = 0L; _sessionClock?.Stop(); _sessionClock = null; _initializationFailed = false; } } internal static void FlushIfDue() { if (!_enabled || _sessionClock == null) { return; } long elapsedMilliseconds = _sessionClock.ElapsedMilliseconds; if (elapsedMilliseconds < _nextFlushMilliseconds) { return; } lock (GetSync()) { if (_enabled && _sessionClock != null && _sessionClock.ElapsedMilliseconds >= _nextFlushMilliseconds) { _writer?.Flush(); _eventsSinceFlush = 0; _nextFlushMilliseconds = _sessionClock.ElapsedMilliseconds + 1000; } } } internal static void Complete(string feature, string operation, long startedTimestamp, int gc0, int gc1, int gc2, string details) { if (_enabled && startedTimestamp != 0) { long durationUs = (Stopwatch.GetTimestamp() - startedTimestamp) * 1000000 / Stopwatch.Frequency; Write("span", feature, operation, durationUs, GC.CollectionCount(0) - gc0, GC.CollectionCount(1) - gc1, GC.CollectionCount(2) - gc2, details); } } private static object GetSync() { if (_sync != null) { return _sync; } object value = new object(); return Interlocked.CompareExchange(ref _sync, value, null) ?? _sync; } private static void Write(string kind, string feature, string operation, long durationUs, int gc0, int gc1, int gc2, string details) { if (!_enabled) { return; } lock (GetSync()) { if (_enabled) { WriteLocked(kind, feature, operation, durationUs, gc0, gc1, gc2, details); if (++_eventsSinceFlush >= 64) { _writer?.Flush(); _eventsSinceFlush = 0; } } } } private static void WriteLocked(string kind, string feature, string operation, long durationUs, int gc0, int gc1, int gc2, string details) { if (_writer != null && _sessionClock != null) { _writer.Write(DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)); _writer.Write('|'); _writer.Write(_sessionClock.ElapsedTicks * 1000000 / Stopwatch.Frequency); _writer.Write('|'); _writer.Write(SafeFrameCount()); _writer.Write('|'); _writer.Write(Thread.CurrentThread.ManagedThreadId); _writer.Write('|'); _writer.Write(Sanitize(kind)); _writer.Write('|'); _writer.Write(Sanitize(feature)); _writer.Write('|'); _writer.Write(Sanitize(operation)); _writer.Write('|'); _writer.Write(durationUs); _writer.Write('|'); _writer.Write(gc0); _writer.Write('|'); _writer.Write(gc1); _writer.Write('|'); _writer.Write(gc2); _writer.Write('|'); _writer.WriteLine(Sanitize(details)); } } private static int SafeFrameCount() { try { return Time.frameCount; } catch { return -1; } } private static string Sanitize(string value) { return string.IsNullOrEmpty(value) ? string.Empty : value.Replace('|', '/').Replace('\r', ' ').Replace('\n', ' '); } } internal readonly struct UiProfileScope : IDisposable { private readonly string _feature; private readonly string _operation; private readonly string _details; private readonly long _startedTimestamp; private readonly int _gc0; private readonly int _gc1; private readonly int _gc2; internal UiProfileScope(string feature, string operation, string details) { _feature = feature; _operation = operation; _details = details; _startedTimestamp = Stopwatch.GetTimestamp(); _gc0 = GC.CollectionCount(0); _gc1 = GC.CollectionCount(1); _gc2 = GC.CollectionCount(2); } public void Dispose() { if (_startedTimestamp != 0) { UiProfiler.Complete(_feature, _operation, _startedTimestamp, _gc0, _gc1, _gc2, _details); } } } } namespace PackRat.Patches { [HarmonyPatch(typeof(ShopInterface))] public static class BackpackPurchasePatch { [HarmonyPatch("AddItem", new Type[] { typeof(ListingUI) })] [HarmonyPrefix] public static bool AddItem_Prefix(ShopInterface __instance, ListingUI ui) { try { if ((Object)(object)ui == (Object)null) { return true; } object obj = ReflectionUtils.TryGetFieldOrProperty(ui, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(ui, "listing"); if (obj == null) { return true; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(obj, "Item"); StorableItemDefinition val = (StorableItemDefinition)((obj2 is StorableItemDefinition) ? obj2 : null); if (val == null || ((BaseItemDefinition)val).ID == null || !((BaseItemDefinition)val).ID.StartsWith("PackRat_Backpack_Tier_", StringComparison.Ordinal)) { return true; } if (!BackpackShopIntegration.IsBackpackTierPurchase(((BaseItemDefinition)val).ID, out var tierIndex)) { return true; } PlayerBackpack instance = PlayerBackpack.Instance; if ((Object)(object)instance != (Object)null && tierIndex <= instance.EquippedTierIndex) { ModLogger.Info($"Blocked purchase selection for backpack tier {tierIndex}; player already owns tier {instance.EquippedTierIndex} or better."); return false; } if ((ReflectionUtils.TryGetFieldOrProperty(obj, "QuantityInCart") ?? ReflectionUtils.TryGetFieldOrProperty(obj, "quantityInCart")) is int num && num >= 1) { ModLogger.Debug($"[Shop] Blocked duplicate cart quantity for backpack tier {tierIndex}."); return false; } return true; } catch (Exception exception) { ModLogger.Error("BackpackPurchasePatch: AddItem prefix error", exception); return true; } } [HarmonyPatch("SetAmount", new Type[] { typeof(ListingUI), typeof(int) })] [HarmonyPrefix] public static void SetAmount_Prefix(ListingUI ui, ref int amount) { try { if (amount > 1 && !((Object)(object)ui == (Object)null)) { object target = ReflectionUtils.TryGetFieldOrProperty(ui, "Listing") ?? ReflectionUtils.TryGetFieldOrProperty(ui, "listing"); object obj = ReflectionUtils.TryGetFieldOrProperty(target, "Item"); StorableItemDefinition val = (StorableItemDefinition)((obj is StorableItemDefinition) ? obj : null); if (val != null && ((BaseItemDefinition)val).ID != null && BackpackShopIntegration.IsBackpackTierPurchase(((BaseItemDefinition)val).ID, out var _)) { amount = 1; ModLogger.Debug("[Shop] Clamped backpack tier quantity to one."); } } } catch (Exception exception) { ModLogger.Error("BackpackPurchasePatch: SetAmount prefix error", exception); } } } [HarmonyPatch(typeof(BodySearchBehaviour))] public static class BodySearchBehaviourPatch { [HarmonyPatch("SearchClean")] [HarmonyPrefix] public static bool SearchClean(BodySearchBehaviour __instance) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if ((Object)(object)PlayerBackpack.Instance == (Object)null || !Configuration.Instance.EnableSearch || !PlayerBackpack.Instance.IsUnlocked || !PlayerBackpack.Instance.IsPoliceSearchable) { return true; } Singleton<BodySearchScreen>.Instance.onSearchClear.RemoveListener(new UnityAction(__instance.SearchClean)); Singleton<BodySearchScreen>.Instance.onSearchFail.RemoveListener(new UnityAction(__instance.SearchFail)); Singleton<BodySearchScreen>.Instance.IsOpen = true; MelonCoroutines.Start(CheckForItems(__instance)); return false; } private static IEnumerator CheckForItems(BodySearchBehaviour behaviour) { ((NPC)behaviour.officer).DialogueHandler.ShowWorldspaceDialogue("Hold on, let me see your backpack as well.", 5f); yield return (object)new WaitForSeconds(3f); Singleton<BodySearchScreen>.Instance.IsOpen = false; behaviour.ConcludeSearch(!PlayerBackpack.Instance.ContainsItemsOfInterest(behaviour.MaxStealthLevel)); } } [HarmonyPatch(typeof(Cart))] public static class CartPatch { [HarmonyPatch("GetWarning")] [HarmonyPostfix] public static void GetWarning(Cart __instance, ref bool __result, ref string warning) { PlayerBackpack instance = PlayerBackpack.Instance; if (!((Object)(object)instance == (Object)null) && instance.IsUnlocked && !warning.StartsWith("Vehicle") && __result) { List<ItemSlot> itemSlots = instance.ItemSlots; itemSlots.InsertRange(0, (IEnumerable<ItemSlot>)PlayerSingleton<PlayerInventory>.Instance.hotbarSlots); if (__instance.Shop.WillCartFit(itemSlots)) { warning = "Inventory won't fit everything. Some items will be placed in your backpack."; } } } } [HarmonyPatch(typeof(HandoverScreen))] public static class HandoverScreenPatch { private sealed class PanelState { public RectTransform BackpackContainer; public RectTransform BackpackSlotContainer; public RectTransform BackpackHeaderRoot; public RectTransform BackpackVisualRoot; public Canvas DedicatedCanvas; public EditorUiDedicatedCanvasBinding EditorDedicatedCanvas; public RectTransform DedicatedCard; public RectTransform DedicatedBrowserHost; public RectTransform DedicatedGrid; public ItemSlotUI SlotPrefab; public RectTransform PagingRoot; public RectTransform VehicleContainer; public Component SourceTitleLabel; public Component SourceSubtitleLabel; public Component ClonedTitleLabel; public Component ClonedSubtitleLabel; public Component OverlayTitleLabel; public Component OverlaySubtitleLabel; public ItemSlotUI[] SlotUIs; public Button PrevButton; public Button NextButton; public Button ToggleButton; public Button DedicatedToggleButton; public RectTransform DedicatedToggleRoot; public RectTransform TransferRoot; public Button TransferButton; public Button BoundTransferButton; public Button EditorBackpackModeButton; public Button EditorVehicleModeButton; public Text TransferStatusLabel; public Text PageLabel; public Text VisualTitleLabel; public Text VisualMetaLabel; public Action PrevAction; public Action NextAction; public Action ToggleAction; public Action TransferAction; public Action ShowBackpackAction; public Action ShowVehicleAction; public LandVehicle NearbyVehicle; public Vector2 VehicleOriginalAnchoredPos; public int CurrentPage; public int SlotsPerPage; public bool ShowingVehicle; public bool IsOpen; public int LastPageInputFrame; public int NextVehicleProbeFrame; public bool Initialized; public string TransferStatus; } private sealed class HandoverRequirement { public string ProductId; public string Quality; public int QualityRank; public int Remaining; } private sealed class HandoverTransferSource { public string Name; public List<ItemSlot> Slots; public int MovedUnits; } private sealed class HeaderCandidate { public Component Label; public float LocalY; public float FontSize; } private const float VehicleMaxDistance = 20f; private const float BackpackGridScale = 0.74f; private const float BackpackContentCenterY = 125f; private static readonly Vector2 BackpackCardSize = new Vector2(420f, 660f); private const string VehicleHeaderTitle = "Vehicle"; private const string VehicleHeaderSubtitle = "This is the vehicle you last drove.\nMust be within 20 meters."; private static readonly Dictionary<int, PanelState> States = new Dictionary<int, PanelState>(); private const int HeaderReapplyFrameCount = 3; private static BackpackUiThemePalette GetCurrentBackpackThemePalette() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) Configuration instance = Configuration.Instance; return BackpackUiThemes.Get(instance.BackpackUiTheme, instance.CustomBackpackUiPrimaryColor); } public static void RefreshActiveLayouts() { try { foreach (PanelState value in States.Values) { if (value == null) { continue; } bool flag = (Object)(object)value.DedicatedCanvas != (Object)null && ((Component)value.DedicatedCanvas).gameObject.activeSelf; bool flag2 = (Object)(object)value.BackpackContainer != (Object)null && ((Component)value.BackpackContainer).gameObject.activeSelf; if (flag || flag2) { if ((Object)(object)value.DedicatedCard != (Object)null) { UpdateDedicatedOverlayLayout(FindOwningScreen(value), value); } else { ConfigureCompactBackpackLayout(FindOwningScreen(value), value); } UpdatePagingLayout(value); } } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.RefreshActiveLayouts", exception); } } [HarmonyPatch("Start")] [HarmonyPostfix] public static void Start(HandoverScreen __instance) { try { ModLogger.Info($"[HandoverUI] Start receipt: id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}"); PruneDeadStates(); EnsurePanel(__instance); } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Start", exception); } } [HarmonyPatch("Open")] [HarmonyPostfix] public static void Open(HandoverScreen __instance) { using (UiProfiler.Measure("handover", "open", $"id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}")) { try { ModLogger.Info($"[HandoverUI] Open receipt: id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}, hasBackpack={HasBackpack()}"); PruneDeadStates(); if (!HasBackpack()) { HidePanelAndRestoreVehicle(__instance); return; } PanelState panelState = EnsurePanel(__instance); if (panelState != null && (!((Object)(object)panelState.BackpackContainer == (Object)null) || !((Object)(object)panelState.DedicatedCanvas == (Object)null))) { LandVehicle val = ResolveNearbyVehicleStorage(panelState, forceRefresh: true); bool hasVehicle = (Object)(object)val != (Object)null; LogVehicleSelector(val); EnsureDedicatedVehicleToggle(__instance, panelState, hasVehicle); panelState.CurrentPage = 0; panelState.SlotsPerPage = ((panelState.SlotUIs != null) ? panelState.SlotUIs.Length : 0); panelState.ShowingVehicle = false; panelState.TransferStatus = null; panelState.IsOpen = true; if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackContainer != (Object)null) { ((Component)panelState.BackpackContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.PagingRoot != (Object)null) { ((Component)panelState.PagingRoot).gameObject.SetActive((Object)(object)panelState.DedicatedCanvas == (Object)null); } UpdateBackpackHeaderTexts(panelState); ApplyVisibleStorageMode(panelState, hasVehicle); if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackContainer != (Object)null) { ((Component)panelState.BackpackContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.DedicatedCanvas == (Object)null && (Object)(object)panelState.BackpackSlotContainer != (Object)null) { ((Component)panelState.BackpackSlotContainer).gameObject.SetActive(true); } if ((Object)(object)panelState.VehicleContainer != (Object)null) { ((Component)panelState.VehicleContainer).gameObject.SetActive(false); } ApplyPrimaryHeaderForMode(__instance, panelState, panelState.ShowingVehicle); MelonCoroutines.Start(ReapplyHeaderNextFrame(__instance, panelState)); if ((Object)(object)panelState.DedicatedCanvas == (Object)null) { ApplyBackpackPage(panelState); } else { UpdateDedicatedOverlayLayout(__instance, panelState); EnsureDedicatedVehicleToggle(__instance, panelState, hasVehicle); UpdateDedicatedVehicleToggle(__instance, panelState, hasVehicle); } RebuildQuickMove(__instance, val); } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Open", exception); } } } private static IEnumerator ReapplyHeaderNextFrame(HandoverScreen screen, PanelState panel) { if ((Object)(object)screen == (Object)null || panel == null) { yield break; } for (int i = 0; i < 3; i++) { yield return null; if ((Object)(object)screen == (Object)null || panel == null || (Object)(object)panel.BackpackContainer == (Object)null || !((Component)panel.BackpackContainer).gameObject.activeSelf || panel.ShowingVehicle) { break; } if ((Object)(object)panel.VehicleContainer != (Object)null && ((Component)panel.VehicleContainer).gameObject.activeSelf) { ((Component)panel.VehicleContainer).gameObject.SetActive(false); } ApplyPrimaryHeaderForMode(screen, panel, showingVehicle: false); } } [HarmonyPatch("Update")] [HarmonyPostfix] public static void Update(HandoverScreen __instance) { PruneDeadStates(); if ((Object)(object)__instance == (Object)null || !States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { return; } if (value.IsOpen && !__instance.IsOpen) { ModLogger.Info("[HandoverUI] Owner screen closed outside Close(); dismissing dedicated backpack overlay."); HidePanelAndRestoreVehicle(__instance); } else { if (!value.IsOpen) { return; } if ((Object)(object)value.DedicatedCanvas != (Object)null) { if (!value.ShowingVehicle && (Object)(object)value.VehicleContainer != (Object)null && ((Component)value.VehicleContainer).gameObject.activeSelf) { ((Component)value.VehicleContainer).gameObject.SetActive(false); } UpdateDedicatedVehicleToggle(__instance, value, (Object)(object)ResolveNearbyVehicleStorage(value) != (Object)null); } else { if ((Object)(object)value.BackpackContainer == (Object)null || !((Component)value.BackpackContainer).gameObject.activeSelf || value.ShowingVehicle) { return; } if ((Object)(object)value.VehicleContainer != (Object)null && ((Component)value.VehicleContainer).gameObject.activeSelf) { ((Component)value.VehicleContainer).gameObject.SetActive(false); } ConfigureCompactBackpackLayout(__instance, value); ApplyPrimaryHeaderForMode(__instance, value, showingVehicle: false); if ((Object)(object)value.BackpackHeaderRoot != (Object)null && ((Component)value.BackpackHeaderRoot).gameObject.activeSelf) { ((Transform)value.BackpackHeaderRoot).SetAsLastSibling(); Canvas component = ((Component)value.BackpackHeaderRoot).GetComponent<Canvas>(); if ((Object)(object)component != (Object)null && component.overrideSorting && component.sortingOrder != 9999) { component.sortingOrder = 9999; } } } } } [HarmonyPatch("Close")] [HarmonyPostfix] public static void Close(HandoverScreen __instance) { //IL_0155: Unknown result type (might be due to invalid IL or missing references) using (UiProfiler.Measure("handover", "close", $"id={((__instance != null) ? new int?(((Object)__instance).GetInstanceID()) : ((int?)null))}")) { try { if (States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { ClearSlotAssignments(value); value.IsOpen = false; if ((Object)(object)value.BackpackContainer != (Object)null) { ((Component)value.BackpackContainer).gameObject.SetActive(false); } if ((Object)(object)value.PagingRoot != (Object)null) { ((Component)value.PagingRoot).gameObject.SetActive(false); } if ((Object)(object)value.DedicatedToggleRoot != (Object)null) { ((Component)value.DedicatedToggleRoot).gameObject.SetActive(false); } if ((Object)(object)value.TransferRoot != (Object)null) { ((Component)value.TransferRoot).gameObject.SetActive(false); } if ((Object)(object)value.DedicatedCanvas != (Object)null) { ((Component)value.DedicatedCanvas).gameObject.SetActive(false); } SetBackpackVisualVisible(value, visible: false); HideOverlayHeader(value); SetHeaderPairActive(value.SourceTitleLabel, value.SourceSubtitleLabel, active: true); if ((Object)(object)value.VehicleContainer != (Object)null) { value.VehicleContainer.anchoredPosition = value.VehicleOriginalAnchoredPos; } } } catch (Exception exception) { ModLogger.Error("HandoverScreenPatch.Close", exception); } } } private static PanelState EnsurePanel(HandoverScreen screen) { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) using (UiProfiler.Measure("handover", "ensure_panel", $"id={((screen != null) ? new int?(((Object)screen).GetInstanceID()) : ((int?)null))}")) { PruneDeadStates(); if ((Object)(object)screen == (Object)null) { return null; } int instanceID = ((Object)screen).GetInstanceID(); if (States.TryGetValue(instanceID, out var value) && value.Initialized && IsComponentAlive((Component)(object)value.BackpackContainer) && IsComponentAlive((Component)(object)value.VehicleContainer) && ((Object)(object)value.BackpackHeaderRoot == (Object)null || IsComponentAlive((Component)(object)value.BackpackHeaderRoot)) && IsComponentAlive((Component)(object)value.PagingRoot) && IsComponentAlive((Component)(object)value.PrevButton) && IsComponentAlive((Component)(object)value.NextButton) && IsComponentAlive((Component)(object)value.ToggleButton) && IsComponentAlive((Component)(object)value.PageLabel)) { RefreshHeaderBindings(value, screen); EnsureDedicatedBackpackOverlay(screen, value); return value; } PanelState panelState = value ?? new PanelState(); panelState.VehicleContainer = screen.VehicleContainer; if ((Object)(object)panelState.VehicleContainer == (Object)null) { return null; } panelState.VehicleOriginalAnchoredPos = panelState.VehicleContainer.anchoredPosition; if (!IsComponentAlive((Component)(object)panelState.BackpackContainer)) { panelState.BackpackContainer = null; } if ((Object)(object)panelState.BackpackContainer == (Object)null) { RectTransform val = Object.Instantiate<RectTransform>(panelState.VehicleContainer, ((Transform)panelState.VehicleContainer).parent); ((Object)val).name = "BackpackContainer"; ((Component)val).gameObject.SetActive(false); panelState.BackpackContainer = val; } CenterBackpackContainer(panelState); panelState.BackpackSlotContainer = FindMatchingRectTransform(panelState.BackpackContainer, screen.VehicleSlotContainer); RectTransform val2 = (((Object)(object)panelState.BackpackSlotContainer != (Object)null) ? panelState.BackpackSlotContainer : panelState.BackpackContainer); ItemSlotUI[] componentsInChildren = ((Component)val2).GetComponentsInChildren<ItemSlotUI>(false); if (componentsInChildren == null || componentsInChildren.Length == 0) { componentsInChildren = ((Component)val2).GetComponentsInChildren<ItemSlotUI>(true); } ItemSlotUI dedicatedSlotTemplate = GetDedicatedSlotTemplate((componentsInChildren != null && componentsInChildren.Length != 0) ? componentsInChildren[0] : null); if ((Object)(object)panelState.DedicatedCanvas == (Object)null) { panelState.SlotUIs = componentsInChildren; panelState.SlotPrefab = dedicatedSlotTemplate; } else if ((Object)(object)panelState.SlotPrefab == (Object)null) { panelState.SlotPrefab = dedicatedSlotTemplate; } RefreshHeaderBindings(panelState, screen); EnsureDedicatedBackpackOverlay(screen, panelState); if ((Object)(object)panelState.DedicatedCard == (Object)null) { EnsureBackpackVisuals(panelState); ConfigureCompactBackpackLayout(screen, panelState); } EnsurePagingControls(panelState); panelState.Initialized = true; States[instanceID] = panelState; return panelState; } } private static void PruneDeadStates() { if (States.Count == 0) { return; } List<int> list = new List<int>(); foreach (KeyValuePair<int, PanelState> state in States) { PanelState value = state.Value; if (value == null) { list.Add(state.Key); } else if (!IsComponentAlive((Component)(object)value.VehicleContainer) && !IsComponentAlive((Component)(object)value.BackpackContainer) && !IsComponentAlive((Component)(object)value.PagingRoot) && !IsComponentAlive((Component)(object)value.BackpackHeaderRoot)) { list.Add(state.Key); } } for (int i = 0; i < list.Count; i++) { if (States.TryGetValue(list[i], out var value2) && (Object)(object)value2?.DedicatedCanvas != (Object)null) { Object.Destroy((Object)(object)((Component)value2.DedicatedCanvas).gameObject); } States.Remove(list[i]); } } private static void RefreshHeaderBindings(PanelState state, HandoverScreen screen) { if (state != null) { ResolveLabels(state, screen); EnsureBackpackHeader(state); } } private static void ResolveLabels(PanelState state, HandoverScreen screen) { ResolveHeaderPairInContainer(state.VehicleContainer, (Transform)(object)screen?.VehicleSlotContainer, null, null, out state.SourceTitleLabel, out state.SourceSubtitleLabel); ResolveHeaderPairInContainer(state.BackpackContainer, (Transform)(object)state.BackpackSlotContainer, (Transform)(object)state.PagingRoot, (Transform)(object)state.BackpackHeaderRoot, out state.ClonedTitleLabel, out state.ClonedSubtitleLabel); if (IsUnderTransform(state.ClonedTitleLabel, (Transform)(object)state.BackpackVisualRoot)) { state.ClonedTitleLabel = null; } if (IsUnderTransform(state.ClonedSubtitleLabel, (Transform)(object)state.BackpackVisualRoot)) { state.ClonedSubtitleLabel = null; } } private static void ResolveHeaderPairInContainer(RectTransform container, Transform slotContainer, Transform pagingRoot, Transform overlayRoot, out Component titleLabel, out Component subtitleLabel) { titleLabel = null; subtitleLabel = null; if ((Object)(object)container == (Object)null) { return; } Component[] componentsInChildren = ((Component)container).GetComponentsInChildren<Component>(true); List<Component> list = new List<Component>(); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && IsTextLikeComponent(val) && !IsUnderTransform(val, slotContainer) && !IsUnderTransform(val, pagingRoot) && !IsUnderTransform(val, overlayRoot)) { list.Add(val); } } if (list.Count == 0) { return; } titleLabel = FindNamedHeaderLabel(container, slotContainer, pagingRoot, overlayRoot, "Title"); subtitleLabel = FindNamedHeaderLabel(container, slotContainer, pagingRoot, overlayRoot, "Subtitle"); foreach (Component item in list) { string text = NormalizeLabelText(GetLabelText(item)); if ((Object)(object)titleLabel == (Object)null && text.Equals("Vehicle", StringComparison.OrdinalIgnoreCase)) { titleLabel = item; } else if ((Object)(object)subtitleLabel == (Object)null && (text.Contains("vehicle you last drove", StringComparison.OrdinalIgnoreCase) || text.Contains("within 20 meters", StringComparison.OrdinalIgnoreCase))) { subtitleLabel = item; } } if ((Object)(object)titleLabel != (Object)null && (Object)(object)subtitleLabel != (Object)null) { return; } List<HeaderCandidate> list2 = new List<HeaderCandidate>(list.Count); for (int j = 0; j < list.Count; j++) { Component val2 = list[j]; list2.Add(new HeaderCandidate { Label = val2, LocalY = GetLocalY(container, val2), FontSize = GetFontSize(val2) }); } list2.Sort(CompareHeaderCandidates); if ((Object)(object)titleLabel == (Object)null && list2.Count > 0) { titleLabel = list2[0].Label; } if (!((Object)(object)subtitleLabel == (Object)null)) { return; } for (int k = 0; k < list2.Count; k++) { Component label = list2[k].Label; if (!((Object)(object)label == (Object)null) && !((Object)(object)label == (Object)(object)titleLabel)) { subtitleLabel = label; break; } } } private static Component FindNamedHeaderLabel(RectTransform container, Transform slotContainer, Transform pagingRoot, Transform overlayRoot, string targetName) { if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(targetName)) { return null; } Transform[] componentsInChildren = ((Component)container).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val).name, targetName, StringComparison.OrdinalIgnoreCase) && (!((Object)(object)slotContainer != (Object)null) || (!((Object)(object)val == (Object)(object)slotContainer) && !val.IsChildOf(slotContainer))) && (!((Object)(object)pagingRoot != (Object)null) || (!((Object)(object)val == (Object)(object)pagingRoot) && !val.IsChildOf(pagingRoot))) && (!((Object)(object)overlayRoot != (Object)null) || (!((Object)(object)val == (Object)(object)overlayRoot) && !val.IsChildOf(overlayRoot)))) { Component textLikeComponent = GetTextLikeComponent(((Component)val).gameObject); if ((Object)(object)textLikeComponent != (Object)null) { return textLikeComponent; } Component val2 = FindFirstTextLikeInSubtree(val); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } private static Component FindFirstTextLikeInSubtree(Transform root) { if ((Object)(object)root == (Object)null) { return null; } Component[] componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val != (Object)null && IsTextLikeComponent(val)) { return val; } } return null; } private static int CompareHeaderCandidates(HeaderCandidate left, HeaderCandidate right) { if (left == right) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int num = right.LocalY.CompareTo(left.LocalY); if (num != 0) { return num; } return right.FontSize.CompareTo(left.FontSize); } private static float GetLocalY(RectTransform container, Component component) { //IL_0037: 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_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)container == (Object)null || (Object)(object)component == (Object)null || (Object)(object)component.transform == (Object)null) { return float.MinValue; } try { Vector3 val = ((Transform)container).InverseTransformPoint(component.transform.position); return val.y; } catch { return float.MinValue; } } private static float GetFontSize(Component label) { if ((Object)(object)label == (Object)null) { return 0f; } TextMeshProUGUI val = (TextMeshProUGUI)(object)((label is TextMeshProUGUI) ? label : null); if (val != null) { try { return ((TMP_Text)val).fontSize; } catch { } } Text val2 = (Text)(object)((label is Text) ? label : null); if (val2 != null) { return val2.fontSize; } object obj2 = ReflectionUtils.TryGetFieldOrProperty(label, "fontSize"); if (obj2 is float result) { return result; } if (obj2 is int num) { return num; } return 0f; } private static Component GetTextLikeComponent(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } TextMeshProUGUI tmpLabel = GetTmpLabel(gameObject); if ((Object)(object)tmpLabel != (Object)null) { return (Component)(object)tmpLabel; } Text component = gameObject.GetComponent<Text>(); if ((Object)(object)component != (Object)null) { return (Component)(object)component; } Component[] components = gameObject.GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && IsTextLikeComponent(val)) { return val; } } return null; } private static void EnsureBackpackHeader(PanelState state) { //IL_00cd: Unknown result type (might be due to invalid IL or missing referen