Decompiled source of ExtraInventorySlotUpgrade v1.0.0

plugins/ExtraInventorySlotUpgrade.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using REPOLib.Modules;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExtraInventorySlotUpgrade")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Extra Inventory Slot")]
[assembly: AssemblyTitle("ExtraInventorySlotUpgrade")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	public class IgnoresAccessChecksToAttribute : Attribute
	{
		public string AssemblyName { get; }

		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
			AssemblyName = assemblyName;
		}
	}
}
namespace ExtraInventorySlotUpgrade
{
	internal static class DebugCommands
	{
		public static void Register()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			try
			{
				Commands.RegisterCommand(new ChatCommand("eis", "Extra Inventory Slot: /eis slots <0-3> | /eis status", (Action<bool, string[]>)Execute, (Func<bool, string, string[], List<string>>)Suggest, (Func<bool>)null, false));
			}
			catch (Exception ex)
			{
				Log.Warn("Could not register the /eis chat command; use the DebugForceExtraSlots config entry instead. " + ex.Message);
			}
		}

		private static void Execute(bool isDebugConsole, string[] args)
		{
			if (args == null || args.Length == 0)
			{
				Status();
				return;
			}
			string text = args[0].ToLowerInvariant();
			int result;
			if (!(text == "slots"))
			{
				if (!(text == "status"))
				{
				}
				Status();
			}
			else if (args.Length < 2 || !int.TryParse(args[1], out result))
			{
				Log.Info("Usage: /eis slots <0-3>  (number of EXTRA slots on top of the vanilla 3)");
			}
			else if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				Log.Info("[/eis] Only the host can force a slot count.");
			}
			else
			{
				PluginConfig.DebugForceExtraSlots.Value = Math.Max(0, Math.Min(result, 3));
				Status();
			}
		}

		private static void Status()
		{
			int num = (((Object)(object)Inventory.instance != (Object)null) ? Inventory.instance.inventorySpots.Count : (-1));
			Log.Info($"[/eis] extra={SlotEngine.ExtraSlots} total={SlotEngine.TotalSlots} " + $"Inventory.inventorySpots.Count={num} " + $"maxExtra={SlotEngine.ConfiguredMaxExtra}");
		}

		private static List<string> Suggest(bool isDebugConsole, string partial, string[] args)
		{
			return new List<string> { "slots", "status" };
		}
	}
	internal static class ExtraInput
	{
		private const int KeyBase = 9000;

		private static readonly InputKey[] VanillaExpressionKeys;

		public static InputKey KeyForSlot(int slot)
		{
			return (InputKey)(9000 + slot);
		}

		public static bool IsSlotKey(InputKey key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Expected I4, but got Unknown
			int num = (int)key;
			if (num >= 9003)
			{
				return num < 9006;
			}
			return false;
		}

		public static IEnumerable<InputKey> SlotKeys()
		{
			for (int slot = 3; slot < 6; slot++)
			{
				yield return KeyForSlot(slot);
			}
		}

		public static void RegisterActions(InputManager manager)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0048: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected I4, but got Unknown
			if (manager?.inputActions != null)
			{
				for (int i = 3; i < 6; i++)
				{
					int num = i + 1;
					InputKey val = KeyForSlot(i);
					InputAction val2 = new InputAction($"Inventory{num}", (InputActionType)0, $"<Keyboard>/{num}", (string)null, (string)null, (string)null);
					val2.Enable();
					manager.inputActions[val] = val2;
					Log.Verbose($"Registered input action Inventory{num} -> <Keyboard>/{num} as InputKey {(int)val}.");
				}
			}
		}

		public static bool ConflictsWithSlotKey(InputKey vanillaKey)
		{
			//IL_001c: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			InputManager instance = InputManager.instance;
			if (instance?.inputActions == null)
			{
				return false;
			}
			if (!instance.inputActions.TryGetValue(vanillaKey, out var value))
			{
				return false;
			}
			foreach (InputKey item in SlotKeys())
			{
				if (!instance.inputActions.TryGetValue(item, out var value2))
				{
					continue;
				}
				Enumerator<InputBinding> enumerator2 = value.bindings.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						InputBinding current2 = enumerator2.Current;
						if (string.IsNullOrEmpty(((InputBinding)(ref current2)).effectivePath))
						{
							continue;
						}
						Enumerator<InputBinding> enumerator3 = value2.bindings.GetEnumerator();
						try
						{
							while (enumerator3.MoveNext())
							{
								InputBinding current3 = enumerator3.Current;
								if (((InputBinding)(ref current2)).effectivePath == ((InputBinding)(ref current3)).effectivePath)
								{
									return true;
								}
							}
						}
						finally
						{
							((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
			}
			return false;
		}

		public static bool IsVanillaExpressionKey(InputKey key)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			InputKey[] vanillaExpressionKeys = VanillaExpressionKeys;
			foreach (InputKey val in vanillaExpressionKeys)
			{
				if (key == val)
				{
					return true;
				}
			}
			return false;
		}

		public static bool ShouldSuppress(InputKey vanillaKey)
		{
			//IL_000e: 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_003c: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if (!PluginConfig.SuppressConflictingBinds.Value)
			{
				return false;
			}
			if (!IsVanillaExpressionKey(vanillaKey))
			{
				return false;
			}
			if (!ConflictsWithSlotKey(vanillaKey))
			{
				return false;
			}
			if (!PluginConfig.SuppressOnlyWhenSlotUnlocked.Value)
			{
				return true;
			}
			InputManager instance = InputManager.instance;
			if (!instance.inputActions.TryGetValue(vanillaKey, out var value))
			{
				return false;
			}
			for (int i = 3; i < 3 + SlotEngine.ExtraSlots; i++)
			{
				if (!instance.inputActions.TryGetValue(KeyForSlot(i), out var value2))
				{
					continue;
				}
				Enumerator<InputBinding> enumerator = value.bindings.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						InputBinding current = enumerator.Current;
						if (string.IsNullOrEmpty(((InputBinding)(ref current)).effectivePath))
						{
							continue;
						}
						Enumerator<InputBinding> enumerator2 = value2.bindings.GetEnumerator();
						try
						{
							while (enumerator2.MoveNext())
							{
								InputBinding current2 = enumerator2.Current;
								if (((InputBinding)(ref current)).effectivePath == ((InputBinding)(ref current2)).effectivePath)
								{
									return true;
								}
							}
						}
						finally
						{
							((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			return false;
		}

		static ExtraInput()
		{
			InputKey[] array = new InputKey[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			VanillaExpressionKeys = (InputKey[])(object)array;
		}
	}
	internal static class ExtraSlotStats
	{
		public const int VanillaSlots = 3;

		public const int AbsoluteMaxSlots = 6;

		private static readonly Dictionary<int, Dictionary<string, int>> Contents = new Dictionary<int, Dictionary<string, int>>();

		private static readonly Dictionary<int, Dictionary<string, int>> Taken = new Dictionary<int, Dictionary<string, int>>();

		private static StatsManager _registeredWith;

		public static string DictionaryName(int slot)
		{
			return "playerInventorySpot" + (slot + 1);
		}

		public static Dictionary<string, int> ContentsDict(int slot)
		{
			switch (slot)
			{
			case 0:
				return StatsManager.instance.playerInventorySpot1;
			case 1:
				return StatsManager.instance.playerInventorySpot2;
			case 2:
				return StatsManager.instance.playerInventorySpot3;
			default:
			{
				if (!Contents.TryGetValue(slot, out var value))
				{
					value = new Dictionary<string, int>();
					Contents[slot] = value;
				}
				return value;
			}
			}
		}

		public static Dictionary<string, int> TakenDict(int slot)
		{
			switch (slot)
			{
			case 0:
				return StatsManager.instance.playerInventorySpot1Taken;
			case 1:
				return StatsManager.instance.playerInventorySpot2Taken;
			case 2:
				return StatsManager.instance.playerInventorySpot3Taken;
			default:
			{
				if (!Taken.TryGetValue(slot, out var value))
				{
					value = new Dictionary<string, int>();
					Taken[slot] = value;
				}
				return value;
			}
			}
		}

		public static void RegisterWithStatsManager(StatsManager sm)
		{
			if ((Object)(object)sm == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)_registeredWith != (Object)(object)sm;
			_registeredWith = sm;
			for (int i = 3; i < 6; i++)
			{
				string text = DictionaryName(i);
				Dictionary<string, int> dictionary = ContentsDict(i);
				if (flag)
				{
					dictionary.Clear();
					TakenDict(i).Clear();
				}
				if (!sm.dictionaryOfDictionaries.ContainsKey(text))
				{
					sm.dictionaryOfDictionaries.Add(text, dictionary);
				}
				if (!sm.doNotSaveTheseDictionaries.Contains(text))
				{
					sm.doNotSaveTheseDictionaries.Add(text);
				}
				sm.stripTheseDictionaries[text] = -1;
				Log.Verbose("Registered stat dictionary \"" + text + "\".");
			}
		}

		public static void ClearTaken()
		{
			foreach (Dictionary<string, int> value in Taken.Values)
			{
				value.Clear();
			}
			Log.Verbose("Cleared extra-slot Taken markers.");
		}
	}
	internal static class ExtraSlotUpgrade
	{
		private static PlayerUpgrade _upgrade;

		private static bool _correcting;

		public static PlayerUpgrade Upgrade => _upgrade;

		public static void Register()
		{
			if (_upgrade != null)
			{
				EnsureStatDictionaryRegistered();
			}
			else if (UpgradeItemFactory.Build())
			{
				_upgrade = Upgrades.RegisterUpgrade("ExtraInventorySlot", UpgradeItemFactory.Item, (Action<PlayerAvatar, int>)StartAction, (Action<PlayerAvatar, int>)UpgradeAction);
				if (_upgrade == null)
				{
					Log.Error("REPOLib refused to register the upgrade. Buying the item will do nothing.");
					return;
				}
				EnsureStatDictionaryRegistered();
				Log.Info("Registered upgrade \"ExtraInventorySlot\".");
			}
		}

		private static void EnsureStatDictionaryRegistered()
		{
			StatsManager instance = StatsManager.instance;
			if (!((Object)(object)instance == (Object)null) && _upgrade != null)
			{
				string text = "playerUpgradeExtraInventorySlot";
				if (!instance.dictionaryOfDictionaries.ContainsKey(text))
				{
					instance.dictionaryOfDictionaries.Add(text, _upgrade.PlayerDictionary);
					Log.Verbose("Added \"" + text + "\" to StatsManager ahead of REPOLib.");
				}
			}
		}

		private static void StartAction(PlayerAvatar player, int level)
		{
			ApplyLevel(player, level, "start");
		}

		private static void UpgradeAction(PlayerAvatar player, int level)
		{
			ApplyLevel(player, level, "upgrade");
		}

		private static void ApplyLevel(PlayerAvatar player, int level, string source)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			if (!_correcting && PluginConfig.HostEnforcesCap.Value && SemiFunc.IsMasterClientOrSingleplayer() && _upgrade != null && level > SlotEngine.ConfiguredMaxExtra)
			{
				_correcting = true;
				try
				{
					Log.Warn($"{SemiFunc.PlayerGetName(player)} reported level {level}, above the cap " + $"of {SlotEngine.ConfiguredMaxExtra}. Clamping and re-broadcasting.");
					_upgrade.SetLevel(player.steamID, SlotEngine.ConfiguredMaxExtra);
					return;
				}
				finally
				{
					_correcting = false;
				}
			}
			if (!player.isLocal)
			{
				Log.Verbose($"Ignoring {source} level {level} for remote player " + SemiFunc.PlayerGetName(player) + ".");
				return;
			}
			int num = Mathf.Clamp(level, 0, SlotEngine.ConfiguredMaxExtra);
			SlotEngine.SetUpgradeExtraSlots(num);
			Log.Info($"Extra Inventory Slot {source}: level {level} -> {num} extra slot(s).");
		}

		public static int GetLocalLevel()
		{
			if (_upgrade == null)
			{
				return 0;
			}
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(instance.steamID))
			{
				return 0;
			}
			return _upgrade.GetLevel(instance.steamID);
		}

		public static bool IsAtCap(PlayerAvatar player)
		{
			if (_upgrade == null || (Object)(object)player == (Object)null)
			{
				return false;
			}
			return _upgrade.GetLevel(player) >= SlotEngine.ConfiguredMaxExtra;
		}
	}
	public class ExtraSlotUpgradeApplier : MonoBehaviour
	{
		private ItemToggle _itemToggle;

		private ItemUpgrade _itemUpgrade;

		private bool _wired;

		private void Awake()
		{
			Wire();
		}

		private void Start()
		{
			Wire();
		}

		private void Wire()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			if (_wired)
			{
				return;
			}
			_itemUpgrade = ((Component)this).GetComponent<ItemUpgrade>();
			_itemToggle = ((Component)this).GetComponent<ItemToggle>();
			if ((Object)(object)_itemUpgrade == (Object)null || _itemUpgrade.upgradeEvent == null)
			{
				Log.Error("Upgrade prop has no ItemUpgrade component; it will do nothing when used.");
				return;
			}
			int persistentEventCount = ((UnityEventBase)_itemUpgrade.upgradeEvent).GetPersistentEventCount();
			for (int i = 0; i < persistentEventCount; i++)
			{
				((UnityEventBase)_itemUpgrade.upgradeEvent).SetPersistentListenerState(i, (UnityEventCallState)0);
			}
			_itemUpgrade.upgradeEvent.RemoveListener(new UnityAction(Apply));
			_itemUpgrade.upgradeEvent.AddListener(new UnityAction(Apply));
			_wired = true;
			Log.Verbose($"Upgrade prop wired ({persistentEventCount} inherited listener(s) muted).");
		}

		public void Apply()
		{
			if ((Object)(object)_itemToggle == (Object)null)
			{
				Log.Error("Upgrade prop has no ItemToggle; cannot tell who used it.");
				return;
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID(_itemToggle.playerTogglePhotonID);
			if ((Object)(object)val == (Object)null)
			{
				Log.Error($"Could not resolve the player from photon ID {_itemToggle.playerTogglePhotonID}.");
			}
			else if (val.isLocal)
			{
				PlayerUpgrade val2 = default(PlayerUpgrade);
				if (!Upgrades.TryGetUpgrade("ExtraInventorySlot", ref val2))
				{
					Log.Error("Upgrade \"ExtraInventorySlot\" is not registered.");
					return;
				}
				int num = val2.AddLevel(val, 1);
				Log.Info($"{SemiFunc.PlayerGetName(val)} bought an extra inventory slot (level {num}).");
			}
		}
	}
	internal static class Log
	{
		private static ManualLogSource _source;

		public static void Init(ManualLogSource source)
		{
			_source = source;
		}

		public static void Info(string msg)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogInfo((object)msg);
			}
		}

		public static void Warn(string msg)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogWarning((object)msg);
			}
		}

		public static void Error(string msg)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogError((object)msg);
			}
		}

		public static void Verbose(string msg)
		{
			if (PluginConfig.VerboseLogging != null && PluginConfig.VerboseLogging.Value)
			{
				ManualLogSource source = _source;
				if (source != null)
				{
					source.LogInfo((object)msg);
				}
			}
		}
	}
	[BepInPlugin("extrainventoryslotupgrade", "Extra Inventory Slot", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "extrainventoryslotupgrade";

		public const string Name = "Extra Inventory Slot";

		public const string Version = "1.0.0";

		internal static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			WarnAboutConflicts();
			_harmony = new Harmony("extrainventoryslotupgrade");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			DebugCommands.Register();
			((Component)this).gameObject.AddComponent<SlotEngineRunner>();
			Log.Info("Extra Inventory Slot v1.0.0 loaded.");
		}

		private void WarnAboutConflicts()
		{
			try
			{
				foreach (string item in (from p in Chainloader.PluginInfos.Values
					where p != null && p.Metadata != null
					where !string.Equals(p.Metadata.GUID, "extrainventoryslotupgrade", StringComparison.OrdinalIgnoreCase)
					where p.Metadata.GUID.IndexOf("inventoryslot", StringComparison.OrdinalIgnoreCase) >= 0 || p.Metadata.Name.IndexOf("inventory slot", StringComparison.OrdinalIgnoreCase) >= 0
					select $"{p.Metadata.Name} ({p.Metadata.GUID} v{p.Metadata.Version})").ToList())
				{
					Log.Warn("=========================================================");
					Log.Warn(" CONFLICT: another inventory-slot mod is loaded: " + item);
					Log.Warn(" Running it alongside Extra Inventory Slot will duplicate HUD slots");
					Log.Warn(" and corrupt slot bookkeeping. Disable one of them.");
					Log.Warn("=========================================================");
				}
			}
			catch (Exception ex)
			{
				Log.Verbose("Conflict scan failed (harmless): " + ex.Message);
			}
		}
	}
	internal class SlotEngineRunner : MonoBehaviour
	{
		private void Update()
		{
			try
			{
				SlotEngine.Tick();
			}
			catch (Exception ex)
			{
				Log.Error("Slot engine tick failed: " + ex);
				((Behaviour)this).enabled = false;
			}
		}
	}
	internal static class PluginConfig
	{
		public const int AbsoluteMaxExtraSlots = 3;

		public static ConfigEntry<int> MaxExtraSlots;

		public static ConfigEntry<bool> HostEnforcesCap;

		public static ConfigEntry<int> DebugForceExtraSlots;

		public static ConfigEntry<bool> SuppressConflictingBinds;

		public static ConfigEntry<bool> SuppressOnlyWhenSlotUnlocked;

		public static ConfigEntry<float> PriceMin;

		public static ConfigEntry<float> PriceMax;

		public static ConfigEntry<float> PriceIncreasePerPurchase;

		public static ConfigEntry<string> UpgradeColor;

		public static ConfigEntry<int> AlbedoResolution;

		public static ConfigEntry<string> FrontFaceUV;

		public static ConfigEntry<string> SideBleedUV;

		public static ConfigEntry<bool> DumpPropTextures;

		public static ConfigEntry<bool> VerboseLogging;

		public static void Bind(ConfigFile cfg)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			PriceMin = cfg.Bind<float>("Shop", "PriceMin", 25000f, "Lower bound of the shop price, in the dollars the shop actually displays (so 25000 shows as $25K). This is NOT Item.value.valueMin — the game multiplies that by ShopManager.itemValueMultiplier and divides by 1000, and the mod does the conversion.");
			PriceMax = cfg.Bind<float>("Shop", "PriceMax", 30000f, "Upper bound of the shop price, in displayed dollars. Note the game still applies its usual upgrade discount of 10% per extra player, exactly as it does for vanilla upgrades.");
			PriceIncreasePerPurchase = cfg.Bind<float>("Shop", "PriceIncreasePerPurchase", 0f, new ConfigDescription("How much each previous purchase inflates the price, as a fraction of the base. Vanilla upgrades use 0.5, so a fourth copy costs three times the first — very visible at our price point. 0 keeps every roll inside PriceMin..PriceMax. Set to -1 to inherit whatever the game's own value is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 4f), Array.Empty<object>()));
			UpgradeColor = cfg.Bind<string>("Shop", "UpgradeColor", "#FF4FA3", "Albedo colour of the upgrade prop, as an HTML hex string. Applied to a per-instance material copy, so vanilla upgrades are never recoloured.");
			AlbedoResolution = cfg.Bind<int>("Shop", "AlbedoResolution", 1024, new ConfigDescription("Resolution of the generated prop texture. The vanilla source is 512; going higher keeps the custom front art crisper when you hold the prop close, at the cost of video memory. Never goes below the source resolution.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 512, 1024, 2048, 4096 }), Array.Empty<object>()));
			FrontFaceUV = cfg.Bind<string>("Shop", "FrontFaceUV", "0.5,0,0.86328125,0.55078125", "UV rectangle of the box's front panel as \"u0,v0,u1,v1\", measured from the vanilla upgrade atlas (pixels x 256-441, y 230-511 of 512). Only touch this if a game update moves the panel and the art lands on the wrong face.");
			SideBleedUV = cfg.Bind<string>("Shop", "SideBleedUV", "0.863281,0.277344,0.917969,0.445313", "UV rectangle on the side panel where the vanilla hero art spilled across the fold (pixels x 442-470, y 284-369 of 512). It gets healed by mirroring the clean texture beside it, otherwise a stray blob is left on the side of the box. Empty to disable.");
			SuppressConflictingBinds = cfg.Bind<bool>("Input", "SuppressConflictingBinds", true, "Keyboard 5 and 6 are bound to Expression1/Expression2 (emotes) in vanilla. When true, slot selection wins on those keys and the emote does not fire. Turn off if this fights another mod — you will then get both the slot and the emote on the same press.");
			SuppressOnlyWhenSlotUnlocked = cfg.Bind<bool>("Input", "SuppressOnlyWhenSlotUnlocked", false, "Narrows the setting above: only mute the emote once the slot sharing its key is actually unlocked. Keeps emotes 1 and 2 usable until you buy slots 5 and 6, at the cost of the key changing behaviour mid-run.");
			MaxExtraSlots = cfg.Bind<int>("Slots", "MaxExtraSlots", 3, new ConfigDescription("Maximum extra inventory slots a player can hold (on top of the vanilla 3). 3 = the intended 6-slot cap.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
			HostEnforcesCap = cfg.Bind<bool>("Slots", "HostEnforcesCap", true, "When you are the host, clamp every player's upgrade level to your MaxExtraSlots and push the correction back out. Turn off to let each client use its own cap, which is only sensible for solo play or testing.");
			DebugForceExtraSlots = cfg.Bind<int>("Debug", "DebugForceExtraSlots", 0, new ConfigDescription("Force this many extra slots, ignoring upgrades. 0 = off (normal behaviour). Also settable live with the /eis chat command. Ignored unless you are the host or playing solo, so it cannot be used to take slots you never bought in someone else's lobby.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			DumpPropTextures = cfg.Bind<bool>("Debug", "DumpPropTextures", false, "Write the vanilla upgrade prop's textures and UV layout to BepInEx/ExtraInventorySlotUpgrade-dump on the next run start. Development aid for positioning custom art; leave off for normal play.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log slot construction, HUD layout details and stat syncing to the BepInEx console.");
		}
	}
	internal static class PropTextureDump
	{
		private static bool _done;

		public static string DumpDirectory => Path.Combine(Paths.BepInExRootPath, "ExtraInventorySlotUpgrade-dump");

		public static void Run(GameObject prop, string baseItemName)
		{
			if (_done || !PluginConfig.DumpPropTextures.Value)
			{
				return;
			}
			_done = true;
			try
			{
				Directory.CreateDirectory(DumpDirectory);
				Log.Info("Dumping prop textures to " + DumpDirectory);
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("Base item: " + baseItemName);
				stringBuilder.AppendLine("Prop root: " + ((Object)prop).name);
				stringBuilder.AppendLine();
				int num = 0;
				Renderer[] componentsInChildren = prop.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					stringBuilder.AppendLine($"Renderer[{num}] \"{((Object)val).name}\" " + "(" + ((object)val).GetType().Name + ") path=" + DescribePath(((Component)val).transform, prop.transform));
					DescribeMesh(val, stringBuilder);
					Material[] sharedMaterials = val.sharedMaterials;
					for (int j = 0; j < sharedMaterials.Length; j++)
					{
						Material val2 = sharedMaterials[j];
						if ((Object)(object)val2 == (Object)null)
						{
							stringBuilder.AppendLine($"  Material[{j}] <null>");
							continue;
						}
						object arg = j;
						string name = ((Object)val2).name;
						Shader shader = val2.shader;
						stringBuilder.AppendLine($"  Material[{arg}] \"{name}\" shader=\"{((shader != null) ? ((Object)shader).name : null)}\"");
						DescribeAndDumpTextures(val2, num, j, stringBuilder);
					}
					stringBuilder.AppendLine();
					num++;
				}
				string text = Path.Combine(DumpDirectory, "layout.txt");
				File.WriteAllText(text, stringBuilder.ToString());
				Log.Info("Wrote " + text);
			}
			catch (Exception ex)
			{
				Log.Warn("Prop texture dump failed: " + ex);
			}
		}

		private static void DescribeMesh(Renderer renderer, StringBuilder report)
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = null;
			SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if (val2 != null)
			{
				val = val2.sharedMesh;
			}
			else
			{
				MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>();
				if ((Object)(object)component != (Object)null)
				{
					val = component.sharedMesh;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				report.AppendLine("  Mesh: <none>");
				return;
			}
			report.AppendLine($"  Mesh \"{((Object)val).name}\" submeshes={val.subMeshCount} readable={val.isReadable}");
			if (!val.isReadable)
			{
				report.AppendLine("    (not readable at runtime — UV bounds unavailable)");
				return;
			}
			try
			{
				Vector2[] uv = val.uv;
				if (uv == null || uv.Length == 0)
				{
					report.AppendLine("    (no UV0 channel)");
					return;
				}
				for (int i = 0; i < val.subMeshCount; i++)
				{
					int[] triangles = val.GetTriangles(i);
					if (triangles.Length == 0)
					{
						report.AppendLine($"    submesh[{i}]: empty");
						continue;
					}
					float num = float.MaxValue;
					float num2 = float.MaxValue;
					float num3 = float.MinValue;
					float num4 = float.MinValue;
					int[] array = triangles;
					foreach (int num5 in array)
					{
						if (num5 >= 0 && num5 < uv.Length)
						{
							Vector2 val3 = uv[num5];
							num = Mathf.Min(num, val3.x);
							num3 = Mathf.Max(num3, val3.x);
							num2 = Mathf.Min(num2, val3.y);
							num4 = Mathf.Max(num4, val3.y);
						}
					}
					report.AppendLine($"    submesh[{i}]: tris={triangles.Length / 3} " + $"uv=({num:0.####}, {num2:0.####}) .. ({num3:0.####}, {num4:0.####})");
				}
			}
			catch (Exception ex)
			{
				report.AppendLine("    (UV read failed: " + ex.Message + ")");
			}
		}

		private static void DescribeAndDumpTextures(Material material, int rendererIndex, int materialIndex, StringBuilder report)
		{
			string[] texturePropertyNames = material.GetTexturePropertyNames();
			foreach (string text in texturePropertyNames)
			{
				Texture texture = material.GetTexture(text);
				if ((Object)(object)texture == (Object)null)
				{
					report.AppendLine("    " + text + " = <none>");
					continue;
				}
				report.AppendLine($"    {text} = \"{((Object)texture).name}\" {texture.width}x{texture.height} " + "(" + ((object)texture).GetType().Name + ")");
				string text2 = Sanitize($"r{rendererIndex}_m{materialIndex}_{text}_{((Object)texture).name}") + ".png";
				string path = Path.Combine(DumpDirectory, text2);
				if (File.Exists(path))
				{
					continue;
				}
				Texture2D val = null;
				try
				{
					val = UpgradeAlbedo.ReadBack(texture);
					if (!((Object)(object)val == (Object)null))
					{
						val.Apply();
						File.WriteAllBytes(path, ImageConversion.EncodeToPNG(val));
						report.AppendLine("      -> " + text2);
					}
				}
				catch (Exception ex)
				{
					report.AppendLine("      -> dump failed: " + ex.Message);
				}
				finally
				{
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
		}

		private static string DescribePath(Transform child, Transform root)
		{
			string text = ((Object)child).name;
			Transform parent = child.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root.parent)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}

		private static string Sanitize(string name)
		{
			StringBuilder stringBuilder = new StringBuilder(name.Length);
			foreach (char c in name)
			{
				stringBuilder.Append((char.IsLetterOrDigit(c) || c == '_' || c == '-') ? c : '_');
			}
			return stringBuilder.ToString();
		}
	}
	internal static class SlotEngine
	{
		public const int VanillaSlots = 3;

		private static readonly List<GameObject> Clones = new List<GameObject>();

		private static readonly Dictionary<int, (Vector2 Show, Vector2 Hide)> VanillaRest = new Dictionary<int, (Vector2, Vector2)>();

		private static Inventory _vanillaRestFor;

		private static Inventory _builtFor;

		private static int _builtExtra = -1;

		private static bool _dumpedLayout;

		private static int _spacingWarnings;

		private static int _lastClampWarnedFor = int.MinValue;

		private static bool _warnedAboutForcedSlots;

		public static int UpgradeExtraSlots { get; private set; }

		public static int ExtraSlots { get; private set; }

		public static int TotalSlots => 3 + ExtraSlots;

		public static int ConfiguredMaxExtra => Mathf.Clamp(PluginConfig.MaxExtraSlots.Value, 0, 3);

		public static void SetUpgradeExtraSlots(int extra)
		{
			int num = Mathf.Clamp(extra, 0, ConfiguredMaxExtra);
			if (num != extra && extra != _lastClampWarnedFor)
			{
				Log.Warn($"Extra slot count {extra} clamped to {num}.");
				_lastClampWarnedFor = extra;
			}
			UpgradeExtraSlots = num;
		}

		public static void Tick()
		{
			SetUpgradeExtraSlots(ExtraSlotUpgrade.GetLocalLevel());
			int num = Mathf.Clamp(PluginConfig.DebugForceExtraSlots.Value, 0, ConfiguredMaxExtra);
			if (num > 0 && !SemiFunc.IsMasterClientOrSingleplayer())
			{
				if (!_warnedAboutForcedSlots)
				{
					_warnedAboutForcedSlots = true;
					Log.Warn("DebugForceExtraSlots is ignored when you are not the host. Buy the upgrade, or host the lobby yourself.");
				}
				num = 0;
			}
			int num2 = ((num > 0) ? num : UpgradeExtraSlots);
			if (num2 != ExtraSlots)
			{
				ExtraSlots = num2;
				_builtExtra = -1;
				Log.Info($"Local extra slot count -> {ExtraSlots} ({TotalSlots} total).");
			}
			Inventory instance = Inventory.instance;
			if ((Object)(object)instance == (Object)null || SemiFunc.RunIsArena())
			{
				return;
			}
			List<InventorySpot> inventorySpots = instance.inventorySpots;
			if (inventorySpots.Count < 3)
			{
				return;
			}
			for (int i = 0; i < 3; i++)
			{
				if ((Object)(object)inventorySpots[i] == (Object)null)
				{
					return;
				}
			}
			if (!((Object)(object)_builtFor == (Object)(object)instance) || _builtExtra != ExtraSlots || !ClonesHealthy(inventorySpots))
			{
				Rebuild(instance);
			}
		}

		private static bool ClonesHealthy(List<InventorySpot> spots)
		{
			if (spots.Count != 3 + ExtraSlots)
			{
				return false;
			}
			for (int i = 3; i < spots.Count; i++)
			{
				if ((Object)(object)spots[i] == (Object)null)
				{
					return false;
				}
			}
			return true;
		}

		private static void Rebuild(Inventory inv)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			List<InventorySpot> inventorySpots = inv.inventorySpots;
			if ((Object)(object)_builtFor != (Object)(object)inv || HasDamagedClones(inventorySpots))
			{
				TearDownAll(inventorySpots);
			}
			CaptureVanillaRest(inv, inventorySpots);
			InventorySpot val = inventorySpots[2];
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Transform parent = ((Component)val).transform.parent;
			bool flag = (Object)(object)parent != (Object)null && (Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null;
			Vector2 val2 = VanillaRest[2].Show - VanillaRest[1].Show;
			DumpLayout(inv, val, parent, flag, val2);
			if (ExtraSlots <= 0 && inventorySpots.Count == 3)
			{
				RepositionStartedSlots(inventorySpots, 3, flag);
				_builtFor = inv;
				_builtExtra = 0;
				return;
			}
			if (!flag && val2 == Vector2.zero)
			{
				if (_spacingWarnings++ < 3)
				{
					Log.Warn("Slot spacing not resolvable yet (slot 2 and 3 showPosition are equal). Retrying next frame.");
				}
				VanillaRest.Clear();
				_vanillaRestFor = null;
				return;
			}
			_spacingWarnings = 0;
			int num = 3 + ExtraSlots;
			while (inventorySpots.Count > num)
			{
				ReleaseAndDestroyLastClone(inventorySpots);
			}
			RepositionStartedSlots(inventorySpots, num, flag);
			int num2 = 0;
			while (inventorySpots.Count < num)
			{
				int count = inventorySpots.Count;
				GameObject val3 = CloneSpot(val, parent, RestingPositionFor(count, num), flag, count);
				if ((Object)(object)val3 == (Object)null)
				{
					break;
				}
				inventorySpots.Add(val3.GetComponent<InventorySpot>());
				Clones.Add(val3);
				num2++;
			}
			inv.spotsFeched = inventorySpots.Count == num;
			_builtFor = inv;
			_builtExtra = inventorySpots.Count - 3;
			Log.Info($"Slots reconciled: {inventorySpots.Count} total ({num2} added this pass).");
		}

		private static void RepositionStartedSlots(List<InventorySpot> spots, int total, bool hasLayoutGroup)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < spots.Count; i++)
			{
				MoveStartedSlot(spots[i], RestingPositionFor(i, total), hasLayoutGroup);
			}
		}

		private static bool HasDamagedClones(List<InventorySpot> spots)
		{
			for (int i = 3; i < spots.Count; i++)
			{
				if ((Object)(object)spots[i] == (Object)null)
				{
					return true;
				}
			}
			return Clones.Exists((GameObject c) => (Object)(object)c == (Object)null);
		}

		private static Vector2 RestingPositionFor(int slot, int total)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0040: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = Vector2.zero;
			for (int i = 0; i < 3; i++)
			{
				val += VanillaRest[i].Show;
			}
			val /= 3f;
			Vector2 val2 = VanillaRest[2].Show - VanillaRest[1].Show;
			return val + val2 * ((float)slot - (float)(total - 1) / 2f);
		}

		private static void CaptureVanillaRest(Inventory inv, List<InventorySpot> spots)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_vanillaRestFor == (Object)(object)inv && VanillaRest.Count == 3)
			{
				return;
			}
			VanillaRest.Clear();
			for (int i = 0; i < 3; i++)
			{
				InventorySpot val = spots[i];
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				VanillaRest[i] = (((SemiUI)val).showPosition, ((SemiUI)val).hidePosition);
			}
			_vanillaRestFor = inv;
			Log.Verbose("Captured vanilla slot resting positions.");
		}

		private static void MoveStartedSlot(InventorySpot spot, Vector2 target, bool hasLayoutGroup)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_006c: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)spot == (Object)null || hasLayoutGroup))
			{
				if (((SemiUI)spot).showPosition == Vector2.zero)
				{
					((Component)spot).transform.localPosition = new Vector3(target.x, target.y, ((Component)spot).transform.localPosition.z);
					return;
				}
				Vector2 val = ((SemiUI)spot).hidePosition - ((SemiUI)spot).showPosition;
				((SemiUI)spot).showPosition = target;
				((SemiUI)spot).hidePosition = target + val;
			}
		}

		private static GameObject CloneSpot(InventorySpot template, Transform parent, Vector2 target, bool hasLayoutGroup, int slot)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			bool activeSelf = ((Component)template).gameObject.activeSelf;
			((Component)template).gameObject.SetActive(false);
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false);
			}
			finally
			{
				((Component)template).gameObject.SetActive(activeSelf);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			((Object)val).name = $"Inventory Spot {slot + 1} (ExtraInventorySlot)";
			val.SetActive(false);
			InventorySpot component = val.GetComponent<InventorySpot>();
			if ((Object)(object)component == (Object)null)
			{
				Log.Error("Cloned slot has no InventorySpot component. Skipping this slot.");
				Object.Destroy((Object)(object)val);
				return null;
			}
			component.inventorySpotIndex = slot;
			InventoryBattery[] componentsInChildren = val.GetComponentsInChildren<InventoryBattery>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].inventorySpot = slot;
			}
			PhotonView component2 = val.GetComponent<PhotonView>();
			if ((Object)(object)component2 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component2);
				Log.Verbose($"Stripped duplicate PhotonView from slot {slot + 1}.");
			}
			RelabelSlotNumber(val, template, slot);
			RewindSemiUiState(template, component, target, hasLayoutGroup, slot);
			PrepareBatteryWidget(val, template, slot);
			val.transform.SetSiblingIndex(((Component)template).transform.GetSiblingIndex() + (slot - 2));
			val.SetActive(true);
			BatteryVisualLogic componentInChildren = val.GetComponentInChildren<BatteryVisualLogic>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				if (componentInChildren.targetScaleOriginal <= 0.001f)
				{
					Log.Error($"Slot {slot + 1}: battery widget cached targetScaleOriginal " + $"{componentInChildren.targetScaleOriginal} — ammo/charge bars will be invisible.");
				}
				else
				{
					Log.Verbose($"Slot {slot + 1}: battery widget cached targetScaleOriginal " + $"{componentInChildren.targetScaleOriginal:0.###}.");
				}
			}
			return val;
		}

		private static void RelabelSlotNumber(GameObject clone, InventorySpot template, int slot)
		{
			string text = (template.inventorySpotIndex + 1).ToString();
			string text2 = (slot + 1).ToString();
			List<string> list = new List<string>();
			TextMeshProUGUI[] componentsInChildren = clone.GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					bool num = ((Object)((Component)val).gameObject).name.StartsWith("Numbers", StringComparison.OrdinalIgnoreCase);
					bool flag = ((TMP_Text)val).text != null && ((TMP_Text)val).text.Trim() == text;
					if (num || flag)
					{
						((TMP_Text)val).text = text2;
						list.Add(((Object)((Component)val).gameObject).name);
					}
				}
			}
			if (list.Count > 0)
			{
				Log.Verbose(string.Format("Slot {0}: relabelled {1} to \"{2}\".", slot + 1, string.Join(", ", list), text2));
			}
			else
			{
				Log.Warn($"Slot {slot + 1}: found no number label; it will read \"{text}\".");
			}
		}

		private static void RewindSemiUiState(InventorySpot template, InventorySpot clone, Vector2 target, bool hasLayoutGroup, int slot)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0038: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			((SemiUI)clone).hidePosition = ((SemiUI)template).hidePosition - ((SemiUI)template).showPosition;
			((SemiUI)clone).showPosition = Vector2.zero;
			((SemiUI)clone).isHidden = false;
			if (!hasLayoutGroup)
			{
				((Component)clone).transform.localPosition = new Vector3(target.x, target.y, ((Component)template).transform.localPosition.z);
			}
			if (((SemiUI)template).originalScale != Vector3.zero)
			{
				((Component)clone).transform.localScale = ((SemiUI)template).originalScale;
			}
			Log.Verbose($"Slot {slot + 1}: resting position {target}, " + $"hidePosition {((SemiUI)clone).hidePosition}, scale {((Component)clone).transform.localScale}.");
		}

		private static void PrepareBatteryWidget(GameObject clone, InventorySpot template, int slot)
		{
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_005c: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			BatteryVisualLogic componentInChildren = ((Component)template).GetComponentInChildren<BatteryVisualLogic>(true);
			BatteryVisualLogic[] componentsInChildren = clone.GetComponentsInChildren<BatteryVisualLogic>(true);
			foreach (BatteryVisualLogic val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if ((Object)(object)val.batteryBarContainer != (Object)null)
				{
					List<GameObject> list = new List<GameObject>();
					foreach (Transform item in val.batteryBarContainer)
					{
						Transform val2 = item;
						if ((Object)(object)val2 != (Object)null)
						{
							list.Add(((Component)val2).gameObject);
						}
					}
					foreach (GameObject item2 in list)
					{
						Object.DestroyImmediate((Object)(object)item2);
					}
					if (list.Count > 0)
					{
						Log.Verbose($"Slot {slot + 1}: removed {list.Count} inherited battery bar(s).");
					}
				}
				if ((Object)(object)componentInChildren != (Object)null)
				{
					float num = ((componentInChildren.targetScaleOriginal > 0.001f) ? componentInChildren.targetScaleOriginal : 1f);
					((Component)val).transform.localScale = new Vector3(num, num, num);
					((Component)val).transform.localPosition = componentInChildren.targetPositionOriginal;
					((Component)val).transform.localRotation = Quaternion.Euler(0f, 0f, componentInChildren.targetRotationOriginal);
					if ((Object)(object)val.batteryBarDrain != (Object)null && componentInChildren.batteryDrainFullXScale > 0f)
					{
						Vector3 localScale = val.batteryBarDrain.localScale;
						val.batteryBarDrain.localScale = new Vector3(componentInChildren.batteryDrainFullXScale, localScale.y, localScale.z);
					}
					if ((Object)(object)val.batteryBarCharge != (Object)null && componentInChildren.batteryChargeFullXScale > 0f)
					{
						Vector3 localScale2 = val.batteryBarCharge.localScale;
						val.batteryBarCharge.localScale = new Vector3(componentInChildren.batteryChargeFullXScale, localScale2.y, localScale2.z);
					}
				}
				else if (((Component)val).transform.localScale.x <= 0.001f)
				{
					((Component)val).transform.localScale = Vector3.one;
				}
				val.itemBattery = null;
				val.doOutro = false;
				Transform val3 = ((Component)val).transform;
				while ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val3).gameObject != (Object)(object)clone)
				{
					if (!((Component)val3).gameObject.activeSelf)
					{
						((Component)val3).gameObject.SetActive(true);
					}
					val3 = val3.parent;
				}
				Log.Verbose($"Slot {slot + 1}: battery widget rest scale " + $"{((Component)val).transform.localScale.x:0.###}, position {((Component)val).transform.localPosition}.");
			}
			if ((Object)(object)clone.GetComponentInChildren<BatteryVisualLogic>(true) == (Object)null)
			{
				Log.Error($"Slot {slot + 1}: no BatteryVisualLogic at all. InventorySpot.Start will " + "throw. Please report the layout dump.");
			}
		}

		private static void ReleaseAndDestroyLastClone(List<InventorySpot> spots)
		{
			int num = spots.Count - 1;
			if (num < 3)
			{
				return;
			}
			InventorySpot val = spots[num];
			if ((Object)(object)val != (Object)null && val.IsOccupied() && (Object)(object)val.CurrentItem != (Object)null)
			{
				Log.Verbose($"Releasing item from slot {num + 1} before removing it.");
				val.CurrentItem.RequestUnequip();
			}
			spots.RemoveAt(num);
			int num2 = num - 3;
			if (num2 >= 0 && num2 < Clones.Count)
			{
				if ((Object)(object)Clones[num2] != (Object)null)
				{
					Object.Destroy((Object)(object)Clones[num2]);
				}
				Clones.RemoveAt(num2);
			}
		}

		private static void TearDownAll(List<InventorySpot> spots)
		{
			//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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			while (spots.Count > 3)
			{
				ReleaseAndDestroyLastClone(spots);
			}
			foreach (GameObject clone in Clones)
			{
				if ((Object)(object)clone != (Object)null)
				{
					Object.Destroy((Object)(object)clone);
				}
			}
			Clones.Clear();
			for (int i = 0; i < 3 && i < spots.Count; i++)
			{
				if (!((Object)(object)spots[i] == (Object)null) && VanillaRest.ContainsKey(i))
				{
					((SemiUI)spots[i]).showPosition = VanillaRest[i].Show;
					((SemiUI)spots[i]).hidePosition = VanillaRest[i].Hide;
				}
			}
			_builtFor = null;
			_builtExtra = -1;
		}

		private static void DumpLayout(Inventory inv, InventorySpot template, Transform parent, bool hasLayoutGroup, Vector2 step)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			if (_dumpedLayout && !PluginConfig.VerboseLogging.Value)
			{
				return;
			}
			_dumpedLayout = true;
			Log.Info("--- inventory HUD layout ---");
			Log.Info("  Inventory owner : " + ((Object)((Component)inv).gameObject).name);
			Log.Info("  Slot container  : " + DescribePath(parent));
			Log.Info("  Container comps : " + string.Join(", ", ComponentNames(((Component)parent).gameObject)));
			Log.Info("  LayoutGroup     : " + (hasLayoutGroup ? ((object)((Component)parent).GetComponent<LayoutGroup>()).GetType().Name : "none"));
			Log.Info($"  Inferred step   : {step}");
			for (int i = 0; i < inv.inventorySpots.Count; i++)
			{
				InventorySpot val = inv.inventorySpots[i];
				if ((Object)(object)val == (Object)null)
				{
					Log.Info($"  Slot {i + 1}: <null>");
				}
				else
				{
					Log.Info($"  Slot {i + 1}: {((Object)((Component)val).gameObject).name} show={((SemiUI)val).showPosition} " + $"hide={((SemiUI)val).hidePosition} local={((Component)val).transform.localPosition} " + $"scale={((Component)val).transform.localScale} origScale={((SemiUI)val).originalScale} " + $"animateAll={((SemiUI)val).animateTheEntireObject} sibling={((Component)val).transform.GetSiblingIndex()}");
				}
			}
			Log.Info("  Template components: " + string.Join(", ", ComponentNames(((Component)template).gameObject)));
		}

		private static List<string> ComponentNames(GameObject go)
		{
			List<string> list = new List<string>();
			Component[] components = go.GetComponents<Component>();
			foreach (Component val in components)
			{
				if ((Object)(object)val != (Object)null)
				{
					list.Add(((object)val).GetType().Name);
				}
			}
			return list;
		}

		private static string DescribePath(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return "<no parent>";
			}
			string text = ((Object)t).name;
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}
	}
	internal static class UpgradeAlbedo
	{
		private const string ResourceName = "ExtraInventorySlotUpgrade.Pack-texture.png";

		private static readonly Dictionary<Texture, Texture2D> Cache = new Dictionary<Texture, Texture2D>();

		private static Texture2D _customArt;

		private static bool _customArtLoaded;

		public static Texture2D Generate(Texture source, Color pink)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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)
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			if (Cache.TryGetValue(source, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				Texture2D val = ReadBack(source, source.width, source.height);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				Recolour(val, pink);
				val.Apply();
				int num = Mathf.Clamp(PluginConfig.AlbedoResolution.Value, source.width, 4096);
				if (num != ((Texture)val).width)
				{
					Texture2D val2 = ReadBack((Texture)(object)val, num, num);
					if ((Object)(object)val2 != (Object)null)
					{
						Object.Destroy((Object)(object)val);
						val = val2;
					}
				}
				HealSideBleed(val);
				PasteFrontFace(val);
				val.Apply(true);
				((Texture)val).wrapMode = source.wrapMode;
				((Texture)val).filterMode = source.filterMode;
				((Object)val).hideFlags = (HideFlags)32;
				Cache[source] = val;
				Log.Info($"Generated albedo from \"{((Object)source).name}\" at {((Texture)val).width}x{((Texture)val).height}.");
				return val;
			}
			catch (Exception ex)
			{
				Log.Warn("Could not generate an albedo from \"" + ((Object)source).name + "\"; falling back to plain colour tinting. " + ex.Message);
				return null;
			}
		}

		public static Texture2D ReadBack(Texture source, int width, int height)
		{
			//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_0048: 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_0064: Expected O, but got Unknown
			if (width <= 0 || height <= 0)
			{
				return null;
			}
			RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit(source, temporary);
				RenderTexture.active = temporary;
				Texture2D val = new Texture2D(width, height, (TextureFormat)4, true, false)
				{
					name = ((Object)source).name + " (Extra Inventory Slot)"
				};
				val.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				return val;
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		public static Texture2D ReadBack(Texture source)
		{
			return ReadBack(source, source.width, source.height);
		}

		private static void Recolour(Texture2D texture, Color pink)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(pink, ref num, ref num2, ref num3);
			Color[] pixels = texture.GetPixels();
			float num4 = default(float);
			float num5 = default(float);
			for (int i = 0; i < pixels.Length; i++)
			{
				Color val = pixels[i];
				Color.RGBToHSV(val, ref num3, ref num4, ref num5);
				float num6 = Mathf.Lerp(num2 * 0.55f, num2, num4);
				Color val2 = Color.HSVToRGB(num, num6, num5);
				val2.a = val.a;
				pixels[i] = val2;
			}
			texture.SetPixels(pixels);
		}

		private static void HealSideBleed(Texture2D albedo)
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			string value = PluginConfig.SideBleedUV.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				return;
			}
			if (!TryParseUvRect(value, out var rect))
			{
				Log.Warn("Could not parse SideBleedUV \"" + value + "\"; expected \"u0,v0,u1,v1\". Skipping the fold cleanup.");
				return;
			}
			int num = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).xMin * (float)((Texture)albedo).width), 0, ((Texture)albedo).width - 1);
			int num2 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).xMax * (float)((Texture)albedo).width), num + 1, ((Texture)albedo).width);
			int num3 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).yMin * (float)((Texture)albedo).height), 0, ((Texture)albedo).height - 1);
			int num4 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).yMax * (float)((Texture)albedo).height), num3 + 1, ((Texture)albedo).height);
			int num5 = num2 - num;
			int num6 = num4 - num3;
			int num7 = ((Texture)albedo).width - num2;
			if (num7 < 1)
			{
				Log.Warn("No clean texture to the right of the fold to heal from; skipping.");
				return;
			}
			int num8 = Mathf.Min(num5, num7);
			Color[] pixels = albedo.GetPixels(num2, num3, num8, num6);
			Color[] pixels2 = albedo.GetPixels(num, num3, num5, num6);
			for (int i = 0; i < num6; i++)
			{
				for (int j = 0; j < num5; j++)
				{
					int num9 = Mathf.Clamp(num5 - 1 - j, 0, num8 - 1);
					Color val = pixels[i * num8 + num9];
					val.a = pixels2[i * num5 + j].a;
					pixels2[i * num5 + j] = val;
				}
			}
			albedo.SetPixels(num, num3, num5, num6, pixels2);
			Log.Verbose($"Healed the fold bleed: {num5}x{num6} at ({num},{num3}).");
		}

		private static void PasteFrontFace(Texture2D albedo)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = LoadCustomArt();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (!TryParseUvRect(PluginConfig.FrontFaceUV.Value, out var rect))
			{
				Log.Warn("Could not parse FrontFaceUV \"" + PluginConfig.FrontFaceUV.Value + "\"; expected \"u0,v0,u1,v1\". Leaving the front face recoloured only.");
				return;
			}
			int num = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).xMin * (float)((Texture)albedo).width), 0, ((Texture)albedo).width - 1);
			int num2 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).xMax * (float)((Texture)albedo).width), num + 1, ((Texture)albedo).width);
			int num3 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).yMin * (float)((Texture)albedo).height), 0, ((Texture)albedo).height - 1);
			int num4 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref rect)).yMax * (float)((Texture)albedo).height), num3 + 1, ((Texture)albedo).height);
			int num5 = num2 - num;
			int num6 = num4 - num3;
			Color[] pixels = albedo.GetPixels(num, num3, num5, num6);
			for (int i = 0; i < num6; i++)
			{
				float num7 = ((float)i + 0.5f) / (float)num6;
				for (int j = 0; j < num5; j++)
				{
					float num8 = ((float)j + 0.5f) / (float)num5;
					Color pixelBilinear = val.GetPixelBilinear(num8, num7);
					Color val2 = pixels[i * num5 + j];
					pixelBilinear.a = val2.a;
					pixels[i * num5 + j] = pixelBilinear;
				}
			}
			albedo.SetPixels(num, num3, num5, num6, pixels);
			Log.Info($"Stamped custom front art into {num5}x{num6} at ({num},{num3}) " + $"of the {((Texture)albedo).width}x{((Texture)albedo).height} albedo.");
		}

		private static Texture2D LoadCustomArt()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			if (_customArtLoaded)
			{
				return _customArt;
			}
			_customArtLoaded = true;
			try
			{
				using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("ExtraInventorySlotUpgrade.Pack-texture.png");
				if (stream == null)
				{
					Log.Warn("Embedded resource \"ExtraInventorySlotUpgrade.Pack-texture.png\" is missing; the prop will be a plain recolour. This is a build problem, not a config one.");
					return null;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false, false);
				if (!ImageConversion.LoadImage(val, array))
				{
					Log.Warn("Custom front art failed to decode.");
					Object.Destroy((Object)(object)val);
					return null;
				}
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Object)val).hideFlags = (HideFlags)32;
				_customArt = val;
				Log.Info($"Loaded custom front art ({((Texture)val).width}x{((Texture)val).height}).");
				return _customArt;
			}
			catch (Exception ex)
			{
				Log.Warn("Could not load the custom front art: " + ex.Message);
				return null;
			}
		}

		private static bool TryParseUvRect(string value, out Rect rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			rect = default(Rect);
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string[] array = value.Split(',');
			if (array.Length != 4)
			{
				return false;
			}
			float[] array2 = new float[4];
			for (int i = 0; i < 4; i++)
			{
				if (!float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return false;
				}
			}
			float num = Mathf.Min(array2[0], array2[2]);
			float num2 = Mathf.Max(array2[0], array2[2]);
			float num3 = Mathf.Min(array2[1], array2[3]);
			float num4 = Mathf.Max(array2[1], array2[3]);
			if (num2 <= num || num4 <= num3)
			{
				return false;
			}
			rect = Rect.MinMaxRect(num, num3, num2, num4);
			return true;
		}
	}
	internal static class UpgradeItemFactory
	{
		public const string UpgradeId = "ExtraInventorySlot";

		public const string ItemAssetName = "Item Upgrade Player Extra Inventory Slot";

		public const string DisplayName = "Extra Inventory Slot";

		private static readonly string[] ColorProperties = new string[5] { "_Color", "_BaseColor", "_MainColor", "_TintColor", "_AlbedoColor" };

		private static bool _attempted;

		public static Item Item { get; private set; }

		public static GameObject Prefab { get; private set; }

		public static bool Build()
		{
			if (_attempted)
			{
				return (Object)(object)Item != (Object)null;
			}
			_attempted = true;
			try
			{
				return BuildInternal();
			}
			catch (Exception ex)
			{
				Log.Error("Failed to build the upgrade item. The mod's slot engine still works, but the shop item will be missing. " + ex);
				return false;
			}
		}

		private static bool BuildInternal()
		{
			GameObject prefab;
			Item val = FindBaseUpgradeItem(out prefab);
			if ((Object)(object)val == (Object)null || (Object)(object)prefab == (Object)null)
			{
				Log.Error("Could not find any vanilla player-upgrade item to clone. Shop item skipped.");
				return false;
			}
			Log.Info("Cloning \"" + ((Object)val).name + "\" as the base for the Extra Inventory Slot upgrade.");
			PropTextureDump.Run(prefab, ((Object)val).name);
			Prefab = ClonePrefab(prefab);
			if ((Object)(object)Prefab == (Object)null)
			{
				return false;
			}
			StripVanillaUpgradeBehaviour(Prefab);
			ColorPresets val2 = CreatePinkPreset();
			Item = CreateItemAsset(val, val2);
			ItemAttributes component = Prefab.GetComponent<ItemAttributes>();
			if ((Object)(object)component == (Object)null)
			{
				Log.Error("Cloned prop has no ItemAttributes component. Shop item skipped.");
				return false;
			}
			component.item = Item;
			ItemUpgrade component2 = Prefab.GetComponent<ItemUpgrade>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.isPlayerUpgrade = true;
				component2.colorPreset = val2;
			}
			TintPink(Prefab);
			if ((Object)(object)Prefab.GetComponent<ExtraSlotUpgradeApplier>() == (Object)null)
			{
				Prefab.AddComponent<ExtraSlotUpgradeApplier>();
			}
			if (Items.RegisterItem(component) == null)
			{
				Log.Error("REPOLib refused to register the item. Shop item skipped.");
				return false;
			}
			Log.Info("Registered \"Extra Inventory Slot\" — expected shop price " + $"${PluginConfig.PriceMin.Value:N0}-${PluginConfig.PriceMax.Value:N0} " + $"(raw value {Item.value.valueMin:N1}-{Item.value.valueMax:N1}).");
			return true;
		}

		private static Item FindBaseUpgradeItem(out GameObject prefab)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			prefab = null;
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				return null;
			}
			Item val = null;
			GameObject val2 = null;
			foreach (Item value in StatsManager.instance.itemDictionary.Values)
			{
				if ((Object)(object)value == (Object)null || value.disabled || (int)value.itemType != 3 || ((Object)value).name == "Item Upgrade Player Extra Inventory Slot" || value.prefab == null || !((PrefabRef<GameObject>)(object)value.prefab).IsValid())
				{
					continue;
				}
				GameObject prefab2;
				try
				{
					prefab2 = ((PrefabRef<GameObject>)(object)value.prefab).Prefab;
				}
				catch (Exception ex)
				{
					Log.Verbose("Skipping \"" + ((Object)value).name + "\": prefab load failed (" + ex.Message + ").");
					continue;
				}
				if ((Object)(object)prefab2 == (Object)null)
				{
					continue;
				}
				ItemUpgrade component = prefab2.GetComponent<ItemUpgrade>();
				if (!((Object)(object)component == (Object)null) && component.isPlayerUpgrade && !((Object)(object)prefab2.GetComponent<ItemAttributes>() == (Object)null))
				{
					if (((Object)value).name.IndexOf("Grab Strength", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						prefab = prefab2;
						return value;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = value;
						val2 = prefab2;
					}
				}
			}
			prefab = val2;
			return val;
		}

		private static GameObject ClonePrefab(GameObject basePrefab)
		{
			bool activeSelf = basePrefab.activeSelf;
			basePrefab.SetActive(false);
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(basePrefab);
			}
			finally
			{
				basePrefab.SetActive(activeSelf);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			((Object)val).name = "Item Upgrade Player Extra Inventory Slot";
			val.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val);
			return val;
		}

		private static void StripVanillaUpgradeBehaviour(GameObject prefab)
		{
			List<Component> list = new List<Component>();
			Component[] components = prefab.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string name = ((object)val).GetType().Name;
					if (name.StartsWith("ItemUpgradePlayer", StringComparison.Ordinal) || name.StartsWith("ItemUpgradeDeathHead", StringComparison.Ordinal) || name.StartsWith("ItemUpgradeMap", StringComparison.Ordinal))
					{
						list.Add(val);
					}
				}
			}
			foreach (Component item in list)
			{
				Log.Verbose("Stripped vanilla upgrade behaviour: " + ((object)item).GetType().Name + ".");
				Object.DestroyImmediate((Object)(object)item);
			}
			if (list.Count == 0)
			{
				Log.Warn("Found no vanilla upgrade behaviour to strip on the cloned prop. If buying this item also grants another upgrade, that is why — please report it.");
			}
		}

		private static Item CreateItemAsset(Item baseItem, ColorPresets colours)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			Item obj = ScriptableObject.CreateInstance<Item>();
			((Object)obj).name = "Item Upgrade Player Extra Inventory Slot";
			((Object)obj).hideFlags = (HideFlags)32;
			obj.itemName = "Extra Inventory Slot";
			obj.itemNameLocalized = null;
			obj.description = "Adds one extra inventory slot. Maximum 3 per player.";
			obj.itemType = baseItem.itemType;
			obj.emojiIcon = baseItem.emojiIcon;
			obj.itemVolume = baseItem.itemVolume;
			obj.itemSecretShopType = (itemSecretShopType)0;
			obj.colorPreset = colours;
			obj.spawnRotationOffset = baseItem.spawnRotationOffset;
			obj.physicalItem = baseItem.physicalItem;
			obj.disabled = false;
			obj.maxAmount = 1;
			obj.maxAmountInShop = 1;
			obj.minPlayerCount = 1;
			obj.maxPurchase = false;
			obj.maxPurchaseAmount = 1;
			Value val = ScriptableObject.CreateInstance<Value>();
			((Object)val).name = "Value - Extra Inventory Slot";
			((Object)val).hideFlags = (HideFlags)32;
			val.valueMin = ToRawValue(Mathf.Min(PluginConfig.PriceMin.Value, PluginConfig.PriceMax.Value));
			val.valueMax = ToRawValue(Mathf.Max(PluginConfig.PriceMin.Value, PluginConfig.PriceMax.Value));
			obj.value = val;
			return obj;
		}

		private static float ToRawValue(float displayedDollars)
		{
			float num = ShopValueMultiplier();
			float num2 = displayedDollars / num;
			Log.Verbose($"Price ${displayedDollars:N0} -> raw value {num2:N1} (multiplier {num}).");
			return num2;
		}

		private static float ShopValueMultiplier()
		{
			ShopManager instance = ShopManager.instance;
			if ((Object)(object)instance != (Object)null && instance.itemValueMultiplier > 0f)
			{
				return instance.itemValueMultiplier;
			}
			return 4f;
		}

		private static ColorPresets CreatePinkPreset()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Color val = ParseColour(PluginConfig.UpgradeColor.Value);
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(val, ref num, ref num2, ref num3);
			ColorPresets obj = ScriptableObject.CreateInstance<ColorPresets>();
			((Object)obj).name = "Color Preset - Extra Inventory Slot";
			((Object)obj).hideFlags = (HideFlags)32;
			obj.colorMain = val;
			obj.colorLight = Color.HSVToRGB(num, Mathf.Clamp01(num2 * 0.6f), Mathf.Clamp01(num3 * 1.2f));
			obj.colorDark = Color.HSVToRGB(num, Mathf.Clamp01(num2 * 1.1f), Mathf.Clamp01(num3 * 0.55f));
			return obj;
		}

		private static void TintPink(GameObject prefab)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			Color val = ParseColour(PluginConfig.UpgradeColor.Value);
			int num = 0;
			foreach (Renderer item in BodyRenderers(prefab))
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = item.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
				for (int i = 0; i < sharedMaterials.Length; i++)
				{
					if ((Object)(object)sharedMaterials[i] == (Object)null)
					{
						continue;
					}
					Material val2 = new Material(sharedMaterials[i])
					{
						name = ((Object)sharedMaterials[i]).name + " (Extra Inventory Slot)",
						hideFlags = (HideFlags)32
					};
					if (!ApplyGeneratedAlbedo(val2, val))
					{
						List<string> list = new List<string>();
						string[] colorProperties = ColorProperties;
						foreach (string text in colorProperties)
						{
							if (val2.HasProperty(text))
							{
								val2.SetColor(text, val);
								list.Add(text);
							}
						}
						if (list.Count == 0)
						{
							string[] obj = new string[5]
							{
								"Material \"",
								((Object)sharedMaterials[i]).name,
								"\" (shader \"",
								null,
								null
							};
							Shader shader = val2.shader;
							obj[3] = ((shader != null) ? ((Object)shader).name : null);
							obj[4] = "\") has no albedo texture and none of the expected colour properties; it will keep its original look.";
							Log.Warn(string.Concat(obj));
						}
						else
						{
							string[] obj2 = new string[7]
							{
								"Colour-tinted \"",
								((Object)sharedMaterials[i]).name,
								"\" (shader \"",
								null,
								null,
								null,
								null
							};
							Shader shader2 = val2.shader;
							obj2[3] = ((shader2 != null) ? ((Object)shader2).name : null);
							obj2[4] = "\") via ";
							obj2[5] = string.Join(", ", list);
							obj2[6] = ".";
							Log.Verbose(string.Concat(obj2));
						}
					}
					array[i] = val2;
					num++;
				}
				item.sharedMaterials = array;
			}
			Log.Info($"Restyled {num} material(s) to {PluginConfig.UpgradeColor.Value}.");
		}

		private static IEnumerable<Renderer> BodyRenderers(GameObject prefab)
		{
			Transform val = prefab.transform.Find("Mesh");
			if ((Object)(object)val != (Object)null)
			{
				MeshRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<MeshRenderer>(true);
				if (componentsInChildren.Length != 0)
				{
					Log.Verbose($"Restyling the \"Mesh\" body ({componentsInChildren.Length} renderer(s)).");
					return (IEnumerable<Renderer>)(object)componentsInChildren;
				}
			}
			List<Renderer> list = new List<Renderer>();
			MeshRenderer[] componentsInChildren2 = prefab.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val2 in componentsInChildren2)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = ((Renderer)val2).sharedMaterials;
				foreach (Material val3 in sharedMaterials)
				{
					if (!((Object)(object)val3 == (Object)null) && !((Object)(object)FindAlbedo(val3, out var _) == (Object)null))
					{
						list.Add((Renderer)(object)val2);
						break;
					}
				}
			}
			Log.Warn($"No \"Mesh\" child on the upgrade prop; falling back to {list.Count} " + "textured renderer(s). Report this if the prop looks wrong.");
			return list;
		}

		private static bool ApplyGeneratedAlbedo(Material material, Color pink)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			string property;
			Texture val = FindAlbedo(material, out property);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Texture2D val2 = UpgradeAlbedo.Generate(val, pink);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			List<string> list = new List<string>();
			string[] texturePropertyNames = material.GetTexturePropertyNames();
			foreach (string text in texturePropertyNames)
			{
				if (!((Object)(object)material.GetTexture(text) != (Object)(object)val))
				{
					material.SetTexture(text, (Texture)(object)val2);
					list.Add(text);
				}
			}
			string[] obj = new string[9]
			{
				"Replaced albedo \"",
				((Object)val).name,
				"\" on ",
				string.Join(", ", list),
				" (primary slot ",
				property,
				", shader \"",
				null,
				null
			};
			Shader shader = material.shader;
			obj[7] = ((shader != null) ? ((Object)shader).name : null);
			obj[8] = "\").";
			Log.Verbose(string.Concat(obj));
			return list.Count > 0;
		}

		private static Texture FindAlbedo(Material material, out string property)
		{
			string[] array = new string[4] { "_MainTex", "_BaseMap", "_BaseColorMap", "_AlbedoMap" };
			foreach (string text in array)
			{
				if (material.HasProperty(text))
				{
					Texture texture = material.GetTexture(text);
					if (!((Object)(object)texture == (Object)null))
					{
						property = text;
						return texture;
					}
				}
			}
			array = material.GetTexturePropertyNames();
			foreach (string text2 in array)
			{
				string text3 = text2.ToLowerInvariant();
				if (!text3.Contains("normal") && !text3.Contains("bump") && !text3.Contains("mask") && !text3.Contains("metallic") && !text3.Contains("occlusion") && !text3.Contains("emis") && !text3.Contains("height") && !text3.Contains("detail") && !text3.Contains("specular"))
				{
					Texture texture2 = material.GetTexture(text2);
					if (!((Object)(object)texture2 == (Object)null))
					{
						property = text2;
						return texture2;
					}
				}
			}
			property = null;
			return null;
		}

		private static Color ParseColour(string html)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Color result = default(Color);
			if (ColorUtility.TryParseHtmlString(html, ref result))
			{
				return result;
			}
			Log.Warn("Could not parse UpgradeColor \"" + html + "\"; falling back to #FF4FA3.");
			ColorUtility.TryParseHtmlString("#FF4FA3", ref result);
			return result;
		}
	}
}
namespace ExtraInventorySlotUpgrade.Patches
{
	[HarmonyPatch(typeof(InputManager))]
	internal static class InputManagerPatches
	{
		[HarmonyPatch("InitializeInputs")]
		[HarmonyPostfix]
		private static void InitializeInputsPostfix(InputManager __instance)
		{
			ExtraInput.RegisterActions(__instance);
		}

