Decompiled source of SoloHost v1.2.1

BepInEx/plugins/SoloHost.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using SoloHost.Rules;
using SoloHost.Secure;
using SoloShared;
using Steamworks;
using Steamworks.Data;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Profiling;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SoloShared
{
	internal static class Loc
	{
		private static Dictionary<string, string> _table = new Dictionary<string, string>(StringComparer.Ordinal);

		private static string _folder;

		private static string _mod = "Solo";

		private static string _loaded = "";

		private static float _nextCheck;

		private static Action<string> _log = delegate
		{
		};

		private static Action<string> _warn = delegate
		{
		};

		private static readonly SortedSet<string> _seen = new SortedSet<string>(StringComparer.Ordinal);

		private static bool _collect;

		internal static string Language
		{
			get
			{
				if (_loaded.Length != 0)
				{
					return _loaded;
				}
				return "en";
			}
		}

		internal static void Init(string modName, string dllPath, Action<string> log, Action<string> warn, bool collectStrings = false)
		{
			_mod = modName ?? "Solo";
			_log = log ?? ((Action<string>)delegate
			{
			});
			_warn = warn ?? ((Action<string>)delegate
			{
			});
			_collect = collectStrings;
			try
			{
				_folder = Path.GetDirectoryName(dllPath);
			}
			catch
			{
				_folder = null;
			}
			Reload(CurrentLanguage());
		}

		internal static string T(string english)
		{
			if (string.IsNullOrEmpty(english))
			{
				return english;
			}
			if (_collect)
			{
				lock (_seen)
				{
					_seen.Add(english);
				}
			}
			if (!_table.TryGetValue(english, out var value) || value.Length <= 0)
			{
				return english;
			}
			return value;
		}

		internal static string F(string english, params object[] args)
		{
			string format = T(english);
			try
			{
				return string.Format(format, args);
			}
			catch
			{
				try
				{
					return string.Format(english, args);
				}
				catch
				{
					return english;
				}
			}
		}

		internal static void Tick(float unscaledTime)
		{
			if (!(unscaledTime < _nextCheck))
			{
				_nextCheck = unscaledTime + 2f;
				string text = CurrentLanguage();
				if (text != _loaded)
				{
					Reload(text);
				}
			}
		}

		private static string CurrentLanguage()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				object obj;
				if (!((Object)(object)selectedLocale != (Object)null))
				{
					obj = "";
				}
				else
				{
					LocaleIdentifier identifier = selectedLocale.Identifier;
					obj = ((LocaleIdentifier)(ref identifier)).Code;
				}
				string text = (string)obj;
				return string.IsNullOrEmpty(text) ? "en" : text;
			}
			catch
			{
				return "en";
			}
		}

		private static void Reload(string code)
		{
			_loaded = code ?? "en";
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (!string.IsNullOrEmpty(_folder) && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase))
			{
				string text = Path.Combine(_folder, "lang");
				foreach (string item in Candidates(_loaded, text))
				{
					string path = Path.Combine(text, item + ".txt");
					if (File.Exists(path))
					{
						int num = Read(path, dictionary);
						_log($"[{_mod}] Language {item}: {num} line(s) from {Path.GetFileName(path)}.");
						break;
					}
				}
			}
			_table = dictionary;
			if (dictionary.Count > 0)
			{
				_log("[" + _mod + "] Following the game's language: " + _loaded + ".");
			}
			if (dictionary.Count == 0 && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase))
			{
				_log("[" + _mod + "] No translation file for '" + _loaded + "' — showing English. Add lang/" + _loaded + ".txt beside the mod to change that.");
			}
		}

		private static IEnumerable<string> Candidates(string code, string dir)
		{
			yield return code;
			string language = code;
			int num = code.IndexOf('-');
			if (num > 0)
			{
				language = code.Substring(0, num);
				yield return language;
			}
			string[] array;
			try
			{
				array = (Directory.Exists(dir) ? Directory.GetFiles(dir, "*.txt") : new string[0]);
			}
			catch
			{
				yield break;
			}
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(array2[i]);
				if (fileNameWithoutExtension.StartsWith(language + "-", StringComparison.OrdinalIgnoreCase))
				{
					yield return fileNameWithoutExtension;
				}
			}
		}

		private static int Read(string path, Dictionary<string, string> into)
		{
			int num = 0;
			try
			{
				string text = null;
				string[] array = File.ReadAllLines(path, Encoding.UTF8);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.Length == 0 || text2[0] == '#')
					{
						continue;
					}
					if (text2[0] == '=')
					{
						text = Unescape(text2.Substring(1).Trim());
					}
					else if (text2[0] == '>' && text != null)
					{
						string text3 = Unescape(text2.Substring(1).Trim());
						if (text3.Length > 0)
						{
							into[text] = text3;
							num++;
						}
						text = null;
					}
				}
			}
			catch (Exception ex)
			{
				_warn("[" + _mod + "] Couldn't read " + Path.GetFileName(path) + ": " + ex.Message);
			}
			return num;
		}

		private static string Unescape(string s)
		{
			return s.Replace("\\n", "\n");
		}

		private static string Escape(string s)
		{
			return s.Replace("\n", "\\n");
		}

		internal static void DumpSeen()
		{
			if (!_collect || string.IsNullOrEmpty(_folder))
			{
				return;
			}
			try
			{
				string text = Path.Combine(_folder, "lang");
				Directory.CreateDirectory(text);
				string path = Path.Combine(text, "_english.txt");
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# Every line " + _mod + " has shown this session.");
				stringBuilder.AppendLine("# Copy this file to <language code>.txt and fill in the > lines.");
				stringBuilder.AppendLine("# Leave a > line empty to keep the English.");
				stringBuilder.AppendLine();
				lock (_seen)
				{
					foreach (string item in _seen)
					{
						stringBuilder.Append("= ").AppendLine(Escape(item));
						stringBuilder.AppendLine("> ");
					}
					File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					_log($"[{_mod}] Wrote {_seen.Count} line(s) to lang/_english.txt.");
				}
			}
			catch (Exception ex)
			{
				_warn("[" + _mod + "] Couldn't write the English list: " + ex.Message);
			}
		}
	}
}
namespace SoloHost
{
	internal static class AntiCheat
	{
		internal sealed class Flag
		{
			public string SteamId;

			public string Name;

			public string Reason;

			public float Time;

			public int Count = 1;
		}

		private static readonly List<Flag> _flags = new List<Flag>();

		private static readonly Dictionary<string, Vector3> _lastPos = new Dictionary<string, Vector3>();

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

		private static float _nextPoll;

		private static float _lastPollTime;

		private static readonly Dictionary<string, float> _excused = new Dictionary<string, float>();

		public static IEnumerable<Flag> Flags => _flags.OrderByDescending((Flag f) => f.Time);

		public static int FlagCount => _flags.Count;

		public static void Excuse(string steamId, float seconds = 5f)
		{
			if (!string.IsNullOrEmpty(steamId))
			{
				_excused[steamId] = Time.time + seconds;
			}
		}

		public static void Excuse(PlayerAvatar p, float seconds = 5f)
		{
			if ((Object)(object)p != (Object)null)
			{
				Excuse(SemiFunc.PlayerGetSteamID(p), seconds);
			}
		}

		public static void ExcuseEveryone(float seconds = 5f)
		{
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				Excuse(item, seconds);
			}
		}

		private static bool IsExcused(string id)
		{
			if (_excused.TryGetValue(id, out var value))
			{
				return Time.time < value;
			}
			return false;
		}

		public static void Clear()
		{
			_flags.Clear();
		}

		public static void ClearFor(string steamId)
		{
			_flags.RemoveAll((Flag f) => f.SteamId == steamId);
		}

