Decompiled source of Summit Supply Shop v2.0.0
tony4twentys-Summit Supply Shop.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using Crispberry_PiPhone; using ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.Core; using Zorro.Core.Serizalization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyTitle("Summit Supply Shop")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Summit Supply Shop")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d7cd2170-3a27-4456-acd7-52eba587f343")] [assembly: AssemblyFileVersion("2.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.0.0")] [module: UnverifiableCode] namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace Summit_Supply_Shop { [BepInPlugin("tony4twentys.Summit_Supply_Shop", "Summit Supply Shop", "2.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("PEAK.exe")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "tony4twentys.Summit_Supply_Shop"; public const string PluginName = "Summit Supply Shop"; public const string PluginVersion = "2.0.0"; public static Plugin Instance; public ConfigEntry<KeyCode> MenuKey; public ConfigEntry<KeyCode> MenuModifier; public ConfigEntry<bool> ShowPauseMenuButton; public ConfigEntry<int> LuggageMinCoins; public ConfigEntry<int> LuggageMaxCoins; public ConfigEntry<bool> ShowCoinPopups; public ConfigEntry<bool> AutoCreateWallets; public ConfigEntry<int> StartingCoins; public ConfigEntry<int> StoreCardSize; public ConfigEntry<bool> AlwaysOpenLandscape; public ConfigEntry<float> CoinSfxVolume; private Harmony _harmony; private Coroutine _catalogRoutine; private void Awake() { //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown Instance = this; MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "MenuKey", (KeyCode)98, "Opens the shop menu. Used with MenuModifier."); MenuModifier = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "MenuModifier", (KeyCode)308, "Hold this with MenuKey. Set to None for the key alone."); ShowPauseMenuButton = ((BaseUnityPlugin)this).Config.Bind<bool>("Controls", "ShowPauseMenuButton", true, "Add a Summit Shop button to the pause menu."); LuggageMinCoins = ((BaseUnityPlugin)this).Config.Bind<int>("Luggage", "MinCoins", 2, "Minimum coins granted when a luggage box is opened."); LuggageMaxCoins = ((BaseUnityPlugin)this).Config.Bind<int>("Luggage", "MaxCoins", 8, "Maximum coins granted when a luggage box is opened."); ShowCoinPopups = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "ShowCoinPopups", true, "Show coin gains and losses on screen in the game font."); AutoCreateWallets = ((BaseUnityPlugin)this).Config.Bind<bool>("Wallets", "AutoCreateWallets", true, "Host: give every player in the lobby a wallet automatically."); StartingCoins = ((BaseUnityPlugin)this).Config.Bind<int>("Wallets", "StartingCoins", 0, "Coins in a freshly created wallet."); StoreCardSize = ((BaseUnityPlugin)this).Config.Bind<int>("Store", "CardSize", 2, "Store item card size from 0 (most on screen) to 4 (largest)."); AlwaysOpenLandscape = ((BaseUnityPlugin)this).Config.Bind<bool>("Phone", "AlwaysOpenLandscape", true, "Phone app only: open Summit Supply Shop in landscape. You can still rotate by hand."); CoinSfxVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Feedback", "CoinSfxVolume", 0.8f, new ConfigDescription("Coin pickup/spend volume. Multiplied by the game Master Volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); ShopPresetStore.Initialize(); LuggageCoins.Initialize(); ShopNet.Ensure(); ShopMenu.EnsureCreated(); CoinToast.Initialize(((Component)this).transform); CoinSfx.Initialize(); PhoneShopApp.TryRegister(); if (ShopNet.IsHost) { WalletStore.EnsureLoaded(); if (!ShopPresetStore.TryLoadDefault()) { ShopCatalog.EnsureLoaded(); } ShopCatalog.SeedFromCatalogIfEmpty(); } _harmony = new Harmony("tony4twentys.Summit_Supply_Shop"); _harmony.PatchAll(typeof(Plugin).Assembly); SceneManager.sceneLoaded += OnSceneLoaded; _catalogRoutine = ((MonoBehaviour)this).StartCoroutine(WaitForCatalog()); LogInfo("Summit Supply Shop v2.0.0 loaded. Open with " + FormatHotkey() + "."); } private void Start() { PhoneShopApp.TryRegister(); } private void Update() { if (CoinToast.IsShowing) { CoinToast.Tick(); } PhoneShopApp.TryRegister(); if (!PhoneShopApp.OwnsHotkey() && IsMenuHotkeyDown()) { ShopMenu.Toggle(); } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; if (_catalogRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_catalogRoutine); } try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } if (ShopNet.IsHost) { try { WalletStore.SaveNow(); } catch { } try { ShopCatalog.SaveNow(); } catch { } try { ShopPresetStore.SaveDefaultQuiet(); } catch { } } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ItemCatalog.TryRefresh(); ShopMenu.OnAfterSceneLoad(); CoinToast.Clear(); LuggageCoins.ClearSceneState(); if (ShopNet.IsHost) { ShopCatalog.SeedFromCatalogIfEmpty(); } } private IEnumerator WaitForCatalog() { float elapsed = 0f; while (!ItemCatalog.TryRefresh()) { elapsed += 0.25f; if (elapsed > 120f) { LogError("Item database never loaded. Shop cannot list items."); yield break; } yield return (object)new WaitForSeconds(0.25f); } LogInfo("Loaded " + ItemCatalog.Count + " items for the shop."); ShopNet.NotifyCatalogReady(); ShopMenu.OnCatalogReady(); } internal bool IsMenuHotkeyDown() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Invalid comparison between Unknown and I4 //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Invalid comparison between Unknown and I4 //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Invalid comparison between Unknown and I4 KeyCode val = (KeyCode)((MenuKey == null) ? 98 : ((int)MenuKey.Value)); if ((int)val == 0) { return false; } KeyCode val2 = (KeyCode)((MenuModifier == null) ? 308 : ((int)MenuModifier.Value)); if ((int)val2 == 0) { return Input.GetKeyDown(val); } bool flag = Input.GetKey(val2); if ((int)val2 == 306 || (int)val2 == 305) { flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); } else if ((int)val2 == 308 || (int)val2 == 307) { flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); } else if ((int)val2 == 304 || (int)val2 == 303) { flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); } if (flag) { return Input.GetKeyDown(val); } return false; } public unsafe static string FormatHotkey() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null) { return "Alt+B"; } KeyCode value = Instance.MenuKey.Value; KeyCode value2 = Instance.MenuModifier.Value; if ((int)value2 == 0) { return ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString(); } return Pretty(value2) + "+" + Pretty(value); } private unsafe static string Pretty(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown switch (key - 303) { case 2: case 3: return "Ctrl"; case 4: case 5: return "Alt"; case 0: case 1: return "Shift"; default: return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); } } public static bool GetShowPauseMenuButton() { if (!((Object)(object)Instance == (Object)null) && Instance.ShowPauseMenuButton != null) { return Instance.ShowPauseMenuButton.Value; } return true; } public static bool GetShowCoinPopups() { if (!((Object)(object)Instance == (Object)null) && Instance.ShowCoinPopups != null) { return Instance.ShowCoinPopups.Value; } return true; } public static bool GetAutoCreateWallets() { if (!((Object)(object)Instance == (Object)null) && Instance.AutoCreateWallets != null) { return Instance.AutoCreateWallets.Value; } return true; } public static bool GetAlwaysOpenLandscape() { if (!((Object)(object)Instance == (Object)null) && Instance.AlwaysOpenLandscape != null) { return Instance.AlwaysOpenLandscape.Value; } return true; } public static float GetCoinSfxVolume() { float num = (((Object)(object)Instance != (Object)null && Instance.CoinSfxVolume != null) ? Instance.CoinSfxVolume.Value : 0.8f); if (num < 0f) { return 0f; } if (num > 1f) { return 1f; } return num; } public static int GetStoreCardSize() { int num = (((Object)(object)Instance != (Object)null && Instance.StoreCardSize != null) ? Instance.StoreCardSize.Value : 2); if (num < 0) { return 0; } if (num > 4) { return 4; } return num; } public static int NudgeStoreCardSize(int delta) { int num = GetStoreCardSize() + delta; if (num < 0) { num = 0; } if (num > 4) { num = 4; } if ((Object)(object)Instance != (Object)null && Instance.StoreCardSize != null) { Instance.StoreCardSize.Value = num; } return num; } public static int StartingCoinsValue() { int val = (((Object)(object)Instance != (Object)null && Instance.StartingCoins != null) ? Instance.StartingCoins.Value : 0); return Math.Max(0, val); } public static int LuggageMinCoinsValue() { int val = (((Object)(object)Instance != (Object)null && Instance.LuggageMinCoins != null) ? Instance.LuggageMinCoins.Value : 2); return Math.Max(0, val); } public static int LuggageMaxCoinsValue() { int val = LuggageMinCoinsValue(); int val2 = (((Object)(object)Instance != (Object)null && Instance.LuggageMaxCoins != null) ? Instance.LuggageMaxCoins.Value : 8); return Math.Max(val, val2); } public static void LogInfo(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } else { Debug.Log((object)("[Summit Supply Shop] " + message)); } } public static void LogError(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogError((object)message); } else { Debug.LogError((object)("[Summit Supply Shop] " + message)); } } } internal sealed class CatalogItem { public ushort Id; public string DisplayName; public string PrefabName; public Item Prefab; public string ListName; } internal static class ItemCatalog { private static readonly List<CatalogItem> _items = new List<CatalogItem>(); private static readonly Dictionary<ushort, CatalogItem> _byId = new Dictionary<ushort, CatalogItem>(); private static readonly HashSet<int> _seenInstances = new HashSet<int>(); private static ushort _nextSynthetic = 60000; public static bool IsReady { get; private set; } public static int Count => _items.Count; public static IReadOnlyList<CatalogItem> Items => _items; public static bool TryRefresh() { ItemDatabase instance; try { instance = SingletonAsset<ItemDatabase>.Instance; } catch { return false; } if ((Object)(object)instance == (Object)null) { return false; } List<Item> list = new List<Item>(); HashSet<int> seen = new HashSet<int>(); if (instance.itemLookup != null) { foreach (KeyValuePair<ushort, Item> item in instance.itemLookup) { Consider(list, seen, item.Value); } } if (((DatabaseAsset<ItemDatabase, Item>)(object)instance).Objects != null) { for (int i = 0; i < ((DatabaseAsset<ItemDatabase, Item>)(object)instance).Objects.Count; i++) { Consider(list, seen, ((DatabaseAsset<ItemDatabase, Item>)(object)instance).Objects[i]); } } if (list.Count == 0) { return IsReady; } int num = 0; for (int j = 0; j < list.Count; j++) { Item val = list[j]; if (!((Object)(object)val == (Object)null) && _seenInstances.Add(((Object)val).GetInstanceID())) { CatalogItem catalogItem = new CatalogItem { Id = UniqueId(val.itemID, SafePrefabName(val)), Prefab = val, PrefabName = SafePrefabName(val), DisplayName = SafeDisplayName(val) }; catalogItem.ListName = catalogItem.DisplayName; _items.Add(catalogItem); if (!_byId.ContainsKey(catalogItem.Id)) { _byId[catalogItem.Id] = catalogItem; } num++; } } if (num > 0) { _items.Sort(CompareForList); AssignListNames(); } IsReady = _items.Count > 0; return IsReady; } public static bool TryGetPrefab(ushort id, out Item prefab) { prefab = null; if (_byId.TryGetValue(id, out var value) && value != null && (Object)(object)value.Prefab != (Object)null) { prefab = value.Prefab; return true; } if (ItemDatabase.TryGetItem(id, ref prefab)) { return (Object)(object)prefab != (Object)null; } return false; } public static string GetName(ushort id) { if (_byId.TryGetValue(id, out var value) && value != null) { return value.DisplayName; } Item val = default(Item); if (ItemDatabase.TryGetItem(id, ref val) && (Object)(object)val != (Object)null) { return SafeDisplayName(val); } return "Unknown item " + id; } private static ushort UniqueId(ushort itemId, string prefabName) { if (itemId != 0 && !_byId.ContainsKey(itemId)) { return itemId; } ushort num = StableShopId(prefabName); if (num != 0 && !_byId.ContainsKey(num)) { return num; } for (int i = 0; i < 65000; i++) { if (_nextSynthetic == 0) { _nextSynthetic = 1; } ushort num2 = _nextSynthetic++; if (num2 != 0 && !_byId.ContainsKey(num2)) { return num2; } } return 1; } private static ushort StableShopId(string prefabName) { int num = -2128831035; if (!string.IsNullOrEmpty(prefabName)) { for (int i = 0; i < prefabName.Length; i++) { num = (num ^ prefabName[i]) * 16777619; } } int num2 = 50000 + Math.Abs(num % 15000); if (num2 <= 0) { num2 = 50001; } return (ushort)num2; } private static void Consider(List<Item> found, HashSet<int> seen, Item item) { if (!((Object)(object)item == (Object)null) && seen.Add(((Object)item).GetInstanceID())) { found.Add(item); } } private static int CompareForList(CatalogItem a, CatalogItem b) { int num = string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase); if (num == 0) { return string.Compare(a.PrefabName, b.PrefabName, StringComparison.OrdinalIgnoreCase); } return num; } private static void AssignListNames() { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < _items.Count; i++) { string key = BaseName(_items[i]); dictionary.TryGetValue(key, out var value); dictionary[key] = value + 1; } for (int j = 0; j < _items.Count; j++) { CatalogItem catalogItem = _items[j]; string text = BaseName(catalogItem); catalogItem.ListName = ((dictionary[text] > 1) ? (text + " (" + Sanitize(catalogItem.PrefabName) + ")") : text); } } private static string BaseName(CatalogItem item) { string text = Sanitize(item.DisplayName); if (string.IsNullOrEmpty(text)) { text = Sanitize(item.PrefabName); } if (string.IsNullOrEmpty(text)) { text = ((item.Id != 0) ? ("Item " + item.Id) : "Unnamed item"); } return text; } private static string Sanitize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { if (c == '=' || c == '[' || c == ']' || c == '\\' || c == '"' || c == '\n' || c == '\r' || c == '#') { stringBuilder.Append(' '); } else { stringBuilder.Append(c); } } return stringBuilder.ToString().Trim(); } private static string SafePrefabName(Item item) { try { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null && !string.IsNullOrEmpty(((Object)((Component)item).gameObject).name)) { string text = ((Object)((Component)item).gameObject).name; int num = text.IndexOf("(Clone)", StringComparison.Ordinal); if (num >= 0) { text = text.Substring(0, num).Trim(); } return text; } if ((Object)(object)item != (Object)null && !string.IsNullOrEmpty(((Object)item).name)) { return ((Object)item).name; } } catch { } return "unknown"; } private static string SafeDisplayName(Item item) { try { if (item?.UIData != null && !string.IsNullOrEmpty(item.UIData.itemName)) { try { string name = item.GetName(); if (!string.IsNullOrEmpty(name) && name.IndexOf("NAME_", StringComparison.OrdinalIgnoreCase) < 0) { return name; } } catch { } return Prettify(item.UIData.itemName); } } catch { } return Prettify(SafePrefabName(item)); } private static string Prettify(string name) { if (string.IsNullOrEmpty(name)) { return "Unknown"; } StringBuilder stringBuilder = new StringBuilder(name.Length + 8); for (int i = 0; i < name.Length; i++) { char c = name[i]; if (c == '_' || c == '-') { stringBuilder.Append(' '); continue; } if (i > 0 && char.IsUpper(c) && !char.IsUpper(name[i - 1]) && name[i - 1] != ' ') { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString().Trim(); } } internal static class ItemIcons { private static readonly Dictionary<ushort, Sprite> Cache = new Dictionary<ushort, Sprite>(); public static Sprite Get(ushort id) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (Cache.TryGetValue(id, out var value) && (Object)(object)value != (Object)null) { return value; } try { if (!ItemCatalog.TryGetPrefab(id, out var prefab) || prefab?.UIData == null) { return null; } Texture2D icon = prefab.UIData.GetIcon(); if ((Object)(object)icon == (Object)null) { return null; } Sprite val = Sprite.Create(icon, new Rect(0f, 0f, (float)((Texture)icon).width, (float)((Texture)icon).height), new Vector2(0.5f, 0.5f), 100f); ((Object)val).name = "SSS_Icon_" + id; Cache[id] = val; return val; } catch { return null; } } } internal enum GrantResult { Failed, Pocket, Backpack, Dropped } internal static class ItemGrant { public static GrantResult GiveToActor(int actorNumber, ushort itemId) { try { if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { Plugin.LogError("Only the host can hand out shop items."); return GrantResult.Failed; } if (!ItemCatalog.TryGetPrefab(itemId, out var prefab) || (Object)(object)prefab == (Object)null) { Plugin.LogError("Shop item " + itemId + " is not in the item database."); return GrantResult.Failed; } Character val = FindCharacter(actorNumber); Player val2 = (((Object)(object)val != (Object)null) ? val.player : FindPlayer(actorNumber)); if ((Object)(object)val2 == (Object)null) { Plugin.LogError("Actor " + actorNumber + " has no spawned player, cannot deliver " + ((Object)prefab).name + "."); return GrantResult.Failed; } if (TryAddToAnyPocket(val2, prefab)) { SyncInventory(val2); Plugin.LogInfo("Gave " + ((Object)prefab).name + " into a pocket for actor " + actorNumber + "."); return GrantResult.Pocket; } if (TryAddToPackAny(val2, prefab)) { SyncInventory(val2); Plugin.LogInfo("Gave " + ((Object)prefab).name + " into a backpack for actor " + actorNumber + "."); return GrantResult.Backpack; } if (TrySpawnInHand(val, prefab)) { Plugin.LogInfo("Spawned " + ((Object)prefab).name + " into hands / at feet for actor " + actorNumber + "."); return GrantResult.Dropped; } if (TryDropNear(val, prefab)) { Plugin.LogInfo("Dropped " + ((Object)prefab).name + " beside actor " + actorNumber + "."); return GrantResult.Dropped; } Plugin.LogError("Could not deliver " + ((Object)prefab).name + " to actor " + actorNumber + "."); return GrantResult.Failed; } catch (Exception ex) { Plugin.LogError("Item grant failed: " + ex); return GrantResult.Failed; } } public static string Describe(GrantResult result) { return result switch { GrantResult.Pocket => string.Empty, GrantResult.Backpack => "Packed into your bag.", GrantResult.Dropped => "Your hands were full, it dropped at your feet.", _ => "Delivery failed.", }; } private static bool TryAddToAnyPocket(Player player, Item prefab) { if ((Object)(object)player == (Object)null || player.itemSlots == null || (Object)(object)prefab == (Object)null) { return false; } for (int i = 0; i < player.itemSlots.Length; i++) { if (TryAddToPocket(player, prefab, i)) { return true; } } return false; } private static bool TryAddToPocket(Player player, Item prefab, int slotIndex) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if ((Object)(object)player == (Object)null || player.itemSlots == null || (Object)(object)prefab == (Object)null) { return false; } if (slotIndex < 0 || slotIndex >= player.itemSlots.Length) { return false; } if (player.itemSlots[slotIndex] == null || !player.itemSlots[slotIndex].IsEmpty()) { return false; } ItemInstanceData val = new ItemInstanceData(Guid.NewGuid()); ItemInstanceDataHandler.AddInstanceData(val); player.itemSlots[slotIndex].SetItem(prefab, val); return !player.itemSlots[slotIndex].IsEmpty(); } private static bool TryAddToPackAny(Player player, Item prefab) { if ((Object)(object)prefab == (Object)null || !TryGetPackData(player, out var pack) || pack.itemSlots == null) { return false; } for (byte b = 0; b < pack.itemSlots.Length; b++) { if (TryAddToPack(pack, prefab, b)) { return true; } } return false; } private static bool TryAddToPack(BackpackData pack, Item prefab, byte slotIndex) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (pack == null || pack.itemSlots == null || (Object)(object)prefab == (Object)null) { return false; } if (slotIndex >= pack.itemSlots.Length) { return false; } if (pack.itemSlots[slotIndex] == null || !pack.itemSlots[slotIndex].IsEmpty()) { return false; } ItemInstanceData val = new ItemInstanceData(Guid.NewGuid()); ItemInstanceDataHandler.AddInstanceData(val); pack.AddItem(prefab, val, slotIndex); return !pack.itemSlots[slotIndex].IsEmpty(); } private static bool TryGetPackData(Player player, out BackpackData pack) { pack = null; if ((Object)(object)player == (Object)null || player.backpackSlot == null || ((ItemSlot)player.backpackSlot).IsEmpty() || ((ItemSlot)player.backpackSlot).data == null) { return false; } if (!((ItemSlot)player.backpackSlot).data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref pack) || pack == null) { ((ItemSlot)player.backpackSlot).data.RegisterNewEntry<BackpackData>((DataEntryKey)7); } if (((ItemSlot)player.backpackSlot).data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref pack)) { return pack != null; } return false; } private static bool TrySpawnInHand(Character character, Item prefab) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || (Object)(object)prefab == (Object)null) { return false; } string text = PrefabObjectName(prefab); if (string.IsNullOrEmpty(text)) { return false; } try { if (character.refs != null && (Object)(object)character.refs.items != (Object)null) { character.refs.items.SpawnItemInHand(text); return true; } } catch (Exception ex) { Plugin.LogError("SpawnItemInHand failed for " + text + ": " + ex.Message); } try { Vector3 pos = DropPosition(character, 2f, 0.2f); GameObject val = TryInstantiate("0_Items/" + text, pos, Quaternion.identity); if ((Object)(object)val == (Object)null) { return false; } Item component = val.GetComponent<Item>(); if ((Object)(object)component != (Object)null) { component.Interact(character); } return true; } catch (Exception ex2) { Plugin.LogError("0_Items instantiate failed for " + text + ": " + ex2.Message); return false; } } private static bool TryDropNear(Character character, Item prefab) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || (Object)(object)((Component)prefab).gameObject == (Object)null) { return false; } string text = PrefabObjectName(prefab); Vector3 pos = DropPosition(character, 1.2f, 0.35f); Quaternion rot = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); GameObject val = TryInstantiateItemRoom(text, pos, rot) ?? TryInstantiate("0_Items/" + text, pos, rot) ?? TryInstantiate(text, pos, rot); if ((Object)(object)val == (Object)null) { return false; } try { PhotonView component = val.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null) { component.RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { false, val.transform.position, val.transform.rotation }); } } catch (Exception ex) { Plugin.LogError("Dropped " + text + " but could not wake its physics: " + ex.Message); } return true; } private static string PrefabObjectName(Item prefab) { if ((Object)(object)prefab == (Object)null) { return string.Empty; } string text = (((Object)(object)((Component)prefab).gameObject != (Object)null) ? ((Object)((Component)prefab).gameObject).name : ((Object)prefab).name); if (string.IsNullOrEmpty(text)) { return string.Empty; } if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(Array.Empty<char>()); } return text; } private static GameObject TryInstantiateItemRoom(string prefabName, Vector3 pos, Quaternion rot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { return PhotonNetwork.InstantiateItemRoom(prefabName, pos, rot, true); } catch (Exception ex) { Plugin.LogError("InstantiateItemRoom('" + prefabName + "') failed: " + ex.Message); return null; } } private static GameObject TryInstantiate(string prefabPath, Vector3 pos, Quaternion rot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) try { return PhotonNetwork.Instantiate(prefabPath, pos, rot, (byte)0, (object[])null); } catch (Exception ex) { Plugin.LogError("PhotonNetwork.Instantiate('" + prefabPath + "') failed: " + ex.Message); return null; } } private static Vector3 DropPosition(Character character, float forward, float up) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return Vector3.zero; } try { Transform transform = character.GetBodypart((BodypartType)2).transform; return transform.position + transform.TransformVector(Vector3.forward * forward) + Vector3.up * up; } catch { } Vector3 val = character.Center; if (val == Vector3.zero && (Object)(object)character.data != (Object)null) { val = character.data.groundPos; } return val + ((Component)character).transform.forward * forward + Vector3.up * up; } private static Character FindCharacter(int actorNumber) { List<Character> list = Character.AllCharacters; if (list == null) { list = PlayerHandler.GetAllPlayerCharacters(); } if (list != null) { for (int i = 0; i < list.Count; i++) { Character val = list[i]; if (!((Object)(object)val == (Object)null) && ActorOf(val) == actorNumber) { return val; } } } if (actorNumber != LocalActor()) { return null; } return Character.localCharacter; } private static Player FindPlayer(int actorNumber) { Character val = FindCharacter(actorNumber); if ((Object)(object)val != (Object)null && (Object)(object)val.player != (Object)null) { return val.player; } if (actorNumber != LocalActor()) { return null; } return Player.localPlayer; } private static int ActorOf(Character character) { try { if ((Object)(object)character != (Object)null && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null && ((MonoBehaviourPun)character).photonView.Owner != null) { return ((MonoBehaviourPun)character).photonView.Owner.ActorNumber; } } catch { } return 0; } private static int LocalActor() { if (PhotonNetwork.LocalPlayer == null) { return 0; } return PhotonNetwork.LocalPlayer.ActorNumber; } private static PhotonView GetPlayerView(Player player) { if ((Object)(object)player == (Object)null) { return null; } try { if ((Object)(object)player.view != (Object)null) { return player.view; } } catch { } try { return ((MonoBehaviourPun)player).photonView; } catch { return null; } } private static void SyncInventory(Player player) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) try { PhotonView playerView = GetPlayerView(player); if (!((Object)(object)playerView == (Object)null)) { InventorySyncData val = default(InventorySyncData); ((InventorySyncData)(ref val))..ctor(player.itemSlots, player.backpackSlot, player.tempFullSlot); playerView.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2] { IBinarySerializable.ToManagedArray<InventorySyncData>(val), false }); } } catch (Exception ex) { Plugin.LogError("Inventory sync failed: " + ex.Message); } } } internal sealed class WalletGroup { public string Id; public string Name; public bool Persistent; } internal sealed class Wallet { public string Id; public string GroupId; public string OwnerKey; public string DisplayName; public int Balance; } internal sealed class LedgerEntry { public const byte Buy = 1; public const byte Sent = 2; public const byte Received = 3; public const byte AskSent = 4; public const byte AskReceived = 5; public const byte AskAccepted = 6; public const byte AskDenied = 7; public const byte Luggage = 8; public const byte Host = 9; public byte Kind; public int Amount; public string OtherName; public string Note; public string ItemName; public string Describe() { string text = (string.IsNullOrEmpty(Note) ? string.Empty : (" — " + Note)); return Kind switch { 1 => "Bought " + (ItemName ?? "item") + " for " + Amount + " coins", 2 => "Sent " + Amount + " coins to " + OtherName + text, 3 => "Received " + Amount + " coins from " + OtherName + text, 4 => "Requested " + Amount + " coins from " + OtherName + text, 5 => OtherName + " requested " + Amount + " coins" + text, 6 => "Paid " + Amount + " coins to " + OtherName + " (request)" + text, 7 => "Denied a " + Amount + " coin request " + (string.IsNullOrEmpty(OtherName) ? "" : ("(" + OtherName + ")")) + text, 8 => "Found " + Amount + " coins in luggage", 9 => ((Amount >= 0) ? "Host added " : "Host removed ") + Math.Abs(Amount) + " coins", _ => Amount + " coins" + text, }; } internal bool IsMoneyTransfer() { byte kind = Kind; if ((uint)(kind - 2) <= 5u) { return true; } return false; } internal bool IsPurchase() { if (Kind != 1 && Kind != 8) { return Kind == 9; } return true; } } internal static class WalletStore { private static readonly Dictionary<string, WalletGroup> Groups = new Dictionary<string, WalletGroup>(StringComparer.Ordinal); private static readonly Dictionary<string, Wallet> Wallets = new Dictionary<string, Wallet>(StringComparer.Ordinal); private static readonly Dictionary<int, string> ActorToWallet = new Dictionary<int, string>(); private static readonly Dictionary<string, List<LedgerEntry>> Ledgers = new Dictionary<string, List<LedgerEntry>>(StringComparer.Ordinal); private static readonly List<WalletGroup> GroupList = new List<WalletGroup>(); private static readonly List<Wallet> WalletList = new List<Wallet>(); private const int MaxLedger = 40; public const string SessionGroupId = "__session__"; private static bool _loaded; private static float _saveAt = -1f; public static string ActiveGroupId { get; private set; } public static WalletGroup SessionGroup { get; private set; } public static string FilePath => Path.Combine(Path.GetDirectoryName(Paths.ConfigPath) ?? Paths.BepInExRootPath, "SummitSupplyShop", "wallets.sss"); public static IReadOnlyList<WalletGroup> AllGroups { get { EnsureLoaded(); return GroupList; } } public static IReadOnlyList<Wallet> AllWallets { get { EnsureLoaded(); return WalletList; } } public static WalletGroup ActiveGroup { get { EnsureLoaded(); if (!string.IsNullOrEmpty(ActiveGroupId) && Groups.TryGetValue(ActiveGroupId, out var value)) { return value; } return null; } } public static bool ActiveIsPersistent => ActiveGroup?.Persistent ?? false; public static event Action Changed; public static bool IsSessionGroup(string groupId) { return string.Equals(groupId, "__session__", StringComparison.Ordinal); } private static bool GroupIsPersistent(string groupId) { if (string.IsNullOrEmpty(groupId) || IsSessionGroup(groupId)) { return false; } if (Groups.TryGetValue(groupId, out var value) && value != null) { return value.Persistent; } return false; } public static void EnsureLoaded() { if (!_loaded) { _loaded = true; LoadFromDisk(); EnsureSessionGroup(); if (string.IsNullOrEmpty(ActiveGroupId) || !Groups.ContainsKey(ActiveGroupId)) { ActiveGroupId = "__session__"; } } } private static void EnsureSessionGroup() { if (SessionGroup == null || !Groups.ContainsKey("__session__")) { SessionGroup = new WalletGroup { Id = "__session__", Name = "This session", Persistent = false }; Groups["__session__"] = SessionGroup; } } public static void Tick() { if (!(_saveAt < 0f) && !(Time.unscaledTime < _saveAt)) { _saveAt = -1f; SaveNow(); } } public static WalletGroup CreateGroup(string name) { EnsureLoaded(); WalletGroup walletGroup = new WalletGroup { Id = Guid.NewGuid().ToString("N"), Name = (string.IsNullOrWhiteSpace(name) ? "Group" : name.Trim()), Persistent = true }; Groups[walletGroup.Id] = walletGroup; GroupList.Add(walletGroup); ActiveGroupId = walletGroup.Id; ScheduleSave(); RaiseChanged(); return walletGroup; } public static bool SetActiveGroup(string groupId) { EnsureLoaded(); if (string.IsNullOrEmpty(groupId) || !Groups.ContainsKey(groupId)) { return false; } ActiveGroupId = groupId; ActorToWallet.Clear(); ScheduleSave(); RaiseChanged(); return true; } public static bool DeleteGroup(string groupId) { EnsureLoaded(); if (IsSessionGroup(groupId) || !Groups.TryGetValue(groupId, out var value) || !value.Persistent) { return false; } List<string> list = new List<string>(); for (int i = 0; i < WalletList.Count; i++) { if (WalletList[i].GroupId == groupId) { list.Add(WalletList[i].Id); } } for (int j = 0; j < list.Count; j++) { DeleteWallet(list[j], notify: false); } Groups.Remove(groupId); GroupList.Remove(value); if (ActiveGroupId == groupId) { ActorToWallet.Clear(); ActiveGroupId = "__session__"; } ScheduleSave(); RaiseChanged(); return true; } public static Wallet CreateWallet(string ownerKey, string displayName, int startingBalance) { EnsureLoaded(); if (ActiveGroup == null) { EnsureSessionGroup(); ActiveGroupId = "__session__"; } Wallet wallet = new Wallet { Id = Guid.NewGuid().ToString("N"), GroupId = ActiveGroupId, OwnerKey = (ownerKey ?? string.Empty), DisplayName = (string.IsNullOrWhiteSpace(displayName) ? "Player" : displayName.Trim()), Balance = Mathf.Max(0, startingBalance) }; Wallets[wallet.Id] = wallet; WalletList.Add(wallet); ScheduleSave(); RaiseChanged(); return wallet; } public static bool DeleteWallet(string walletId, bool notify = true) { EnsureLoaded(); if (!Wallets.TryGetValue(walletId, out var value)) { return false; } Wallets.Remove(walletId); WalletList.Remove(value); Ledgers.Remove(walletId); List<int> list = new List<int>(); foreach (KeyValuePair<int, string> item in ActorToWallet) { if (item.Value == walletId) { list.Add(item.Key); } } for (int i = 0; i < list.Count; i++) { ActorToWallet.Remove(list[i]); } if (notify) { ScheduleSave(); RaiseChanged(); } return true; } public static Wallet Get(string walletId) { EnsureLoaded(); if (string.IsNullOrEmpty(walletId)) { return null; } Wallets.TryGetValue(walletId, out var value); return value; } public static Wallet FindInActiveGroup(string ownerKey) { EnsureLoaded(); if (string.IsNullOrEmpty(ownerKey) || string.IsNullOrEmpty(ActiveGroupId)) { return null; } for (int i = 0; i < WalletList.Count; i++) { Wallet wallet = WalletList[i]; if (wallet.GroupId == ActiveGroupId && string.Equals(wallet.OwnerKey, ownerKey, StringComparison.Ordinal)) { return wallet; } } return null; } public static void BindActor(int actorNumber, string walletId) { if (actorNumber > 0 && !string.IsNullOrEmpty(walletId) && Wallets.ContainsKey(walletId) && (!ActorToWallet.TryGetValue(actorNumber, out var value) || !(value == walletId))) { ActorToWallet[actorNumber] = walletId; RaiseChanged(); } } public static void UnbindActor(int actorNumber) { if (ActorToWallet.Remove(actorNumber)) { RaiseChanged(); } } public static Wallet GetBound(int actorNumber) { if (!ActorToWallet.TryGetValue(actorNumber, out var value)) { return null; } return Get(value); } public static bool TryAdjust(string walletId, int delta, out int newBalance) { newBalance = 0; Wallet wallet = Get(walletId); if (wallet == null) { return false; } long num = (long)wallet.Balance + (long)delta; if (num < 0) { num = 0L; } if (num > int.MaxValue) { num = 2147483647L; } wallet.Balance = (int)num; newBalance = wallet.Balance; ScheduleSave(); RaiseChanged(); return true; } public static bool TrySpend(string walletId, int amount, out int newBalance) { newBalance = 0; if (amount < 0) { return false; } Wallet wallet = Get(walletId); if (wallet == null || wallet.Balance < amount) { return false; } wallet.Balance -= amount; newBalance = wallet.Balance; ScheduleSave(); RaiseChanged(); return true; } public static bool TryTransfer(string fromId, string toId, int amount, out int fromBalance, out int toBalance) { fromBalance = 0; toBalance = 0; if (amount <= 0 || fromId == toId) { return false; } Wallet wallet = Get(fromId); Wallet wallet2 = Get(toId); if (wallet == null || wallet2 == null || wallet.Balance < amount) { return false; } wallet.Balance -= amount; long num = (long)wallet2.Balance + (long)amount; wallet2.Balance = (int)((num > int.MaxValue) ? int.MaxValue : num); fromBalance = wallet.Balance; toBalance = wallet2.Balance; ScheduleSave(); RaiseChanged(); return true; } public static int GiveAllInActiveGroup(int amount) { EnsureLoaded(); if (amount == 0 || string.IsNullOrEmpty(ActiveGroupId)) { return 0; } int num = 0; for (int i = 0; i < WalletList.Count; i++) { Wallet wallet = WalletList[i]; if (!(wallet.GroupId != ActiveGroupId)) { long num2 = (long)wallet.Balance + (long)amount; if (num2 < 0) { num2 = 0L; } if (num2 > int.MaxValue) { num2 = 2147483647L; } wallet.Balance = (int)num2; num++; } } if (num > 0) { ScheduleSave(); RaiseChanged(); } return num; } public static string OwnerKey(Player player) { if (player == null) { return "local"; } if (!string.IsNullOrEmpty(player.UserId)) { return "id:" + player.UserId; } string text = (string.IsNullOrEmpty(player.NickName) ? "player" : player.NickName.Trim()); return "nick:" + text; } public static string DisplayName(Player player) { if (player == null) { return "Player"; } if (!string.IsNullOrEmpty(player.NickName)) { return player.NickName; } return "Player " + player.ActorNumber; } public static void Record(string walletId, byte kind, int amount, string otherName, string note, string itemName) { if (!string.IsNullOrEmpty(walletId)) { if (!Ledgers.TryGetValue(walletId, out var value)) { value = new List<LedgerEntry>(40); Ledgers[walletId] = value; } value.Add(new LedgerEntry { Kind = kind, Amount = amount, OtherName = (otherName ?? string.Empty), Note = (note ?? string.Empty), ItemName = (itemName ?? string.Empty) }); while (value.Count > 40) { value.RemoveAt(0); } ScheduleSave(); } } public static List<LedgerEntry> Recent(string walletId, int max) { List<LedgerEntry> list = new List<LedgerEntry>(); if (string.IsNullOrEmpty(walletId) || !Ledgers.TryGetValue(walletId, out var value) || value == null) { return list; } for (int i = Math.Max(0, value.Count - max); i < value.Count; i++) { list.Add(value[i]); } return list; } private static void ScheduleSave() { _saveAt = Time.unscaledTime + 0.35f; } private static void RaiseChanged() { WalletStore.Changed?.Invoke(); } private static void LoadFromDisk() { Groups.Clear(); Wallets.Clear(); Ledgers.Clear(); GroupList.Clear(); WalletList.Clear(); SessionGroup = null; ActiveGroupId = null; string filePath = FilePath; if (!File.Exists(filePath)) { return; } try { string[] array = File.ReadAllLines(filePath); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text) || text.StartsWith("SSS", StringComparison.Ordinal)) { continue; } if (text.StartsWith("ACTIVE ", StringComparison.Ordinal)) { string text2 = text.Substring(7).Trim(); if (!IsSessionGroup(text2)) { ActiveGroupId = text2; } } else if (text.StartsWith("GROUP ", StringComparison.Ordinal)) { string[] array2 = Split(text.Substring(6), 2); if (array2.Length >= 2 && !IsSessionGroup(array2[0])) { WalletGroup walletGroup = new WalletGroup { Id = array2[0], Name = Unescape(array2[1]), Persistent = true }; Groups[walletGroup.Id] = walletGroup; GroupList.Add(walletGroup); } } else if (text.StartsWith("WALLET ", StringComparison.Ordinal)) { string[] array3 = Split(text.Substring(7), 5); if (array3.Length >= 5) { int.TryParse(array3[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result); Wallet wallet = new Wallet { Id = array3[0], GroupId = array3[1], OwnerKey = Unescape(array3[2]), DisplayName = Unescape(array3[3]), Balance = Math.Max(0, result) }; Wallets[wallet.Id] = wallet; WalletList.Add(wallet); } } else if (text.StartsWith("LEDGER ", StringComparison.Ordinal)) { string[] array4 = Split(text.Substring(7), 6); if (array4.Length >= 6) { byte.TryParse(array4[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2); int.TryParse(array4[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3); Record(array4[0], result2, result3, Unescape(array4[3]), Unescape(array4[4]), Unescape(array4[5])); } } } } catch (Exception ex) { Plugin.LogError("Failed to load wallets: " + ex.Message); } } public static void SaveNow() { try { string filePath = FilePath; string directoryName = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } StringBuilder stringBuilder = new StringBuilder(512 + WalletList.Count * 80); stringBuilder.AppendLine("SSS1"); if (ActiveIsPersistent) { stringBuilder.Append("ACTIVE ").Append(ActiveGroupId).Append('\n'); } for (int i = 0; i < GroupList.Count; i++) { WalletGroup walletGroup = GroupList[i]; if (walletGroup != null && walletGroup.Persistent) { stringBuilder.Append("GROUP ").Append(walletGroup.Id).Append('|') .Append(Escape(walletGroup.Name)) .Append('\n'); } } for (int j = 0; j < WalletList.Count; j++) { Wallet wallet = WalletList[j]; if (wallet != null && GroupIsPersistent(wallet.GroupId)) { stringBuilder.Append("WALLET ").Append(wallet.Id).Append('|') .Append(wallet.GroupId) .Append('|') .Append(Escape(wallet.OwnerKey)) .Append('|') .Append(Escape(wallet.DisplayName)) .Append('|') .Append(wallet.Balance.ToString(CultureInfo.InvariantCulture)) .Append('\n'); } } foreach (KeyValuePair<string, List<LedgerEntry>> ledger in Ledgers) { Wallet wallet2 = Get(ledger.Key); if (wallet2 == null || !GroupIsPersistent(wallet2.GroupId)) { continue; } List<LedgerEntry> value = ledger.Value; if (value != null) { for (int k = 0; k < value.Count; k++) { LedgerEntry ledgerEntry = value[k]; stringBuilder.Append("LEDGER ").Append(ledger.Key).Append('|') .Append(ledgerEntry.Kind.ToString(CultureInfo.InvariantCulture)) .Append('|') .Append(ledgerEntry.Amount.ToString(CultureInfo.InvariantCulture)) .Append('|') .Append(Escape(ledgerEntry.OtherName)) .Append('|') .Append(Escape(ledgerEntry.Note)) .Append('|') .Append(Escape(ledgerEntry.ItemName)) .Append('\n'); } } } string text = filePath + ".tmp"; File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8); if (File.Exists(filePath)) { File.Replace(text, filePath, filePath + ".bak"); } else { File.Move(text, filePath); } try { File.Delete(filePath + ".bak"); } catch { } } catch (Exception ex) { Plugin.LogError("Failed to save wallets: " + ex.Message); } } private static string[] Split(string value, int count) { return value.Split(new char[1] { '|' }, count); } private static string Escape(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("\\", "\\\\").Replace("|", "\\p").Replace("\n", "\\n"); } private static string Unescape(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("\\n", "\n").Replace("\\p", "|").Replace("\\\\", "\\"); } } internal sealed class ShopListing { public ushort Id; public int Price; public string Name; } internal static class ShopCatalog { private static readonly Dictionary<ushort, ShopListing> ById = new Dictionary<ushort, ShopListing>(); private static readonly List<ShopListing> List = new List<ShopListing>(); private static readonly HashSet<ushort> Skipped = new HashSet<ushort>(); private static readonly Dictionary<string, int> DefaultPrices = CreateDefaults(); private static bool _loaded; private static float _saveAt = -1f; private static bool _remote; public static IReadOnlyList<ShopListing> Items { get { EnsureLoaded(); return List; } } public static string FilePath => Path.Combine(Path.GetDirectoryName(Paths.ConfigPath) ?? Paths.BepInExRootPath, "SummitSupplyShop", "shop.sss"); public static event Action Changed; public static bool Contains(ushort id) { EnsureLoaded(); return ById.ContainsKey(id); } public static void EnsureLoaded() { if (!_loaded) { _loaded = true; if (!_remote) { LoadFromPath(FilePath); } } } public static void Tick() { if (!_remote && !(_saveAt < 0f) && !(Time.unscaledTime < _saveAt)) { _saveAt = -1f; SaveNow(); } } public static void SeedFromCatalogIfEmpty() { EnsureLoaded(); FillMissingFromCatalog(); } public static void FillMissingFromCatalog() { EnsureLoaded(); if (!ItemCatalog.IsReady) { return; } bool flag = false; IReadOnlyList<CatalogItem> items = ItemCatalog.Items; for (int i = 0; i < items.Count; i++) { CatalogItem catalogItem = items[i]; if (catalogItem != null && !ById.ContainsKey(catalogItem.Id) && !Skipped.Contains(catalogItem.Id)) { Add(catalogItem.Id, DefaultPriceFor(catalogItem), catalogItem.DisplayName, notify: false); flag = true; } } if (flag) { ScheduleSave(); ShopCatalog.Changed?.Invoke(); } } public static bool TryGet(ushort id, out ShopListing listing) { EnsureLoaded(); return ById.TryGetValue(id, out listing); } public static bool Add(ushort id, int price, string name, bool notify = true) { EnsureLoaded(); if (id == 0) { return false; } price = Mathf.Max(0, price); Skipped.Remove(id); if (ById.TryGetValue(id, out var value)) { value.Price = price; if (!string.IsNullOrEmpty(name)) { value.Name = name; } } else { ShopListing shopListing = new ShopListing { Id = id, Price = price, Name = (string.IsNullOrEmpty(name) ? ItemCatalog.GetName(id) : name) }; ById[id] = shopListing; List.Add(shopListing); } if (notify) { ScheduleSave(); ShopCatalog.Changed?.Invoke(); } return true; } public static bool Remove(ushort id) { EnsureLoaded(); if (!ById.TryGetValue(id, out var value)) { return false; } ById.Remove(id); List.Remove(value); Skipped.Add(id); ScheduleSave(); ShopCatalog.Changed?.Invoke(); return true; } public static bool SetPrice(ushort id, int price) { EnsureLoaded(); if (!ById.TryGetValue(id, out var value)) { return false; } value.Price = Mathf.Max(0, price); ScheduleSave(); ShopCatalog.Changed?.Invoke(); return true; } public static void ReplaceFromNetwork(int[] ids, int[] prices, string[] names) { _remote = true; _loaded = true; ById.Clear(); List.Clear(); Skipped.Clear(); if (ids == null) { ShopCatalog.Changed?.Invoke(); return; } for (int i = 0; i < ids.Length; i++) { ushort id = (ushort)ids[i]; int price = ((prices != null && i < prices.Length) ? prices[i] : 0); string name = ((names != null && i < names.Length) ? names[i] : ItemCatalog.GetName(id)); Add(id, price, name, notify: false); } ShopCatalog.Changed?.Invoke(); } public static void Pack(out int[] ids, out int[] prices, out string[] names) { EnsureLoaded(); int count = List.Count; ids = new int[count]; prices = new int[count]; names = new string[count]; for (int i = 0; i < count; i++) { ids[i] = List[i].Id; prices[i] = List[i].Price; names[i] = List[i].Name ?? string.Empty; } } public static int DefaultPriceFor(CatalogItem item) { if (item == null) { return 10; } if (DefaultPrices.TryGetValue(Fold(item.DisplayName), out var value)) { return value; } if (DefaultPrices.TryGetValue(Fold(item.PrefabName), out value)) { return value; } return 10; } public static void WriteToPath(string path) { EnsureLoaded(); string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } StringBuilder stringBuilder = new StringBuilder(256 + List.Count * 32 + Skipped.Count * 12); stringBuilder.AppendLine("SSS1"); for (int i = 0; i < List.Count; i++) { ShopListing shopListing = List[i]; stringBuilder.Append("ITEM ").Append(shopListing.Id.ToString(CultureInfo.InvariantCulture)).Append('|') .Append(shopListing.Price.ToString(CultureInfo.InvariantCulture)) .Append('|') .Append((shopListing.Name ?? string.Empty).Replace("|", "/")) .Append('\n'); } foreach (ushort item in Skipped) { stringBuilder.Append("SKIP ").Append(item.ToString(CultureInfo.InvariantCulture)).Append('\n'); } string text = path + ".tmp"; File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8); if (File.Exists(path)) { File.Replace(text, path, path + ".bak"); } else { File.Move(text, path); } try { File.Delete(path + ".bak"); } catch { } } public static bool ReadFromPath(string path) { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return false; } ById.Clear(); List.Clear(); Skipped.Clear(); _loaded = true; LoadFromPath(path); if (List.Count <= 0) { return Skipped.Count > 0; } return true; } public static void SaveNow() { if (_remote) { return; } try { WriteToPath(FilePath); ShopPresetStore.SaveDefaultQuiet(); } catch (Exception ex) { Plugin.LogError("Failed to save shop: " + ex.Message); } } private static void ScheduleSave() { if (!_remote) { _saveAt = Time.unscaledTime + 0.35f; } } private static void LoadFromPath(string path) { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return; } try { string[] array = File.ReadAllLines(path); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text) || text.StartsWith("SSS", StringComparison.Ordinal)) { continue; } if (text.StartsWith("SKIP ", StringComparison.Ordinal)) { if (ushort.TryParse(text.Substring(5).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { Skipped.Add(result); } } else if (text.StartsWith("ITEM ", StringComparison.Ordinal)) { string[] array2 = text.Substring(5).Split(new char[1] { '|' }, 3); if (array2.Length >= 2 && ushort.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3); string name = ((array2.Length > 2) ? array2[2] : ItemCatalog.GetName(result2)); Add(result2, result3, name, notify: false); } } } } catch (Exception ex) { Plugin.LogError("Failed to load shop: " + ex.Message); } } private static string Fold(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static Dictionary<string, int> CreateDefaults() { return new Dictionary<string, int>(StringComparer.Ordinal) { { "bandage", 5 }, { "bandages", 5 }, { "medkit", 10 }, { "firstaidkit", 10 }, { "antidote", 8 }, { "cureall", 20 }, { "energybar", 4 }, { "granola", 4 }, { "trailmix", 5 }, { "sportsdrink", 6 }, { "lollipop", 3 }, { "scoutcookies", 6 }, { "rope", 12 }, { "ropespool", 12 }, { "spooler", 12 }, { "piton", 8 }, { "chainlauncher", 25 }, { "rescuehook", 18 }, { "backpack", 30 }, { "lantern", 10 }, { "torch", 6 }, { "flare", 4 }, { "compass", 8 }, { "binoculars", 8 }, { "balloon", 7 }, { "parasol", 12 }, { "bugle", 15 }, { "cactus", 6 }, { "coconut", 3 }, { "honeycomb", 5 }, { "egg", 4 }, { "marshmallow", 3 }, { "cookedmarshmallow", 4 }, { "beehive", 8 }, { "scorpion", 6 }, { "tick", 6 }, { "pandorasbox", 20 }, { "ancientidol", 25 }, { "piratecompass", 15 }, { "warpcompass", 18 }, { "magicbean", 12 }, { "cureallpotion", 20 }, { "heatpack", 6 }, { "snowball", 2 }, { "airplanefood", 6 }, { "airlinefood", 6 } }; } } internal static class ShopPresetStore { public const string DefaultName = "default"; private static string _folder; public static string FolderPath { get { EnsureFolder(); return _folder; } } public static void Initialize() { EnsureFolder(); } public static bool Save(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Type a name for this preset first."; return false; } EnsureFolder(); if (string.IsNullOrEmpty(_folder)) { error = "Could not create the Presets folder next to the plugin."; return false; } try { string text2 = Path.Combine(_folder, text + ".sss"); ShopCatalog.WriteToPath(text2); Plugin.LogInfo("Saved shop preset " + text + " to " + text2); return true; } catch (Exception ex) { error = "Failed to save preset: " + ex.Message; Plugin.LogError(error); return false; } } public static List<string> ListNames() { List<string> list = new List<string>(); EnsureFolder(); if (string.IsNullOrEmpty(_folder) || !Directory.Exists(_folder)) { return list; } try { string[] files = Directory.GetFiles(_folder, "*.sss"); for (int i = 0; i < files.Length; i++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); if (!string.IsNullOrEmpty(fileNameWithoutExtension)) { list.Add(fileNameWithoutExtension); } } list.Sort(StringComparer.OrdinalIgnoreCase); } catch (Exception ex) { Plugin.LogError("Failed to list shop presets: " + ex.Message); } return list; } public static bool Load(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Pick a preset to load."; return false; } EnsureFolder(); string path = Path.Combine(_folder ?? string.Empty, text + ".sss"); if (!File.Exists(path)) { error = "No preset named \"" + text + "\"."; return false; } try { if (!ShopCatalog.ReadFromPath(path)) { error = "Preset \"" + text + "\" is empty."; return false; } ShopCatalog.FillMissingFromCatalog(); Plugin.LogInfo("Loaded shop preset " + text); return true; } catch (Exception ex) { error = "Failed to load preset: " + ex.Message; Plugin.LogError(error); return false; } } public static bool SaveDefault() { string error; return Save("default", out error); } public static void SaveDefaultQuiet() { try { SaveDefault(); } catch (Exception ex) { Plugin.LogError("Failed to save default shop preset: " + ex.Message); } } public static bool TryLoadDefault() { EnsureFolder(); string path = Path.Combine(_folder ?? string.Empty, "default.sss"); if (!File.Exists(path)) { return false; } try { if (!ShopCatalog.ReadFromPath(path)) { return false; } ShopCatalog.FillMissingFromCatalog(); Plugin.LogInfo("Loaded last-used shop listings from the default preset."); return true; } catch (Exception ex) { Plugin.LogError("Failed to load the default shop preset: " + ex.Message); return false; } } public static bool IsDefaultName(string name) { return string.Equals(Sanitize(name), "default", StringComparison.OrdinalIgnoreCase); } public static bool Delete(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Pick a preset to delete."; return false; } if (IsDefaultName(text)) { error = "The default preset is updated automatically when you close the menu."; return false; } EnsureFolder(); string path = Path.Combine(_folder ?? string.Empty, text + ".sss"); if (!File.Exists(path)) { error = "No preset named \"" + text + "\"."; return false; } try { File.Delete(path); return true; } catch (Exception ex) { error = "Failed to delete preset: " + ex.Message; Plugin.LogError(error); return false; } } public static string Sanitize(string name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); char[] array = name.Trim().ToCharArray(); for (int i = 0; i < array.Length; i++) { char c = array[i]; bool flag = c < ' '; if (!flag) { for (int j = 0; j < invalidFileNameChars.Length; j++) { if (c == invalidFileNameChars[j]) { flag = true; break; } } } if (flag) { array[i] = '_'; } } string text = new string(array).Trim(); if (text.Length > 40) { text = text.Substring(0, 40).Trim(); } return text; } private static void EnsureFolder() { if (!string.IsNullOrEmpty(_folder) && Directory.Exists(_folder)) { return; } try { string location = typeof(Plugin).Assembly.Location; string text = (string.IsNullOrEmpty(location) ? null : Path.GetDirectoryName(location)); if (string.IsNullOrEmpty(text)) { text = Paths.PluginPath; } _folder = Path.Combine(text, "Presets"); Directory.CreateDirectory(_folder); } catch (Exception ex) { Plugin.LogError("Failed to create shop Presets folder: " + ex.Message); } } } internal sealed class PeerWalletView { public int Actor; public string Name; public string WalletId; public string OwnerKey; public int Balance; public bool Bound; } internal sealed class MoneyAskView { public string Id; public int FromActor; public string FromName; public int Amount; public string Note; } internal sealed class ShopNet : MonoBehaviour, IOnEventCallback, IInRoomCallbacks, IMatchmakingCallbacks { private sealed class PendingAsk { public string Id; public int FromActor; public int ToActor; public int Amount; public string Note; } public const byte EventCode = 182; public const string Magic = "tony4twentys.Summit_Supply_Shop"; public const int Protocol = 1; private const byte Hello = 1; private const byte Snapshot = 2; private const byte Buy = 3; private const byte BuyResult = 4; private const byte Transfer = 5; private const byte Ask = 6; private const byte AskOffer = 7; private const byte AskReply = 8; private const byte Status = 9; private const byte HistoryEvt = 10; private const byte Toast = 11; private const int ToastPopup = 0; private const int ToastGift = 1; private const int ToastLuggage = 2; private const byte LuggageClaim = 12; private const byte WalletSnap = 13; private const byte Checkout = 14; public static readonly List<PeerWalletView> Peers = new List<PeerWalletView>(8); public static readonly List<LedgerEntry> History = new List<LedgerEntry>(40); private static bool _checkoutOk; private readonly Dictionary<string, PendingAsk> _asks = new Dictionary<string, PendingAsk>(StringComparer.Ordinal); private Coroutine _handshake; private bool _subscribed; private int _hostBatch; private bool _hostDirty; private bool _hostCatalogDirty; public static ShopNet Instance { get; private set; } public static bool Synced { get; private set; } public static string StatusText { get; private set; } = string.Empty; public static string GroupName { get; private set; } = string.Empty; public static int LocalBalance { get; private set; } public static bool HasWallet { get; private set; } public static MoneyAskView IncomingAsk { get; private set; } public static int SessionStamp { get; private set; } public static bool IsHost { get { if (PhotonNetwork.InRoom) { return PhotonNetwork.IsMasterClient; } return true; } } public static void Ensure() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("SSS_Net"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent<ShopNet>(); if (PhotonNetwork.InRoom) { Instance.OnJoinedRoom(); } } } private void OnEnable() { if (!_subscribed) { PhotonNetwork.AddCallbackTarget((object)this); WalletStore.Changed += OnWalletChanged; ShopCatalog.Changed += OnCatalogChanged; _subscribed = true; ((MonoBehaviour)this).StartCoroutine(SaveLoop()); } } private void OnDisable() { if (_subscribed) { PhotonNetwork.RemoveCallbackTarget((object)this); WalletStore.Changed -= OnWalletChanged; ShopCatalog.Changed -= OnCatalogChanged; _subscribed = false; } } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } public static void NotifyCatalogReady() { if (!((Object)(object)Instance == (Object)null) && IsHost) { Instance.RunHostWork(delegate { WalletStore.EnsureLoaded(); ShopCatalog.EnsureLoaded(); ShopCatalog.SeedFromCatalogIfEmpty(); Instance._hostCatalogDirty = true; Instance._hostDirty = true; }); } } public void OnEvent(EventData photonEvent) { if (photonEvent == null || photonEvent.Code != 182 || !(photonEvent.CustomData is object[] array) || array.Length < 3 || !(array[0] is string text) || text != "tony4twentys.Summit_Supply_Shop" || !(array[1] is int num) || num != 1 || !(array[2] is byte b)) { return; } int sender = photonEvent.Sender; switch (b) { case 1: if (IsHost) { BroadcastSnapshot(); PushHistory(); } break; case 2: if (!IsHost) { ApplySnapshot(array); } break; case 13: if (!IsHost) { ApplyWalletSnap(array); } break; case 3: if (IsHost) { HostHandleBuy(sender, array); } break; case 14: if (IsHost) { HostHandleCheckout(sender, array); } break; case 4: ApplyBuyResult(array); break; case 5: if (IsHost) { HostHandleTransfer(sender, array); } break; case 6: if (IsHost) { HostHandleAsk(sender, array); } break; case 7: ApplyAskOffer(array); break; case 8: if (IsHost) { HostHandleAskReply(sender, array); } break; case 10: ApplyHistory(array); break; case 9: if (array.Length > 3 && array[3] is string statusText) { StatusText = statusText; RaiseState(); } break; case 11: ApplyToast(array); break; case 12: if (IsHost && array.Length > 3) { LuggageCoins.HostPay(sender, Convert.ToInt32(array[3])); } break; } } public void OnJoinedRoom() { IncomingAsk = null; if (IsHost) { BeginHostWork(); try { WalletStore.EnsureLoaded(); ShopCatalog.EnsureLoaded(); ShopCatalog.SeedFromCatalogIfEmpty(); AutoBindRoom(); _hostCatalogDirty = true; _hostDirty = true; } finally { EndHostWork(); } PushHistory(); Synced = true; } else { Synced = false; StatusText = "Waiting for host shop..."; StartHandshake(); } RaiseState(); } public void OnLeftRoom() { StopHandshake(); IncomingAsk = null; Synced = true; HasWallet = false; LocalBalance = 0; Peers.Clear(); History.Clear(); RaiseState(); } public void OnPlayerEnteredRoom(Player newPlayer) { if (IsHost) { BeginHostWork(); try { TryAutoBind(newPlayer); _hostDirty = true; } finally { EndHostWork(); } PushHistory(); } } public void OnPlayerLeftRoom(Player otherPlayer) { if (IsHost && otherPlayer != null) { RunHostWork(delegate { WalletStore.UnbindActor(otherPlayer.ActorNumber); }); } } public void OnMasterClientSwitched(Player newMasterClient) { OnJoinedRoom(); } public void OnCreatedRoom() { } public void OnCreateRoomFailed(short returnCode, string message) { } public void OnJoinRoomFailed(short returnCode, string message) { } public void OnJoinRandomFailed(short returnCode, string message) { } public void OnFriendListUpdate(List<FriendInfo> friendList) { } public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { } public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { } public static void RequestBuy(ushort itemId) { if (!((Object)(object)Instance == (Object)null)) { if (IsHost) { Instance.HostHandleBuy(LocalActor(), new object[4] { "tony4twentys.Summit_Supply_Shop", 1, (byte)3, (int)itemId }); } else { Send(new object[4] { "tony4twentys.Summit_Supply_Shop", 1, (byte)3, (int)itemId }, (ReceiverGroup)2); } } } public static void RequestCheckout(int[] ids, int[] qtys) { if (!((Object)(object)Instance == (Object)null) && ids != null && qtys != null && ids.Length != 0 && ids.Length == qtys.Length) { if (IsHost) { Instance.HostHandleCheckout(LocalActor(), new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)14, ids, qtys }); } else { Send(new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)14, ids, qtys }, (ReceiverGroup)2); } } } public static void RequestTransfer(int targetActor, int amount, string note) { if (!((Object)(object)Instance == (Object)null) && amount > 0 && targetActor != LocalActor()) { note = SanitizeNote(note); if (IsHost) { Instance.HostHandleTransfer(LocalActor(), new object[6] { "tony4twentys.Summit_Supply_Shop", 1, (byte)5, targetActor, amount, note }); } else { Send(new object[6] { "tony4twentys.Summit_Supply_Shop", 1, (byte)5, targetActor, amount, note }, (ReceiverGroup)2); } } } public static void RequestMoney(int targetActor, int amount, string note) { if (!((Object)(object)Instance == (Object)null) && amount > 0 && targetActor != LocalActor()) { note = SanitizeNote(note); if (IsHost) { Instance.HostHandleAsk(LocalActor(), new object[6] { "tony4twentys.Summit_Supply_Shop", 1, (byte)6, targetActor, amount, note }); } else { Send(new object[6] { "tony4twentys.Summit_Supply_Shop", 1, (byte)6, targetActor, amount, note }, (ReceiverGroup)2); } } } public static void ReplyMoneyAsk(bool accept) { if (IncomingAsk != null && !((Object)(object)Instance == (Object)null)) { string id = IncomingAsk.Id; IncomingAsk = null; if (IsHost) { Instance.HostHandleAskReply(LocalActor(), new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)8, id, accept }); RaiseState(); } else { Send(new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)8, id, accept }, (ReceiverGroup)2); RaiseState(); } } } public static void HostCreateWalletFor(Player player) { if (!IsHost || player == null || (Object)(object)Instance == (Object)null || WalletStore.ActiveGroup == null) { return; } Instance.RunHostWork(delegate { string ownerKey = WalletStore.OwnerKey(player); Wallet wallet = WalletStore.FindInActiveGroup(ownerKey); if (wallet == null) { wallet = WalletStore.CreateWallet(ownerKey, WalletStore.DisplayName(player), 0); } WalletStore.BindActor(player.ActorNumber, wallet.Id); }); Instance.PushHistoryFor(player.ActorNumber); } public static void HostAdjust(string walletId, int delta) { if (!IsHost || (Object)(object)Instance == (Object)null) { return; } int actor = 0; bool ok = false; Instance.RunHostWork(delegate { if (WalletStore.TryAdjust(walletId, delta, out var _)) { ok = true; WalletStore.Record(walletId, 9, delta, "Host", string.Empty, string.Empty); actor = ActorForWallet(walletId); Instance.SendToast(actor, delta, "from the host"); } }); if (ok) { Instance.PushHistoryFor(actor); } } public static void HostGiveAll(int amount) { if (!IsHost || (Object)(object)Instance == (Object)null) { return; } Instance.RunHostWork(delegate { WalletStore.GiveAllInActiveGroup(amount); string activeGroupId = WalletStore.ActiveGroupId; IReadOnlyList<Wallet> allWallets = WalletStore.AllWallets; for (int i = 0; i < allWallets.Count; i++) { Wallet wallet = allWallets[i]; if (wallet != null && !(wallet.GroupId != activeGroupId)) { WalletStore.Record(wallet.Id, 9, amount, "Host", "Give all", string.Empty); Instance.SendToast(ActorForWallet(wallet.Id), amount, "from the host"); } } }); Instance.PushHistory(); } public static int LocalActorNumber() { if (PhotonNetwork.LocalPlayer == null) { return 0; } return PhotonNetwork.LocalPlayer.ActorNumber; } public static void SetStatus(string message) { StatusText = message ?? string.Empty; ShopMenu.RefreshStatusIfOpen(); } public static void ClaimLuggageCoins(int luggageId) { if (!((Object)(object)Instance == (Object)null)) { if (IsHost) { LuggageCoins.HostPay(LocalActor(), luggageId); return; } Send(new object[4] { "tony4twentys.Summit_Supply_Shop", 1, (byte)12, luggageId }, (ReceiverGroup)2); } } public static void RunAfter(float seconds, Action action) { if (!((Object)(object)Instance == (Object)null) && action != null) { ((MonoBehaviour)Instance).StartCoroutine(Instance.Delayed(seconds, action)); } } private IEnumerator Delayed(float seconds, Action action) { yield return (object)new WaitForSecondsRealtime(seconds); try { action(); } catch (Exception ex) { Plugin.LogError("Delayed shop action failed: " + ex.Message); } } public static void HostGrantLuggageCoins(int actor) { if (!IsHost || (Object)(object)Instance == (Object)null || actor <= 0) { return; } bool paid = false; Instance.RunHostWork(delegate { Wallet wallet = WalletStore.GetBound(actor) ?? HostEnsureWallet(actor); if (wallet == null) { Instance.SendStatus(actor, "Open a wallet with the host to collect luggage coins."); } else { int num = Random.Range(Plugin.LuggageMinCoinsValue(), Plugin.LuggageMaxCoinsValue() + 1); if (num > 0 && WalletStore.TryAdjust(wallet.Id, num, out var _)) { WalletStore.Record(wallet.Id, 8, num, string.Empty, "Luggage", string.Empty); Instance.SendLuggageToast(actor, num); paid = true; } } }); if (paid) { Instance.PushHistoryFor(actor); } } public static void HostDataChanged() { if (!((Object)(object)Instance == (Object)null) && IsHost) { Instance.RunHostWork(AutoBindRoom); } } private void OnWalletChanged() { if (IsHost) { _hostDirty = true; if (_hostBatch == 0) { FlushHostWork(); } } } private void OnCatalogChanged() { ShopMenu.RebuildStoreIfOpen(); if (IsHost) { _hostDirty = true; _hostCatalogDirty = true; if (_hostBatch == 0) { FlushHostWork(); } } } private void RunHostWork(Action action) { BeginHostWork(); try { action?.Invoke(); } finally { EndHostWork(); } } private void BeginHostWork() { _hostBatch++; } private void EndHostWork() { _hostBatch--; if (_hostBatch <= 0) { _hostBatch = 0; FlushHostWork(); } } private void FlushHostWork() { if (IsHost && _hostBatch <= 0 && _hostDirty) { _hostDirty = false; bool hostCatalogDirty = _hostCatalogDirty; _hostCatalogDirty = false; RebuildLocalSession(); if (hostCatalogDirty) { BroadcastSnapshot(); } else { BroadcastWallets(); } } } private IEnumerator SaveLoop() { WaitForSecondsRealtime wait = new WaitForSecondsRealtime(1f); while (true) { yield return wait; if (IsHost) { WalletStore.Tick(); ShopCatalog.Tick(); } } } private void StartHandshake() { StopHandshake(); _handshake = ((MonoBehaviour)this).StartCoroutine(HandshakeLoop()); } private void StopHandshake() { if (_handshake != null) { ((MonoBehaviour)this).StopCoroutine(_handshake); _handshake = null; } } private IEnumerator HandshakeLoop() { float elapsed = 0f; while (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && !Synced && elapsed < 12f) { Send(new object[3] { "tony4twentys.Summit_Supply_Shop", 1, (byte)1 }, (ReceiverGroup)2); yield return (object)new WaitForSecondsRealtime(2f); elapsed += 2f; } if (!Synced && PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { StatusText = "Host does not have Summit Supply Shop."; RaiseState(); } _handshake = null; } private static void AutoBindRoom() { if (PhotonNetwork.CurrentRoom == null || PhotonNetwork.CurrentRoom.Players == null) { TryAutoBind(PhotonNetwork.LocalPlayer); return; } foreach (KeyValuePair<int, Player> player in PhotonNetwork.CurrentRoom.Players) { TryAutoBind(player.Value); } } private static void TryAutoBind(Player player) { if (player != null) { string ownerKey = WalletStore.OwnerKey(player); Wallet wallet = WalletStore.FindInActiveGroup(ownerKey); if (wallet == null && Plugin.GetAutoCreateWallets() && !WalletStore.ActiveIsPersistent) { wallet = WalletStore.CreateWallet(ownerKey, WalletStore.DisplayName(player), Plugin.StartingCoinsValue()); } if (wallet != null) { WalletStore.BindActor(player.ActorNumber, wallet.Id); } } } private static Wallet HostEnsureWallet(int actor) { Player val = FindPlayer(actor); if (val == null) { return null; } string ownerKey = WalletStore.OwnerKey(val); Wallet wallet = WalletStore.FindInActiveGroup(ownerKey); if (wallet == null) { if (!Plugin.GetAutoCreateWallets() || WalletStore.ActiveIsPersistent) { return null; } wallet = WalletStore.CreateWallet(ownerKey, WalletStore.DisplayName(val), Plugin.StartingCoinsValue()); } WalletStore.BindActor(actor, wallet.Id); return wallet; } private void RebuildLocalSession() { WalletStore.EnsureLoaded(); ShopCatalog.EnsureLoaded(); WalletGroup activeGroup = WalletStore.ActiveGroup; GroupName = ((activeGroup != null) ? activeGroup.Name : string.Empty); Peers.Clear(); HasWallet = false; LocalBalance = 0; int num = LocalActor(); string walletId = null; if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.Players != null) { foreach (KeyValuePair<int, Player> player in PhotonNetwork.CurrentRoom.Players) { Player value = player.Value; if (value != null) { Wallet bound = WalletStore.GetBound(value.ActorNumber); PeerWalletView peerWalletView = new PeerWalletView { Actor = value.ActorNumber, Name = WalletStore.DisplayName(value), OwnerKey = WalletStore.OwnerKey(value), WalletId = ((bound != null) ? bound.Id : string.Empty), Balance = (bound?.Balance ?? 0), Bound = (bound != null) }; Peers.Add(peerWalletView); if (value.ActorNumber == num) { HasWallet = bound != null; LocalBalance = peerWalletView.Balance; walletId = peerWalletView.WalletId; } } } } else { Player localPlayer = PhotonNetwork.LocalPlayer; string ownerKey = WalletStore.OwnerKey(localPlayer); Wallet wallet = WalletStore.FindInActiveGroup(ownerKey); HasWallet = wallet != null; LocalBalance = wallet?.Balance ?? 0; walletId = wallet?.Id; Peers.Add(new PeerWalletView { Actor = num, Name = WalletStore.DisplayName(localPlayer), OwnerKey = ownerKey, WalletId = ((wallet != null) ? wallet.Id : string.Empty), Balance = LocalBalance, Bound = (wallet != null) }); } LoadLocalHistory(walletId); Synced = true; RaiseState(); } private void BroadcastSnapshot() { if (IsHost) { ShopCatalog.Pack(out var ids, out var prices, out var names); int count = Peers.Count; string[] array = new string[count]; string[] array2 = new string[count]; string[] array3 = new string[count]; int[] array4 = new int[count]; int[] array5 = new int[count]; bool[] array6 = new bool[count]; for (int i = 0; i < count; i++) { PeerWalletView peerWalletView = Peers[i]; array[i] = peerWalletView.WalletId ?? string.Empty; array2[i] = peerWalletView.OwnerKey ?? string.Empty; array3[i] = peerWalletView.Name ?? string.Empty; array4[i] = peerWalletView.Actor; array5[i] = peerWalletView.Balance; array6[i] = peerWalletView.Bound; } Send(new object[13] { "tony4twentys.Summit_Supply_Shop", 1, (byte)2, GroupName ?? string.Empty, ids, prices, names, array, array2, array3, array4, array5, array6 }, (ReceiverGroup)1); } } private void BroadcastWallets() { if (IsHost) { int count = Peers.Count; string[] array = new string[count]; string[] array2 = new string[count]; string[] array3 = new string[count]; int[] array4 = new int[count]; int[] array5 = new int[count]; bool[] array6 = new bool[count]; for (int i = 0; i < count; i++) { PeerWalletView peerWalletView = Peers[i]; array[i] = peerWalletView.WalletId ?? string.Empty; array2[i] = peerWalletView.OwnerKey ?? string.Empty; array3[i] = peerWalletView.Name ?? string.Empty; array4[i] = peerWalletView.Actor; array5[i] = peerWalletView.Balance; array6[i] = peerWalletView.Bound; } Send(new object[10] { "tony4twentys.Summit_Supply_Shop", 1, (byte)13, GroupName ?? string.Empty, array, array2, array3, array4, array5, array6 }, (ReceiverGroup)0); } } private void ApplyWalletSnap(object[] data) { if (data != null && data.Length >= 10 && Synced) { GroupName = (data[3] as string) ?? GroupName; ApplyWalletArrays(data[4] as string[], data[5] as string[], data[6] as string[], data[7] as int[], data[8] as int[], data[9] as bool[]); RaiseState(); } } private void ApplySnapshot(object[] data) { if (data.Length >= 13) { StopHandshake(); GroupName = (data[3] as string) ?? string.Empty; ShopCatalog.ReplaceFromNetwork(ToIntArray(data[4]), ToIntArray(data[5]), data[6] as string[]); ApplyWalletArrays(data[7] as string[], data[8] as string[], data[9] as string[], data[10] as int[], data[11] as int[], data[12] as bool[]); Synced = true; StatusText = (HasWallet ? string.Empty : "Host has not created your wallet yet."); RaiseState(); } } private void ApplyWalletArrays(string[] walletIds, string[] ownerKeys, string[] peerNames, int[] actors, int[] balances, bool[] bound) { Peers.Clear(); HasWallet = false; LocalBalance = 0; int num = LocalActor(); int num2 = ((actors != null) ? actors.Length : 0); for (int i = 0; i < num2; i++) { PeerWalletView peerWalletView = new PeerWalletView { Actor = actors[i], Name = ((peerNames != null && i < peerNames.Length) ? peerNames[i] : "Player"), OwnerKey = ((ownerKeys != null && i < ownerKeys.Length) ? ownerKeys[i] : string.Empty), WalletId = ((walletIds != null && i < walletIds.Length) ? walletIds[i] : string.Empty), Balance = ((balances != null && i < balances.Length) ? balances[i] : 0), Bound = (bound != null && i < bound.Length && bound[i]) }; Peers.Add(peerWalletView); if (peerWalletView.Actor == num) { HasWallet = peerWalletView.Bound; LocalBalance = peerWalletView.Balance; } } } private void HostHandleBuy(int sender, object[] data) { if (data.Length < 4) { return; } ushort num = ToUshort(data[3]); Wallet bound = WalletStore.GetBound(sender); if (bound == null) { SendStatus(sender, "No wallet."); return; } if (!ShopCatalog.TryGet(num, out var listing)) { SendStatus(sender, "That item is not in the shop."); return; } GrantResult grantResult = GrantResult.Failed; bool flag = false; BeginHostWork(); try { if (!WalletStore.TrySpend(bound.Id, listing.Price, out var newBalance)) { SendStatus(sender, "Not enough coins."); return; } flag = true; grantResult = ItemGrant.GiveToActor(sender, num); if (grantResult == GrantResult.Failed) { WalletStore.TryAdjust(bound.Id, listing.Price, out newBalance); SendStatus(sender, "The shop could not deliver that item. You were refunded."); return; } string itemName = (string.IsNullOrEmpty(listing.Name) ? ItemCatalog.GetName(num) : listing.Name); WalletStore.Record(bound.Id, 1, listing.Price, string.Empty, string.Empty, itemName); } finally { EndHostWork(); } if (flag && grantResult != GrantResult.Failed) { string text = (string.IsNullOrEmpty(listing.Name) ? ItemCatalog.GetName(num) : listing.Name); SendToast(sender, -listing.Price, "for " + text); string text2 = ItemGrant.Describe(grantResult); if (text2.Length > 0) { SendStatus(sender, text2); } object[] array = new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)4, true, (int)num }; if (sender == LocalActor()) { ApplyBuyResult(array); } else { SendTo(sender, array); } PushHistoryFor(sender); } } private void HostHandleCheckout(int sender, object[] data) { if (data == null || data.Length < 5) { return; } int[] array = ToIntArray(data[3]); int[] array2 = ToIntArray(data[4]); if (array == null || array2 == null || array.Length == 0 || array.Length != array2.Length) { return; } Wallet bound = WalletStore.GetBound(sender); if (bound == null) { SendStatus(sender, "No wallet."); return; } int num = 0; List<ShopListing> list = new List<ShopListing>(array.Length); List<int> list2 = new List<int>(array.Length); for (int i = 0; i < array.Length; i++) { ushort id = (ushort)array[i]; int num2 = array2[i]; if (num2 > 0) { if (!ShopCatalog.TryGet(id, out var listing)) { SendStatus(sender, "That item is not in the shop."); return; } long num3 = (long)listing.Price * (long)num2; if (num3 > int.MaxValue) { return; } num += (int)num3; list.Add(listing); list2.Add(num2); } } if (list.Count == 0) { return; } BeginHostWork(); bool flag = false; int num4 = 0; int num5 = 0; try { if (!WalletStore.TrySpend(bound.Id, num, out var newBalance)) { SendStatus(sender, "Not enough coins."); return; } flag = true; for (int j = 0; j < list.Count; j++) { ShopListing shopListing = list[j]; int num6 = list2[j]; int num7 = 0; for (int k = 0; k < num6; k++) { if (ItemGrant.GiveToActor(sender, shopListing.Id) == GrantResult.Failed) { break; } num7++; } int num8 = shopListing.Price * num7; num4 += num8; num5 += num7; if (num7 > 0) { string text = (string.IsNullOrEmpty(shopListing.Name) ? ItemCatalog.GetName(shopListing.Id) : shopListing.Name); if (num7 > 1) { text = num7 + "x " + text; } WalletStore.Record(bound.Id, 1, num8, string.Empty, string.Empty, text); } int num9 = num6 - num7; if (num9 > 0) { WalletStore.TryAdjust(bound.Id, shopListing.Price * num9, out newBalance); } } } finally { EndHostWork(); } if (!flag) { return; } if (num5 <= 0) { SendStatus(sender, "The shop could not deliver those items. You were refunded."); return; } SendToast(sender, -num4, (num5 == 1) ? "for your cart" : ("for " + num5 + " items")); if (num4 < num) { SendStatus(sender, "Some items could not be delivered and were refunded."); } object[] array3 = new object[5] { "tony4twentys.Summit_Supply_Shop", 1, (byte)4, true, 0 }; if (sender == LocalActor()) { ApplyBuyResult(array3); } else { SendTo(sender, array3); } PushHistoryFor(sender); } private static void ApplyBuyResult(object[] data) { if (data.Length < 5) { return; } object obj = data[3]; bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } int num2 = num & (flag ? 1 : 0); ushort num3 = ToUshort(data[4]); if (num2 != 0) { if (num3 == 0) { StatusText = "Purchase complete."; _checkoutOk = true; } else { StatusText = "Purchased " + ItemCatalog.GetName(num3) + "."; } } RaiseState(); } public static bool TakeCheckoutSuccess() { bool checkoutOk = _checkoutOk; _checkoutOk = false; return checkoutOk; } private void HostHandleTransfer(int sender, object[] data) { if (data.Length < 5) { return; } int num = Convert.ToInt32(data[3]); int num2 = Convert.ToInt32(data[4]); string text = ((data.Length > 5) ? SanitizeNote(data[5] as string) : string.Empty); if (num == sender) { return; } Wallet bound = WalletStore.GetBound(sender); Wallet bound2 = WalletStore.GetBound(num); if (bound == null || bound2 == null) { SendStatus(sender, "Both players need wallets."); return; } string text2 = WalletStore.DisplayName(FindPlayer(sender)); string text3 = WalletStore.DisplayName(FindPlayer(num)); BeginHostWork(); try { if (!WalletStore.TryTransfer(bound.Id, bound2.Id, num2, out var _, out var _)) { SendStatus(sender, "Not enough coins."); return; } WalletStore.Record(bound.Id, 2, num2, text3, text, string.Empty); WalletStore.Record(bound2.Id, 3, num2, text2, text, string.Empty); } finally { EndHostWork(); } SendGiftNotice(num, text2, num2, text); SendToast(sender, -num2, "to " + text3); SendStatus(num, text2 + " sent you " + num2 + " coins" + ((text.Length > 0) ? (": " + text) : ".")); PushHistoryFor(sender); PushHistoryFor(num); } private void HostHandleAsk(int sender, object[] data) { if (data.Length < 5) { return; } int num = Convert.ToInt32(data[3]); int num2 = Convert.ToInt32(data[4]); string text = ((data.Length > 5) ? SanitizeNote(data[5] as string) : string.Empty); if (num == sender) { return; } Wallet bound = WalletStore.GetBound(sender); Wallet bound2 = WalletStore.GetBound(num); if (bound == null || bound2 == null) { SendStatus(sender, "Both players need wallets."); return; } PendingAsk pendingAsk = new PendingAsk { Id = Guid.NewGuid().ToString("N"), FromActor = sender, ToActor = num, Amount = num2, Note = text }; _asks[pendingAsk.Id] = pendingAsk; WalletStore.Record(bound.Id, 4, num2, WalletStore.DisplayName(FindPlayer(num)), text, string.Empty); WalletStore.Record(bound2.Id, 5, num2, WalletStore.DisplayName(FindPlayer(sender)), text, string.Empty); object[] array = new object[8] { "tony4twentys.Summit_Supply_Shop", 1, (byte)7, pendingAsk.Id, sender, WalletStore.DisplayName(FindPlayer(sender)), num2, text }; if (num == LocalActor()) { ApplyAskOffer(array); } else { SendTo(num, array); } SendStatus(sender, "Request sent."); PushHistory(); } private static void ApplyAskOffer(object[] data) { if (data.Length >= 7) { IncomingAsk = new MoneyAskView { Id = (data[3] as string), FromAc