		[HarmonyPatch("KeyDown")]
		[HarmonyPrefix]
		private static bool KeyDownPrefix(InputManager __instance, InputKey key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (ExtraInput.IsSlotKey(key) && __instance.disableMovementTimer > 0f)
			{
				__result = false;
				return false;
			}
			if (ExtraInput.ShouldSuppress(key))
			{
				__result = false;
				return false;
			}
			return true;
		}

		[HarmonyPatch("KeyHold")]
		[HarmonyPrefix]
		private static bool KeyHoldPrefix(InputKey key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (ExtraInput.ShouldSuppress(key))
			{
				__result = false;
				return false;
			}
			return true;
		}

		[HarmonyPatch("KeyUp")]
		[HarmonyPrefix]
		private static bool KeyUpPrefix(InputKey key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (ExtraInput.ShouldSuppress(key))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventorySpot))]
	internal static class InventorySpotPatches
	{
		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		private static void UpdatePrefix(InventorySpot __instance)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			int inventorySpotIndex = __instance.inventorySpotIndex;
			if (inventorySpotIndex >= 3 && inventorySpotIndex < 6 && SemiFunc.InputDown(ExtraInput.KeyForSlot(inventorySpotIndex)))
			{
				__instance.HandleInput();
			}
		}
	}
	[HarmonyPatch(typeof(ItemUpgrade))]
	internal static class ItemUpgradePatches
	{
		[HarmonyPatch("PlayerUpgrade")]
		[HarmonyPrefix]
		private static bool PlayerUpgradePrefix(ItemUpgrade __instance)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((Component)__instance).GetComponent<ExtraSlotUpgradeApplier>() == (Object)null)
			{
				return true;
			}
			ItemToggle component = ((Component)__instance).GetComponent<ItemToggle>();
			if ((Object)(object)component == (Object)null || !component.toggleState)
			{
				return true;
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID(component.playerTogglePhotonID);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if (!ExtraSlotUpgrade.IsAtCap(val))
			{
				return true;
			}
			if (val.isLocal)
			{
				int num = 3 + SlotEngine.ConfiguredMaxExtra;
				SemiFunc.UIFocusText($"You already have all {num} inventory slots", new Color(1f, 0.31f, 0.64f), Color.white, 3f);
				Log.Info("Blocked upgrade use: local player is already at the slot cap. The item was not consumed.");
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PunManager))]
	internal static class PunManagerPatches
	{
		[HarmonyPatch("TruckPopulateItemVolumes")]
		[HarmonyPostfix]
		private static void TruckPopulateItemVolumesPostfix()
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				ExtraSlotStats.ClearTaken();
			}
		}

		[HarmonyPatch("SetItemNameLOGIC")]
		[HarmonyPrefix]
		private static bool SetItemNameLogicPrefix(string _name, int photonViewID, ItemAttributes _itemAttributes)
		{
			try
			{
				if (photonViewID == -1 && SemiFunc.IsMultiplayer())
				{
					return false;
				}
				ItemAttributes val = _itemAttributes;
				if (SemiFunc.IsMultiplayer())
				{
					PhotonView val2 = PhotonView.Find(photonViewID);
					if (!Object.op_Implicit((Object)(object)val2))
					{
						return false;
					}
					val = ((Component)val2).GetComponent<ItemAttributes>();
				}
				if (!Object.op_Implicit((Object)(object)_itemAttributes) && !SemiFunc.IsMultiplayer())
				{
					return false;
				}
				ItemBattery val3 = null;
				ItemEquippable val4 = null;
				if (Object.op_Implicit((Object)(object)val))
				{
					val.instanceName = _name;
					val3 = ((Component)val).GetComponent<ItemBattery>();
					val4 = ((Component)val).GetComponent<ItemEquippable>();
				}
				if (Object.op_Implicit((Object)(object)val3))
				{
					val3.SetBatteryLife(StatsManager.instance.GetBatteryLevel(_name));
				}
				if (!Object.op_Implicit((Object)(object)val4))
				{
					return false;
				}
				int num = 0;
				bool flag = false;
				PlayerAvatar val5 = null;
				int hashCode = _name.GetHashCode();
				foreach (PlayerAvatar item in SemiFunc.PlayerGetList())
				{
					string steamID = item.steamID;
					for (int i = 0; i < 6; i++)
					{
						Dictionary<string, int> dictionary = ExtraSlotStats.ContentsDict(i);
						Dictionary<string, int> dictionary2 = ExtraSlotStats.TakenDict(i);
						if (dictionary.TryGetValue(steamID, out var value) && value == hashCode && !dictionary2.ContainsKey(steamID))
						{
							num = i;
							flag = true;
							val5 = item;
							dictionary2[steamID] = 1;
							break;
						}
					}
					if (flag)
					{
						break;
					}
				}
				if (flag)
				{
					int num2 = -1;
					if (SemiFunc.IsMultiplayer())
					{
						num2 = val5.photonView.ViewID;
					}
					val4.RequestEquip(num, num2);
					Log.Verbose($"Restored \"{_name}\" into slot {num + 1} of {val5.steamID}.");
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.Error("SetItemNameLOGIC replacement threw; falling back to the vanilla method. Extra slots will not restore this transition. " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(ShopManager))]
	internal static class ShopManagerPatches
	{
		[HarmonyPatch("UpgradeValueGet")]
		[HarmonyPrefix]
		private static bool UpgradeValueGetPrefix(float _value, Item item, ref float __result)
		{
			if ((Object)(object)item == (Object)null || ((Object)item).name != "Item Upgrade Player Extra Inventory Slot")
			{
				return true;
			}
			float value = PluginConfig.PriceIncreasePerPurchase.Value;
			if (value < 0f)
			{
				return true;
			}
			if ((Object)(object)GameDirector.instance == (Object)null || (Object)(object)StatsManager.instance == (Object)null)
			{
				return true;
			}
			int num = Mathf.Min(6, GameDirector.instance.PlayerList.Count);
			float num2 = _value - _value * 0.1f * (float)(num - 1);
			num2 += num2 * value * (float)StatsManager.instance.GetItemsUpgradesPurchased(((Object)item).name);
			__result = Mathf.Ceil(num2);
			return false;
		}
	}
	[HarmonyPatch(typeof(StatsManager))]
	internal static class StatsManagerPatches
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void StartPostfix(StatsManager __instance)
		{
			ExtraSlotStats.RegisterWithStatsManager(__instance);
		}

		[HarmonyPatch("RunStartStats")]
		[HarmonyPostfix]
		[HarmonyBefore(new string[] { "REPOLib" })]
		private static void RunStartStatsPostfix()
		{
			ExtraSlotUpgrade.Register();
		}

		[HarmonyPatch("PlayerInventoryUpdate")]
		[HarmonyPostfix]
		private static void PlayerInventoryUpdatePostfix(string _steamID, string itemName, int spot, bool sync)
		{
			if (spot >= 3 && spot < 6 && SemiFunc.IsMasterClientOrSingleplayer())
			{
				int num = (string.IsNullOrEmpty(itemName) ? (-1) : itemName.GetHashCode());
				Dictionary<string, int> dictionary = ExtraSlotStats.ContentsDict(spot);
				Dictionary<string, int> dictionary2 = ExtraSlotStats.TakenDict(spot);
				if (num != -1)
				{
					dictionary[_steamID] = num;
				}
				else
				{
					dictionary.Remove(_steamID);
				}
				if (dictionary.TryGetValue(_steamID, out var value) && value != -1)
				{
					dictionary2[_steamID] = 1;
				}
				else
				{
					dictionary2.Remove(_steamID);
				}
				if (sync && (Object)(object)PunManager.instance != (Object)null)
				{
					PunManager.instance.UpdateStat(ExtraSlotStats.DictionaryName(spot), _steamID, num);
				}
				Log.Verbose($"Slot {spot + 1} of {_steamID} -> \"{itemName}\" (hash {num}, sync {sync}).");
			}
		}

		[HarmonyPatch("StuffNeedingResetAtTheEndOfAScene")]
		[HarmonyPostfix]
		private static void StuffNeedingResetPostfix()
		{
			ExtraSlotStats.ClearTaken();
		}
	}
}