		public static void Poll()
		{
			if (!Plugin.Ready || !Plugin.AntiCheatEnabled.Value || !SemiFunc.IsMultiplayer() || !SemiFunc.IsMasterClientOrSingleplayer() || Time.time < _nextPoll)
			{
				return;
			}
			float dt = ((_lastPollTime > 0f) ? (Time.time - _lastPollTime) : 0.5f);
			_lastPollTime = Time.time;
			_nextPoll = Time.time + 0.5f;
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if (!((Object)(object)item == (Object)null) && !item.isLocal)
				{
					string text = SemiFunc.PlayerGetSteamID(item);
					if (!string.IsNullOrEmpty(text))
					{
						string name = SemiFunc.PlayerGetName(item) ?? text;
						CheckSpeed(item, text, name, dt);
						CheckUpgrades(item, text, name);
					}
				}
			}
		}

		private static void CheckSpeed(PlayerAvatar p, string id, string name, float dt)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)p).transform.position;
			if (!FlightController.IsTowing(p) && !FlightController.HasFlight(p) && !FlightController.HasNoclip(p) && !IsExcused(id) && _lastPos.TryGetValue(id, out var value) && dt > 0.01f)
			{
				float num = Vector3.Distance(position, value) / dt;
				if (num > Plugin.AntiCheatTeleportSpeed.Value)
				{
					Raise(id, name, $"moved {num:0} m/s (teleport?)");
				}
				else if (num > Plugin.AntiCheatMaxSpeed.Value)
				{
					Raise(id, name, $"moving {num:0} m/s (speed hack?)");
				}
			}
			_lastPos[id] = position;
		}

		private static void CheckUpgrades(PlayerAvatar p, string id, string name)
		{
			string[] upgradeNames;
			if (!_lastUpgrades.TryGetValue(id, out var value))
			{
				value = new Dictionary<string, int>();
				_lastUpgrades[id] = value;
				upgradeNames = SharedUpgrades.UpgradeNames;
				foreach (string text in upgradeNames)
				{
					value[text] = DevActions.GetUpgradeLevel(p, text);
				}
				return;
			}
			bool flag = IsExcused(id);
			upgradeNames = SharedUpgrades.UpgradeNames;
			foreach (string text2 in upgradeNames)
			{
				int upgradeLevel = DevActions.GetUpgradeLevel(p, text2);
				value.TryGetValue(text2, out var value2);
				int num = upgradeLevel - value2;
				if (!flag && num >= Plugin.AntiCheatUpgradeJump.Value)
				{
					Raise(id, name, $"{text2} jumped +{num} at once");
				}
				value[text2] = upgradeLevel;
			}
		}

		internal static void Raise(string id, string name, string reason)
		{
			Flag flag = _flags.FirstOrDefault((Flag f) => f.SteamId == id && f.Reason == reason);
			if (flag != null)
			{
				flag.Count++;
				flag.Time = Time.time;
				return;
			}
			_flags.Add(new Flag
			{
				SteamId = id,
				Name = name,
				Reason = reason,
				Time = Time.time
			});
			Plugin.Log.LogWarning((object)("[SoloHost/AntiCheat] " + name + ": " + reason));
			if (Plugin.AntiCheatNotify.Value)
			{
				DevActions.Notify("Flagged " + name + ": " + reason);
			}
		}

		public static void ResetTracking()
		{
			_lastPos.Clear();
			_lastUpgrades.Clear();
			_lastPollTime = 0f;
		}
	}
	internal static class ArenaRules
	{
		[HarmonyPatch(typeof(Arena), "StateSet")]
		internal static class ArenaGameOverGuard
		{
			private static bool _holding;

			private static bool Prefix(States state)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Invalid comparison between Unknown and I4
				if (!Plugin.Ready || (int)state != 8)
				{
					return true;
				}
				if (!RespawnPending())
				{
					_holding = false;
					return true;
				}
				if (!_holding)
				{
					_holding = true;
					Plugin.Log.LogInfo((object)"[SoloHost] Holding the arena open — somebody is still owed a life.");
				}
				return false;
			}
		}

		internal static readonly string[] Upgrades = new string[13]
		{
			"Health", "Stamina", "ExtraJump", "Launch", "MapPlayerCount", "Range", "Speed", "Strength", "Throw", "TumbleWings",
			"CrouchRest", "TumbleClimb", "DeathHeadBattery"
		};

		private static Dictionary<string, Dictionary<string, int>> _snapshot;

		private static float _nextPass;

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

		private static readonly Dictionary<string, float> _dueBack = new Dictionary<string, float>();

		private static Arena _livesArena;

		private static int _spawnTurn;

		private static bool _endgame;

		private static bool _arrived;

		private const float HoldGrace = 5f;

		public static bool Sumo { get; private set; }

		public static void Begin(string spec, bool sumo)
		{
			Sumo = sumo;
			_endgame = false;
			if (_snapshot == null)
			{
				_snapshot = Capture();
			}
			ApplyTargets(ParseTargets(spec));
			Plugin.Log.LogInfo((object)("[SoloHost] Mini-game loadout applied (" + (sumo ? "sumo" : "arena") + "): " + spec));
		}

		public static void End()
		{
			Sumo = false;
			ForgetLives();
			if (_snapshot == null)
			{
				return;
			}
			foreach (KeyValuePair<string, Dictionary<string, int>> item in _snapshot)
			{
				Dictionary<string, int> value = item.Value;
				string[] upgrades = Upgrades;
				foreach (string text in upgrades)
				{
					int value2;
					int level = (value.TryGetValue(text, out value2) ? value2 : 0);
					SetTarget(item.Key, text, level);
				}
			}
			Plugin.Log.LogInfo((object)$"[SoloHost] Upgrades restored for {_snapshot.Count} player(s).");
			_snapshot = null;
		}

		public static void BeginEndgame(string spec, bool sumo)
		{
			Sumo = sumo;
			_endgame = true;
			_arrived = false;
			_snapshot = null;
			ApplyTargets(ParseTargets(spec));
			Plugin.Log.LogInfo((object)("[SoloHost] Endgame " + (sumo ? "sumo" : "arena") + " loadout: " + spec));
		}

		public static void Discard()
		{
			Sumo = false;
			ForgetLives();
			_arrived = false;
			_snapshot = null;
		}

		public static void Tick()
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			bool flag = SemiFunc.RunIsArena();
			if (Sumo)
			{
				if (flag)
				{
					_arrived = true;
				}
				else if (_arrived)
				{
					Sumo = false;
					_arrived = false;
				}
			}
			LivesTick();
			if (Sumo && flag && !(Time.time < _nextPass))
			{
				_nextPass = Time.time + 0.5f;
				KeepEveryoneDown();
				if (Plugin.SumoNoItems.Value)
				{
					ClearItems();
				}
			}
		}

		private static void KeepEveryoneDown()
		{
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				PlayerTumble val = (((Object)(object)item != (Object)null) ? item.tumble : null);
				if ((Object)(object)val == (Object)null || item.isDisabled || item.deadSet)
				{
					continue;
				}
				try
				{
					if (!item.isTumbling)
					{
						val.TumbleRequest(true, false);
					}
					val.TumbleOverrideTime(3f);
				}
				catch
				{
				}
			}
		}

		private static void ClearItems()
		{
			int num = 0;
			PhysGrabObject[] array = SceneScan.All<PhysGrabObject>(2f);
			foreach (PhysGrabObject val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				GameObject gameObject = ((Component)val).gameObject;
				if ((Object)(object)gameObject.GetComponentInChildren<PlayerTumble>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<PlayerDeathHead>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<PhysGrabHinge>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<ItemAttributes>(true) == (Object)null)
				{
					continue;
				}
				try
				{
					PhotonView component = ((Component)val).GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
					{
						PhotonNetwork.Destroy(component);
					}
					else
					{
						Object.Destroy((Object)(object)gameObject);
					}
					num++;
				}
				catch
				{
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)$"[SoloHost] Sumo: cleared {num} item(s).");
			}
		}

		private static void ForgetLives()
		{
			_endgame = false;
			_livesArena = null;
			_livesLeft.Clear();
			_dueBack.Clear();
		}

		internal static int Allowance()
		{
			if (_endgame)
			{
				return 0;
			}
			if (IsRaceLevel())
			{
				return 0;
			}
			return Mathf.Max(0, Sumo ? Plugin.SumoExtraLives.Value : Plugin.BrawlExtraLives.Value);
		}

		private static bool IsRaceLevel()
		{
			try
			{
				RunManager instance = RunManager.instance;
				return (Object)(object)instance != (Object)null && MiniGames.IsRace(instance.levelCurrent);
			}
			catch
			{
				return false;
			}
		}

		internal static int LivesLeft(PlayerAvatar p)
		{
			string text = (((Object)(object)p != (Object)null) ? SemiFunc.PlayerGetSteamID(p) : null);
			if (string.IsNullOrEmpty(text))
			{
				return -1;
			}
			if (!_livesLeft.TryGetValue(text, out var value))
			{
				return -1;
			}
			return value;
		}

		internal static bool RespawnPending()
		{
			if (Allowance() <= 0)
			{
				return false;
			}
			float time = Time.time;
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if (!((Object)(object)item == (Object)null) && (item.isDisabled || item.deadSet))
				{
					string text = SemiFunc.PlayerGetSteamID(item);
					if (!string.IsNullOrEmpty(text) && _livesLeft.TryGetValue(text, out var value) && value > 0 && _dueBack.TryGetValue(text, out var value2) && time < value2 + 5f)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static void LivesTick()
		{
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			Arena instance = Arena.instance;
			if ((Object)(object)instance == (Object)null)
			{
				if ((Object)(object)_livesArena != (Object)null)
				{
					ForgetLives();
				}
				return;
			}
			if (instance != _livesArena)
			{
				_livesArena = instance;
				_livesLeft.Clear();
				_dueBack.Clear();
				_spawnTurn = 0;
			}
			int num = Allowance();
			if (num <= 0)
			{
				return;
			}
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				string text = SemiFunc.PlayerGetSteamID(item);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				if (!_livesLeft.ContainsKey(text))
				{
					_livesLeft[text] = num;
				}
				if (!item.isDisabled && !item.deadSet)
				{
					_dueBack.Remove(text);
				}
				else if (_livesLeft[text] > 0)
				{
					Vector3 spot;
					if (!_dueBack.TryGetValue(text, out var value))
					{
						_dueBack[text] = Time.time + Mathf.Max(0f, Plugin.ArenaRespawnDelay.Value);
					}
					else if (!(Time.time < value) && !((Object)(object)item.playerDeathHead == (Object)null) && !((Object)(object)item.playerDeathHead.physGrabObject == (Object)null) && TrySpawnSpot(out spot))
					{
						_dueBack.Remove(text);
						_livesLeft[text]--;
						Respawn(item, spot, _livesLeft[text]);
					}
				}
			}
		}

		private static void Respawn(PlayerAvatar p, Vector3 where, int left)
		{
			//IL_000d: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PhysGrabObject physGrabObject = p.playerDeathHead.physGrabObject;
				physGrabObject.Teleport(where, ((Component)physGrabObject).transform.rotation);
				if ((Object)(object)physGrabObject.rb != (Object)null)
				{
					physGrabObject.rb.velocity = Vector3.zero;
					physGrabObject.rb.angularVelocity = Vector3.zero;
				}
				AntiCheat.Excuse(p);
				p.Revive(false);
				DevActions.TeleportPlayer(p, where, ((Component)p).transform.rotation);
				DevActions.HealPlayer(p);
				if (Sumo && (Object)(object)p.tumble != (Object)null)
				{
					p.tumble.TumbleRequest(true, false);
					p.tumble.TumbleOverrideTime(3f);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Couldn't put " + SemiFunc.PlayerGetName(p) + " back in: " + ex.Message));
				return;
			}
			DevActions.Notify((left > 0) ? $"{SemiFunc.PlayerGetName(p)} is back in — {left} live(s) left." : (SemiFunc.PlayerGetName(p) + " is back in on their last life."));
		}

		private static bool TrySpawnSpot(out Vector3 spot)
		{
			//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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			spot = Vector3.zero;
			List<ArenaPlatform> list = (from pl in Object.FindObjectsOfType<ArenaPlatform>()
				where (Object)(object)pl != (Object)null && (Object)(object)pl.meshRenderer != (Object)null && ((int)pl.currentState == 0 || (int)pl.currentState == 1)
				select pl).ToList();
			if (list.Count == 0)
			{
				return false;
			}
			Bounds bounds = ((Renderer)list[Mathf.Abs(_spawnTurn++) % list.Count].meshRenderer).bounds;
			spot = new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + Mathf.Max(1f, Plugin.ArenaRespawnHeight.Value), ((Bounds)(ref bounds)).center.z);
			return true;
		}

		private static Dictionary<string, Dictionary<string, int>> Capture()
		{
			Dictionary<string, Dictionary<string, int>> dictionary = new Dictionary<string, Dictionary<string, int>>();
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return dictionary;
			}
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				string text = SemiFunc.PlayerGetSteamID(item);
				if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text))
				{
					Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
					string[] upgrades = Upgrades;
					foreach (string text2 in upgrades)
					{
						dictionary2[text2] = Level(instance, text2, text);
					}
					dictionary[text] = dictionary2;
				}
			}
			return dictionary;
		}

		internal static Dictionary<string, int> ParseTargets(string spec)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] upgrades = Upgrades;
			foreach (string key in upgrades)
			{
				dictionary[key] = 0;
			}
			if (string.IsNullOrWhiteSpace(spec))
			{
				return dictionary;
			}
			upgrades = spec.Split(',');
			foreach (string obj in upgrades)
			{
				string text = obj.Trim();
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string name = text.Substring(0, num).Trim();
					string text2 = Upgrades.FirstOrDefault((string u) => u.Equals(name, StringComparison.OrdinalIgnoreCase));
					int result;
					if (text2 == null)
					{
						Plugin.Log.LogWarning((object)("[SoloHost] Mini-game loadout: no upgrade called '" + name + "'."));
					}
					else if (int.TryParse(text.Substring(num + 1).Trim(), out result))
					{
						dictionary[text2] = Mathf.Max(0, result);
					}
				}
			}
			return dictionary;
		}

		private static void ApplyTargets(Dictionary<string, int> targets)
		{
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				string text = SemiFunc.PlayerGetSteamID(item);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				foreach (KeyValuePair<string, int> target in targets)
				{
					SetTarget(text, target.Key, target.Value);
				}
			}
		}

		private static void SetTarget(string steamId, string token, int level)
		{
			//IL_0074: 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)
			StatsManager instance = StatsManager.instance;
			PunManager instance2 = PunManager.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
			{
				return;
			}
			int num = level - Level(instance, token, steamId);
			if (num == 0)
			{
				return;
			}
			try
			{
				if (SemiFunc.IsMultiplayer() && (Object)(object)instance2.photonView != (Object)null)
				{
					instance2.photonView.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { steamId, token, num });
				}
				else
				{
					instance2.TesterUpgradeCommandRPC(steamId, token, num, default(PhotonMessageInfo));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Couldn't set " + token + " for " + steamId + ": " + ex.Message));
			}
		}

		private static int Level(StatsManager sm, string token, string steamId)
		{
			if (sm.dictionaryOfDictionaries == null)
			{
				return 0;
			}
			if (!sm.dictionaryOfDictionaries.TryGetValue("playerUpgrade" + token, out var value) || value == null || !value.TryGetValue(steamId, out var value2))
			{
				return 0;
			}
			return value2;
		}
	}
	internal static class BackgroundRun
	{
		private static bool _reported;

		public static void Tick()
		{
			if (!Plugin.Ready)
			{
				return;
			}
			bool value = Plugin.RunInBackground.Value;
			if (Application.runInBackground != value)
			{
				Application.runInBackground = value;
				if (value && !_reported)
				{
					_reported = true;
					Plugin.Log.LogInfo((object)"[SoloHost] Keeping the game running while unfocused. Without this, alt-tabbing stops the loop that drains Photon's send queue and the connection dies with WinSock 10055.");
				}
			}
		}
	}
	internal static class Banishment
	{
		private sealed class State
		{
			public Vector3 Spot;

			public bool HasSpot;

			public float NextAllowed;
		}

		private const float MinDistance = 600f;

		private const float MaxDistance = 900f;

		private const float Height = 250f;

		private const float Drop = 120f;

		private const float Cooldown = 0.6f;

		private static readonly Dictionary<string, State> _banished = new Dictionary<string, State>();

		private static float _nextCheck;

		public static int Count => _banished.Count;

		public static bool IsBanished(PlayerAvatar p)
		{
			if ((Object)(object)p != (Object)null)
			{
				return _banished.ContainsKey(SemiFunc.PlayerGetSteamID(p) ?? "");
			}
			return false;
		}

		public static void ToggleBanish(PlayerAvatar target)
		{
			if (!DevActions.Allowed() || (Object)(object)target == (Object)null)
			{
				return;
			}
			string text = SemiFunc.PlayerGetSteamID(target);
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = SemiFunc.PlayerGetName(target);
				if (_banished.Remove(text))
				{
					BringBack(target);
					DevActions.Notify(text2 + " is no longer banished.");
					return;
				}
				State state = new State();
				_banished[text] = state;
				Send(target, state);
				DevActions.Notify("Banished " + text2 + ".");
			}
		}

		public static void ReleaseAll()
		{
			if (!DevActions.Allowed())
			{
				return;
			}
			int num = 0;
			foreach (string item in new List<string>(_banished.Keys))
			{
				_banished.Remove(item);
				PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(item);
				if ((Object)(object)val != (Object)null)
				{
					BringBack(val);
					num++;
				}
			}
			DevActions.Notify($"Released {num} banished player(s).");
		}

		public static List<string> BanishedIds()
		{
			return new List<string>(_banished.Keys);
		}

		public static int ReleaseAllQuietly()
		{
			int count = _banished.Count;
			_banished.Clear();
			return count;
		}

		public static void OnLevelChanged()
		{
			foreach (State value in _banished.Values)
			{
				value.HasSpot = false;
			}
		}

		public static void Tick()
		{
			//IL_0092: 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)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (_banished.Count == 0 || !SemiFunc.IsMasterClientOrSingleplayer() || Time.time < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.time + 0.2f;
			float time = Time.time;
			foreach (KeyValuePair<string, State> item in _banished)
			{
				PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(item.Key);
				if (!((Object)(object)val == (Object)null) && !val.isDisabled && !val.deadSet)
				{
					State value = item.Value;
					if (!(time < value.NextAllowed) && (!value.HasSpot || Where(val).y < value.Spot.y - 120f || HorizontalDistance(Where(val), value.Spot) > 60f))
					{
						Send(val, value);
					}
				}
			}
		}

		private static void Send(PlayerAvatar p, State s)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!s.HasSpot)
			{
				s.Spot = PickSpot();
				s.HasSpot = true;
			}
			AntiCheat.Excuse(p, 10f);
			DevActions.TeleportPlayer(p, s.Spot, ((Component)p).transform.rotation);
			s.NextAllowed = Time.time + 0.6f;
		}

		private static void BringBack(PlayerAvatar p)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_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_0055: Unknown result type (might be due to invalid IL or missing references)
			AntiCheat.Excuse(p);
			if (DevActions.TryGetTruck(out var where))
			{
				DevActions.TeleportPlayer(p, where, ((Component)p).transform.rotation);
			}
			else if ((Object)(object)Relay.Me != (Object)null)
			{
				DevActions.TeleportPlayer(p, ((Component)Relay.Me).transform.position + Vector3.up, ((Component)p).transform.rotation);
			}
		}

		private static Vector3 PickSpot()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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)
			Vector3 where;
			Vector3 val = (DevActions.TryGetTruck(out where) ? where : (((Object)(object)Relay.Me != (Object)null) ? ((Component)Relay.Me).transform.position : Vector3.zero));
			float num = Random.Range(0f, MathF.PI * 2f);
			float num2 = Random.Range(600f, 900f);
			return val + new Vector3(Mathf.Cos(num) * num2, 250f, Mathf.Sin(num) * num2);
		}

		private static Vector3 Where(PlayerAvatar p)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (p.isTumbling && (Object)(object)p.tumble != (Object)null)
			{
				return ((Component)p.tumble).transform.position;
			}
			return ((Component)p).transform.position;
		}

		private static float HorizontalDistance(Vector3 a, Vector3 b)
		{
			//IL_0018: 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)
			a.y = 0f;
			b.y = 0f;
			return Vector3.Distance(a, b);
		}
	}
	internal static class BanManager
	{
		private static readonly Dictionary<string, string> _bans = new Dictionary<string, string>();

		private static bool _loaded;

		private static float _nextPoll;

		private static string FilePath => Path.Combine(Paths.ConfigPath, "solohost_bans.txt");

		public static IEnumerable<KeyValuePair<string, string>> Bans
		{
			get
			{
				EnsureLoaded();
				return _bans;
			}
		}

		public static bool IsBanned(string steamId)
		{
			EnsureLoaded();
			if (!string.IsNullOrEmpty(steamId))
			{
				return _bans.ContainsKey(steamId);
			}
			return false;
		}

		public static void Ban(PlayerAvatar avatar)
		{
			if (!((Object)(object)avatar == (Object)null) && DevActions.Allowed())
			{
				string text = SemiFunc.PlayerGetSteamID(avatar);
				if (!string.IsNullOrEmpty(text))
				{
					EnsureLoaded();
					_bans[text] = SemiFunc.PlayerGetName(avatar) ?? text;
					Save();
					Kick(avatar);
					DevActions.Notify("Banned " + _bans[text] + " — they'll be auto-kicked if they rejoin.");
				}
			}
		}

		public static bool Remember(string steamId, string name)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return false;
			}
			EnsureLoaded();
			if (_bans.ContainsKey(steamId))
			{
				return false;
			}
			_bans[steamId] = (string.IsNullOrEmpty(name) ? steamId : name);
			Save();
			return true;
		}

		public static void Unban(string steamId)
		{
			EnsureLoaded();
			if (_bans.Remove(steamId))
			{
				Save();
				DevActions.Notify("Un-banned " + steamId + ".");
			}
		}

		public static void UnbanAll()
		{
			EnsureLoaded();
			int count = _bans.Count;
			_bans.Clear();
			Save();
			DevActions.Notify($"Un-banned everyone ({count}).");
			Plugin.Log.LogInfo((object)$"[SoloHost] Ban list cleared ({count} entries).");
		}

		public static void Kick(PlayerAvatar avatar)
		{
			if ((Object)(object)avatar == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)NetworkManager.instance != (Object)null && SemiFunc.IsMasterClient())
				{
					NetworkManager.instance.KickPlayer(avatar);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Kick failed: " + ex.Message));
			}
		}

		public static void PollAutoKick()
		{
			if (!Plugin.Ready || !SemiFunc.IsMultiplayer() || !SemiFunc.IsMasterClient() || Time.time < _nextPoll)
			{
				return;
			}
			_nextPoll = Time.time + 1f;
			EnsureLoaded();
			if (_bans.Count == 0)
			{
				return;
			}
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if (!((Object)(object)item == (Object)null) && !item.isLocal && IsBanned(SemiFunc.PlayerGetSteamID(item)))
				{
					Plugin.Log.LogInfo((object)("[SoloHost] Auto-kicking banned player " + SemiFunc.PlayerGetName(item) + "."));
					Kick(item);
				}
			}
		}

		public static void EnsureLoaded()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			try
			{
				if (!File.Exists(FilePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(FilePath);
				foreach (string text in array)
				{
					if (!string.IsNullOrWhiteSpace(text))
					{
						string[] array2 = text.Split('\t');
						string text2 = array2[0].Trim();
						if (text2.Length > 0)
						{
							_bans[text2] = ((array2.Length > 1) ? array2[1] : text2);
						}
					}
				}
				Plugin.Log.LogInfo((object)$"[SoloHost] Loaded {_bans.Count} ban(s).");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Ban list load failed: " + ex.Message));
			}
		}

		private static void Save()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, string> ban in _bans)
				{
					list.Add(ban.Key + "\t" + ban.Value);
				}
				File.WriteAllLines(FilePath, list);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Ban list save failed: " + ex.Message));
			}
		}
	}
	internal static class CartDimension
	{
		private sealed class CartVault
		{
			public ValuableObject Valuable;

			public PhysGrabObjectImpactDetector Impact;

			public float Total;
		}

		private static readonly Dictionary<int, CartVault> _vaults = new Dictionary<int, CartVault>();

		private static float _nextScan;

		public static float BankedTotalMirror;

		public static float TotalBanked
		{
			get
			{
				float num = 0f;
				foreach (CartVault value in _vaults.Values)
				{
					num += value.Total;
				}
				return num;
			}
		}

		public static void Reset()
		{
			_vaults.Clear();
			BankedTotalMirror = 0f;
		}

		public static float BankedFor(PhysGrabCart cart)
		{
			if ((Object)(object)cart == (Object)null)
			{
				return 0f;
			}
			PhotonView component = ((Component)cart).GetComponent<PhotonView>();
			if ((Object)(object)component == (Object)null)
			{
				return 0f;
			}
			if (!_vaults.TryGetValue(component.ViewID, out var value) || !((Object)(object)value.Valuable != (Object)null))
			{
				return 0f;
			}
			return value.Total;
		}

		public static void Tick()
		{
			if (!Plugin.Ready || !Plugin.CartDimensionEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			foreach (KeyValuePair<int, CartVault> vault in _vaults)
			{
				PhotonView val = PhotonView.Find(vault.Key);
				PhysGrabCart val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhysGrabCart>() : null);
				if ((Object)(object)val2 != (Object)null)
				{
					MaintainVault(val2, vault.Key);
				}
			}
			if (Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 0.5f;
			PhysGrabCart[] array = SceneScan.All<PhysGrabCart>(5f);
			foreach (PhysGrabCart val3 in array)
			{
				if (!((Object)(object)val3 == (Object)null))
				{
					PhotonView component = ((Component)val3).GetComponent<PhotonView>();
					if (!((Object)(object)component == (Object)null))
					{
						GetVault(val3, component.ViewID);
						AbsorbInto(val3, component.ViewID);
					}
				}
			}
			DropOrphanedVaults();
		}

		private static void AbsorbInto(PhysGrabCart cart, int cartId)
		{
			List<PhysGrabObject> itemsInCart = cart.itemsInCart;
			if (itemsInCart == null || itemsInCart.Count == 0)
			{
				return;
			}
			for (int num = itemsInCart.Count - 1; num >= 0; num--)
			{
				PhysGrabObject val = itemsInCart[num];
				if (!((Object)(object)val == (Object)null) && ShouldAbsorb(val))
				{
					ValuableObject val2 = ((Component)val).GetComponent<ValuableObject>() ?? ((Component)val).GetComponentInChildren<ValuableObject>(true);
					if (!((Object)(object)val2 == (Object)null))
					{
						CartVault vault = GetVault(cart, cartId);
						if (vault != null && !((Object)(object)val2 == (Object)(object)vault.Valuable))
						{
							float dollarValueCurrent = val2.dollarValueCurrent;
							if (!(dollarValueCurrent <= 0f))
							{
								vault.Total += dollarValueCurrent;
								SetValue(vault.Valuable, vault.Total);
								itemsInCart.RemoveAt(num);
								Destroy(val);
								Plugin.Log.LogInfo((object)($"[SoloHost/CART] Absorbed ${dollarValueCurrent:0} from '{((Object)val).name}' " + $"(cart {cartId} contents now ${vault.Total:0})."));
							}
						}
					}
				}
			}
		}

		private static CartVault GetVault(PhysGrabCart cart, int cartId)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (_vaults.TryGetValue(cartId, out var value) && (Object)(object)value.Valuable != (Object)null)
			{
				return value;
			}
			PrefabRef val = PickContentsPrefab();
			if (val == null)
			{
				Plugin.Log.LogWarning((object)"[SoloHost/CART] No valuable prefab available for cart contents.");
				return null;
			}
			Vector3 val2 = (((Object)(object)cart.inCart != (Object)null) ? cart.inCart.position : ((Component)cart).transform.position);
			GameObject val3 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val).ResourcePath, val2, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val).Prefab, val2, Quaternion.identity));
			ValuableObject val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<ValuableObject>() : null);
			if ((Object)(object)val4 == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[SoloHost/CART] Cart contents valuable failed to spawn.");
				return null;
			}
			((Object)val3).name = "Cart Contents";
			ItemAttributes component = val3.GetComponent<ItemAttributes>();
			if ((Object)(object)component != (Object)null)
			{
				component.instanceName = "Cart Contents";
				component.itemName = "Cart Contents";
			}
			CartVault cartVault = new CartVault
			{
				Valuable = val4,
				Impact = val3.GetComponent<PhysGrabObjectImpactDetector>(),
				Total = 0f
			};
			_vaults[cartId] = cartVault;
			StripInteraction(val3);
			SetValue(val4, 0f);
			Plugin.Log.LogInfo((object)$"[SoloHost/CART] Created contents valuable for cart {cartId}.");
			return cartVault;
		}

		private static PrefabRef PickContentsPrefab()
		{
			List<SpawnCatalog.Entry> valuables = SpawnCatalog.Valuables;
			string[] array = Plugin.CartContentsPrefab.Value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				foreach (SpawnCatalog.Entry item in valuables)
				{
					if (item.Display.Equals(text, StringComparison.OrdinalIgnoreCase) || ("Valuable " + item.Display).Equals(text, StringComparison.OrdinalIgnoreCase))
					{
						object payload = item.Payload;
						PrefabRef val = (PrefabRef)((payload is PrefabRef) ? payload : null);
						if (val != null && (Object)(object)((PrefabRef<GameObject>)(object)val).Prefab != (Object)null)
						{
							Plugin.Log.LogInfo((object)("[SoloHost/CART] Cart contents will ride in '" + item.Display + "'."));
							return val;
						}
					}
				}
			}
			Plugin.Log.LogWarning((object)"[SoloHost/CART] None of the configured contents props are in this level's presets — falling back to its cheapest valuable.");
			for (int num = valuables.Count - 1; num >= 0; num--)
			{
				object payload2 = valuables[num].Payload;
				PrefabRef val2 = (PrefabRef)((payload2 is PrefabRef) ? payload2 : null);
				if (val2 != null && (Object)(object)((PrefabRef<GameObject>)(object)val2).Prefab != (Object)null)
				{
					return val2;
				}
			}
			return null;
		}

		private static void StripInteraction(GameObject go)
		{
			//IL_0047: 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)
			Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.enabled = false;
				}
			}
			Rigidbody[] componentsInChildren2 = go.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody val2 in componentsInChildren2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.velocity = Vector3.zero;
					val2.angularVelocity = Vector3.zero;
					val2.isKinematic = true;
					val2.useGravity = false;
					val2.interpolation = (RigidbodyInterpolation)0;
					val2.Sleep();
				}
			}
		}

		private static void MaintainVault(PhysGrabCart cart, int cartId)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (_vaults.TryGetValue(cartId, out var value) && !((Object)(object)value.Valuable == (Object)null))
			{
				GameObject gameObject = ((Component)value.Valuable).gameObject;
				gameObject.transform.position = InsideCart(cart, gameObject);
				StripInteraction(gameObject);
				if ((Object)(object)value.Impact != (Object)null)
				{
					value.Impact.indestructibleSpawnTimer = 1f;
				}
			}
		}

		private static Vector3 InsideCart(PhysGrabCart cart, GameObject contents)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)cart.inCart != (Object)null) ? cart.inCart : ((Component)cart).transform);
			if ((Object)(object)cart.inCart == (Object)null)
			{
				return val.position;
			}
			float num = val.localScale.y * 0.5f;
			float num2 = Mathf.Min(HalfHeight(contents) + 0.02f, num);
			return val.position - val.up * (num - num2);
		}

		private static Bounds? WorldBounds(GameObject go)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)go == (Object)null)
			{
				return null;
			}
			Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren.Length == 0)
			{
				return null;
			}
			Bounds bounds = componentsInChildren[0].bounds;
			for (int i = 1; i < componentsInChildren.Length; i++)
			{
				((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
			}
			return bounds;
		}

		private static float HalfHeight(GameObject go)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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)
			if ((Object)(object)go == (Object)null)
			{
				return 0f;
			}
			Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren.Length == 0)
			{
				return 0f;
			}
			Bounds bounds = componentsInChildren[0].bounds;
			for (int i = 1; i < componentsInChildren.Length; i++)
			{
				((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
			}
			return ((Bounds)(ref bounds)).extents.y;
		}

		private static void DropOrphanedVaults()
		{
			foreach (int item in _vaults.Keys.ToList())
			{
				PhotonView val = PhotonView.Find(item);
				if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null)
				{
					continue;
				}
				if ((Object)(object)_vaults[item].Valuable != (Object)null)
				{
					GameObject gameObject = ((Component)_vaults[item].Valuable).gameObject;
					Collider[] componentsInChildren = gameObject.GetComponentsInChildren<Collider>(true);
					foreach (Collider val2 in componentsInChildren)
					{
						if ((Object)(object)val2 != (Object)null)
						{
							val2.enabled = true;
						}
					}
					Rigidbody component = gameObject.GetComponent<Rigidbody>();
					if ((Object)(object)component != (Object)null)
					{
						component.isKinematic = false;
					}
					Plugin.Log.LogInfo((object)$"[SoloHost/CART] Cart {item} is gone — its contents are loose.");
				}
				_vaults.Remove(item);
			}
		}

		private static bool ShouldAbsorb(PhysGrabObject pgo)
		{
			return LootFilter.IsLooseCargo(pgo);
		}

		private static void SetValue(ValuableObject vo, float total)
		{
			if ((Object)(object)vo == (Object)null)
			{
				return;
			}
			vo.dollarValueOriginal = total;
			vo.dollarValueCurrent = total;
			vo.dollarValueSet = true;
			try
			{
				PhotonView component = ((Component)vo).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
				{
					component.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { total });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost/CART] Value sync failed: " + ex.Message));
			}
		}

		private static void Destroy(PhysGrabObject pgo)
		{
			try
			{
				PhotonView component = ((Component)pgo).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
				{
					PhotonNetwork.Destroy(component);
				}
				else
				{
					Object.Destroy((Object)(object)((Component)pgo).gameObject);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost/CART] Absorb destroy failed: " + ex.Message));
			}
		}
	}
	internal static class ClownExtraction
	{
		private sealed class ClownState
		{
			public int PointViewId;

			public ExtractionPoint Point;

			public GameObject ClownGo;

			public PhysGrabObject Pgo;

			public Rigidbody Rb;

			public bool WasGrabbed;

			public bool Released;

			public Vector3 LastPos;

			public Quaternion LastRot;

			public float RespawnAt;

			public float NextReport;
		}

		internal const string Marker = "SOLOHOST_CLOWN";

		private static readonly Dictionary<int, ClownState> _byPoint = new Dictionary<int, ClownState>();

		private static readonly HashSet<ClownTrap> _disarmed = new HashSet<ClownTrap>();

		private static bool Enabled
		{
			get
			{
				if (Plugin.Ready)
				{
					return Plugin.ClownExtractionEnabled.Value;
				}
				return false;
			}
		}

		internal static Vector3 SavedOffset => new Vector3(Plugin.ClownSpotX.Value, Plugin.ClownSpotY.Value, Plugin.ClownSpotZ.Value);

		internal static Vector3 SavedEuler => new Vector3(Plugin.ClownSpotRotX.Value, Plugin.ClownSpotRotY.Value, Plugin.ClownSpotRotZ.Value);

		public static string SpotSummary
		{
			get
			{
				if (Plugin.ClownSpotSaved.Value)
				{
					if (!IsDefaultSpot)
					{
						return Loc.F("your spot ({0}, {1}, {2})", Plugin.ClownSpotX.Value.ToString("0.00"), Plugin.ClownSpotY.Value.ToString("0.00"), Plugin.ClownSpotZ.Value.ToString("0.00"));
					}
					return Loc.T("the default spot");
				}
				return Loc.T("placed automatically");
			}
		}

		public static bool IsDefaultSpot
		{
			get
			{
				if (Mathf.Approximately(Plugin.ClownSpotX.Value, -2.114112f) && Mathf.Approximately(Plugin.ClownSpotY.Value, -0.02088949f))
				{
					return Mathf.Approximately(Plugin.ClownSpotZ.Value, 1.469957f);
				}
				return false;
			}
		}

		public static void OnPointActivated(ExtractionPoint ep)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || (Object)(object)ep == (Object)null || ep.isShop || !SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)ep.photonView == (Object)null)
			{
				return;
			}
			int viewID = ep.photonView.ViewID;
			if (_byPoint.ContainsKey(viewID))
			{
				return;
			}
			if ((Object)(object)SpawnCatalog.TryGetClownPrefab(out var _) == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[SoloHost] Clown finish: no clown prefab found in the item/valuable pools.");
				return;
			}
			_byPoint[viewID] = new ClownState
			{
				PointViewId = viewID,
				Point = ep
			};
			Quaternion val = ((Component)ep).transform.rotation * Quaternion.Euler(SavedEuler);
			if (!Plugin.ClownSpotSaved.Value)
			{
				val *= Quaternion.Euler(0f, Plugin.ClownYawOffset.Value, 0f);
			}
			Vector3 val2;
			string text;
			if (Plugin.ClownSpotSaved.Value)
			{
				val2 = ((Component)ep).transform.TransformPoint(SavedOffset);
				text = "saved";
			}
			else
			{
				Vector3 val3 = (((Object)(object)ep.safetySpawn != (Object)null) ? ep.safetySpawn.position : ((Component)ep).transform.position) + Vector3.up * 1f;
				RaycastHit val4 = default(RaycastHit);
				if (Physics.Raycast(val3, Vector3.down, ref val4, 12f, -5, (QueryTriggerInteraction)1))
				{
					val2 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.3f;
					text = "floor";
				}
				else
				{
					val2 = val3;
					text = "device-fallback";
				}
			}
			GameObject val5 = Instantiate(viewID, val2, val);
			Plugin.Log.LogInfo((object)string.Format("[SoloHost] Clown finish: spawned '{0}' at {1} (anchor={2}) for point {3}.", ((Object)(object)val5 != (Object)null) ? ((Object)val5).name : "null", val2, text, viewID));
		}

		private static GameObject Instantiate(int pointId, Vector3 pos, Quaternion rot)
		{
			//IL_0050: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			string resourcePath;
			GameObject val = SpawnCatalog.TryGetClownPrefab(out resourcePath);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = ((!((Object)(object)GameManager.instance != (Object)null) || GameManager.instance.gameMode == 0) ? Object.Instantiate<GameObject>(val, pos, rot) : PhotonNetwork.InstantiateRoomObject(resourcePath, pos, rot, (byte)0, new object[2] { "SOLOHOST_CLOWN", pointId }));
			Register(val2, pointId);
			if (_byPoint.TryGetValue(pointId, out var value))
			{
				value.LastPos = pos;
				value.LastRot = rot;
			}
			return val2;
		}

		public static void RegisterFromTrap(ClownTrap trap)
		{
			if (!((Object)(object)trap == (Object)null))
			{
				PhotonView component = ((Component)trap).GetComponent<PhotonView>();
				object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null);
				if (array != null && array.Length >= 2 && array[0] is string text && !(text != "SOLOHOST_CLOWN"))
				{
					int pointViewId = ((array[1] is int num) ? num : (-1));
					Register(((Component)trap).gameObject, pointViewId);
				}
			}
		}

		private static void Register(GameObject clownGo, int pointViewId)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)clownGo == (Object)null)
			{
				return;
			}
			ClownTrap componentInChildren = clownGo.GetComponentInChildren<ClownTrap>(true);
			PhysGrabObject pgo = clownGo.GetComponent<PhysGrabObject>() ?? clownGo.GetComponentInChildren<PhysGrabObject>(true);
			Rigidbody rb = clownGo.GetComponent<Rigidbody>() ?? clownGo.GetComponentInChildren<Rigidbody>(true);
			Disarm(componentInChildren);
			Pin(rb);
			ZeroValue(clownGo);
			if (!_byPoint.TryGetValue(pointViewId, out var value))
			{
				value = new ClownState
				{
					PointViewId = pointViewId
				};
				_byPoint[pointViewId] = value;
			}
			value.ClownGo = clownGo;
			value.Pgo = pgo;
			value.Rb = rb;
			if (value.LastPos == Vector3.zero)
			{
				value.LastPos = clownGo.transform.position;
				value.LastRot = clownGo.transform.rotation;
			}
			if ((Object)(object)value.Point == (Object)null && pointViewId >= 0)
			{
				PhotonView val = PhotonView.Find(pointViewId);
				if ((Object)(object)val != (Object)null)
				{
					value.Point = ((Component)val).GetComponent<ExtractionPoint>();
				}
			}
		}

		private static void Disarm(ClownTrap trap)
		{
			if (!((Object)(object)trap == (Object)null))
			{
				_disarmed.Add(trap);
				((Trap)trap).trapActive = false;
				((Trap)trap).trapTriggered = true;
				((Behaviour)trap).enabled = false;
			}
		}

		private static void Pin(Rigidbody rb)
		{
			if ((Object)(object)rb != (Object)null)
			{
				rb.constraints = (RigidbodyConstraints)126;
			}
		}

		private static void Unpin(Rigidbody rb)
		{
			if ((Object)(object)rb != (Object)null)
			{
				rb.constraints = (RigidbodyConstraints)0;
			}
		}

		private static void ZeroValue(GameObject go)
		{
			ValuableObject val = go.GetComponent<ValuableObject>() ?? go.GetComponentInChildren<ValuableObject>(true);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			val.dollarValueOverride = 0;
			val.dollarValueOriginal = 0f;
			val.dollarValueCurrent = 0f;
			val.dollarValueSet = true;
			try
			{
				PhotonView component = ((Component)val).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
				{
					component.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { 0f });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Clown value zero-sync failed: " + ex.Message));
			}
		}

		public static bool IsDisarmed(ClownTrap trap)
		{
			if ((Object)(object)trap != (Object)null)
			{
				return _disarmed.Contains(trap);
			}
			return false;
		}

		public static bool TryGetCurrentOffset(out Vector3 local, out float distance)
		{
			//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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			local = Vector3.zero;
			distance = 0f;
			foreach (ClownState value in _byPoint.Values)
			{
				if (!((Object)(object)value.ClownGo == (Object)null) && !((Object)(object)value.Point == (Object)null))
				{
					Vector3 position = value.ClownGo.transform.position;
					local = ((Component)value.Point).transform.InverseTransformPoint(position);
					distance = Vector3.Distance(position, ((Component)value.Point).transform.position);
					return true;
				}
			}
			return false;
		}

		public static void SaveCurrentSpot()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_00b7: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			ClownState clownState = null;
			foreach (ClownState value in _byPoint.Values)
			{
				if ((Object)(object)value.ClownGo != (Object)null && (Object)(object)value.Point != (Object)null)
				{
					clownState = value;
					break;
				}
			}
			if (clownState == null)
			{
				DevActions.Notify("No clown out right now — activate an extraction first.");
				return;
			}
			Transform transform = ((Component)clownState.Point).transform;
			Vector3 val = transform.InverseTransformPoint(clownState.ClownGo.transform.position);
			Quaternion val2 = Quaternion.Inverse(transform.rotation) * clownState.ClownGo.transform.rotation;
			Vector3 eulerAngles = ((Quaternion)(ref val2)).eulerAngles;
			Plugin.ClownSpotX.Value = val.x;
			Plugin.ClownSpotY.Value = val.y;
			Plugin.ClownSpotZ.Value = val.z;
			Plugin.ClownSpotRotX.Value = eulerAngles.x;
			Plugin.ClownSpotRotY.Value = eulerAngles.y;
			Plugin.ClownSpotRotZ.Value = eulerAngles.z;
			Plugin.ClownSpotSaved.Value = true;
			DevActions.Notify($"Saved clown spot ({val.x:0.00}, {val.y:0.00}, {val.z:0.00}) " + $"facing {eulerAngles.y:0}° — future clowns spawn here.");
			Plugin.Log.LogInfo((object)$"[SoloHost] Clown spot saved: pos {val}, euler {eulerAngles}");
		}

		internal static void AdoptDefaultSpot()
		{
			if (!Plugin.ClownDefaultApplied.Value)
			{
				Plugin.ClownDefaultApplied.Value = true;
				if (Plugin.ClownSpotSaved.Value && (Plugin.ClownSpotX.Value != 0f || Plugin.ClownSpotY.Value != 0f || Plugin.ClownSpotZ.Value != 0f))
				{
					Plugin.Log.LogInfo((object)"[SoloHost] Keeping your saved clown spot; the shipped one is not applied.");
					return;
				}
				Plugin.ClownSpotX.Value = -2.114112f;
				Plugin.ClownSpotY.Value = -0.02088949f;
				Plugin.ClownSpotZ.Value = 1.469957f;
				Plugin.ClownSpotRotX.Value = 0f;
				Plugin.ClownSpotRotY.Value = 0f;
				Plugin.ClownSpotRotZ.Value = 0f;
				Plugin.ClownSpotSaved.Value = true;
				Plugin.Log.LogInfo((object)"[SoloHost] Using the clown spot SoloHost ships with. Save your own from the menu to replace it, or clear it for automatic placement.");
			}
		}

		public static void UseDefaultSpot()
		{
			if (DevActions.Allowed())
			{
				Plugin.ClownSpotX.Value = -2.114112f;
				Plugin.ClownSpotY.Value = -0.02088949f;
				Plugin.ClownSpotZ.Value = 1.469957f;
				Plugin.ClownSpotRotX.Value = 0f;
				Plugin.ClownSpotRotY.Value = 0f;
				Plugin.ClownSpotRotZ.Value = 0f;
				Plugin.ClownSpotSaved.Value = true;
				DevActions.Notify("Clown spot back to the default.");
			}
		}

		public static void ClearSavedSpot()
		{
			Plugin.ClownSpotSaved.Value = false;
			DevActions.Notify("Cleared saved clown spot — back to auto-placement.");
		}

		public static bool ShouldBlockCompletion(ExtractionPoint ep)
		{
			if (!Enabled || (Object)(object)ep == (Object)null || (Object)(object)ep.photonView == (Object)null)
			{
				return false;
			}
			if (!_byPoint.TryGetValue(ep.photonView.ViewID, out var value))
			{
				return false;
			}
			if (value.Released)
			{
				return false;
			}
			return (Object)(object)value.ClownGo != (Object)null;
		}

		public static void Tick()
		{
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || _byPoint.Count == 0)
			{
				return;
			}
			RoundDirector instance = RoundDirector.instance;
			bool flag = SemiFunc.IsMasterClientOrSingleplayer();
			if ((Object)(object)instance != (Object)null)
			{
				if (instance.extractionPoints <= 0)
				{
					_ = instance.haulGoal;
				}
				else
				{
					_ = instance.haulGoal / instance.extractionPoints;
				}
			}
			foreach (ClownState value in _byPoint.Values)
			{
				if ((Object)(object)value.ClownGo == (Object)null)
				{
					if (!value.Released && flag && !((Object)(object)value.Point == (Object)null))
					{
						if (value.RespawnAt <= 0f)
						{
							value.RespawnAt = Time.time + 1.5f;
							Plugin.Log.LogInfo((object)$"[SoloHost] Clown for point {value.PointViewId} was destroyed — respawning.");
						}
						else if (!(Time.time < value.RespawnAt))
						{
							value.RespawnAt = 0f;
							value.WasGrabbed = false;
							Instantiate(value.PointViewId, value.LastPos, value.LastRot);
						}
					}
				}
				else
				{
					if ((Object)(object)value.Pgo == (Object)null)
					{
						continue;
					}
					bool flag2 = value.Pgo.grabbed || value.Pgo.grabbedLocal || (value.Pgo.playerGrabbing != null && value.Pgo.playerGrabbing.Count > 0);
					if (Plugin.ClownLocked.Value)
					{
						Pin(value.Rb);
						if (flag)
						{
							value.ClownGo.transform.SetPositionAndRotation(value.LastPos, value.LastRot);
						}
						value.WasGrabbed = flag2;
					}
					else
					{
						if (!flag2)
						{
							value.LastPos = value.ClownGo.transform.position;
							value.LastRot = value.ClownGo.transform.rotation;
						}
						if (flag2 && !value.WasGrabbed)
						{
							Unpin(value.Rb);
						}
						else if (!flag2 && value.WasGrabbed)
						{
							Pin(value.Rb);
						}
						value.WasGrabbed = flag2;
					}
					if (!flag || value.Released || (Object)(object)value.Point == (Object)null || (Object)(object)instance == (Object)null)
					{
						continue;
					}
					if (Time.time >= value.NextReport)
					{
						value.NextReport = Time.time + 5f;
						Plugin.Log.LogInfo((object)($"[SoloHost] Extraction {value.PointViewId} held by its clown: " + $"grabbed={flag2} haul={value.Point.haulCurrent}/{value.Point.haulGoal} " + $"goalFetched={value.Point.haulGoalFetched} " + $"requiresEnough={Plugin.ClownRequiresEnough.Value}"));
					}
					if (flag2)
					{
						ExtractionPoint point = value.Point;
						if (!Plugin.ClownRequiresEnough.Value || (point.haulGoalFetched && point.haulGoal > 0 && point.haulCurrent >= point.haulGoal))
						{
							value.Released = true;
							DespawnClown(value);
							point.StateSet((State)3);
							Plugin.Log.LogInfo((object)("[SoloHost] Clown grabbed with enough value " + $"({point.haulCurrent}/{point.haulGoal}) — finishing extraction {value.PointViewId}."));
						}
					}
				}
			}
		}

		public static void OnPointFailed(ExtractionPoint ep)
		{
			//IL_006f: 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)
			if (Enabled && !((Object)(object)ep == (Object)null) && !((Object)(object)ep.photonView == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer() && _byPoint.TryGetValue(ep.photonView.ViewID, out var value))
			{
				value.Released = false;
				value.WasGrabbed = false;
				if ((Object)(object)value.ClownGo == (Object)null)
				{
					value.RespawnAt = 0f;
					Instantiate(value.PointViewId, value.LastPos, value.LastRot);
					Plugin.Log.LogInfo((object)$"[SoloHost] Extraction {value.PointViewId} failed — clown restored.");
				}
			}
		}

		private static void DespawnClown(ClownState st)
		{
			if (st == null || (Object)(object)st.ClownGo == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0)
				{
					PhotonView component = st.ClownGo.GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null && PhotonNetwork.IsMasterClient)
					{
						PhotonNetwork.Destroy(component);
					}
					else
					{
						Object.Destroy((Object)(object)st.ClownGo);
					}
				}
				else
				{
					Object.Destroy((Object)(object)st.ClownGo);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloHost] Clown despawn failed: " + ex.Message));
			}
			st.ClownGo = null;
			st.Rb = null;
			st.Pgo = null;
		}

		public static void ForceRelease(ExtractionPoint ep)
		{
			if (!((Object)(object)ep == (Object)null) && !((Object)(object)ep.photonView == (Object)null) && _byPoint.TryGetValue(ep.photonView.ViewID, out var value) && !value.Released)
			{
				value.Released = true;
				DespawnClown(value);
			}
		}

		public static void Reset()
		{
			_byPoint.Clear();
			_disarmed.Clear();
		}
	}
	internal static class Cosmetics
	{
		private static readonly Dictionary<string, Color> Palette = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"Default",
				Color.clear
			},
			{
				"White",
				Color.white
			},
			{
				"Red",
				new Color(1f, 0.25f, 0.2f)
			},
			{
				"Green",
				new Color(0.35f, 1f, 0.4f)
			},
			{
				"Blue",
				new Color(0.35f, 0.6f, 1f)
			},
			{
				"Purple",
				new Color(0.7f, 0.4f, 1f)
			},
			{
				"Orange",
				new Color(1f, 0.6f, 0.15f)
			},
			{
				"Pink",
				new Color(1f, 0.45f, 0.8f)
			},
			{
				"Cyan",
				new Color(0.3f, 1f, 1f)
			},
			{
				"Yellow",
				new Color(1f, 0.95f, 0.3f)
			}
		};

		private static Color? _originalFlashlight;

		private static float _nextEyeRefresh;

		internal static IEnumerable<string> ColorNames => Palette.Keys;

		public static void Tick()
		{
			if (Plugin.Ready)
			{
				ApplyFlashlight();
				ApplyEyes();
				ApplyWings();
			}
		}

		public static void LateTick()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Ready && Plugin.ThirdPerson.Value)
			{
				PlayerAvatar instance = PlayerAvatar.instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.playerAvatarVisuals != (Object)null)
				{
					instance.playerAvatarVisuals.showSelfOverrideTimer = 0.5f;
				}
				Camera main = Camera.main;
				if (!((Object)(object)main == (Object)null))
				{
					Transform transform = ((Component)main).transform;
					transform.position += -transform.forward * Plugin.ThirdPersonDistance.Value + Vector3.up * Plugin.ThirdPersonHeight.Value;
				}
			}
		}

		private static void ApplyFlashlight()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			FlashlightController instance = FlashlightController.Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.spotlight == (Object)null))
			{
				if (!_originalFlashlight.HasValue)
				{
					_originalFlashlight = instance.spotlight.color;
				}
				if (!Palette.TryGetValue(Plugin.FlashlightColor.Value ?? "Default", out var value) || value == Color.clear)
				{
					instance.spotlight.color = _originalFlashlight.Value;
				}
				else
				{
					instance.spotlight.color = value;
				}
			}
		}

		private static void ApplyEyes()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			string value = Plugin.EyeEffect.Value;
			if (!string.IsNullOrEmpty(value) && !value.Equals("None", StringComparison.OrdinalIgnoreCase) && !(Time.time < _nextEyeRefresh))
			{
				_nextEyeRefresh = Time.time + 1f;
				PlayerAvatar instance = PlayerAvatar.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.playerHealth == (Object)null) && Enum.TryParse<EyeOverrideState>(value, ignoreCase: true, out EyeOverrideState result))
				{
					instance.playerHealth.EyeMaterialOverride(result, 1.5f, 0);
				}
			}
		}

		private static void ApplyWings()
		{
			if (!Plugin.WingsAlwaysShow.Value)
			{
				return;
			}
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			ItemUpgradePlayerTumbleWingsLogic componentInChildren = ((Component)instance).GetComponentInChildren<ItemUpgradePlayerTumbleWingsLogic>(true);
			if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.transformWings == (Object)null))
			{
				if (!((Component)componentInChildren.transformWings).gameObject.activeSelf)
				{
					((Component)componentInChildren.transformWings).gameObject.SetActive(true);
				}
				if ((Object)(object)componentInChildren.lightWings != (Object)null && !((Behaviour)componentInChildren.lightWings).enabled)
				{
					((Behaviour)componentInChildren.lightWings).enabled = true;
				}
			}
		}

		public static void Restore()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			FlashlightController instance = FlashlightController.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.spotlight != (Object)null && _originalFlashlight.HasValue)
			{
				instance.spotlight.color = _originalFlashlight.Value;
			}
		}
	}
	internal static class DevActions
	{
		private sealed class PendingCompletion
		{
			public ExtractionPoint Point;

			public int GoalPer;

			public float GiveUpAt;
		}

		private sealed class FillPacker
		{
			private readonly Vector3 _origin;

			private readonly float _footX;

			private readonly float _footZ;

			private readonly float _height;

			private readonly float _gap;

			private readonly int _cap;

			private float _y;

			private int _placed;

			public bool Full
			{
				get
				{
					if (_placed < _cap)
					{
						return _y >= _height;
					}
					return true;
				}
			}

			public FillPacker(Vector3 origin, float footX, float footZ, float height, float gap, int cap)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				_origin = origin;
				_footX = footX;
				_footZ = footZ;
				_height = height;
				_gap = gap;
				_cap = cap;
			}

			public int Pack(PrefabRef pf, int wanted, Action<Vector3, Quaternion> place)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				//IL_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0114: Unknown result type (might be due to invalid IL or missing references)
				//IL_0196: Unknown result type (might be due to invalid IL or missing references)
				//IL_0198: Unknown result type (might be due to invalid IL or missing references)
				if (pf == null || (Object)(object)((PrefabRef<GameObject>)(object)pf).Prefab == (Object)null || wanted <= 0 || Full)
				{
					return 0;
				}
				Vector3 val = PrefabSize(((PrefabRef<GameObject>)(object)pf).Prefab);
				float num = Mathf.Max(0.05f, val.x) + _gap;
				float num2 = Mathf.Max(0.05f, val.z) + _gap;
				float num3 = Mathf.Max(0.05f, val.y) + _gap;
				int num4 = Mathf.FloorToInt(_footX / num);
				int num5 = Mathf.FloorToInt(_footZ / num2);
				int num6 = Mathf.FloorToInt(_footX / num2);
				int num7 = Mathf.FloorToInt(_footZ / num);
				bool flag = num6 * num7 > num4 * num5;
				int num8 = Mathf.Max(1, flag ? num6 : num4);
				int num9 = Mathf.Max(1, flag ? num7 : num5);
				float num10 = (flag ? num2 : num);
				float num11 = (flag ? num : num2);
				Quaternion arg = (flag ? Quaternion.Euler(0f, 90f, 0f) : Quaternion.identity);
				int num12 = 0;
				Vector3 arg2 = default(Vector3);
				while (num12 < wanted && !Full && !(_y + num3 > _height))
				{
					for (int i = 0; i < num9; i++)
					{
						if (num12 >= wanted)
						{
							break;
						}
						if (_placed >= _cap)
						{
							break;
						}
						for (int j = 0; j < num8; j++)
						{
							if (num12 >= wanted)
							{
								break;
							}
							if (_placed >= _cap)
							{
								break;
							}
							((Vector3)(ref arg2))..ctor(_origin.x + (float)j * num10 + num10 * 0.5f, _origin.y + _y + num3 * 0.5f, _origin.z + (float)i * num11 + num11 * 0.5f);
							place(arg2, arg);
							num12++;
							_placed++;
						}
					}
					_y += num3;
				}
				return num12;
			}
		}

		private sealed class QuietScope : IDisposable
		{
			public void Dispose()
			{
				_quiet--;
			}
		}

		public static readonly HashSet<string> InfiniteEnergy = new HashSet<string>();

		private static readonly List<PhysGrabHinge> _doorQueue = new List<PhysGrabHinge>();

		private static int _doorsTornOut;

		private const int DoorsPerFrame = 4;

		private static readonly List<EnemyParent> _tempEnemies = new List<EnemyParent>();

		private static PendingCompletion _pending;

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

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

		public static readonly HashSet<string> ProtectedPlayers = new HashSet<string>();

		private static int _quiet;

		internal static bool OneShotEnabled { get; private set; }

		public static int DoorsPending => _doorQueue.Count;

		public static int FilledCount => _filled.Count((GameObject g) => (Object)(object)g != (Object)null);

		public static bool Allowed(bool notify = true)
		{
			if (!HostGate.Active)
			{
				if (notify)
				{
					Notify("SoloHost is host-only — you aren't hosting this lobby.");
				}
				return false;
			}
			if (HostLockout.LockedByHost)
			{
				if (notify)
				{
					Notify("The host has disabled this mod's shared features here.");
				}
				return false;
			}
			try
			{
				if ((Object)(object)RunManager.instance != (Object)null && SemiFunc.IsMasterClientOrSingleplayer())
				{
					return true;
				}
			}
			catch
			{
			}
			if (notify)
			{
				Notify("Host / singleplayer only.");
			}
			return false;
		}

		public static Vector3 FacingFlat()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookForward = Relay.LookForward;
			lookForward.y = 0f;
			if (!(((Vector3)(ref lookForward)).sqrMagnitude < 0.01f))
			{
				return ((Vector3)(ref lookForward)).normalized;
			}
			return Vector3.forward;
		}

		public static Vector3 InFrontOfPlayer(float dist = 2f)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mePosition = Relay.MePosition;
			Vector3 val = Relay.LookForward;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
			{
				val = Vector3.forward;
			}
			((Vector3)(ref val)).Normalize();
			Vector3 val2 = mePosition + val * dist;
			RaycastHit val3 = default(RaycastHit);
			if (Physics.Raycast(val2 + Vector3.up * 1.5f, Vector3.down, ref val3, 8f, -5, (QueryTriggerInteraction)1))
			{
				val2 = ((RaycastHit)(ref val3)).point;
			}
			return val2 + Vector3.up * 0.3f;
		}

		public static Quaternion SpawnRotation()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookForward = Relay.LookForward;
			lookForward.y = 0f;
			if (((Vector3)(ref lookForward)).sqrMagnitude < 0.01f)
			{
				return Quaternion.identity;
			}
			return Quaternion.LookRotation(((Vector3)(ref lookForward)).normalized, Vector3.up);
		}

		public static void SpawnEnemy(EnemySetup setup)
		{
			if (Allowed() && !((Object)(object)setup == (Object)null) && setup.spawnObjects != null)
			{
				int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100);
				for (int i = 0; i < num; i++)
				{
					SpawnEnemyOnce(setup, i, num);
				}
				Notify(Plugin.SpawnEnemiesPermanent.Value ? $"Spawned {num}x '{((Object)setup).name}' — permanent (respawns when killed)." : $"Spawned {num}x '{((Object)setup).name}' — temporary (stays until killed, then gone).");
			}
		}

		private static Vector3 BatchOffset(int index, int total)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (total <= 1)
			{
				return Vector3.zero;
			}
			float num = (float)index / (float)total * MathF.PI * 2f;
			float num2 = 0.8f + (float)index * 0.05f;
			return new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * num2;
		}

		private static void SpawnEnemyOnce(EnemySetup setup, int index, int total)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = InFrontOfPlayer() + BatchOffset(index, total);
			int num = 0;
			foreach (PrefabRef spawnObject in setup.spawnObjects)
			{
				if (spawnObject == null)
				{
					continue;
				}
				GameObject val2 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)spawnObject).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)spawnObject).Prefab, val, SpawnRotation()));
				EnemyParent component = val2.GetComponent<EnemyParent>();
				if ((Object)(object)component != (Object)null)
				{
					component.SetupDone = true;
					Enemy componentInChildren = val2.GetComponentInChildren<Enemy>();
					if (componentInChildren != null)
					{
						componentInChildren.EnemyTeleported(val);
					}
					component.firstSpawnPointUsed = true;
					if (!Plugin.SpawnEnemiesPermanent.Value)
					{
						TrackTempSpawn(component);
					}
				}
				num++;
			}
		}

		public static void SpawnItem(Item item)
		{
			//IL_0036: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!Allowed() || (Object)(object)item == (Object)null || item.prefab == null)
			{
				return;
			}
			int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100);
			for (int i = 0; i < num; i++)
			{
				Vector3 val = InFrontOfPlayer() + BatchOffset(i, num);
				if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0)
				{
					PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)item.prefab).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null);
				}
				else
				{
					Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)item.prefab).Prefab, val, SpawnRotation());
				}
			}
			Notify($"Spawned {num}x '{((Object)item).name}' in front of you.");
		}

		public static void SpawnValuable(PrefabRef pf)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (!Allowed() || pf == null)
			{
				return;
			}
			int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100);
			for (int i = 0; i < num; i++)
			{
				Vector3 val = InFrontOfPlayer() + BatchOffset(i, num);
				GameObject val2 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)pf).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)pf).Prefab, val, SpawnRotation()));
				ValuableObject val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ValuableObject>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					val3.DollarValueSetLogic();
				}
			}
			Notify($"Spawned {num}x valuable in front of you.");
		}

		public static void MaxUpgrades(int amount)
		{
			if (!Allowed())
			{
				return;
			}
			if ((Object)(object)Relay.Requester != (Object)null)
			{
				PlayerAvatar requester = Relay.Requester;
				int num = 0;
				using (Quiet())
				{
					string[] upgradeNames = SharedUpgrades.UpgradeNames;
					foreach (string upgradeName in upgradeNames)
					{
						GiveUpgrade(requester, upgradeName, amount);
						num++;
					}
				}
				Notify($"Gave {SemiFunc.PlayerGetName(requester)} +{amount} of {num} upgrades.");
				return;
			}
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null || instance.dictionaryOfDictionaries == null)
			{
				Notify("No StatsManager yet.");
				return;
			}
			List<string> list = (from k in instance.dictionaryOfDictionaries.Keys
				where k.StartsWith("playerUpgrade")
				select k.Substring("playerUpgrade".Length)).ToList();
			AntiCheat.ExcuseEveryone();
			int num2 = list.Count((string n) => RunCommand($"upgrade {n} {amount}"));
			Notify($"Gave +{amount} of {num2}/{list.Count} upgrades.");
		}

		public static void SetLevel(int n)
		{
			if (Allowed() && RunCommand($"levelnum {n}"))
			{
				Notify($"Jumping to level {n}.");
			}
		}

		public static void GiveCash(long n)
		{
			if (!Allowed())
			{
				return;
			}
			if (Plugin.BigMoney.Value)
			{
				Wealth.SetCurrency(n);
				Notify("Balance set to $" + Wealth.Format(n) + "K.");
				return;
			}
			int num = (int)((n > int.MaxValue) ? int.MaxValue : ((n >= 0) ? n : 0));
			if (RunCommand($"cash {num}"))
			{
				Notify($"Cash set to {num}.");
			}
		}

		public static void ToggleGodMode()
		{
			if (Allowed())
			{
				if ((Object)(object)Relay.Requester != (Object)null)
				{
					SetPlayerGod(Relay.Requester, !IsPlayerGod(Relay.Requester));
				}
				else
				{
					RunCommand("godmode");
				}
			}
		}

		public static void ToggleInfiniteEnergy()
		{
			if (Allowed())
			{
				if ((Object)(object)Relay.Requester != (Object)null)
				{
					ToggleInfiniteSprint(Relay.Requester);
				}
				else
				{
					RunCommand("energy");
				}
			}
		}

		public static void Heal()
		{
			if (Allowed())
			{
				PlayerAvatar me = Relay.Me;
				if ((Object)(object)me != (Object)null && (Object)(object)me.playerHealth != (Object)null)
				{
					HealPlayer(me);
				}
				else
				{
					RunCommand("health 100");
				}
			}
		}

		public static void HealPlayer(PlayerAvatar target, int amount = 0)
		{
			if (Allowed() && !((Object)(object)target == (Object)null) && !((Object)(object)target.playerHealth == (Object)null))
			{
				AntiCheat.Excuse(target);
				int num = ((amount > 0) ? amount : Mathf.Max(1, target.playerHealth.maxHealth));
				if (target.isLocal || !SemiFunc.IsMultiplayer())
				{
					target.playerHealth.Heal(num, true);
				}
				else
				{
					target.playerHealth.HealOther(num, true);
				}
				Notify((amount > 0) ? $"Healed {SemiFunc.PlayerGetName(target)} +{num}." : $"Healed {SemiFunc.PlayerGetName(target)} to full ({num}).");
			}
		}

		public static void HealEveryone(int amount = 0)
		{
			if (!Allowed())
			{
				return;
			}
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if ((Object)(object)item != (Object)null)
				{
					HealPlayer(item, amount);
				}
			}
		}

		public static void RevivePlayer(PlayerAvatar target)
		{
			if (Allowed() && !((Object)(object)target == (Object)null))
			{
				if (!target.isDisabled && !target.deadSet)
				{
					Notify(SemiFunc.PlayerGetName(target) + " isn't dead.");
					return;
				}
				AntiCheat.Excuse(target);
				target.Revive(false);
				Notify("Revived " + SemiFunc.PlayerGetName(target) + ".");
			}
		}

		public static void Revive()
		{
			if (!Allowed())
			{
				return;
			}
			int num = 0;
			foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
			{
				if (!((Object)(object)item == (Object)null) && (item.isDisabled || item.deadSet))
				{
					AntiCheat.Excuse(item);
					item.Revive(false);
					num++;
				}
			}
			Notify((num > 0) ? $"Revived {num} player(s)." : "Nobody needs reviving.");
		}

		public static void ToggleOneShot()
		{
			if (OneShotEnabled || Allowed())
			{
				OneShotEnabled = !OneShotEnabled;
				Notify(OneShotEnabled ? "One-shot: ON — any hit kills." : "One-shot: OFF.");
			}
		}

		public static void SpawnOneShotGun()
		{
			//IL_00b4: 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)
			//IL_00de: 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_00f6: 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)
			if (!Allowed())
			{
				return;
			}
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null || instance.itemDictionary == null)
			{
				Notify("No item dictionary yet.");
				return;
			}
			string want = Plugin.GunItemName.Value.ToLowerInvariant();
			Item val = ((IEnumerable<Item>)instance.itemDictionary.Values).FirstOrDefault((Func<Item, bool>)((Item x) => (Object)(object)x != (Object)null && ((Object)x).name.ToLowerInvariant().Contains(want) && x.prefab != null && (Object)(object)((PrefabRef<GameObject>)(object)x.prefab).Prefab != (Object)null && (Object)(object)((PrefabRef<GameObject>)(object)x.prefab).Prefab.GetComponentInChildren<ItemGun>(true) != (Object)null)) ?? ((IEnumerable<Item>)instance.itemDictionary.Values).FirstOrDefault((Func<Item, bool>)((Item x) => (Object)(object)x != (Object)null && ((Object)x).name.ToLowerInvariant().Contains(want)));
			if ((Object)(object)val == (Object)null || val.prefab == null)
			{
				Notify("No item matching '" + want + "'. Try a different Gun/GunItemName.");
				return;
			}
			Vector3 val2 = InFrontOfPlayer();
			RenameGun(((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val.prefab).ResourcePath, val2, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val.prefab).Prefab, val2, SpawnRotation()), val);
			if (!OneShotEnabled)
			{
				ToggleOneShot();
			}
			Notify("Spawned '" + Plugin.GunDisplayName.Value + "'. One-shot is ON.");
		}

		private static void RenameGun(GameObject go, Item item)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			string value = Plugin.GunDisplayName.Value;
			try
			{
				ItemAttributes component = go.GetComponent<ItemAttributes>();
				if ((Object)(object)component != (Object)null)
				{
					component.itemName = value;
					component.instanceName = value;
				}
				if ((Object)(object)item != (Object)null)
				{
					item.itemName = value;
					item.itemNameLocalized = null;
				}
				((Object)go).name = "SoloHost " + value;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[So