Decompiled source of Summit Supply Shop v2.0.0

tony4twentys-Summit Supply Shop.dll

Decompiled a week ago
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