Decompiled source of MoreStandsForShops v1.1.1

MoreStandsForShops.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MoreStandsForShops.Network;
using MoreStandsForShops.Rewriters;
using MoreStandsForShops.Shop;
using MoreStandsForShops.Spawners;
using MoreStandsForShops.Stands.Upgrade;
using MoreStandsForShops.Utilities;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("MoreStandsForShops")]
[assembly: AssemblyDescription("More shop stands for R.E.P.O.")]
[assembly: AssemblyCompany("DarkSpider90")]
[assembly: AssemblyProduct("MoreStandsForShops")]
[assembly: AssemblyCopyright("Copyright (c) 2026 DarkSpider90")]
[assembly: ComVisible(false)]
[assembly: Guid("2BE30F9F-3160-41F9-BC85-989F4333B8B2")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MoreStandsForShops
{
	public static class CleanPresetDatabase
	{
		private const string LevelPrefix = "Level Generator/Level/";

		public static List<SpawnPointData> GetSpawnPoints()
		{
			List<SpawnPointData> list = new List<SpawnPointData>();
			list.Add(Point("center_extract_right_rear", "Module - Shop - N - Center Extract(Clone)", 7.04f, 0.03f, -5.075f, 270f, 34, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Center Extract(Clone)/WALLS/RIGHT/Not Connected/shop sign lynx" }));
			list.Add(Point("center_extract_right_mid", "Module - Shop - N - Center Extract(Clone)", 7.04f, 0.03f, -1.635f, 270f, 1, Array.Empty<string>()));
			list.Add(Point("center_extract_top_mid", "Module - Shop - N - Center Extract(Clone)", 0.471f, 0.03f, 7.04f, 180f, 1, new string[2] { "Main/Level Generator/Level/Module - Shop - N - Center Extract(Clone)/WALLS/TOP/Not Connected/Candy Shelf 2", "Main/Level Generator/Level/Module - Shop - N - Center Extract(Clone)/WALLS/TOP/Not Connected/Shop prop fridge" }));
			list.Add(Point("corner_stands_bottom_left", "Module - Shop - N - Corner Stands(Clone)", -1.72f, 0.03f, -7.04f, 0f, 1, new string[3] { "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/Shop Ice Cream Freezer", "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/shop sign tyre (1)", "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/shop sign tyre (2)" }));
			list.Add(Point("corner_stands_bottom_mid", "Module - Shop - N - Corner Stands(Clone)", 0.637f, 0.03f, -7.04f, 0f, 8, new string[3] { "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/Candy Shelf 2", "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/Shop Ice Cream Freezer", "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/BOT/Not Connected/shop sign tyre (1)" }));
			list.Add(Point("corner_stands_left_mid", "Module - Shop - N - Corner Stands(Clone)", -7.04f, 0.03f, -1.868f, 90f, 5, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/LEFT/Not Connected/Shop Ice Cream Freezer (1)" }));
			list.Add(Point("corner_stands_right_secret", "Module - Shop - N - Corner Stands(Clone)", 7.04f, 0.03f, -1.349f, 270f, 1, new string[2] { "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/RIGHT/Not Connected/Shop Magazine Holder", "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/RIGHT/Not Connected/shop sign lynx" }));
			list.Add(Point("corner_stands_right_mid", "Module - Shop - N - Corner Stands(Clone)", 7.04f, 0.03f, -0.682f, 270f, 5, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Corner Stands(Clone)/WALLS/RIGHT/Not Connected/Shop Magazine Holder" }));
			list.Add(Point("corner_stands_top_mid", "Module - Shop - N - Corner Stands(Clone)", 0.054f, 0.03f, 7.04f, 180f, 9, Array.Empty<string>()));
			list.Add(Point("middle_stands_left_secret", "Module - Shop - N - Middle Stands(Clone)", -7.04f, 0.03f, -1.385f, 90f, 1, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/LEFT/Not Connected/shop sign ai" }));
			list.Add(Point("middle_stands_left_mid", "Module - Shop - N - Middle Stands(Clone)", -7.04f, 0.03f, -0.481f, 90f, 1, new string[2] { "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/LEFT/Not Connected/shop sign ai", "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/LEFT/Not Connected/shop sign hotdog" }));
			list.Add(Point("middle_stands_right_mid", "Module - Shop - N - Middle Stands(Clone)", 7.04f, 0.03f, -0.639f, 270f, 5, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/RIGHT/Not Connected/Shop Magazine Holder" }));
			list.Add(Point("middle_stands_top_left", "Module - Shop - N - Middle Stands(Clone)", -5.504f, 0.03f, 7.04f, 180f, 13, new string[2] { "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/RIGHT/Not Connected/Shop Magazine Holder", "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/TOP/Not Connected/Shop Magazine Stand (1)" }));
			list.Add(Point("middle_stands_top_car_service", "Module - Shop - N - Middle Stands(Clone)", -4.184f, 0.03f, 7.04f, 180f, 1, new string[1] { "Main/Level Generator/Level/Module - Shop - N - Middle Stands(Clone)/WALLS/TOP/Connected/Soda Machine (1)" }));
			list.Add(Point("middle_stands_top_mid", "Module - Shop - N - Middle Stands(Clone)", 0.051f, 0.03f, 7.04f, 180f, 13, Array.Empty<string>()));
			return list;
		}

		private static SpawnPointData Point(string id, string mainModule, float x, float y, float z, float yaw, int sourceCount, string[] disablePaths)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			return new SpawnPointData
			{
				VariantId = id,
				MainModule = "Level Generator/Level/" + mainModule,
				LocalPosition = new Vector3(x, y, z),
				LocalYaw = yaw,
				SourceCount = sourceCount,
				DisablePaths = disablePaths,
				RejectIfPresentPaths = Array.Empty<string>()
			};
		}
	}
	public class SpawnPointData
	{
		public string VariantId { get; set; }

		public string MainModule { get; set; }

		public Vector3 LocalPosition { get; set; }

		public float LocalYaw { get; set; }

		public int SourceCount { get; set; }

		public string[] DisablePaths { get; set; } = Array.Empty<string>();


		public string[] RejectIfPresentPaths { get; set; } = Array.Empty<string>();

	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "DarkSpider90.MoreStandsForShops";

		public const string PLUGIN_NAME = "MoreStandsForShops";

		public const string PLUGIN_VERSION = "1.1.0";
	}
	[BepInPlugin("DarkSpider90.MoreStandsForShops", "MoreStandsForShops", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ConfigEntry<bool> EnableMod;

		internal static ConfigEntry<bool> EnableAdditionalUpgradeStand;

		internal static ConfigEntry<bool> EnableVanillaShelfTableRewrite;

		internal static ConfigEntry<bool> DisableShopPoolLimit;

		internal static ConfigEntry<bool> DebugLogs;

		internal static Dictionary<string, ConfigEntry<int>> ItemCounts = new Dictionary<string, ConfigEntry<int>>();

		internal static Dictionary<string, ConfigEntry<int>> SameItemCopies = new Dictionary<string, ConfigEntry<int>>();

		internal static Dictionary<string, ConfigEntry<int>> ItemSpawnChances = new Dictionary<string, ConfigEntry<int>>();

		private static readonly string[] VanillaItemSpawnChanceNames = new string[70]
		{
			"Duct Taped Grenades", "Feather Drone", "Grenade", "Human Grenade", "Indestructible Drone", "Recharge Drone", "Roll Drone", "Rubber Duck", "Shockwave Grenade", "Stun Grenade",
			"Zero Gravity Drone", "Zero Gravity Orb", "Boltzap", "Explosive Mine", "Gun", "Pulse Pistol", "Shockwave Mine", "Shotgun", "Tranq Gun", "Trapzap",
			"Defibro", "Duck Bucket", "Frying Pan", "Phase Bridge", "Photon Blaster", "C.A.R.T.", "Energy Crystal", "Baseball Bat", "C.A.R.T. Cannon", "C.A.R.T. Laser",
			"Extraction Tracker", "Inflatable Hammer", "Leaf Blower", "Prodzap", "Roll Staff", "Semibot Walkies", "Sledge Hammer", "Sword", "Valuable Tracker", "Void Staff",
			"Zero Gravity Staff", "Autoscan Upgrade", "Crouch Rest Upgrade", "Death Head Battery Upgrade", "Extra Jump Upgrade", "Extra Life Upgrade", "Health Upgrade", "Item Resist Upgrade", "Item Value Upgrade", "Mana Regeneration Upgrade",
			"Map Enemy Tracker Upgrade", "Map Player Count Upgrade", "Map Player Tracker Upgrade", "Map Zoom Upgrade", "Range Upgrade", "Scout Cooldown Upgrade", "Sprint Speed Upgrade", "Sprint Usage Upgrade", "Stamina Upgrade", "Strength Upgrade",
			"Tumble Climb Upgrade", "Tumble Launch Upgrade", "Tumble Wings Upgrade", "Valuable Count Upgrade", "Large Health Pack (100)", "Medium Health Pack (50)", "Small Health Pack (25)", "POCKET C.A.R.T.", "Hauler", "Scout"
		};

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LoadConfig();
			_harmony = new Harmony("DarkSpider90.MoreStandsForShops");
			_harmony.PatchAll();
			Log.LogInfo((object)"MoreStandsForShops v1.1.0 loaded.");
		}

		private void LoadConfig()
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Expected O, but got Unknown
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Expected O, but got Unknown
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Expected O, but got Unknown
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Expected O, but got Unknown
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Expected O, but got Unknown
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Expected O, but got Unknown
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Expected O, but got Unknown
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Expected O, but got Unknown
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Expected O, but got Unknown
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dd: Expected O, but got Unknown
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Expected O, but got Unknown
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_0451: Expected O, but got Unknown
			//IL_047e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Expected O, but got Unknown
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Expected O, but got Unknown
			//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Expected O, but got Unknown
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Expected O, but got Unknown
			//IL_055a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0564: Expected O, but got Unknown
			//IL_0591: Unknown result type (might be due to invalid IL or missing references)
			//IL_059b: Expected O, but got Unknown
			//IL_05c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d2: Expected O, but got Unknown
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0609: Expected O, but got Unknown
			EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Mod", true, "Enable or disable the entire mod.");
			EnableAdditionalUpgradeStand = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Additional Upgrade Stand", true, "Spawn a second upgrade stand in the shop.");
			EnableVanillaShelfTableRewrite = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Vanilla Shelf Table Rewrite", true, "Move small items to the lower health shelf and make vanilla table small slots accept medium/large/large_high items.");
			DisableShopPoolLimit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Disable Shop Pool Limit", true, "Raise vanilla shop spawn budget to the filtered item pool size so free matching slots keep trying to fill.");
			DebugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Debug Logs", false, "Enable detailed debug logging for troubleshooting.");
			string text = "Item Counts";
			ItemCounts["Upgrades Per Stand"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Upgrades Per Stand", 8, new ConfigDescription("Upgrade items per stand (0-14).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 14), Array.Empty<object>()));
			ItemCounts["Total Upgrades"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Total Upgrades", 14, new ConfigDescription("Total upgrade items across the vanilla stand and passive second stand (0-28).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 28), Array.Empty<object>()));
			ItemCounts["Drones"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Drones", 4, new ConfigDescription("Drone items to spawn (0-8).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8), Array.Empty<object>()));
			ItemCounts["Power Crystals"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Power Crystals", 3, new ConfigDescription("Power crystals (0-5).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			ItemCounts["Orbs"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Orbs", 1, new ConfigDescription("Orbs (0-3).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			ItemCounts["Grenades"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Grenades", 4, new ConfigDescription("Grenades (0-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			ItemCounts["Mines"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Mines", 2, new ConfigDescription("Mines (0-3).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			ItemCounts["Melee"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Melee", 4, new ConfigDescription("Melee weapons (0-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			ItemCounts["Guns"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Guns", 4, new ConfigDescription("Guns (0-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			ItemCounts["Launchers"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Launchers (Staff)", 2, new ConfigDescription("Launchers/staffs (0-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			ItemCounts["Tools"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Tools", 2, new ConfigDescription("Tools (0-4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ItemCounts["Health Packs"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Health Packs", 3, new ConfigDescription("Health packs (0-5).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			ItemCounts["Carts"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Carts", 2, new ConfigDescription("Carts (0-4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ItemCounts["Pocket Carts"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Pocket Carts", 1, new ConfigDescription("Pocket carts (0-4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ItemCounts["Vehicles"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Vehicles", 1, new ConfigDescription("Vehicles (0-4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ItemCounts["C.A.R.T. Weapons"] = ((BaseUnityPlugin)this).Config.Bind<int>(text, "C.A.R.T. Weapons", 1, new ConfigDescription("Total C.A.R.T. Cannon and C.A.R.T. Laser items (0-4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			string text2 = "Same Item Copies";
			SameItemCopies["Upgrades"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Upgrades", 3, new ConfigDescription("Max copies of the same upgrade item (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Drones"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Drones", 2, new ConfigDescription("Max copies of the same drone (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Guns"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Guns", 2, new ConfigDescription("Max copies of the same gun (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Melee"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Melee", 2, new ConfigDescription("Max copies of the same melee weapon (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Launchers"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Launchers (Staff)", 2, new ConfigDescription("Max copies of the same launcher (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Grenades"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Grenades", 3, new ConfigDescription("Max copies of the same grenade (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Mines"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Mines", 2, new ConfigDescription("Max copies of the same mine (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["Health Packs"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Health Packs", 2, new ConfigDescription("Max copies of the same health pack (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			SameItemCopies["C.A.R.T. Weapons"] = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "C.A.R.T. Weapons", 1, new ConfigDescription("Max copies of the same C.A.R.T. Cannon or C.A.R.T. Laser item (1-6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>()));
			BindVanillaItemSpawnChanceConfigs();
		}

		private static void BindVanillaItemSpawnChanceConfigs()
		{
			bool createdAny = false;
			string[] vanillaItemSpawnChanceNames = VanillaItemSpawnChanceNames;
			for (int i = 0; i < vanillaItemSpawnChanceNames.Length; i++)
			{
				BindItemSpawnChanceConfig(vanillaItemSpawnChanceNames[i], ref createdAny);
			}
			if (createdAny)
			{
				((BaseUnityPlugin)Instance).Config.Save();
				Log.LogInfo((object)$"Created vanilla item spawn chance config entries for {ItemSpawnChances.Count} items.");
			}
		}

		internal static void EnsureItemSpawnChanceConfigs(IEnumerable<Item> items)
		{
			if ((Object)(object)Instance == (Object)null || items == null)
			{
				return;
			}
			bool createdAny = false;
			foreach (Item item in items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					BindItemSpawnChanceConfig(ItemConfigName(item), ref createdAny);
				}
			}
			if (createdAny)
			{
				((BaseUnityPlugin)Instance).Config.Save();
				Log.LogInfo((object)$"Created/updated item spawn chance config entries for {ItemSpawnChances.Count} items.");
			}
		}

		internal static int GetItemSpawnChance(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return 0;
			}
			string key = ItemConfigName(item);
			if (!ItemSpawnChances.TryGetValue(key, out var value))
			{
				return 100;
			}
			return value.Value;
		}

		private static void BindItemSpawnChanceConfig(string key, ref bool createdAny)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			if (!string.IsNullOrWhiteSpace(key) && !ItemSpawnChances.ContainsKey(key))
			{
				ItemSpawnChances[key] = ((BaseUnityPlugin)Instance).Config.Bind<int>("Item Spawn Chances", key, 100, new ConfigDescription("Relative spawn chance/weight for this exact item. 0 disables it in this mod's shop pools; 100 is default.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
				createdAny = true;
			}
		}

		private static string ItemConfigName(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return "<null>";
			}
			if (!string.IsNullOrWhiteSpace(item.itemName))
			{
				return item.itemName;
			}
			return ((Object)item).name;
		}
	}
}
namespace MoreStandsForShops.Utilities
{
	public static class ItemVolumeHelper
	{
		private static readonly Vector3[] CrystalSlotPositions = (Vector3[])(object)new Vector3[5]
		{
			new Vector3(-0.72f, 1.68f, 0.5f),
			new Vector3(-0.36f, 1.68f, 0.5f),
			new Vector3(0f, 1.68f, 0.5f),
			new Vector3(0.36f, 1.68f, 0.5f),
			new Vector3(0.72f, 1.68f, 0.5f)
		};

		private static readonly Vector3[] DroneSlotPositions = (Vector3[])(object)new Vector3[8]
		{
			new Vector3(-0.77f, 1.18f, 0.5f),
			new Vector3(-0.55f, 1.18f, 0.5f),
			new Vector3(-0.33f, 1.18f, 0.5f),
			new Vector3(-0.11f, 1.18f, 0.5f),
			new Vector3(0.11f, 1.18f, 0.5f),
			new Vector3(0.33f, 1.18f, 0.5f),
			new Vector3(0.55f, 1.18f, 0.5f),
			new Vector3(0.77f, 1.18f, 0.5f)
		};

		public static void AssignVolumesForDroneCrystalStand(GameObject stand)
		{
			//IL_0054: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			ItemVolume[] componentsInChildren = stand.GetComponentsInChildren<ItemVolume>(true);
			if (componentsInChildren.Length == 0)
			{
				CreateDroneCrystalVolumes(stand);
				return;
			}
			Array.Sort(componentsInChildren, (ItemVolume a, ItemVolume b) => ((Component)a).transform.position.y.CompareTo(((Component)b).transform.position.y));
			int num = componentsInChildren.Length / 2;
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				bool flag = i < num;
				componentsInChildren[i].itemVolume = (itemVolume)((!flag) ? 4 : 0);
				componentsInChildren[i].itemSecretShopType = (itemSecretShopType)0;
				MoreStandsShelfVolume obj = ((Component)componentsInChildren[i]).gameObject.GetComponent<MoreStandsShelfVolume>() ?? ((Component)componentsInChildren[i]).gameObject.AddComponent<MoreStandsShelfVolume>();
				obj.Zone = ((!flag) ? MoreStandsShelfZone.Crystal : MoreStandsShelfZone.Drone);
				obj.Handled = false;
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)string.Format("[ItemVolumeHelper] Volume {0} (Y={1:F3}) -> {2}", ((Object)componentsInChildren[i]).name, ((Component)componentsInChildren[i]).transform.position.y, (i < num) ? "small" : "power_crystal"));
				}
			}
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)$"[ItemVolumeHelper] Assigned {num} lower volume(s) to 'small' (drones) and {componentsInChildren.Length - num} upper volume(s) to 'power_crystal'.");
			}
		}

		private static void CreateDroneCrystalVolumes(GameObject stand)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)stand == (Object)null))
			{
				for (int i = 0; i < CrystalSlotPositions.Length; i++)
				{
					CreateVolumeSlot(stand.transform, $"MoreStandsForShops Crystal Slot {i + 1:00}", (itemVolume)4, MoreStandsShelfZone.Crystal, CrystalSlotPositions[i]);
				}
				for (int j = 0; j < DroneSlotPositions.Length; j++)
				{
					CreateVolumeSlot(stand.transform, $"MoreStandsForShops Drone Slot {j + 1:00}", (itemVolume)0, MoreStandsShelfZone.Drone, DroneSlotPositions[j]);
				}
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[ItemVolumeHelper] Created {CrystalSlotPositions.Length} crystal ItemVolume(s) and {DroneSlotPositions.Length} drone ItemVolume(s) on drone/crystal stand.");
				}
			}
		}

		private static ItemVolume CreateVolumeSlot(Transform parent, string name, itemVolume itemVolume, MoreStandsShelfZone zone, Vector3 localPosition)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0040: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			val.transform.localPosition = localPosition;
			val.transform.localRotation = Quaternion.identity;
			ItemVolume val2 = val.AddComponent<ItemVolume>();
			val2.itemVolume = itemVolume;
			val2.itemSecretShopType = (itemSecretShopType)0;
			MoreStandsShelfVolume moreStandsShelfVolume = val.AddComponent<MoreStandsShelfVolume>();
			moreStandsShelfVolume.Zone = zone;
			moreStandsShelfVolume.Handled = false;
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)$"[ItemVolumeHelper] Created {name} zone={itemVolume} local={localPosition}.");
			}
			return val2;
		}
	}
	public sealed class MoreStandsMultiSizeVolume : MonoBehaviour
	{
		public string GroupId;
	}
	public enum MoreStandsShelfZone
	{
		Drone,
		Crystal,
		Grenade
	}
	public sealed class MoreStandsShelfVolume : MonoBehaviour
	{
		public MoreStandsShelfZone Zone;

		public bool Handled;
	}
	public sealed class MoreStandsUpgradeVolume : MonoBehaviour
	{
	}
	internal static class ScenePathUtility
	{
		public static Transform FindTransformByPath(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				return null;
			}
			return ShopSceneCache.Current.FindTransformByPath(path);
		}

		public static string GetTransformPath(Transform transform)
		{
			return ShopSceneCache.Current.GetTransformPath(transform);
		}

		public static List<string> DisableExactPaths(IEnumerable<string> paths, string logPrefix)
		{
			List<string> list = new List<string>();
			HashSet<string> hashSet = new HashSet<string>();
			if (paths == null)
			{
				return list;
			}
			foreach (string path in paths)
			{
				if (string.IsNullOrWhiteSpace(path) || !hashSet.Add(path))
				{
					continue;
				}
				Transform val = FindTransformByPath(path);
				if ((Object)(object)val == (Object)null)
				{
					if (Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)(logPrefix + " Preset disable path not present: " + path));
					}
					continue;
				}
				int num = DisableTree(val, list);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"{logPrefix} Disabled preset blocker: {GetTransformPath(val)} (nodes={num})");
				}
			}
			return list;
		}

		private static int DisableTree(Transform root, List<string> disabled)
		{
			if ((Object)(object)root == (Object)null)
			{
				return 0;
			}
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			int num = 0;
			Transform[] array = componentsInChildren;
			foreach (Transform val in array)
			{
				if (!((Object)(object)val == (Object)null) && (((Component)val).gameObject.activeSelf || ((Component)val).gameObject.activeInHierarchy))
				{
					((Component)val).gameObject.SetActive(false);
					disabled.Add(GetTransformPath(val));
					num++;
				}
			}
			return num;
		}

		public static bool HasActivePath(IEnumerable<string> paths, out string activePath)
		{
			activePath = null;
			if (paths == null)
			{
				return false;
			}
			foreach (string path in paths)
			{
				Transform val = FindTransformByPath(path);
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
				{
					activePath = GetTransformPath(val);
					return true;
				}
			}
			return false;
		}
	}
	internal sealed class ShopSceneCache
	{
		private static ShopSceneCache _current;

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

		private readonly Dictionary<string, Transform> transformByPath = new Dictionary<string, Transform>(StringComparer.Ordinal);

		private readonly int sceneHandle;

		internal GameObject[] Roots { get; }

		internal Transform[] Transforms { get; }

		internal ItemVolume[] ItemVolumes { get; }

		internal Renderer[] Renderers { get; }

		internal static ShopSceneCache Current
		{
			get
			{
				//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)
				Scene activeScene = SceneManager.GetActiveScene();
				if (_current == null || _current.sceneHandle != ((Scene)(ref activeScene)).handle)
				{
					_current = new ShopSceneCache();
				}
				return _current;
			}
		}

		private ShopSceneCache()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			sceneHandle = ((Scene)(ref activeScene)).handle;
			Roots = ((Scene)(ref activeScene)).GetRootGameObjects();
			Transforms = (from transform in Resources.FindObjectsOfTypeAll<Transform>()
				where (Object)(object)transform != (Object)null && ((Component)transform).gameObject.activeInHierarchy
				select transform).ToArray();
			ItemVolumes = (from volume in Resources.FindObjectsOfTypeAll<ItemVolume>()
				where (Object)(object)volume != (Object)null && ((Component)volume).gameObject.activeInHierarchy
				select volume).ToArray();
			Renderers = (from renderer in Resources.FindObjectsOfTypeAll<Renderer>()
				where (Object)(object)renderer != (Object)null && ((Component)renderer).gameObject.activeInHierarchy
				select renderer).ToArray();
			Transform[] transforms = Transforms;
			foreach (Transform val in transforms)
			{
				string text = BuildPath(val);
				pathByInstanceId[((Object)val).GetInstanceID()] = text;
				if (!transformByPath.ContainsKey(text))
				{
					transformByPath.Add(text, val);
				}
			}
			ConfigEntry<bool> debugLogs = Plugin.DebugLogs;
			if (debugLogs != null && debugLogs.Value)
			{
				Plugin.Log.LogInfo((object)($"[ShopSceneCache] Built scene cache: transforms={Transforms.Length}, " + $"itemVolumes={ItemVolumes.Length}, renderers={Renderers.Length}."));
			}
		}

		internal static ShopSceneCache Rebuild()
		{
			_current = new ShopSceneCache();
			return _current;
		}

		internal static void Clear()
		{
			_current = null;
		}

		internal Transform FindTransformByPath(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				return null;
			}
			string[] pathCandidates = GetPathCandidates(path);
			foreach (string text in pathCandidates)
			{
				if (transformByPath.TryGetValue(text, out var value) && (Object)(object)value != (Object)null && ((Component)value).gameObject.activeInHierarchy)
				{
					return value;
				}
				GameObject[] roots = Roots;
				foreach (GameObject val in roots)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Transform val2 = val.transform.Find(text);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
					if (text.StartsWith(((Object)val).name + "/", StringComparison.Ordinal))
					{
						string text2 = text.Substring(((Object)val).name.Length + 1);
						val2 = val.transform.Find(text2);
						if ((Object)(object)val2 != (Object)null)
						{
							return val2;
						}
					}
				}
			}
			return null;
		}

		internal string GetTransformPath(Transform transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return "<null>";
			}
			int instanceID = ((Object)transform).GetInstanceID();
			if (pathByInstanceId.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			value = BuildPath(transform);
			pathByInstanceId[instanceID] = value;
			if (!transformByPath.ContainsKey(value))
			{
				transformByPath.Add(value, transform);
			}
			return value;
		}

		private static string[] GetPathCandidates(string path)
		{
			string text = path.Trim().Trim('/');
			if (!text.StartsWith("Main/", StringComparison.Ordinal))
			{
				return new string[1] { text };
			}
			return new string[2]
			{
				text,
				text.Substring("Main/".Length)
			};
		}

		private static string BuildPath(Transform transform)
		{
			Stack<string> stack = new Stack<string>();
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				stack.Push(((Object)val).name);
				val = val.parent;
			}
			return string.Join("/", stack);
		}
	}
}
namespace MoreStandsForShops.Stands.Upgrade
{
	internal sealed class UpgradeStandRerollController : MonoBehaviour, IOnEventCallback
	{
		private enum RerollState
		{
			Idle,
			Holding,
			Rollback,
			PressFail,
			PressSucceed,
			CloseHatch,
			RollStart,
			Rolling,
			RollEnd,
			OpenHatch,
			WaitingForHost,
			Broken
		}

		private readonly struct CachedUpgrade
		{
			internal readonly ItemUpgrade Upgrade;

			internal readonly Item Item;

			internal readonly Vector3 Position;

			internal readonly Quaternion Rotation;

			internal CachedUpgrade(ItemUpgrade upgrade, Item item, Vector3 position, Quaternion rotation)
			{
				//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_0016: 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)
				Upgrade = upgrade;
				Item = item;
				Position = position;
				Rotation = rotation;
			}
		}

		private readonly struct PendingReplacement
		{
			internal readonly ItemUpgrade OriginalUpgrade;

			internal readonly Item Item;

			internal readonly Vector3 Position;

			internal readonly Quaternion Rotation;

			internal PendingReplacement(ItemUpgrade originalUpgrade, Item item, Vector3 position, Quaternion rotation)
			{
				//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_0016: 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)
				OriginalUpgrade = originalUpgrade;
				Item = item;
				Position = position;
				Rotation = rotation;
			}
		}

		private const byte RerollSyncEvent = 187;

		private const string SyncMagic = "MSFS_REROLL_V1";

		private const string MsgHoldRequestStart = "HoldRequestStart";

		private const string MsgHoldRequestStop = "HoldRequestStop";

		private const string MsgHoldVisualStart = "HoldVisualStart";

		private const string MsgHoldVisualStop = "HoldVisualStop";

		private const string MsgHoldVisualProgress = "HoldVisualProgress";

		private const string MsgRerollRequest = "RerollRequest";

		private const string MsgRerollVisual = "RerollVisual";

		private const string MsgBreakBuildUpVisual = "BreakBuildUpVisual";

		private const string MsgBrokenVisual = "BrokenVisual";

		private const float HoldProgressSyncInterval = 0.15f;

		private const float ButtonUseDistance = 3.25f;

		private const float ButtonCastRadius = 0.06f;

		private const float BuildUpStageDuration = 0.3f;

		private const float BuildUpFinalHoldDuration = 0.5f;

		[SerializeField]
		private Transform scanBox;

		[SerializeField]
		private Transform buttonRoot;

		[SerializeField]
		private Transform buttonColliderRoot;

		[SerializeField]
		private Transform hatch;

		[SerializeField]
		private Transform upgradeCompartment;

		[SerializeField]
		private Transform allMeshesTransform;

		[SerializeField]
		private StaticGrabObject buttonGrabObject;

		[SerializeField]
		private GameObject hatchClosed;

		[SerializeField]
		private GameObject hatchHurtCollider;

		[SerializeField]
		private GameObject rerollCompartmentHurtColliders;

		[SerializeField]
		private GameObject buttonRubble;

		[SerializeField]
		private GameObject fireHurtCollider;

		[SerializeField]
		private Light fireLight;

		[SerializeField]
		private Light buildUpLight;

		[SerializeField]
		private ParticleSystem hatchParticles;

		[SerializeField]
		private ParticleSystem rollingParticles;

		[SerializeField]
		private ParticleSystem particleButtonBreak;

		[SerializeField]
		private ParticleSystem particleFireLoop;

		[SerializeField]
		private ParticleSystem buildUpParticles;

		[SerializeField]
		private MeshRenderer buttonRenderer;

		[SerializeField]
		private Material buttonNormalMaterial;

		[SerializeField]
		private Material buttonDenyMaterial;

		[SerializeField]
		private LocalizedAsset interactLocalized;

		[SerializeField]
		private AnimationCurve buttonPressAnimationCurve;

		[SerializeField]
		private AnimationCurve buttonDenyCurve;

		[SerializeField]
		private AnimationCurve hatchAnimationCurve;

		[SerializeField]
		private AnimationCurve rollStartShakeCurve;

		[SerializeField]
		private AnimationCurve rollCurve;

		[SerializeField]
		private AnimationCurve rollEndShakeCurve;

		[SerializeField]
		private AnimationCurve buildUpIntroCurve;

		[SerializeField]
		private AnimationCurve buildUpOutroCurve;

		[SerializeField]
		private AnimationCurve flickerFadeLightCurve;

		[SerializeField]
		private BuildUpStage[] buildUpStages;

		[SerializeField]
		private SpringQuaternion meshRotationSpring;

		[SerializeField]
		private SpringVector3 meshPositionSpring;

		[SerializeField]
		private SpringFloat buttonRotationSpring;

		[SerializeField]
		private Sound soundButtonPress;

		[SerializeField]
		private Sound soundButtonDeny;

		[SerializeField]
		private Sound soundHatchClose;

		[SerializeField]
		private Sound soundRollStart;

		[SerializeField]
		private Sound soundRolling;

		[SerializeField]
		private Sound soundRollEnd;

		[SerializeField]
		private Sound soundHatchOpen;

		[SerializeField]
		private Sound soundButtonTwistUp;

		[SerializeField]
		private Sound soundButtonTwistDown;

		[SerializeField]
		private Sound soundStageBeep;

		[SerializeField]
		private Sound soundStateOn;

		[SerializeField]
		private Sound soundStateOff;

		[SerializeField]
		private Sound soundRerollStart;

		[SerializeField]
		private Sound soundRerollEnd;

		[SerializeField]
		private Sound soundRerollTick;

		[SerializeField]
		private Sound soundRerollSettle;

		[SerializeField]
		private Sound soundFinalRollSqueak;

		[SerializeField]
		private Sound soundHatchCloseImpact;

		[SerializeField]
		private Sound soundHatchOpenImpact;

		[SerializeField]
		private Sound soundButtonBreak;

		[SerializeField]
		private Sound soundBuildUpLoop;

		[SerializeField]
		private Sound soundFluorescentLightTurnOff;

		[SerializeField]
		private Sound soundLilButtonFire;

		private RerollState state;

		private Vector3 buttonOriginalPosition;

		private Vector3 hatchOriginalPosition;

		private Vector3 hatchOriginalScale;

		private Vector3 allMeshesOriginalPosition;

		private Quaternion buttonOriginalRotation;

		private Quaternion compartmentOriginalRotation;

		private Quaternion allMeshesOriginalRotation;

		private Color rollbackCurrentStageStartLightColor;

		private Color rollbackCurrentStageEndLightColor;

		private bool stateStart = true;

		private bool activationHeld;

		private bool activationStartedThisFrame;

		private bool activationReleasedThisFrame;

		private bool remoteHoldVisual;

		private bool holdVisualBroadcasted;

		private bool isBroken;

		private bool visualOnlyReroll;

		private bool finalRollSqueakPlayed;

		private bool hatchCloseImpactPlayed;

		private bool hatchOpenImpactPlayed;

		private bool fireActive;

		private bool[] chargeStageTriggered;

		private bool buildUpActive;

		private bool holdRequestSent;

		private bool resumeChargeFromRollback;

		private bool rollbackResumeRequested;

		private float stateTimer;

		private float stateTimerMax;

		private float chargeElapsed;

		private float buttonAnimationEval;

		private float buttonRotationTarget;

		private float buttonRotationAngle;

		private float buttonStageRotationAngle = 45f;

		private float hatchAnimationEval;

		private float compartmentAnimationEval;

		private float firePerlinOffsetX;

		private float firePerlinOffsetY;

		private float fireLightFadeInTimer;

		private float buildUpTimer;

		private float holdProgressSyncTimer;

		private float rollbackCurrentStageElapsed;

		private float rollbackCurrentStageStartEmission;

		private float rollbackCurrentStageStartLightIntensity;

		private float rollbackCurrentStageEndLightIntensity;

		private readonly List<CachedUpgrade> cachedUpgrades = new List<CachedUpgrade>();

		private readonly List<PendingReplacement> pendingReplacements = new List<PendingReplacement>();

		private int rollbackTopStage;

		private int rollbackCurrentStage = -1;

		private int rerollCount;

		private int maxRerollCount = -1;

		private int rerollTicksPlayed;

		private static readonly FieldInfo SpringQuaternionVelocityField = typeof(SpringQuaternion).GetField("springVelocity", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo SpringVector3VelocityField = typeof(SpringVector3).GetField("springVelocity", BindingFlags.Instance | BindingFlags.NonPublic);

		private int RerollCost => 5 + rerollCount * 5;

		private Vector3 ButtonSoundPosition
		{
			get
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)buttonRoot != (Object)null))
				{
					return ((Component)this).transform.position;
				}
				return buttonRoot.position;
			}
		}

		private Vector3 HatchSoundPosition
		{
			get
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)hatch != (Object)null))
				{
					return ((Component)this).transform.position;
				}
				return hatch.position;
			}
		}

		private Vector3 CompartmentSoundPosition
		{
			get
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)upgradeCompartment != (Object)null))
				{
					return ((Component)this).transform.position;
				}
				return upgradeCompartment.position;
			}
		}

		private float HoldDuration
		{
			get
			{
				if (buildUpStages != null && buildUpStages.Length != 0)
				{
					return (float)buildUpStages.Length * 0.3f + 0.5f;
				}
				return 1.7f;
			}
		}

		private float ChargeStageDuration => 0.3f;

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent.Code != 187 || !(photonEvent.CustomData is object[] array) || array.Length < 2 || !(array[0] is string text) || text != "MSFS_REROLL_V1" || !(array[1] is string text2))
			{
				return;
			}
			float syncedChargeElapsed = ((array.Length > 2) ? ReadProgressPayload(array[2], chargeElapsed) : chargeElapsed);
			if (isBroken && text2 != "BrokenVisual" && text2 != "BreakBuildUpVisual")
			{
				return;
			}
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Sync] Event received. " + $"msg={text2}, sender={photonEvent.Sender}, " + $"isMaster={PhotonNetwork.IsMasterClient}, state={state}."));
			}
			if (text2 == null)
			{
				return;
			}
			switch (text2.Length)
			{
			case 15:
				switch (text2[4])
				{
				case 'R':
					if (text2 == "HoldRequestStop" && PhotonNetwork.IsMasterClient)
					{
						StopRemoteHoldVisual(syncedChargeElapsed);
						BroadcastHoldVisualStop();
					}
					break;
				case 'V':
					if (text2 == "HoldVisualStart" && !PhotonNetwork.IsMasterClient)
					{
						BeginRemoteHoldVisual(syncedChargeElapsed);
					}
					break;
				}
				break;
			case 18:
				switch (text2[0])
				{
				case 'H':
					if (text2 == "HoldVisualProgress" && !PhotonNetwork.IsMasterClient)
					{
						ApplyRemoteHoldProgress(syncedChargeElapsed);
					}
					break;
				case 'B':
					if (text2 == "BreakBuildUpVisual" && !PhotonNetwork.IsMasterClient)
					{
						StartBreakBuildUpVisual();
					}
					break;
				}
				break;
			case 12:
				switch (text2[0])
				{
				case 'R':
					if (text2 == "RerollVisual" && !PhotonNetwork.IsMasterClient)
					{
						BeginVisualReroll();
					}
					break;
				case 'B':
					if (text2 == "BrokenVisual" && !PhotonNetwork.IsMasterClient)
					{
						BreakButton();
					}
					break;
				}
				break;
			case 16:
				if (text2 == "HoldRequestStart" && PhotonNetwork.IsMasterClient)
				{
					BeginRemoteHoldVisual(syncedChargeElapsed);
					BroadcastHoldVisualStart();
				}
				break;
			case 14:
				if (text2 == "HoldVisualStop" && !PhotonNetwork.IsMasterClient)
				{
					StopRemoteHoldVisual(syncedChargeElapsed);
				}
				break;
			case 13:
				if (text2 == "RerollRequest" && PhotonNetwork.IsMasterClient)
				{
					TryStartReroll(visualOnly: false, broadcastVisual: true);
				}
				break;
			case 17:
				break;
			}
		}

		private void RaiseRerollEvent(string message, ReceiverGroup receivers, float? progress = null)
		{
			//IL_004d: 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_0053: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			if (SemiFunc.IsMultiplayer())
			{
				object[] array = ((!progress.HasValue) ? new object[2] { "MSFS_REROLL_V1", message } : new object[3] { "MSFS_REROLL_V1", message, progress.Value });
				PhotonNetwork.RaiseEvent((byte)187, (object)array, new RaiseEventOptions
				{
					Receivers = receivers
				}, SendOptions.SendReliable);
			}
		}

		private void BroadcastRerollVisual()
		{
			RaiseRerollEvent("RerollVisual", (ReceiverGroup)0);
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Broadcast reroll visual.");
			}
		}

		private void BroadcastHoldVisualStop()
		{
			if (!holdVisualBroadcasted)
			{
				return;
			}
			holdVisualBroadcasted = false;
			holdProgressSyncTimer = 0f;
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				float num = Mathf.Clamp(chargeElapsed, 0f, HoldDuration);
				RaiseRerollEvent("HoldVisualStop", (ReceiverGroup)0, num);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Sync] Broadcast hold visual stop. progress={num:0.00}.");
				}
			}
		}

		private void BroadcastHoldVisualProgress(bool force)
		{
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && holdVisualBroadcasted)
			{
				holdProgressSyncTimer += Time.deltaTime;
				if (force || !(holdProgressSyncTimer < 0.15f))
				{
					holdProgressSyncTimer = 0f;
					RaiseRerollEvent("HoldVisualProgress", (ReceiverGroup)0, Mathf.Clamp(chargeElapsed, 0f, HoldDuration));
				}
			}
		}

		private void BroadcastHoldVisualStart()
		{
			if (holdVisualBroadcasted)
			{
				return;
			}
			holdVisualBroadcasted = true;
			holdProgressSyncTimer = 0f;
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				float num = Mathf.Clamp(chargeElapsed, 0f, HoldDuration);
				RaiseRerollEvent("HoldVisualStart", (ReceiverGroup)0, num);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Sync] Broadcast hold visual start. progress={num:0.00}.");
				}
			}
		}

		private void BeginVisualReroll()
		{
			if (state != 0 && state != RerollState.WaitingForHost && state != RerollState.Holding && state != RerollState.Rollback)
			{
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Sync] Ignored visual reroll in state={state}.");
				}
			}
			else
			{
				remoteHoldVisual = false;
				holdRequestSent = false;
				visualOnlyReroll = true;
				StateSet(RerollState.PressSucceed);
			}
		}

		private void BeginRemoteHoldVisual(float syncedChargeElapsed = -1f)
		{
			if (isBroken)
			{
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Ignored remote hold visual: stand is broken.");
				}
				return;
			}
			if (state != 0 && state != RerollState.Rollback && state != RerollState.WaitingForHost)
			{
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Sync] Ignored remote hold visual in state={state}.");
				}
				return;
			}
			if (syncedChargeElapsed >= 0f)
			{
				chargeElapsed = Mathf.Clamp(syncedChargeElapsed, 0f, HoldDuration);
				resumeChargeFromRollback = chargeElapsed > 0f;
			}
			else
			{
				resumeChargeFromRollback = state == RerollState.Rollback;
			}
			remoteHoldVisual = true;
			visualOnlyReroll = true;
			StateSet(RerollState.Holding);
		}

		private void StopRemoteHoldVisual(float syncedChargeElapsed = -1f)
		{
			if (remoteHoldVisual)
			{
				remoteHoldVisual = false;
				if (syncedChargeElapsed >= 0f)
				{
					chargeElapsed = Mathf.Clamp(syncedChargeElapsed, 0f, HoldDuration);
				}
				if (state == RerollState.Holding)
				{
					StateSet(RerollState.Rollback);
				}
			}
		}

		private void ApplyRemoteHoldProgress(float syncedChargeElapsed)
		{
			if (remoteHoldVisual && state == RerollState.Holding)
			{
				chargeElapsed = Mathf.Clamp(syncedChargeElapsed, 0f, HoldDuration);
				SyncChargeStageTriggers(chargeElapsed);
				ApplyChargeVisualsSilent(chargeElapsed);
			}
		}

		private void RequestHostReroll()
		{
			if (SemiFunc.IsMultiplayer())
			{
				RaiseRerollEvent("RerollRequest", (ReceiverGroup)2);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Sent reroll request to host.");
				}
			}
		}

		private void RequestHostHoldStart()
		{
			if (!holdRequestSent && SemiFunc.IsMultiplayer() && !PhotonNetwork.IsMasterClient)
			{
				holdRequestSent = true;
				RaiseRerollEvent("HoldRequestStart", (ReceiverGroup)2, Mathf.Clamp(chargeElapsed, 0f, HoldDuration));
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Sent hold-start request to host.");
				}
			}
		}

		private void RequestHostHoldStop()
		{
			if (holdRequestSent && SemiFunc.IsMultiplayer() && !PhotonNetwork.IsMasterClient)
			{
				holdRequestSent = false;
				RaiseRerollEvent("HoldRequestStop", (ReceiverGroup)2, Mathf.Clamp(chargeElapsed, 0f, HoldDuration));
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Sent hold-stop request to host.");
				}
			}
		}

		private void BroadcastBroken()
		{
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				RaiseRerollEvent("BrokenVisual", (ReceiverGroup)0);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Broadcast broken visual.");
				}
			}
		}

		private void BroadcastBreakBuildUpVisual()
		{
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				RaiseRerollEvent("BreakBuildUpVisual", (ReceiverGroup)0);
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)"[UpgradeStandReroll.Sync] Broadcast break build-up visual.");
				}
			}
		}

		private static float ReadProgressPayload(object data, float fallback)
		{
			if (!(data is float result))
			{
				if (!(data is double num))
				{
					if (data is int num2)
					{
						return num2;
					}
					return fallback;
				}
				return (float)num;
			}
			return result;
		}

		private void OnEnable()
		{
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		private void OnDisable()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
		}

		private void Start()
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			ResolveReferences();
			CaptureOriginalTransforms();
			DisableVanillaButtonNetworking();
			ResetBuildUpVisuals();
			if (buildUpStages == null || buildUpStages.Length == 0)
			{
				buildUpStages = (BuildUpStage[])(object)new BuildUpStage[4];
			}
			chargeStageTriggered = new bool[buildUpStages.Length];
			buttonStageRotationAngle = 180f / (float)Mathf.Max(1, buildUpStages.Length);
			if (buttonRotationSpring == null)
			{
				buttonRotationSpring = new SpringFloat();
			}
			if (meshRotationSpring == null)
			{
				meshRotationSpring = new SpringQuaternion();
			}
			if (meshPositionSpring == null)
			{
				meshPositionSpring = new SpringVector3();
			}
			buttonRotationSpring.speed = 80f;
			buttonRotationSpring.damping = 0.25f;
			SpringQuaternion obj = meshRotationSpring;
			obj.speed *= 0.75f;
			SpringVector3 obj2 = meshPositionSpring;
			obj2.speed *= 0.75f;
			if ((Object)(object)buttonRenderer != (Object)null && (Object)(object)buttonNormalMaterial != (Object)null)
			{
				((Renderer)buttonRenderer).material = buttonNormalMaterial;
			}
			if ((Object)(object)buttonRubble != (Object)null)
			{
				buttonRubble.SetActive(false);
			}
			if ((Object)(object)fireHurtCollider != (Object)null)
			{
				fireHurtCollider.SetActive(false);
			}
			if ((Object)(object)fireLight != (Object)null)
			{
				((Component)fireLight).gameObject.SetActive(false);
			}
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll] Controller ready. scanBox=" + NameOrNull(scanBox) + ", button=" + NameOrNull(buttonRoot) + ", collider=" + NameOrNull(buttonColliderRoot) + "."));
			}
		}

		private void Update()
		{
			ReadActivationInput();
			bool buttonFocused = IsLocalPlayerLookingAtButton();
			UpdateHover(buttonFocused);
			UpdateState(buttonFocused);
			UpdateButtonRotationSpring();
			ApplyButtonRotation();
			UpdateMeshSprings();
			UpdateFire();
			UpdateBuildUpLoop();
			if (stateTimer <= stateTimerMax)
			{
				stateTimer += Time.deltaTime;
			}
		}

		private void ReadActivationInput()
		{
			activationStartedThisFrame = false;
			activationReleasedThisFrame = false;
			if (!((Object)(object)InputManager.instance == (Object)null))
			{
				if (InputManager.instance.KeyDown((InputKey)10) || InputManager.instance.KeyDown((InputKey)2))
				{
					activationHeld = true;
					activationStartedThisFrame = true;
				}
				if (InputManager.instance.KeyUp((InputKey)10) || InputManager.instance.KeyUp((InputKey)2))
				{
					activationHeld = false;
					activationReleasedThisFrame = true;
				}
			}
		}

		private void UpdateHover(bool buttonFocused)
		{
			bool flag = !buttonFocused || isBroken;
			if (!flag)
			{
				RerollState rerollState = state;
				bool flag2 = (uint)rerollState <= 2u;
				flag = !flag2;
			}
			if (!flag)
			{
				if ((Object)(object)Aim.instance != (Object)null)
				{
					Aim.instance.SetState((State)((state != RerollState.Holding) ? 1 : 2));
				}
				string text = BuildHoverText();
				if ((Object)(object)buttonGrabObject != (Object)null)
				{
					buttonGrabObject.hoverText = text;
					buttonGrabObject.ShowHoverText();
				}
				else if ((Object)(object)ItemInfoUI.instance != (Object)null)
				{
					ItemInfoUI.instance.ItemInfoText((ItemAttributes)null, text, false);
				}
			}
		}

		private string BuildHoverText()
		{
			string text = "<color=#FF0000>-" + SemiFunc.DollarGetString(RerollCost) + "k</color>";
			if ((Object)(object)interactLocalized != (Object)null)
			{
				return interactLocalized.GetLocalizedString((IList<object>)new object[1]
				{
					new
					{
						cost = text
					}
				});
			}
			return "Reroll upgrades " + text;
		}

		private bool IsLocalPlayerLookingAtButton()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if ((Object)(object)val == (Object)null || (Object)(object)val.localCamera == (Object)null)
			{
				return false;
			}
			Transform overrideTransform = val.localCamera.GetOverrideTransform();
			if ((Object)(object)overrideTransform == (Object)null)
			{
				return false;
			}
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(overrideTransform.position, overrideTransform.forward);
			RaycastHit val3 = default(RaycastHit);
			if (Physics.Raycast(val2, ref val3, 3.25f, -1, (QueryTriggerInteraction)2) && IsButtonTarget(((RaycastHit)(ref val3)).transform))
			{
				return true;
			}
			foreach (RaycastHit item in from h in Physics.SphereCastAll(val2, 0.06f, 3.25f, -1, (QueryTriggerInteraction)2)
				orderby ((RaycastHit)(ref h)).distance
				select h)
			{
				RaycastHit current = item;
				if (!((Object)(object)((RaycastHit)(ref current)).transform == (Object)null) && IsButtonTarget(((RaycastHit)(ref current)).transform))
				{
					return true;
				}
			}
			return false;
		}

		private bool IsButtonTarget(Transform hitTransform)
		{
			if ((Object)(object)hitTransform == (Object)null)
			{
				return false;
			}
			if ((Object)(object)buttonRoot != (Object)null && ((Object)(object)hitTransform == (Object)(object)buttonRoot || hitTransform.IsChildOf(buttonRoot)))
			{
				return true;
			}
			if ((Object)(object)buttonColliderRoot != (Object)null && ((Object)(object)hitTransform == (Object)(object)buttonColliderRoot || hitTransform.IsChildOf(buttonColliderRoot)))
			{
				return true;
			}
			StaticGrabObject componentInParent = ((Component)hitTransform).GetComponentInParent<StaticGrabObject>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return (Object)(object)componentInParent == (Object)(object)buttonGrabObject;
			}
			return false;
		}

		private List<PendingReplacement> BuildPendingReplacements(List<CachedUpgrade> upgrades)
		{
			//IL_006f: 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)
			Dictionary<string, int> displayedCounts = BuildDisplayedCounts(upgrades);
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			List<PendingReplacement> list = new List<PendingReplacement>();
			foreach (CachedUpgrade upgrade in upgrades)
			{
				Item val = SelectReplacement(upgrade.Item, displayedCounts, dictionary);
				if (!((Object)(object)val == (Object)null))
				{
					string key = ItemKey(val);
					dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1));
					list.Add(new PendingReplacement(upgrade.Upgrade, val, upgrade.Position, upgrade.Rotation));
				}
			}
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Built replacement list. " + $"source={upgrades.Count}, replacements={list.Count}."));
			}
			return list;
		}

		private List<CachedUpgrade> ScanUpgradesInside()
		{
			//IL_0031: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			List<CachedUpgrade> list = new List<CachedUpgrade>();
			if ((Object)(object)scanBox == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[UpgradeStandReroll.Items] Missing scan box; cannot scan upgrades.");
				return list;
			}
			HashSet<ItemUpgrade> hashSet = new HashSet<ItemUpgrade>();
			Collider[] array = Physics.OverlapBox(scanBox.position, scanBox.localScale * 0.5f, scanBox.rotation);
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				ItemUpgrade val2 = ((Component)val).GetComponent<ItemUpgrade>() ?? ((Component)val).GetComponentInParent<ItemUpgrade>();
				if (!((Object)(object)val2 == (Object)null) && hashSet.Add(val2))
				{
					ItemAttributes component = ((Component)val2).GetComponent<ItemAttributes>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.item == (Object)null))
					{
						list.Add(new CachedUpgrade(val2, component.item, ((Component)val2).transform.position, ((Component)val2).transform.rotation));
					}
				}
			}
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Scan complete. " + $"colliders={array.Length}, upgrades={list.Count}, " + $"scanBox={NameOrNull(scanBox)}, position={scanBox.position}, scale={scanBox.localScale}."));
			}
			return list;
		}

		private void DestroyCachedUpgrades()
		{
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Items] Destroying cached upgrades. count={pendingReplacements.Count}.");
			}
			foreach (PendingReplacement pendingReplacement in pendingReplacements)
			{
				DestroyUpgrade(pendingReplacement.OriginalUpgrade);
			}
		}

		private void SpawnPendingReplacements()
		{
			//IL_004e: 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)
			if (Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.Items] Spawning replacement upgrades. count={pendingReplacements.Count}.");
			}
			foreach (PendingReplacement pendingReplacement in pendingReplacements)
			{
				SpawnReplacement(pendingReplacement.Item, pendingReplacement.Position, pendingReplacement.Rotation);
			}
		}

		private static Dictionary<string, int> BuildDisplayedCounts(IEnumerable<CachedUpgrade> upgrades)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (CachedUpgrade upgrade in upgrades)
			{
				string key = ItemKey(upgrade.Item);
				dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1));
			}
			return dictionary;
		}

		private static Item SelectReplacement(Item previous, Dictionary<string, int> displayedCounts, Dictionary<string, int> selectedCounts)
		{
			return SelectReplacementInternal(previous, displayedCounts, selectedCounts, allowPrevious: false) ?? SelectReplacementInternal(previous, displayedCounts, selectedCounts, allowPrevious: true);
		}

		private static Item SelectReplacementInternal(Item previous, Dictionary<string, int> displayedCounts, Dictionary<string, int> selectedCounts, bool allowPrevious)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				return null;
			}
			ConfigEntry<int> value;
			int num = (Plugin.SameItemCopies.TryGetValue("Upgrades", out value) ? value.Value : 6);
			int num2 = ((!((Object)(object)GameDirector.instance != (Object)null)) ? 1 : GameDirector.instance.PlayerList.Count);
			List<(Item, int)> list = new List<(Item, int)>();
			foreach (Item value4 in StatsManager.instance.itemDictionary.Values)
			{
				if ((Object)(object)value4 == (Object)null || value4.disabled || (int)value4.itemType != 3 || (!allowPrevious && (Object)(object)value4 == (Object)(object)previous))
				{
					continue;
				}
				int itemSpawnChance = Plugin.GetItemSpawnChance(value4);
				if (itemSpawnChance > 0)
				{
					string key = ItemKey(value4);
					int value2;
					int num3 = (displayedCounts.TryGetValue(key, out value2) ? value2 : 0);
					int value3;
					int num4 = (selectedCounts.TryGetValue(key, out value3) ? value3 : 0);
					int num5 = SemiFunc.StatGetItemsPurchased(((Object)value4).name);
					if (num3 + num4 < num && (value4.maxAmountInShop <= 0 || num5 + num3 + num4 < value4.maxAmountInShop) && (!value4.maxPurchase || StatsManager.instance.GetItemsUpgradesPurchasedTotal(((Object)value4).name) < value4.maxPurchaseAmount) && (value4.minPlayerCount <= 1 || num2 >= value4.minPlayerCount))
					{
						list.Add((value4, Mathf.Max(1, itemSpawnChance)));
					}
				}
			}
			if (list.Count == 0)
			{
				if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] No replacement candidates. " + $"previous={ItemKey(previous)}, allowPrevious={allowPrevious}, sameLimit={num}."));
				}
				return null;
			}
			int num6 = list.Sum<(Item, int)>(((Item item, int weight) candidate) => candidate.weight);
			int num7 = Random.Range(0, num6);
			foreach (var (val, num8) in list)
			{
				if (num7 < num8)
				{
					if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Selected replacement. previous=" + ItemKey(previous) + ", selected=" + ((Object)val).name + ", " + $"allowPrevious={allowPrevious}, candidates={list.Count}, totalWeight={num6}."));
					}
					return val;
				}
				num7 -= num8;
			}
			Item item = list[list.Count - 1].Item1;
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Selected fallback replacement. " + $"previous={ItemKey(previous)}, selected={((Object)item).name}, candidates={list.Count}."));
			}
			return item;
		}

		private static void DestroyUpgrade(ItemUpgrade upgrade)
		{
			if (!((Object)(object)upgrade == (Object)null))
			{
				PhotonView component = ((Component)upgrade).GetComponent<PhotonView>();
				GameObject val = (((Object)(object)component != (Object)null) ? ((Component)component).gameObject : ((Component)upgrade).gameObject);
				if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Destroy upgrade. " + $"object={((Object)val).name}, hasPhotonView={(Object)(object)component != (Object)null}, multiplayer={SemiFunc.IsMultiplayer()}."));
				}
				if (SemiFunc.IsMultiplayer() && (Object)(object)component != (Object)null)
				{
					PhotonNetwork.Destroy(val);
				}
				else
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private void SpawnReplacement(Item item, Vector3 position, Quaternion fallbackRotation)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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)
			Quaternion val = fallbackRotation;
			if ((Object)(object)ShopManager.instance != (Object)null && (Object)(object)ShopManager.instance.itemRotateHelper != (Object)null)
			{
				Transform transform = ((Component)ShopManager.instance.itemRotateHelper).transform;
				transform.parent = ((Component)this).transform;
				transform.position = position;
				transform.localRotation = item.spawnRotationOffset;
				val = transform.rotation;
				transform.parent = ((Component)ShopManager.instance).transform;
			}
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Items] Spawn replacement. " + $"item={((Object)item).name}, position={position}, rotation={((Quaternion)(ref val)).eulerAngles}, multiplayer={SemiFunc.IsMultiplayer()}."));
			}
			if (SemiFunc.IsMultiplayer())
			{
				PhotonNetwork.InstantiateRoomObject(item.prefab.ResourcePath, position, val, (byte)0, (object[])null);
			}
			else
			{
				Object.Instantiate<GameObject>(item.prefab.Prefab, position, val);
			}
		}

		private static string ItemKey(Item item)
		{
			if (!((Object)(object)item == (Object)null))
			{
				return ((Object)item).name;
			}
			return string.Empty;
		}

		private void ResolveReferences()
		{
			if ((Object)(object)buttonRoot == (Object)null)
			{
				buttonRoot = FindChildByNamePart(((Component)this).transform, "button");
			}
			if ((Object)(object)buttonGrabObject == (Object)null)
			{
				buttonGrabObject = ((Component)this).GetComponentsInChildren<StaticGrabObject>(true).FirstOrDefault();
			}
			if ((Object)(object)buttonColliderRoot == (Object)null && (Object)(object)buttonGrabObject != (Object)null)
			{
				buttonColliderRoot = buttonGrabObject.colliderTransform;
			}
			if ((Object)(object)scanBox == (Object)null)
			{
				scanBox = FindChildByNameParts(((Component)this).transform, "upgrade", "inside", "box");
			}
			if ((Object)(object)scanBox == (Object)null)
			{
				scanBox = FindChildByNameParts(((Component)this).transform, "inside", "box");
			}
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.References] Resolved. button=" + NameOrNull(buttonRoot) + ", buttonCollider=" + NameOrNull(buttonColliderRoot) + ", scanBox=" + NameOrNull(scanBox) + ", hatch=" + NameOrNull(hatch) + ", compartment=" + NameOrNull(upgradeCompartment) + ", allMeshes=" + NameOrNull(allMeshesTransform) + "."));
			}
		}

		private void CaptureOriginalTransforms()
		{
			//IL_0015: 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)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0056: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)buttonRoot != (Object)null)
			{
				buttonOriginalPosition = buttonRoot.localPosition;
				buttonOriginalRotation = buttonRoot.localRotation;
			}
			if ((Object)(object)hatch != (Object)null)
			{
				hatchOriginalPosition = hatch.localPosition;
				hatchOriginalScale = hatch.localScale;
			}
			if ((Object)(object)upgradeCompartment != (Object)null)
			{
				compartmentOriginalRotation = upgradeCompartment.localRotation;
			}
			if ((Object)(object)allMeshesTransform != (Object)null)
			{
				allMeshesOriginalPosition = allMeshesTransform.localPosition;
				allMeshesOriginalRotation = allMeshesTransform.localRotation;
			}
		}

		private void DisableVanillaButtonNetworking()
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			PhysGrabObjectGrabArea[] componentsInChildren = ((Component)this).GetComponentsInChildren<PhysGrabObjectGrabArea>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Behaviour)componentsInChildren[i]).enabled = false;
				num++;
			}
			StaticGrabObject[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<StaticGrabObject>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				((Behaviour)componentsInChildren2[i]).enabled = false;
				num2++;
			}
			PhotonView[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<PhotonView>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				((Behaviour)componentsInChildren3[i]).enabled = false;
				num3++;
			}
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.References] Disabled vanilla button networking. " + $"grabAreas={num}, staticGrabs={num2}, photonViews={num3}."));
			}
		}

		private static Transform FindChildByNamePart(Transform root, string part)
		{
			string value = part.ToLowerInvariant();
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name.ToLowerInvariant().Contains(value))
				{
					return val;
				}
			}
			return null;
		}

		private static Transform FindChildByNameParts(Transform root, params string[] parts)
		{
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				string text = ((Object)val).name.ToLowerInvariant();
				bool flag = true;
				foreach (string text2 in parts)
				{
					if (!text.Contains(text2.ToLowerInvariant()))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					return val;
				}
			}
			return null;
		}

		private static string NameOrNull(Transform transform)
		{
			if (!((Object)(object)transform == (Object)null))
			{
				return ((Object)transform).name;
			}
			return "<null>";
		}

		private void TryStartReroll(bool visualOnly, bool broadcastVisual)
		{
			if (state != RerollState.Holding && state != 0)
			{
				return;
			}
			if (visualOnly)
			{
				visualOnlyReroll = true;
				StateSet(RerollState.PressSucceed);
			}
			else
			{
				if (!SemiFunc.IsMasterClientOrSingleplayer())
				{
					return;
				}
				if (isBroken)
				{
					StateSet(RerollState.PressFail);
					return;
				}
				List<CachedUpgrade> list = ScanUpgradesInside();
				if (list.Count == 0)
				{
					if (Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)"[UpgradeStandReroll] Reroll skipped: no upgrades inside stand.");
					}
					StateSet(RerollState.PressFail);
					return;
				}
				int rerollCost = RerollCost;
				if (SemiFunc.StatGetRunCurrency() < rerollCost)
				{
					if (Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)$"[UpgradeStandReroll] Reroll skipped: not enough currency. cost={rerollCost}, current={SemiFunc.StatGetRunCurrency()}.");
					}
					StateSet(RerollState.PressFail);
					return;
				}
				List<PendingReplacement> list2 = BuildPendingReplacements(list);
				if (list2.Count == 0)
				{
					if (Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)"[UpgradeStandReroll] Reroll skipped: no valid replacement upgrades.");
					}
					StateSet(RerollState.PressFail);
					return;
				}
				SemiFunc.StatSetRunCurrency(SemiFunc.StatGetRunCurrency() - rerollCost);
				if ((Object)(object)CurrencyUI.instance != (Object)null)
				{
					CurrencyUI.instance.FetchCurrency();
				}
				if (maxRerollCount < 0)
				{
					maxRerollCount = Random.Range(1, 4);
				}
				rerollCount++;
				cachedUpgrades.Clear();
				cachedUpgrades.AddRange(list);
				pendingReplacements.Clear();
				pendingReplacements.AddRange(list2);
				visualOnlyReroll = false;
				BroadcastHoldVisualStop();
				if (broadcastVisual)
				{
					BroadcastRerollVisual();
				}
				if (Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)$"[UpgradeStandReroll] Reroll accepted. upgrades={list.Count}, replacements={list2.Count}, cost={rerollCost}, rerollCount={rerollCount}, maxBeforeBreak={maxRerollCount}.");
				}
				StateSet(RerollState.PressSucceed);
			}
		}

		private bool CanAttemptRerollLocally()
		{
			if (isBroken)
			{
				return false;
			}
			if (SemiFunc.StatGetRunCurrency() < RerollCost)
			{
				return false;
			}
			return (Object)(object)scanBox != (Object)null;
		}

		private void UpdateState(bool buttonFocused)
		{
			switch (state)
			{
			case RerollState.Idle:
				StateIdle(buttonFocused);
				break;
			case RerollState.Holding:
				StateHolding(buttonFocused);
				break;
			case RerollState.Rollback:
				StateRollback(buttonFocused);
				break;
			case RerollState.PressFail:
				StatePressFail();
				break;
			case RerollState.PressSucceed:
				StatePressSucceed();
				break;
			case RerollState.CloseHatch:
				StateCloseHatch();
				break;
			case RerollState.RollStart:
				StateRollStart();
				break;
			case RerollState.Rolling:
				StateRolling();
				break;
			case RerollState.RollEnd:
				StateRollEnd();
				break;
			case RerollState.OpenHatch:
				StateOpenHatch();
				break;
			case RerollState.WaitingForHost:
				StateWaitingForHost();
				break;
			case RerollState.Broken:
				StateBroken();
				break;
			}
		}

		private void StateIdle(bool buttonFocused)
		{
			if (stateStart)
			{
				stateTimerMax = 0f;
				stateTimer = 0f;
				visualOnlyReroll = false;
				buttonAnimationEval = 0f;
				chargeElapsed = 0f;
				buttonRotationTarget = 0f;
				SetButtonPositionPressed(0f);
				SetButtonNormal();
				ResetBuildUpVisuals();
				stateStart = false;
				remoteHoldVisual = false;
				holdRequestSent = false;
				holdVisualBroadcasted = false;
				resumeChargeFromRollback = false;
				holdProgressSyncTimer = 0f;
				rollbackTopStage = 0;
				rollbackCurrentStage = -1;
				rollbackCurrentStageElapsed = 0f;
				rollbackCurrentStageStartEmission = 0f;
				rollbackResumeRequested = false;
			}
			if (isBroken)
			{
				StateSet(RerollState.Broken);
			}
			else if (buttonFocused && activationStartedThisFrame)
			{
				if (!CanAttemptRerollLocally())
				{
					StateSet(RerollState.PressFail);
				}
				else
				{
					StateSet(RerollState.Holding);
				}
			}
		}

		private void StateHolding(bool buttonFocused)
		{
			if (stateStart)
			{
				stateTimerMax = HoldDuration;
				stateTimer = 0f;
				if (resumeChargeFromRollback)
				{
					chargeElapsed = Mathf.Clamp(chargeElapsed, 0f, HoldDuration);
					SyncChargeStageTriggers(chargeElapsed);
					ApplyChargeVisualsSilent(chargeElapsed);
					if (Plugin.DebugLogs.Value)
					{
						Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.State] Resume charging from rollback. chargeElapsed={chargeElapsed:0.00}.");
					}
				}
				else
				{
					chargeElapsed = 0f;
					buttonRotationTarget = 0f;
					ResetChargeStageTriggers();
					ResetBuildUpVisuals();
				}
				resumeChargeFromRollback = false;
				if ((Object)(object)buildUpLight != (Object)null)
				{
					((Component)buildUpLight).gameObject.SetActive(true);
				}
				stateStart = false;
				if (!remoteHoldVisual)
				{
					if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
					{
						BroadcastHoldVisualStart();
					}
					else if (SemiFunc.IsMultiplayer())
					{
						RequestHostHoldStart();
					}
				}
			}
			if (!remoteHoldVisual && (!activationHeld || activationReleasedThisFrame || !buttonFocused))
			{
				if (PhotonNetwork.IsMasterClient)
				{
					BroadcastHoldVisualStop();
				}
				else
				{
					RequestHostHoldStop();
				}
				StateSet(RerollState.Rollback);
				return;
			}
			chargeElapsed += Time.deltaTime;
			UpdateChargeVisuals(chargeElapsed);
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				BroadcastHoldVisualProgress(force: false);
			}
			if (chargeElapsed >= HoldDuration)
			{
				if (remoteHoldVisual)
				{
					chargeElapsed = HoldDuration;
					UpdateChargeVisuals(chargeElapsed);
				}
				else if (SemiFunc.IsMasterClientOrSingleplayer())
				{
					TryStartReroll(visualOnly: false, broadcastVisual: true);
				}
				else if (SemiFunc.IsMultiplayer())
				{
					holdRequestSent = false;
					RequestHostReroll();
					StateSet(RerollState.WaitingForHost);
				}
				else
				{
					StateSet(RerollState.PressFail);
				}
			}
		}

		private void StateRollback(bool buttonFocused)
		{
			BroadcastHoldVisualStop();
			if (stateStart)
			{
				rollbackResumeRequested = false;
				rollbackTopStage = Mathf.Clamp(Mathf.FloorToInt(chargeElapsed / ChargeStageDuration), 0, Mathf.Max(0, buildUpStages.Length - 1));
				BeginRollbackStage(rollbackTopStage);
				if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
				{
					Plugin.Log.LogInfo((object)("[UpgradeStandReroll.State] Stage rollback started. " + $"chargeElapsed={chargeElapsed:0.00}, topStage={rollbackTopStage}."));
				}
				stateTimer = 0f;
				stateTimerMax = 0f;
				stateStart = false;
			}
			if (rollbackCurrentStage < 0)
			{
				return;
			}
			if (!remoteHoldVisual && buttonFocused && activationStartedThisFrame)
			{
				rollbackResumeRequested = true;
			}
			rollbackCurrentStageElapsed += Time.deltaTime;
			float num = Mathf.Clamp01(rollbackCurrentStageElapsed / 0.5f);
			float eased = (SemiFunc.Photosensitivity() ? num : Evaluate(buildUpOutroCurve, num));
			ApplyRollbackStageVisuals(eased);
			if (num < 1f)
			{
				return;
			}
			ApplyStageEmission(buildUpStages[rollbackCurrentStage], 0f);
			if (!remoteHoldVisual && rollbackResumeRequested)
			{
				ResumeChargingFromRollback(rollbackCurrentStage);
				return;
			}
			rollbackCurrentStage--;
			if (rollbackCurrentStage < 0)
			{
				chargeElapsed = 0f;
				ResetBuildUpVisuals();
				buttonRotationTarget = 0f;
				StateSet(RerollState.Idle);
			}
			else
			{
				BeginRollbackStage(rollbackCurrentStage);
			}
		}

		private void StatePressFail()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.3f;
				stateTimer = 0f;
				buttonAnimationEval = 0f;
				buttonRotationTarget = 0f;
				SetButtonDeny();
				if (soundButtonDeny != null)
				{
					soundButtonDeny.Play(ButtonSoundPosition, 1f, 1f, 1f, 1f);
				}
				ShakeButton();
				stateStart = false;
			}
			buttonAnimationEval = Mathf.Clamp01(buttonAnimationEval + 3f * Time.deltaTime);
			buttonRotationTarget = 22.5f * Evaluate(buttonDenyCurve, buttonAnimationEval);
			if (stateTimer >= stateTimerMax)
			{
				StateSet(isBroken ? RerollState.Broken : RerollState.Idle);
			}
		}

		private void StatePressSucceed()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.3f;
				stateTimer = 0f;
				buttonAnimationEval = 0f;
				if (soundButtonPress != null)
				{
					soundButtonPress.Play(ButtonSoundPosition, 1f, 1f, 1f, 1f);
				}
				ShakeButton();
				stateStart = false;
			}
			buttonAnimationEval = Mathf.Clamp01(buttonAnimationEval + 8f * Time.deltaTime);
			SetButtonPositionPressed(Evaluate(buttonPressAnimationCurve, buttonAnimationEval));
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.CloseHatch);
			}
		}

		private void StateCloseHatch()
		{
			//IL_005c: 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_014b: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.4f;
				stateTimer = 0f;
				hatchAnimationEval = 0f;
				hatchCloseImpactPlayed = false;
				if ((Object)(object)hatchParticles != (Object)null)
				{
					hatchParticles.Play(true);
				}
				if (soundHatchClose != null)
				{
					soundHatchClose.Play(HatchSoundPosition, 1f, 1f, 1f, 1f);
				}
				if (soundRerollStart != null)
				{
					soundRerollStart.Play(((Component)this).transform.position, 1f, 1f, 1f, 1f);
				}
				ShakeHatch();
				stateStart = false;
			}
			hatchAnimationEval = Mathf.Clamp01(stateTimer / stateTimerMax);
			ApplyHatchClosed(hatchAnimationEval);
			if (hatchAnimationEval >= 0.2f)
			{
				if ((Object)(object)hatchClosed != (Object)null)
				{
					hatchClosed.SetActive(true);
				}
				if ((Object)(object)hatchHurtCollider != (Object)null)
				{
					hatchHurtCollider.SetActive(true);
				}
			}
			if (hatchAnimationEval >= 0.2f && !hatchCloseImpactPlayed)
			{
				hatchCloseImpactPlayed = true;
				if (soundHatchCloseImpact != null)
				{
					soundHatchCloseImpact.Play(HatchSoundPosition, 1f, 1f, 1f, 1f);
				}
			}
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.RollStart);
			}
		}

		private void StateRollStart()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.3f;
				stateTimer = 0f;
				compartmentAnimationEval = 0f;
				if ((Object)(object)hatchHurtCollider != (Object)null)
				{
					hatchHurtCollider.SetActive(false);
				}
				if (soundRollStart != null)
				{
					soundRollStart.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
				if (!visualOnlyReroll)
				{
					DestroyCachedUpgrades();
				}
				ShakeCompartment();
				if (meshRotationSpring != null)
				{
					AddSpringVelocity(meshRotationSpring, new Vector3(5f, 0f, 0f));
				}
				if (meshPositionSpring != null)
				{
					AddSpringVelocity(meshPositionSpring, new Vector3(0f, 2f, 0f));
				}
				stateStart = false;
			}
			compartmentAnimationEval = Mathf.Clamp01(stateTimer / stateTimerMax);
			float num = Evaluate(rollStartShakeCurve, compartmentAnimationEval);
			ApplyCompartmentRotation(num * 20f);
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.Rolling);
			}
		}

		private void StateRolling()
		{
			//IL_007d: 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_018b: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.5f;
				stateTimer = 0f;
				compartmentAnimationEval = 0f;
				rerollTicksPlayed = 0;
				finalRollSqueakPlayed = false;
				if ((Object)(object)rerollCompartmentHurtColliders != (Object)null)
				{
					rerollCompartmentHurtColliders.SetActive(true);
				}
				if ((Object)(object)rollingParticles != (Object)null)
				{
					rollingParticles.Play(true);
				}
				if (soundRolling != null)
				{
					soundRolling.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
				stateStart = false;
			}
			compartmentAnimationEval = Mathf.Clamp01(stateTimer / stateTimerMax);
			float num = Evaluate(rollCurve, compartmentAnimationEval);
			ApplyCompartmentRotation(num * 360f);
			float num2 = stateTimerMax / 12f;
			while (rerollTicksPlayed < 12 && stateTimer >= (float)rerollTicksPlayed * num2)
			{
				if (soundRerollTick != null)
				{
					Sound val = soundRerollTick;
					float num3 = rerollTicksPlayed;
					float num4 = 1f;
					val.Pitch = num3 / num4;
					soundRerollTick.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
				rerollTicksPlayed++;
			}
			if (compartmentAnimationEval >= 0.9f && !finalRollSqueakPlayed)
			{
				finalRollSqueakPlayed = true;
				if (soundFinalRollSqueak != null)
				{
					soundFinalRollSqueak.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
			}
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.RollEnd);
			}
		}

		private void StateRollEnd()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 0.625f;
				stateTimer = 0f;
				compartmentAnimationEval = 0f;
				if ((Object)(object)rerollCompartmentHurtColliders != (Object)null)
				{
					rerollCompartmentHurtColliders.SetActive(false);
				}
				if ((Object)(object)rollingParticles != (Object)null)
				{
					rollingParticles.Stop(true);
				}
				if (soundRollEnd != null)
				{
					soundRollEnd.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
				if (soundRerollSettle != null)
				{
					soundRerollSettle.Play(CompartmentSoundPosition, 1f, 1f, 1f, 1f);
				}
				ShakeCompartment();
				if (meshRotationSpring != null)
				{
					AddSpringVelocity(meshRotationSpring, new Vector3(15f, 0f, 0f));
				}
				if (meshPositionSpring != null)
				{
					AddSpringVelocity(meshPositionSpring, new Vector3(0f, 1f, 0f));
				}
				if (maxRerollCount > 0 && rerollCount >= maxRerollCount)
				{
					StartBreakBuildUpVisual();
					if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
					{
						BroadcastBreakBuildUpVisual();
					}
				}
				stateStart = false;
			}
			compartmentAnimationEval = Mathf.Clamp01(stateTimer / stateTimerMax);
			float num = Evaluate(rollEndShakeCurve, compartmentAnimationEval);
			ApplyCompartmentRotation(num * 15f);
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.OpenHatch);
			}
		}

		private void StateOpenHatch()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (stateStart)
			{
				stateTimerMax = 2f;
				stateTimer = 0f;
				hatchAnimationEval = 1f;
				hatchOpenImpactPlayed = false;
				if (!visualOnlyReroll)
				{
					SpawnPendingReplacements();
				}
				if ((Object)(object)hatchParticles != (Object)null)
				{
					hatchParticles.Play(true);
				}
				if (soundHatchOpen != null)
				{
					soundHatchOpen.Play(HatchSoundPosition, 1f, 1f, 1f, 1f);
				}
				if (soundRerollEnd != null)
				{
					soundRerollEnd.Play(((Component)this).transform.position, 1f, 1f, 1f, 1f);
				}
				ShakeHatch();
				stateStart = false;
			}
			float num = Mathf.Clamp01(stateTimer / 0.4f);
			hatchAnimationEval = 1f - num;
			ApplyHatchClosed(hatchAnimationEval);
			if (hatchAnimationEval <= 0.7f && (Object)(object)hatchClosed != (Object)null)
			{
				hatchClosed.SetActive(false);
			}
			if (hatchAnimationEval <= 0.8f && !hatchOpenImpactPlayed)
			{
				hatchOpenImpactPlayed = true;
				if (soundHatchOpenImpact != null)
				{
					soundHatchOpenImpact.Play(HatchSoundPosition, 1f, 1f, 1f, 1f);
				}
			}
			if (stateTimer >= 0.4f)
			{
				float num2 = Mathf.Clamp01((stateTimer - 0.4f) / 0.2f);
				SetButtonPositionPressed(1f - num2);
				FadeBuildUpVisuals(1f - num2);
			}
			if (stateTimer >= 0.8f)
			{
				buttonRotationTarget = 0f;
			}
			if (stateTimer >= stateTimerMax)
			{
				cachedUpgrades.Clear();
				pendingReplacements.Clear();
				if (!visualOnlyReroll && maxRerollCount > 0 && rerollCount >= maxRerollCount)
				{
					BreakButton();
					BroadcastBroken();
					StateSet(RerollState.Broken);
				}
				else
				{
					StateSet(RerollState.Idle);
				}
			}
		}

		private void StateWaitingForHost()
		{
			if (stateStart)
			{
				stateTimerMax = 2f;
				stateTimer = 0f;
				stateStart = false;
			}
			if (stateTimer >= stateTimerMax)
			{
				StateSet(RerollState.Idle);
			}
		}

		private void StateBroken()
		{
			if (stateStart)
			{
				BreakButton();
				stateTimer = 0f;
				stateTimerMax = 0f;
				stateStart = false;
			}
		}

		private void StateSet(RerollState nextState)
		{
			if (Plugin.DebugLogs.Value && state != nextState && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)$"[UpgradeStandReroll.State] {state} -> {nextState}.");
			}
			state = nextState;
			stateStart = true;
		}

		internal void ConfigureFromVanilla(UpgradeStand vanillaStand)
		{
			scanBox = vanillaStand.upgradeInsideBoxCheck;
			buttonGrabObject = vanillaStand.buttonGrabObject;
			buttonRoot = vanillaStand.button;
			buttonColliderRoot = (((Object)(object)buttonGrabObject != (Object)null) ? buttonGrabObject.colliderTransform : null);
			hatch = vanillaStand.hatch;
			upgradeCompartment = vanillaStand.upgradeCompartment;
			hatchClosed = vanillaStand.hatchClosed;
			hatchHurtCollider = vanillaStand.hatchHurtCollider;
			rerollCompartmentHurtColliders = vanillaStand.rerollCompartmentHurtColliders;
			allMeshesTransform = vanillaStand.allMeshesTransform;
			interactLocalized = vanillaStand.interactLocalized;
			hatchParticles = vanillaStand.hatchParticles;
			rollingParticles = vanillaStand.rollingParticles;
			buttonPressAnimationCurve = vanillaStand.buttonPressAnimationCurve;
			buttonDenyCurve = vanillaStand.buttonDenyCurve;
			hatchAnimationCurve = vanillaStand.hatchAnimationCurve;
			rollStartShakeCurve = vanillaStand.rollStartShakeCurve;
			rollCurve = vanillaStand.rollCurve;
			rollEndShakeCurve = vanillaStand.rollEndShakeCurve;
			soundButtonPress = vanillaStand.soundButtonPress;
			soundButtonDeny = vanillaStand.soundButtonDeny;
			soundHatchClose = vanillaStand.soundHatchClose;
			soundRollStart = vanillaStand.soundRollStart;
			soundRolling = vanillaStand.soundRolling;
			soundRollEnd = vanillaStand.soundRollEnd;
			soundHatchOpen = vanillaStand.soundHatchOpen;
			soundButtonTwistUp = vanillaStand.soundButtonTwistUp;
			soundButtonTwistDown = vanillaStand.soundButtonTwistDown;
			soundStageBeep = vanillaStand.soundStageBeep;
			soundStateOn = vanillaStand.soundStateOn;
			soundStateOff = vanillaStand.soundStateOff;
			soundRerollStart = vanillaStand.soundRerollStart;
			soundRerollEnd = vanillaStand.soundRerollEnd;
			soundRerollTick = vanillaStand.soundRerollTick;
			soundRerollSettle = vanillaStand.soundRerollSettle;
			soundFinalRollSqueak = vanillaStand.soundFinalRollSqueak;
			soundHatchCloseImpact = vanillaStand.soundHatchCloseImpact;
			soundHatchOpenImpact = vanillaStand.soundHatchOpenImpact;
			buttonRenderer = vanillaStand.buttonRenderer;
			buttonNormalMaterial = vanillaStand.buttonNormalMaterial;
			buttonDenyMaterial = vanillaStand.buttonDenyMaterial;
			buildUpStages = vanillaStand.buildUpStages;
			buildUpLight = vanillaStand.buildUpLight;
			buildUpIntroCurve = vanillaStand.buildUpIntroCurve;
			buildUpOutroCurve = vanillaStand.buildUpOutroCurve;
			buttonRubble = vanillaStand.buttonRubble;
			fireHurtCollider = vanillaStand.fireHurtCollider;
			fireLight = vanillaStand.fireLight;
			particleButtonBreak = vanillaStand.particleButtonBreak;
			particleFireLoop = vanillaStand.particleFireLoop;
			buildUpParticles = vanillaStand.buildUpParticles;
			soundButtonBreak = vanillaStand.soundButtonBreak;
			soundBuildUpLoop = vanillaStand.soundBuildUpLoop;
			flickerFadeLightCurve = vanillaStand.flickerFadeLightCurve;
			meshRotationSpring = vanillaStand.meshRotationSpring;
			meshPositionSpring = vanillaStand.meshPositionSpring;
			buttonRotationSpring = vanillaStand.buttonRotationSpring;
			soundFluorescentLightTurnOff = vanillaStand.soundFluorescentLightTurnOff;
			soundLilButtonFire = vanillaStand.soundLilButtonFire;
			if (Plugin.DebugLogs.Value && Plugin.DebugLogs.Value)
			{
				Plugin.Log.LogInfo((object)("[UpgradeStandReroll.Binding] Copied vanilla references. vanilla=" + ((Object)vanillaStand).name + ", scanBox=" + NameOrNull(scanBox) + ", button=" + NameOrNull(buttonRoot) + ", hatch=" + NameOrNull(hatch) + ", compartment=" + NameOrNull(upgradeCompartment) + ", " + $"buildUpStages={((buildUpStages != null) ? buildUpStages.Length : 0)}."));
			}
		}

		private float ComputeChargingButtonTarget(float elapsed)
		{
			int num = Mathf.Max(1, buildUpStages.Length);
			return (float)Mathf.Clamp(Mathf.FloorToInt(elapsed / ChargeStageDuration) + 1, 0, num) * buttonStageRotationAngle;
		}

		private void SyncChargeStageTriggers(float elapsed)
		{
			if (chargeStageTriggered == null || chargeStageTriggered.Length != buildUpStages.Length)
			{
				chargeStageTriggered = new bool[buildUpStages.Length];
			}
			for (int i = 0; i < chargeStageTriggered.Length; i++)
			{
				chargeStageTriggered[i] = elapsed >= (float)i * ChargeStageDuration;
			}
		}

		private void ApplyChargeVisualsSilent(float elapsed)
		{
			if (buildUpStages != null && buildUpStages.Length != 0)
			{
				buttonRotationTarget = ComputeChargingButtonTarget(elapsed);
				for (int i = 0; i < buildUpStages.Length; i++)
				{
					float num = (float)i * ChargeStageDuration;
					float num2 = Mathf.Clamp01((elapsed - num) / ChargeStageDuration);
					float value = (SemiFunc.Photosensitivity() ? num2 : Evaluate(buildUpIntroCurve, num2));
					ApplyStageEmission(buildUpStages[i], value);
				}
				UpdateBuildUpLight(elapsed);
			}
		}

		private void BeginRollbackStage(int stageIndex)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: 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_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (buildUpStages == null || buildUpStages.Length == 0)
			{
				rollbackCurrentStage = -1;
				return;
			}
			rollbackCurrentStage = Mathf.Clamp(stageIndex, 0, buildUpStages.Length - 1);
			rollbackCurrentStageElapsed = 0f;
			BuildUpStage val = buildUpStages[rollbackCurrentStage];
			rollbackCurrentStageStartEmission = ((rollbackCurrentStage == rollbackTopStage) ? Mathf.Clamp01(GetStageIntroValue(rollbackCurrentStage, chargeElapsed)) : 1f);
			if ((Objec