Decompiled source of PEAK MX v1.1.1

PEAK-MX.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MxAhg.Shared;
using Peak.Network;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using UnityEngine;
using Zorro.Core;
using Zorro.Core.Serizalization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 MxAhg.Shared
{
	internal static class Protocol
	{
		public const byte EventCode = 172;

		public const string Magic = "MX_AHG";

		public const int Version = 1;

		public const int CommandChallenge = 1;

		public const int CommandReport = 2;

		public const string AgentVersionKey = "mxahg_v";

		public const string ProtocolKey = "mxahg_p";

		public const string FileSignatureKey = "mxahg_s";

		public const string FileCountKey = "mxahg_c";

		public const string LoadedSignatureKey = "mxahg_l";

		public const string LoadedCountKey = "mxahg_lc";

		public const string HeartbeatKey = "mxahg_h";

		public const string SelfHashKey = "mxahg_i";

		public const int MaxFiles = 512;

		public const int MaxPlugins = 256;

		public const int MaxHarmonyOwners = 256;

		public static string Short(string value, int length = 16)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "?";
			}
			if (value.Length > length)
			{
				return value.Substring(0, length);
			}
			return value;
		}

		public static string HashFile(string path)
		{
			using SHA256 sHA = SHA256.Create();
			using FileStream inputStream = File.OpenRead(path);
			return ToHex(sHA.ComputeHash(inputStream));
		}

		public static string HashText(string value)
		{
			using SHA256 sHA = SHA256.Create();
			return ToHex(sHA.ComputeHash(Encoding.UTF8.GetBytes(value ?? "")));
		}

		public static string ComputeProof(string nonce, Snapshot snapshot)
		{
			if (snapshot == null)
			{
				return "";
			}
			return HashText(nonce + "|" + snapshot.FileSignature + "|" + snapshot.LoadedSignature + "|" + snapshot.SelfHash + "|" + (snapshot.Truncated ? "1" : "0") + "|" + snapshot.Error + "|" + snapshot.BuildCanonical());
		}

		public static string[] ToFlatFiles(IReadOnlyList<FileEntry> entries)
		{
			if (entries == null || entries.Count == 0)
			{
				return Array.Empty<string>();
			}
			string[] array = new string[entries.Count * 4];
			for (int i = 0; i < entries.Count; i++)
			{
				int num = i * 4;
				array[num] = entries[i].RelativePath ?? "";
				array[num + 1] = entries[i].Sha256 ?? "";
				array[num + 2] = entries[i].AssemblyName ?? "";
				array[num + 3] = entries[i].AssemblyVersion ?? "";
			}
			return array;
		}

		public static string[] ToFlatPlugins(IReadOnlyList<PluginEntry> entries)
		{
			if (entries == null || entries.Count == 0)
			{
				return Array.Empty<string>();
			}
			string[] array = new string[entries.Count * 5];
			for (int i = 0; i < entries.Count; i++)
			{
				int num = i * 5;
				array[num] = entries[i].Guid ?? "";
				array[num + 1] = entries[i].Name ?? "";
				array[num + 2] = entries[i].Version ?? "";
				array[num + 3] = entries[i].RelativePath ?? "";
				array[num + 4] = entries[i].Sha256 ?? "";
			}
			return array;
		}

		public static List<FileEntry> ParseFiles(object value)
		{
			string[] array = ReadStringArray(value);
			List<FileEntry> list = new List<FileEntry>();
			int num = Math.Min(array.Length / 4, 512);
			for (int i = 0; i < num; i++)
			{
				int num2 = i * 4;
				list.Add(new FileEntry
				{
					RelativePath = Limit(array[num2], 320),
					Sha256 = Limit(array[num2 + 1], 64),
					AssemblyName = Limit(array[num2 + 2], 160),
					AssemblyVersion = Limit(array[num2 + 3], 80)
				});
			}
			return list;
		}

		public static List<PluginEntry> ParsePlugins(object value)
		{
			string[] array = ReadStringArray(value);
			List<PluginEntry> list = new List<PluginEntry>();
			int num = Math.Min(array.Length / 5, 256);
			for (int i = 0; i < num; i++)
			{
				int num2 = i * 5;
				list.Add(new PluginEntry
				{
					Guid = Limit(array[num2], 180),
					Name = Limit(array[num2 + 1], 180),
					Version = Limit(array[num2 + 2], 80),
					RelativePath = Limit(array[num2 + 3], 320),
					Sha256 = Limit(array[num2 + 4], 64)
				});
			}
			return list;
		}

		public static string[] ReadStringArray(object value)
		{
			if (value is string[] result)
			{
				return result;
			}
			if (value is object[] array)
			{
				int num = Math.Min(array.Length, 2560);
				string[] array2 = new string[num];
				for (int i = 0; i < num; i++)
				{
					array2[i] = array[i]?.ToString() ?? "";
				}
				return array2;
			}
			return Array.Empty<string>();
		}

		public static string Limit(string value, int maxLength)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "";
			}
			if (value.Length > maxLength)
			{
				return value.Substring(0, maxLength);
			}
			return value;
		}

		private static string ToHex(byte[] bytes)
		{
			StringBuilder stringBuilder = new StringBuilder(bytes.Length * 2);
			for (int i = 0; i < bytes.Length; i++)
			{
				stringBuilder.Append(bytes[i].ToString("x2"));
			}
			return stringBuilder.ToString();
		}
	}
	internal sealed class FileEntry
	{
		public string RelativePath = "";

		public string Sha256 = "";

		public string AssemblyName = "";

		public string AssemblyVersion = "";
	}
	internal sealed class PluginEntry
	{
		public string Guid = "";

		public string Name = "";

		public string Version = "";

		public string RelativePath = "";

		public string Sha256 = "";
	}
	internal sealed class Snapshot
	{
		public readonly List<FileEntry> Files = new List<FileEntry>();

		public readonly List<PluginEntry> Plugins = new List<PluginEntry>();

		public readonly List<string> HarmonyOwners = new List<string>();

		public string FileSignature = "";

		public string LoadedSignature = "";

		public string SelfHash = "";

		public string Error = "";

		public bool Truncated;

		public void Recalculate()
		{
			Files.Sort((FileEntry a, FileEntry b) => string.Compare(a.RelativePath, b.RelativePath, StringComparison.OrdinalIgnoreCase));
			Plugins.Sort((PluginEntry a, PluginEntry b) => string.Compare(a.Guid, b.Guid, StringComparison.OrdinalIgnoreCase));
			HarmonyOwners.Sort(StringComparer.OrdinalIgnoreCase);
			FileSignature = Protocol.HashText(BuildFileCanonical());
			LoadedSignature = Protocol.HashText(BuildLoadedCanonical());
		}

		public string BuildCanonical()
		{
			return BuildFileCanonical() + "\n--loaded--\n" + BuildLoadedCanonical();
		}

		private string BuildFileCanonical()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < Files.Count; i++)
			{
				AppendField(stringBuilder, Files[i].RelativePath.ToLowerInvariant());
				AppendField(stringBuilder, Files[i].Sha256.ToLowerInvariant());
				AppendField(stringBuilder, Files[i].AssemblyName.ToLowerInvariant());
				AppendField(stringBuilder, Files[i].AssemblyVersion);
				stringBuilder.Append('\n');
			}
			return stringBuilder.ToString();
		}

		private string BuildLoadedCanonical()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < Plugins.Count; i++)
			{
				AppendField(stringBuilder, Plugins[i].Guid.ToLowerInvariant());
				AppendField(stringBuilder, Plugins[i].Name.ToLowerInvariant());
				AppendField(stringBuilder, Plugins[i].Version);
				AppendField(stringBuilder, Plugins[i].RelativePath.ToLowerInvariant());
				AppendField(stringBuilder, Plugins[i].Sha256.ToLowerInvariant());
				stringBuilder.Append('\n');
			}
			stringBuilder.Append("owners\n");
			for (int j = 0; j < HarmonyOwners.Count; j++)
			{
				AppendField(stringBuilder, HarmonyOwners[j].ToLowerInvariant());
			}
			return stringBuilder.ToString();
		}

		private static void AppendField(StringBuilder sb, string value)
		{
			value = value ?? "";
			sb.Append(value.Length).Append(':').Append(value)
				.Append('|');
		}
	}
	internal static class Scanner
	{
		public const string AgentGuid = "com.maxkir041.mxahg";

		public const string PeakMxGuid = "com.maxkir041.peakmx";

		public static Snapshot Scan(string selfAssemblyPath, bool excludePeakMx = false)
		{
			Snapshot snapshot = new Snapshot();
			List<string> list = new List<string>();
			string text = Paths.PluginPath;
			if (string.IsNullOrWhiteSpace(text) || !Directory.Exists(text))
			{
				text = Path.GetDirectoryName(selfAssemblyPath) ?? "";
			}
			try
			{
				if (!string.IsNullOrWhiteSpace(selfAssemblyPath) && File.Exists(selfAssemblyPath))
				{
					snapshot.SelfHash = Protocol.HashFile(selfAssemblyPath);
				}
			}
			catch (Exception ex)
			{
				list.Add("self: " + ex.Message);
			}
			try
			{
				string[] array = (Directory.Exists(text) ? Directory.GetFiles(text, "*.dll", SearchOption.AllDirectories) : Array.Empty<string>());
				Array.Sort(array, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
				for (int i = 0; i < array.Length; i++)
				{
					if (snapshot.Files.Count >= 512)
					{
						snapshot.Truncated = true;
						break;
					}
					if (ShouldExcludeFile(Path.GetFileName(array[i]), excludePeakMx))
					{
						continue;
					}
					try
					{
						AssemblyName assemblyName = null;
						try
						{
							assemblyName = AssemblyName.GetAssemblyName(array[i]);
						}
						catch
						{
						}
						snapshot.Files.Add(new FileEntry
						{
							RelativePath = RelativePath(text, array[i]),
							Sha256 = Protocol.HashFile(array[i]),
							AssemblyName = (assemblyName?.Name ?? Path.GetFileNameWithoutExtension(array[i])),
							AssemblyVersion = (assemblyName?.Version?.ToString() ?? "")
						});
					}
					catch (Exception ex2)
					{
						list.Add(Path.GetFileName(array[i]) + ": " + ex2.Message);
					}
				}
			}
			catch (Exception ex3)
			{
				list.Add("files: " + ex3.Message);
			}
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					if (snapshot.Plugins.Count >= 256)
					{
						snapshot.Truncated = true;
						break;
					}
					PluginInfo value = pluginInfo.Value;
					object obj2;
					if (value == null)
					{
						obj2 = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj2 = ((metadata != null) ? metadata.GUID : null);
					}
					if (obj2 == null)
					{
						obj2 = pluginInfo.Key ?? "";
					}
					string guid = (string)obj2;
					if (ShouldExcludePlugin(guid, excludePeakMx))
					{
						continue;
					}
					string text2 = ((value != null) ? value.Location : null) ?? "";
					string sha = "";
					try
					{
						if (File.Exists(text2))
						{
							sha = Protocol.HashFile(text2);
						}
					}
					catch
					{
					}
					List<PluginEntry> plugins = snapshot.Plugins;
					PluginEntry obj4 = new PluginEntry
					{
						Guid = guid
					};
					object obj5;
					if (value == null)
					{
						obj5 = null;
					}
					else
					{
						BepInPlugin metadata2 = value.Metadata;
						obj5 = ((metadata2 != null) ? metadata2.Name : null);
					}
					if (obj5 == null)
					{
						obj5 = "";
					}
					obj4.Name = (string)obj5;
					object obj6;
					if (value == null)
					{
						obj6 = null;
					}
					else
					{
						BepInPlugin metadata3 = value.Metadata;
						obj6 = ((metadata3 == null) ? null : metadata3.Version?.ToString());
					}
					if (obj6 == null)
					{
						obj6 = "";
					}
					obj4.Version = (string)obj6;
					obj4.RelativePath = RelativePath(text, text2);
					obj4.Sha256 = sha;
					plugins.Add(obj4);
				}
			}
			catch (Exception ex4)
			{
				list.Add("plugins: " + ex4.Message);
			}
			try
			{
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods())
				{
					Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod);
					if (((patchInfo != null) ? patchInfo.Owners : null) == null)
					{
						continue;
					}
					foreach (string owner in patchInfo.Owners)
					{
						if (!string.IsNullOrWhiteSpace(owner) && !ShouldExcludePlugin(owner, excludePeakMx) && !IsUnstableHarmonyOwner(owner))
						{
							hashSet.Add(owner);
						}
					}
				}
				foreach (string item in hashSet)
				{
					if (snapshot.HarmonyOwners.Count >= 256)
					{
						snapshot.Truncated = true;
						break;
					}
					snapshot.HarmonyOwners.Add(item);
				}
			}
			catch (Exception ex5)
			{
				list.Add("harmony: " + ex5.Message);
			}
			snapshot.Error = string.Join("; ", list.ToArray());
			snapshot.Recalculate();
			return snapshot;
		}

		public static bool IsTechnicalFile(string path)
		{
			string fileName = Path.GetFileName(path ?? "");
			if (!string.Equals(fileName, "MX-AHG.dll", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(fileName, "PEAK-MX.dll", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static bool IsTechnicalPlugin(string guid)
		{
			if (!string.Equals(guid, "com.maxkir041.mxahg", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(guid, "com.maxkir041.peakmx", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static bool IsUnstableHarmonyOwner(string owner)
		{
			if (!string.IsNullOrWhiteSpace(owner))
			{
				return owner.StartsWith("harmony-auto-", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool ShouldExcludeFile(string fileName, bool excludePeakMx)
		{
			if (string.Equals(fileName, "MX-AHG.dll", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (excludePeakMx)
			{
				return string.Equals(fileName, "PEAK-MX.dll", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool ShouldExcludePlugin(string guid, bool excludePeakMx)
		{
			if (string.Equals(guid, "com.maxkir041.mxahg", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (excludePeakMx)
			{
				return string.Equals(guid, "com.maxkir041.peakmx", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static string RelativePath(string root, string file)
		{
			if (string.IsNullOrWhiteSpace(file))
			{
				return "";
			}
			try
			{
				Uri uri = new Uri(AppendSlash(Path.GetFullPath(root)));
				Uri uri2 = new Uri(Path.GetFullPath(file));
				return Uri.UnescapeDataString(uri.MakeRelativeUri(uri2).ToString()).Replace('/', Path.DirectorySeparatorChar);
			}
			catch
			{
				return Path.GetFileName(file);
			}
		}

		private static string AppendSlash(string value)
		{
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			if (!value.EndsWith(directorySeparatorChar.ToString(), StringComparison.Ordinal))
			{
				directorySeparatorChar = Path.AltDirectorySeparatorChar;
				if (!value.EndsWith(directorySeparatorChar.ToString(), StringComparison.Ordinal))
				{
					directorySeparatorChar = Path.DirectorySeparatorChar;
					return value + directorySeparatorChar;
				}
			}
			return value;
		}
	}
}
namespace PeakMX
{
	internal static class AntiCheat
	{
		private const string EnabledKey = "pmx_ac";

		private const string LockClientsKey = "pmx_ac_lock";

		private const string HostActorKey = "pmx_ac_host";

		private const string HostVersionKey = "pmx_ac_ver";

		private static float _nextPublish;

		private static bool _lastPublishedEnabled;

		private static bool _hostEnabled;

		private static bool _hostLocksClients;

		private static int _hostActor;

		private static string _hostVersion = "";

		private static int _lastLockNoticeHostActor = int.MinValue;

		private static bool _wasMasterClient;

		public static bool HostEnabled => _hostEnabled;

		public static bool HostLocksClients => _hostLocksClients;

		public static int HostActor => _hostActor;

		public static string HostVersion => _hostVersion;

		public static bool ClientToolsLocked
		{
			get
			{
				try
				{
					return PhotonNetwork.InRoom && _hostEnabled && _hostLocksClients && !PhotonNetwork.IsMasterClient;
				}
				catch
				{
					return false;
				}
			}
		}

		public static void Tick()
		{
			try
			{
				if (!PhotonNetwork.InRoom)
				{
					_hostEnabled = false;
					_hostLocksClients = false;
					_hostActor = 0;
					_hostVersion = "";
					_nextPublish = 0f;
					_lastPublishedEnabled = false;
					_lastLockNoticeHostActor = int.MinValue;
					_wasMasterClient = false;
					return;
				}
				ReadRoomState();
				bool isMasterClient = PhotonNetwork.IsMasterClient;
				bool flag = isMasterClient && !_wasMasterClient;
				_wasMasterClient = isMasterClient;
				if (isMasterClient)
				{
					if (flag)
					{
						AdoptInheritedRoomPolicy();
					}
					ApplyHostMasterSwitch();
					PublishHostState();
				}
				if (ClientToolsLocked)
				{
					DisableRestrictedLocalTools();
				}
				else
				{
					_lastLockNoticeHostActor = int.MinValue;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("[AntiCheat] " + ex.Message));
				}
			}
		}

		public static void SetHostEnabled(bool enabled)
		{
			ModConfig.AntiCheatEnabled = enabled;
			ModConfig.AdminProtectionEnabled = enabled;
			ApplyHostMasterSwitch();
			_nextPublish = 0f;
			PublishHostState();
		}

		public static string StatusText(Lang lang)
		{
			if (!PhotonNetwork.InRoom)
			{
				return Localization.Pick(lang, "Античит активируется в комнате.", "Anti-cheat becomes active inside a room.", "反作弊会在房间内启用。", "反作弊會在房間內啟用。");
			}
			if (PhotonNetwork.IsMasterClient)
			{
				if (!ModConfig.AntiCheatEnabled)
				{
					return Localization.Pick(lang, "Античит хоста выключен.", "Host anti-cheat is disabled.", "房主反作弊已关闭。", "房主反作弊已關閉。");
				}
				return Localization.Pick(lang, "Античит хоста включён.", "Host anti-cheat is enabled.", "房主反作弊已启用。", "房主反作弊已啟用。");
			}
			if (_hostEnabled)
			{
				if (!_hostLocksClients)
				{
					return Localization.Pick(lang, "Хост включил античит без блокировки клиентских инструментов.", "The host enabled anti-cheat without locking client tools.", "房主启用了反作弊,但未锁定客户端工具。", "房主啟用了反作弊,但未鎖定用戶端工具。");
				}
				return Localization.Pick(lang, "Хост включил античит. Инструменты PEAK-MX на клиентах заблокированы.", "The host enabled anti-cheat. PEAK-MX tools are locked on clients.", "房主启用了反作弊。客户端 PEAK-MX 工具已锁定。", "房主啟用了反作弊。用戶端 PEAK-MX 工具已鎖定。");
			}
			return Localization.Pick(lang, "Хост не включал античит.", "The host has not enabled anti-cheat.", "房主未启用反作弊。", "房主未啟用反作弊。");
		}

		public static void DisableRestrictedLocalTools()
		{
			ModConfig.SpeedMod = false;
			ModConfig.JumpMod = false;
			ModConfig.InfiniteJumps = false;
			ModConfig.ClimbMod = false;
			ModConfig.VineClimbMod = false;
			ModConfig.RopeClimbMod = false;
			ModConfig.GodMode = false;
			ModConfig.InfiniteStamina = false;
			ModConfig.NoFallDamage = false;
			ModConfig.NoFallingRagdoll = false;
			ModConfig.NoWeight = false;
			ModConfig.LockStatus = false;
			ModConfig.NoStatusEffects = false;
			ModConfig.NoInjury = false;
			ModConfig.NoHunger = false;
			ModConfig.NoCold = false;
			ModConfig.NoPoison = false;
			ModConfig.NoCurse = false;
			ModConfig.NoDrowsy = false;
			ModConfig.NoHot = false;
			ModConfig.NoCrab = false;
			ModConfig.NoThorns = false;
			ModConfig.NoSpores = false;
			ModConfig.NoWeb = false;
			ModConfig.NoArrows = false;
			ModConfig.NoPetrify = false;
			ModConfig.NoFlyTrap = false;
			ModConfig.TeleportToPing = false;
			ModConfig.NoSlipperySurfaces = false;
			ModConfig.LongInteraction = false;
			ModConfig.CinematicCamera = false;
			ModConfig.GlobalVoice = false;
			ModConfig.PlayerEsp = false;
			ModConfig.ItemEsp = false;
			if (ModConfig.WorldEsp != null)
			{
				ModConfig.WorldEsp.Value = false;
			}
			ModConfig.GameSpeedMod = false;
			ModConfig.PingHandSizeMultiplier = 1f;
			ModConfig.OverrideExpeditionTime = false;
			ModConfig.Fly = false;
			ModConfig.Noclip = false;
			ModConfig.InfiniteItems = false;
			ModConfig.UnlimitedLanternFuel = false;
			ModConfig.UnlimitedItemUses = false;
			ModConfig.RapidRandomOutfitColor = false;
			int num = ((_hostActor > 0) ? _hostActor : (-1));
			if (_lastLockNoticeHostActor != num)
			{
				_lastLockNoticeHostActor = num;
				GameApi.AddAdminLog((Localization.Current == Lang.Russian) ? "anti-cheat: клиентские инструменты заблокированы хостом" : "anti-cheat: client tools locked by host", showNotice: false);
			}
		}

		private static void ApplyHostMasterSwitch()
		{
			if (ModConfig.AntiCheatEnabled)
			{
				if (ModConfig.ManyPlayersPluginAudit != null)
				{
					ModConfig.ManyPlayersPluginAudit.Value = false;
				}
				if (ModConfig.ManyPlayersPluginAuditKick != null)
				{
					ModConfig.ManyPlayersPluginAuditKick.Value = false;
				}
			}
			ModConfig.AdminProtectionEnabled = ModConfig.AntiCheatEnabled;
		}

		private static void AdoptInheritedRoomPolicy()
		{
			if (_hostEnabled && !ModConfig.AntiCheatEnabled)
			{
				ModConfig.AntiCheatEnabled = true;
				ModConfig.AdminProtectionEnabled = true;
				if (ModConfig.AntiCheatLockClients != null)
				{
					ModConfig.AntiCheatLockClients.Value = _hostLocksClients;
				}
				GameApi.AddAdminLog((Localization.Current == Lang.Russian) ? "anti-cheat: политика комнаты унаследована новым хостом" : "anti-cheat: room policy inherited by the new host");
			}
		}

		private static void PublishHostState()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null)
			{
				bool antiCheatEnabled = ModConfig.AntiCheatEnabled;
				if (antiCheatEnabled != _lastPublishedEnabled || !(Time.realtimeSinceStartup < _nextPublish))
				{
					_nextPublish = Time.realtimeSinceStartup + 2f;
					_lastPublishedEnabled = antiCheatEnabled;
					Hashtable val = new Hashtable
					{
						[(object)"pmx_ac"] = antiCheatEnabled,
						[(object)"pmx_ac_lock"] = ModConfig.AntiCheatLockClients == null || ModConfig.AntiCheatLockClients.Value,
						[(object)"pmx_ac_host"] = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0),
						[(object)"pmx_ac_ver"] = "1.1.1"
					};
					PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
					_hostEnabled = antiCheatEnabled;
					_hostLocksClients = ModConfig.AntiCheatLockClients == null || ModConfig.AntiCheatLockClients.Value;
					_hostActor = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0);
					_hostVersion = "1.1.1";
				}
			}
		}

		private static void ReadRoomState()
		{
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) == null)
				{
					return;
				}
				Hashtable customProperties = ((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties;
				bool flag = default(bool);
				int num;
				if (((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"pmx_ac", out object value))
				{
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				_hostEnabled = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				bool flag2 = default(bool);
				int num2;
				if (((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"pmx_ac_lock", out object value2))
				{
					if (value2 is bool)
					{
						flag2 = (bool)value2;
						num2 = ((1 == 0) ? 1 : 0);
					}
					else
					{
						num2 = 1;
					}
				}
				else
				{
					num2 = 1;
				}
				_hostLocksClients = (byte)((uint)num2 | (flag2 ? 1u : 0u)) != 0;
				_hostActor = ((((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"pmx_ac_host", out object value3) && int.TryParse(value3?.ToString(), out var result)) ? result : 0);
				_hostVersion = ((!((Dictionary<object, object>)(object)customProperties).TryGetValue((object)"pmx_ac_ver", out object value4)) ? "" : (value4?.ToString() ?? ""));
			}
			catch
			{
				_hostEnabled = false;
				_hostLocksClients = false;
				_hostActor = 0;
				_hostVersion = "";
			}
		}
	}
	public static class CheatTracker
	{
		public static void Tick()
		{
		}
	}
	internal static class CompatibilityDiagnostics
	{
		public struct Row
		{
			public readonly string Name;

			public readonly bool Ok;

			public readonly string Detail;

			public Row(string name, bool ok, string detail)
			{
				Name = name;
				Ok = ok;
				Detail = detail;
			}
		}

		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		public static List<Row> Rows(bool russian)
		{
			List<Row> list = new List<Row>();
			list.Add(new Row(russian ? "PEAK-MX" : "PEAK-MX", ok: true, "v1.1.1"));
			list.Add(new Row(russian ? "Версия игры" : "Game version", ok: true, Safe(() => Application.version, "?")));
			list.Add(new Row(russian ? "Photon" : "Photon", Safe(() => PhotonNetwork.NetworkingClient != null, fallback: false), Safe(() => (!PhotonNetwork.InRoom) ? ((!russian) ? "not in room" : "не в комнате") : string.Format("{0}: {1}", russian ? "комната" : "room", PhotonNetwork.PlayerList.Length), "?")));
			list.Add(CheckMethod(typeof(CharacterMovement), "RocketExplodeRPC", russian ? "Взрыв игрока" : "Player explosion", russian ? "RPC взрыва найден" : "Explosion RPC found"));
			list.Add(CheckMethod(typeof(CharacterMovement), "FallFactor", russian ? "Урон от падения" : "Fall damage", russian ? "метод найден" : "method found"));
			list.Add(CheckMethod(typeof(CharacterMovement), "MaxVelDmg", russian ? "Скорость падения" : "Fall velocity", russian ? "метод найден" : "method found"));
			list.Add(CheckMethod(typeof(CharacterMovement), "AcceptableAngle", new Type[1] { typeof(float) }, russian ? "Угол падения" : "Fall angle", russian ? "метод найден" : "method found"));
			list.Add(CheckMethod(typeof(CharacterInput), "ResetInput", russian ? "Блокировка ввода" : "Input blocker", russian ? "метод найден" : "method found"));
			list.Add(CheckField(typeof(Player), "itemSlots", russian ? "Основные слоты" : "Main slots"));
			list.Add(CheckField(typeof(Player), "backpackSlot", russian ? "Слот на спине" : "Back slot"));
			list.Add(CheckField(typeof(Player), "tempFullSlot", russian ? "Амулет/временный слот" : "Amulet/temp slot"));
			list.Add(CheckMember(typeof(ItemSlot), "prefab", russian ? "Предмет в слоте" : "Slot item"));
			list.Add(CheckMember(typeof(CharacterData), "dead", russian ? "Состояние смерти" : "Dead state"));
			list.Add(CheckMember(typeof(CharacterData), "currentItem", russian ? "Текущий предмет" : "Current item"));
			list.Add(CheckMember(typeof(CharacterData), "currentStamina", russian ? "Стамина" : "Stamina"));
			list.Add(CheckMethod(typeof(NetworkingUtilities), "HostRoomOptions", russian ? "Большие лобби" : "Large lobbies", russian ? "метод комнаты найден" : "room method found"));
			list.Add(CheckMethod(typeof(AirportCheckInKiosk), "StartGame", new Type[1] { typeof(int) }, russian ? "Старт экспедиции" : "Expedition start", russian ? "метод киоска найден" : "kiosk method found"));
			list.Add(CheckMethod(typeof(AirportCheckInKiosk), "LoadIslandMaster", new Type[2]
			{
				typeof(int),
				typeof(byte[])
			}, russian ? "Загрузка острова" : "Island loading", russian ? "метод загрузки найден" : "load method found"));
			list.Add(CheckMethod(typeof(PlayerHandler), "AssignMixerGroup", new Type[1] { typeof(Character) }, russian ? "Голосовые группы" : "Voice groups", russian ? "метод найден" : "method found"));
			list.Add(CheckMethod(typeof(UIPlayerNames), "DisableName", new Type[1] { typeof(int) }, russian ? "UI имен игроков" : "Player names UI", russian ? "метод найден" : "method found"));
			list.Add(CheckMethod(typeof(PointPinger), "ReceivePoint_Rpc", new Type[2]
			{
				typeof(Vector3),
				typeof(Vector3)
			}, russian ? "Пинги карты" : "Map pings", russian ? "RPC найден" : "RPC found"));
			list.Add(CheckEnum<STATUSTYPE>("Arrow", russian ? "Стрелы" : "Arrows"));
			list.Add(CheckEnum<STATUSTYPE>("Petrify", russian ? "Окаменение" : "Petrify"));
			list.Add(CheckEnum<STATUSTYPE>("FlyTrap", russian ? "Мухоловка" : "Flytrap"));
			list.Add(CheckField(typeof(CharacterVoiceHandler), "m_source", russian ? "Голосовой источник" : "Voice source"));
			list.Add(CheckField(typeof(VoiceObscuranceFilter), "_voiceHandler", russian ? "Фильтр эха голоса" : "Voice echo filter"));
			list.Add(CheckField(typeof(MapHandler), "respawnThePeak", russian ? "Телепорт на пик" : "Peak teleport"));
			bool flag = Safe(delegate
			{
				GameApi.EnsureItemsLoaded();
				return GameApi.ItemNames.Count > 0;
			}, fallback: false);
			list.Add(new Row(russian ? "База предметов" : "Item database", flag, flag ? GameApi.ItemNames.Count.ToString() : (russian ? "предметы не загружены" : "items not loaded")));
			int num = 0;
			List<string> list2 = new List<string>();
			foreach (GameApi.CosmeticCategory category in Enum.GetValues(typeof(GameApi.CosmeticCategory)))
			{
				int num2 = Safe(() => GameApi.GetCosmeticOptions(category).Length, 0);
				num += num2;
				list2.Add(category.ToString() + "=" + num2);
			}
			list.Add(new Row(russian ? "База косметики" : "Cosmetic database", num > 0, (num > 0) ? string.Join(", ", list2) : (russian ? "косметика не найдена" : "cosmetics not found")));
			list.Add(new Row(russian ? "Ачивки" : "Achievements", Safe(() => SteamAch.AllTypes.Length != 0, fallback: false), Safe<string>(() => SteamAch.AllTypes.Length.ToString(), "?")));
			return list;
		}

		public static string BuildReport(bool russian)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("PEAK-MX compatibility report");
			stringBuilder.AppendLine("Mod: 1.1.1");
			stringBuilder.AppendLine("Game: " + Safe(() => Application.version, "?"));
			stringBuilder.AppendLine("Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
			stringBuilder.AppendLine();
			List<Row> list = Rows(russian);
			for (int num = 0; num < list.Count; num++)
			{
				Row row = list[num];
				stringBuilder.Append(row.Ok ? "[OK] " : "[WARN] ");
				stringBuilder.Append(row.Name);
				if (!string.IsNullOrWhiteSpace(row.Detail))
				{
					stringBuilder.Append(": ").Append(row.Detail);
				}
				stringBuilder.AppendLine();
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(MxAhgHost.BuildReport(Localization.Current));
			return stringBuilder.ToString();
		}

		private static Row CheckMethod(Type type, string method, string name, string okDetail)
		{
			bool flag = Safe(() => type.GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null, fallback: false);
			return new Row(name, flag, flag ? okDetail : ("missing: " + type.Name + "." + method));
		}

		private static Row CheckMethod(Type type, string method, Type[] args, string name, string okDetail)
		{
			bool flag = Safe(() => type.GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null) != null, fallback: false);
			return new Row(name, flag, flag ? okDetail : ("missing: " + type.Name + "." + method));
		}

		private static Row CheckField(Type type, string field, string name)
		{
			bool flag = Safe(() => type.GetField(field, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null, fallback: false);
			return new Row(name, flag, flag ? "ok" : ("missing: " + type.Name + "." + field));
		}

		private static Row CheckMember(Type type, string member, string name)
		{
			bool flag = Safe(() => type.GetField(member, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null || type.GetProperty(member, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null || type.GetMethod("get_" + member, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null, fallback: false);
			return new Row(name, flag, flag ? "ok" : ("missing: " + type.Name + "." + member));
		}

		private static Row CheckEnum<T>(string value, string name) where T : struct
		{
			bool flag = Safe(() => Enum.IsDefined(typeof(T), value), fallback: false);
			return new Row(name, flag, flag ? value : ("missing: " + value));
		}

		private static T Safe<T>(Func<T> func, T fallback)
		{
			try
			{
				return func();
			}
			catch
			{
				return fallback;
			}
		}
	}
	public static class Features
	{
		private static readonly FieldRef<CharacterData, bool> InvincibleRef = AccessTools.FieldRefAccess<CharacterData, bool>("isInvincible");

		private static readonly FieldInfo ClimbSpeedF = AccessTools.Field(typeof(CharacterClimbing), "climbSpeedMod");

		private static readonly FieldInfo VineSpeedF = AccessTools.Field(typeof(CharacterVineClimbing), "climbSpeedMod");

		private static readonly FieldInfo RopeSpeedF = AccessTools.Field(typeof(CharacterRopeHandling), "climbSpeedMod");

		private static float _baseJumpImpulse = float.NaN;

		private static float[] _lockedStatuses;

		private static bool _godApplied;

		private static bool _speedApplied;

		private static bool _jumpApplied;

		private static bool _climbApplied;

		private static bool _vineApplied;

		private static bool _ropeApplied;

		private static float _itemTimer;

		private static float _timeOverrideTimer;

		private static float _adminTimer;

		private static float _cosmeticShuffleTimer;

		private static float _baseInteractionDistance = -1f;

		private static float _baseInteractionArea = -1f;

		private static bool _timeScaleApplied;

		private static readonly Dictionary<PointPing, float> PingBaseFrustumSizes = new Dictionary<PointPing, float>();

		private static GameObject _cinematicObject;

		private static CameraOverride _cinematicOverride;

		private static Vector2 _cinematicLook;

		public static void Tick()
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null)
			{
				return;
			}
			if (NeedsMovementTick())
			{
				try
				{
					ApplyMovement(localCharacter);
				}
				catch (Exception e)
				{
					Warn("movement", e);
				}
			}
			if (NeedsCheatTick())
			{
				try
				{
					ApplyCheats(localCharacter);
				}
				catch (Exception e2)
				{
					Warn("cheats", e2);
				}
			}
			try
			{
				ApplyInfiniteItems();
			}
			catch (Exception e3)
			{
				Warn("items", e3);
			}
			try
			{
				ApplyWorldOverrides();
			}
			catch (Exception e4)
			{
				Warn("world", e4);
			}
			if (NeedsUtilityTick())
			{
				try
				{
					ApplyUtilityModifiers(localCharacter);
				}
				catch (Exception e5)
				{
					Warn("utility", e5);
				}
			}
			try
			{
				ApplyAdminProtection();
			}
			catch (Exception e6)
			{
				Warn("admin", e6);
			}
			try
			{
				ApplyCosmeticShuffle();
			}
			catch (Exception e7)
			{
				Warn("cosmetics", e7);
			}
			try
			{
				GameApi.ApplyFrozenPlayers(Time.deltaTime);
			}
			catch (Exception e8)
			{
				Warn("freeze", e8);
			}
			try
			{
				GameApi.ApplyInventoryLocks(Time.deltaTime);
			}
			catch (Exception e9)
			{
				Warn("inventory-lock", e9);
			}
		}

		private static bool NeedsMovementTick()
		{
			if (!ModConfig.SpeedMod && !_speedApplied && !ModConfig.JumpMod && !_jumpApplied && !ModConfig.InfiniteJumps && !ModConfig.ClimbMod && !_climbApplied && !ModConfig.VineClimbMod && !_vineApplied && !ModConfig.RopeClimbMod)
			{
				return _ropeApplied;
			}
			return true;
		}

		private static bool NeedsCheatTick()
		{
			if (!ModConfig.GodMode && !_godApplied && !ModConfig.InfiniteStamina && !ModConfig.NoFallDamage && !ModConfig.NoFallingRagdoll && !ModConfig.NoSlipperySurfaces && !ModConfig.NoWeight && !ModConfig.NoStatusEffects && !ModConfig.NoInjury && !ModConfig.NoHunger && !ModConfig.NoCold && !ModConfig.NoPoison && !ModConfig.NoCurse && !ModConfig.NoDrowsy && !ModConfig.NoHot && !ModConfig.NoCrab && !ModConfig.NoThorns && !ModConfig.NoSpores && !ModConfig.NoWeb && !ModConfig.NoArrows && !ModConfig.NoPetrify && !ModConfig.NoFlyTrap && !ModConfig.LockStatus)
			{
				return _lockedStatuses != null;
			}
			return true;
		}

		private static bool NeedsUtilityTick()
		{
			if (!ModConfig.LongInteraction && !(_baseInteractionDistance >= 0f) && !ModConfig.GameSpeedMod && !_timeScaleApplied && !ModConfig.CinematicCamera && !((Object)(object)_cinematicObject != (Object)null) && !(Mathf.Abs(ModConfig.PingHandSizeMultiplier - 1f) >= 0.01f))
			{
				return PingBaseFrustumSizes.Count > 0;
			}
			return true;
		}

		private static void ApplyInfiniteItems()
		{
			if (!ModConfig.InfiniteItems)
			{
				return;
			}
			_itemTimer += Time.deltaTime;
			if (!(_itemTimer < 0.5f))
			{
				_itemTimer = 0f;
				float value = ((ModConfig.RechargeValue > 0f) ? ModConfig.RechargeValue : 999f);
				int num = GameApi.SlotCount();
				for (int i = 0; i < num; i++)
				{
					GameApi.RechargeSlot(i, value);
				}
			}
		}

		private static void ApplyWorldOverrides()
		{
			if (!ModConfig.OverrideExpeditionTime)
			{
				_timeOverrideTimer = 0f;
				return;
			}
			_timeOverrideTimer += Time.deltaTime;
			if (_timeOverrideTimer < 0.5f)
			{
				return;
			}
			_timeOverrideTimer = 0f;
			if (ModConfig.ExpeditionTimeSeconds <= 0.01f)
			{
				float num = GameApi.ExpeditionTimeSeconds();
				if (!(num > 0.01f))
				{
					return;
				}
				ModConfig.ExpeditionTimeSeconds = num;
			}
			GameApi.SetExpeditionTime(ModConfig.ExpeditionTimeSeconds);
		}

		private static void ApplyAdminProtection()
		{
			if (!ModConfig.AdminProtectionEnabled)
			{
				_adminTimer = 0f;
				return;
			}
			_adminTimer += Time.deltaTime;
			if (!(_adminTimer < 2f))
			{
				_adminTimer = 0f;
				GameApi.EnforceAdminProtection();
			}
		}

		private static void ApplyCosmeticShuffle()
		{
			if (!ModConfig.RapidRandomOutfitColor)
			{
				_cosmeticShuffleTimer = 0f;
				return;
			}
			_cosmeticShuffleTimer += Time.unscaledDeltaTime;
			float num = Mathf.Clamp(ModConfig.RapidRandomOutfitColorInterval, 0.08f, 2f);
			if (!(_cosmeticShuffleTimer < num))
			{
				_cosmeticShuffleTimer = 0f;
				GameApi.RandomizeOutfitAndColor(track: false);
			}
		}

		private static void ApplyMovement(Character c)
		{
			CharacterMovement val = c.refs?.movement;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (ModConfig.SpeedMod)
			{
				val.movementModifier = ModConfig.SpeedAmount;
				_speedApplied = true;
			}
			else if (_speedApplied)
			{
				val.movementModifier = 1f;
				_speedApplied = false;
			}
			if (ModConfig.JumpMod)
			{
				if (float.IsNaN(_baseJumpImpulse) || _baseJumpImpulse <= 0f)
				{
					_baseJumpImpulse = ((val.jumpImpulse > 0f) ? val.jumpImpulse : 4f);
				}
				val.jumpImpulse = _baseJumpImpulse * ModConfig.JumpAmount;
				_jumpApplied = true;
			}
			else if (_jumpApplied)
			{
				if (!float.IsNaN(_baseJumpImpulse) && _baseJumpImpulse > 0f)
				{
					val.jumpImpulse = _baseJumpImpulse;
				}
				_jumpApplied = false;
			}
			if (ModConfig.InfiniteJumps)
			{
				c.data.jumpsRemaining = Mathf.Max(c.data.jumpsRemaining, 1);
				if (c.data.sinceJump > 0.28f)
				{
					c.data.sinceGrounded = Mathf.Min(c.data.sinceGrounded, 0.18f);
				}
			}
			ApplyClimbField<CharacterClimbing>(c, ClimbSpeedF, ModConfig.ClimbMod, ModConfig.ClimbAmount, ref _climbApplied);
			ApplyClimbField<CharacterVineClimbing>(c, VineSpeedF, ModConfig.VineClimbMod, ModConfig.VineClimbAmount, ref _vineApplied);
			ApplyClimbField<CharacterRopeHandling>(c, RopeSpeedF, ModConfig.RopeClimbMod, ModConfig.RopeClimbAmount, ref _ropeApplied);
		}

		private static void ApplyClimbField<T>(Character c, FieldInfo f, bool on, float amount, ref bool applied) where T : Component
		{
			if (f == null)
			{
				return;
			}
			T component = ((Component)c).GetComponent<T>();
			if (!((Object)(object)component == (Object)null))
			{
				if (on)
				{
					f.SetValue(component, amount);
					applied = true;
				}
				else if (applied)
				{
					f.SetValue(component, 1f);
					applied = false;
				}
			}
		}

		private static void ApplyCheats(Character c)
		{
			CharacterData data = c.data;
			CharacterAfflictions val = ((c.refs != null) ? c.refs.afflictions : null);
			if (ModConfig.GodMode)
			{
				InvincibleRef.Invoke(data) = true;
				_godApplied = true;
			}
			else if (_godApplied)
			{
				InvincibleRef.Invoke(data) = false;
				data.RecalculateInvincibility();
				_godApplied = false;
			}
			if (ModConfig.InfiniteStamina)
			{
				data.currentStamina = 1f;
				data.extraStamina = Mathf.Max(data.extraStamina, 0f);
			}
			if (ModConfig.NoFallDamage)
			{
				data.fallSeconds = 0f;
			}
			if (ModConfig.NoFallingRagdoll)
			{
				data.fallSeconds = 0f;
			}
			if (ModConfig.NoSlipperySurfaces)
			{
				data.slippy = 0f;
				data.sinceFallSlide = Mathf.Max(data.sinceFallSlide, 2f);
			}
			if (!((Object)(object)val != (Object)null))
			{
				return;
			}
			if (ModConfig.NoStatusEffects)
			{
				for (int i = 0; i < CharacterAfflictions.NumStatusTypes; i++)
				{
					val.SetStatus((STATUSTYPE)i, 0f, false);
				}
				try
				{
					val.RemoveAllThorns();
				}
				catch
				{
				}
			}
			else
			{
				ClearBlockedStatus(val, (STATUSTYPE)0, ModConfig.NoInjury);
				ClearBlockedStatus(val, (STATUSTYPE)1, ModConfig.NoHunger);
				ClearBlockedStatus(val, (STATUSTYPE)2, ModConfig.NoCold);
				ClearBlockedStatus(val, (STATUSTYPE)3, ModConfig.NoPoison);
				ClearBlockedStatus(val, (STATUSTYPE)5, ModConfig.NoCurse);
				ClearBlockedStatus(val, (STATUSTYPE)6, ModConfig.NoDrowsy);
				ClearBlockedStatus(val, (STATUSTYPE)8, ModConfig.NoHot);
				ClearBlockedStatus(val, (STATUSTYPE)4, ModConfig.NoCrab);
				ClearBlockedStatus(val, (STATUSTYPE)9, ModConfig.NoThorns);
				ClearBlockedStatus(val, (STATUSTYPE)10, ModConfig.NoSpores);
				ClearBlockedStatus(val, (STATUSTYPE)11, ModConfig.NoWeb);
				ClearBlockedStatus(val, (STATUSTYPE)12, ModConfig.NoArrows);
				ClearBlockedStatus(val, (STATUSTYPE)13, ModConfig.NoPetrify);
				ClearBlockedStatus(val, (STATUSTYPE)14, ModConfig.NoFlyTrap);
			}
			if (ModConfig.NoWeight)
			{
				val.SetStatus((STATUSTYPE)7, 0f, true);
			}
			if (ModConfig.NoThorns)
			{
				GameApi.ClearThorns(c, track: false);
			}
			if (ModConfig.NoArrows)
			{
				GameApi.ClearArrows(c, track: false);
			}
			if (ModConfig.LockStatus)
			{
				if (_lockedStatuses == null && val.currentStatuses != null)
				{
					_lockedStatuses = (float[])val.currentStatuses.Clone();
				}
				if (_lockedStatuses != null && val.currentStatuses != null)
				{
					Array.Copy(_lockedStatuses, val.currentStatuses, Mathf.Min(_lockedStatuses.Length, val.currentStatuses.Length));
				}
			}
			else
			{
				_lockedStatuses = null;
			}
		}

		private static void ClearBlockedStatus(CharacterAfflictions affl, STATUSTYPE type, bool blocked)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (blocked)
			{
				affl.SetStatus(type, 0f, false);
			}
		}

		private static void ApplyUtilityModifiers(Character c)
		{
			ApplyInteractionRange();
			ApplyGameSpeed();
			ApplyCinematicCamera(c);
			ApplyPingHandScale();
		}

		private static void ApplyInteractionRange()
		{
			Interaction instance = Interaction.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if (_baseInteractionDistance < 0f)
				{
					_baseInteractionDistance = instance.distance;
					_baseInteractionArea = instance.area;
				}
				if (ModConfig.LongInteraction)
				{
					instance.distance = Mathf.Max(_baseInteractionDistance, ModConfig.InteractionDistance);
					instance.area = Mathf.Max(_baseInteractionArea, 1.25f);
				}
				else if (_baseInteractionDistance >= 0f)
				{
					instance.distance = _baseInteractionDistance;
					instance.area = _baseInteractionArea;
				}
			}
		}

		private static void ApplyGameSpeed()
		{
			if (ModConfig.GameSpeedMod)
			{
				Time.timeScale = Mathf.Clamp(ModConfig.GameSpeed, 0.05f, 5f);
				_timeScaleApplied = true;
			}
			else if (_timeScaleApplied)
			{
				Time.timeScale = 1f;
				_timeScaleApplied = false;
			}
		}

		private static void ApplyCinematicCamera(Character c)
		{
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.CinematicCamera)
			{
				DisableCinematicCamera();
				return;
			}
			if ((Object)(object)_cinematicObject == (Object)null)
			{
				_cinematicObject = new GameObject("PEAK-MX Cinematic Camera");
				Object.DontDestroyOnLoad((Object)(object)_cinematicObject);
				_cinematicOverride = _cinematicObject.AddComponent<CameraOverride>();
				Transform val = (((Object)(object)MainCamera.instance != (Object)null) ? ((Component)MainCamera.instance).transform : ((Component)c).transform);
				_cinematicObject.transform.position = val.position;
				_cinematicObject.transform.rotation = val.rotation;
				Quaternion rotation = _cinematicObject.transform.rotation;
				Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
				_cinematicLook = new Vector2(eulerAngles.y, eulerAngles.x);
			}
			_cinematicOverride.fov = Mathf.Clamp(ModConfig.CinematicCameraFov, 1f, 120f);
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			_cinematicLook.x += Input.GetAxis("Mouse X") * 140f * unscaledDeltaTime;
			_cinematicLook.y -= Input.GetAxis("Mouse Y") * 140f * unscaledDeltaTime;
			_cinematicLook.y = Mathf.Clamp(_cinematicLook.y, -89f, 89f);
			_cinematicObject.transform.rotation = Quaternion.Euler(_cinematicLook.y, _cinematicLook.x, 0f);
			Vector3 zero = Vector3.zero;
			if (Input.GetKey((KeyCode)119))
			{
				zero.z += 1f;
			}
			if (Input.GetKey((KeyCode)115))
			{
				zero.z -= 1f;
			}
			if (Input.GetKey((KeyCode)100))
			{
				zero.x += 1f;
			}
			if (Input.GetKey((KeyCode)97))
			{
				zero.x -= 1f;
			}
			if (Input.GetKey((KeyCode)32) || Input.GetKey((KeyCode)101))
			{
				zero.y += 1f;
			}
			if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)113))
			{
				zero.y -= 1f;
			}
			float num = Mathf.Max(0.1f, ModConfig.CinematicCameraSpeed) * (Input.GetKey((KeyCode)304) ? 4f : 1f);
			Transform transform = _cinematicObject.transform;
			transform.position += _cinematicObject.transform.TransformDirection(((Vector3)(ref zero)).normalized) * num * unscaledDeltaTime;
			if ((Object)(object)MainCamera.instance != (Object)null)
			{
				MainCamera.instance.SetCameraOverride(_cinematicOverride);
			}
		}

		private static void DisableCinematicCamera()
		{
			if ((Object)(object)_cinematicObject == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)MainCamera.instance != (Object)null)
				{
					MainCamera.instance.SetCameraOverride((CameraOverride)null);
				}
			}
			catch
			{
			}
			Object.Destroy((Object)(object)_cinematicObject);
			_cinematicObject = null;
			_cinematicOverride = null;
		}

		private static void ApplyPingHandScale()
		{
			float num = Mathf.Clamp(ModConfig.PingHandSizeMultiplier, 0.1f, 10f);
			if (Mathf.Abs(num - 1f) < 0.01f && PingBaseFrustumSizes.Count == 0)
			{
				return;
			}
			PointPing[] array = Object.FindObjectsByType<PointPing>((FindObjectsSortMode)0);
			foreach (PointPing val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (!PingBaseFrustumSizes.ContainsKey(val))
					{
						PingBaseFrustumSizes[val] = Mathf.Max(0.001f, val.sizeOfFrustum);
					}
					val.sizeOfFrustum = PingBaseFrustumSizes[val] * num;
				}
			}
			if (!(Mathf.Abs(num - 1f) < 0.01f) || PingBaseFrustumSizes.Count <= 0)
			{
				return;
			}
			foreach (KeyValuePair<PointPing, float> pingBaseFrustumSize in PingBaseFrustumSizes)
			{
				if ((Object)(object)pingBaseFrustumSize.Key != (Object)null)
				{
					pingBaseFrustumSize.Key.sizeOfFrustum = pingBaseFrustumSize.Value;
				}
			}
			PingBaseFrustumSizes.Clear();
		}

		private static void Warn(string where, Exception e)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("[Features:" + where + "] " + e.Message));
			}
		}
	}
	public static class GameApi
	{
		public enum CosmeticCategory
		{
			Skin,
			Accessory,
			Eyes,
			Mouth,
			Outfit,
			Hat,
			Sash,
			Medal
		}

		public struct AdminListEntry
		{
			public string Key;

			public string Label;

			public AdminListEntry(string key, string label)
			{
				Key = key;
				Label = label;
			}
		}

		private struct AdminMotionSample
		{
			public Vector3 Position;

			public float Time;
		}

		private struct WorldObjectScanHit
		{
			public string Label;

			public Vector3 Position;

			public float Distance;
		}

		private struct SlotPrefabAccessor
		{
			public MethodInfo Getter;

			public FieldInfo Field;
		}

		public static readonly List<Item> Items = new List<Item>();

		public static readonly List<string> ItemNames = new List<string>();

		public static readonly List<Character> PlayerChars = new List<Character>();

		public static readonly List<string> PlayerNames = new List<string>();

		private static readonly HashSet<int> LocalCosmeticUnlocks = new HashSet<int>();

		private static readonly HashSet<string> SessionBans = new HashSet<string>();

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

		private static readonly HashSet<string> InventoryLocks = new HashSet<string>();

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

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

		private static readonly List<string> FrozenKeysScratch = new List<string>();

		private static readonly List<string> InventoryLockKeysScratch = new List<string>();

		private static readonly Dictionary<string, AdminMotionSample> AdminMotion = new Dictionary<string, AdminMotionSample>();

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

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

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

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

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

		private static readonly HashSet<string> AdminRareTouchedScratch = new HashSet<string>();

		private static readonly List<string> AdminRareCurrentScratch = new List<string>();

		private static readonly Dictionary<string, int> AdminRareCountsScratch = new Dictionary<string, int>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> AdminAmuletFirstOwnerScratch = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Character> AdminAmuletDuplicateTargetScratch = new Dictionary<string, Character>(StringComparer.Ordinal);

		private static readonly HashSet<string> AdminAmuletDuplicateSnapshot = new HashSet<string>(StringComparer.Ordinal);

		private static readonly List<string> AdminRareRemoveScratch = new List<string>();

		public static readonly List<string> AdminProtectionLog = new List<string>();

		private static float _freezeRpcTimer;

		private static float _inventoryLockTimer;

		private static float _knifeFightAt = -1f;

		private static int _knifeFightCountdownSecond = -1;

		private static bool _itemsEverLoaded;

		private const float AdminRareItemWindowSeconds = 10f;

		private const int AdminRareItemBurstThreshold = 3;

		private static readonly string[] AdminRareKeywords = new string[28]
		{
			"cursed", "curse", "skull", "pandora", "effigy", "faerie", "bugle", "warp compass", "amulet", "medallion",
			"talisman", "charm", "panacea", "cure-all", "cure all", "mystic", "mystical", "arcane", "healing gem", "прокля",
			"череп", "амулет", "медальон", "талисман", "панаце", "лекар", "исцел", "мист"
		};

		private static Customization _customizationDb;

		private static readonly MethodInfo CustomGetData = typeof(CharacterCustomization).GetMethod("GetCustomizationData", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetData = typeof(CharacterCustomization).GetMethod("SetCustomizationData", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetSkin = typeof(CharacterCustomization).GetMethod("SetCharacterSkinColor", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetAccessory = typeof(CharacterCustomization).GetMethod("SetCharacterAccessory", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetEyes = typeof(CharacterCustomization).GetMethod("SetCharacterEyes", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetMouth = typeof(CharacterCustomization).GetMethod("SetCharacterMouth", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetOutfit = typeof(CharacterCustomization).GetMethod("SetCharacterOutfit", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetHat = typeof(CharacterCustomization).GetMethod("SetCharacterHat", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetSash = typeof(CharacterCustomization).GetMethod("SetCharacterSash", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo CustomSetMedal = typeof(CharacterCustomization).GetMethod("SetCharacterMedal", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo RunTimeField = typeof(RunManager).GetField("timeSinceRunStarted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo CharacterViewField = typeof(Character).GetField("view", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo RocketExplodeRpcMethod = typeof(CharacterMovement).GetMethod("RocketExplodeRPC", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly object SlotPrefabAccessorLock = new object();

		private static readonly Dictionary<Type, SlotPrefabAccessor> SlotPrefabAccessors = new Dictionary<Type, SlotPrefabAccessor>();

		private static readonly Segment[] WarpSegments;

		private static readonly string[] SegmentNamesEn;

		private static readonly string[] SegmentNamesRu;

		private const ItemTags ScoutAmuletTag = (ItemTags)512;

		public static readonly List<string> LuggageLabels;

		public static readonly List<Luggage> LuggageObjects;

		public static readonly List<string> WorldObjectLabels;

		private static readonly List<Vector3> WorldObjectPositions;

		public static readonly string[] StatusEn;

		public static readonly string[] StatusRu;

		private const int ArrowStatusIndex = 12;

		private const int PetrifyStatusIndex = 13;

		public static string AdminNoticeText { get; private set; }

		public static float AdminNoticeUntil { get; private set; }

		public static int SegmentCount => WarpSegments.Length;

		private static Item SlotPrefab(ItemSlot slot)
		{
			if (slot == null)
			{
				return null;
			}
			try
			{
				SlotPrefabAccessor slotPrefabAccessor = SlotPrefabAccessorFor(((object)slot).GetType());
				if (slotPrefabAccessor.Getter != null)
				{
					object? obj = slotPrefabAccessor.Getter.Invoke(slot, null);
					return (Item)((obj is Item) ? obj : null);
				}
				if (slotPrefabAccessor.Field != null)
				{
					object? value = slotPrefabAccessor.Field.GetValue(slot);
					return (Item)((value is Item) ? value : null);
				}
			}
			catch (Exception ex) when (ex is MissingFieldException || ex is MissingMethodException || ex is TargetInvocationException || ex is ArgumentException || ex is InvalidOperationException)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("[GameApi] ItemSlot prefab lookup failed: " + ex.Message));
				}
			}
			return null;
		}

		private static SlotPrefabAccessor SlotPrefabAccessorFor(Type type)
		{
			if (type == null)
			{
				return default(SlotPrefabAccessor);
			}
			lock (SlotPrefabAccessorLock)
			{
				if (SlotPrefabAccessors.TryGetValue(type, out var value))
				{
					return value;
				}
				SlotPrefabAccessor slotPrefabAccessor = new SlotPrefabAccessor
				{
					Getter = (type.GetProperty("prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetGetMethod(nonPublic: true) ?? type.GetMethod("get_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)),
					Field = (type.GetField("prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
				};
				SlotPrefabAccessors[type] = slotPrefabAccessor;
				return slotPrefabAccessor;
			}
		}

		public static void LoadItems()
		{
			try
			{
				Items.Clear();
				ItemNames.Clear();
				Object obj = Resources.Load("ItemDatabase", typeof(ItemDatabase));
				ItemDatabase val = (ItemDatabase)(object)((obj is ItemDatabase) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					try
					{
						if (val.itemLookup != null)
						{
							foreach (Item value in val.itemLookup.Values)
							{
								Add(value);
							}
						}
					}
					catch (Exception ex)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogDebug((object)("[GameApi] LoadItems itemLookup: " + ex.Message));
						}
					}
					try
					{
						foreach (Item @object in ((DatabaseAsset<ItemDatabase, Item>)(object)(ObjectDatabaseAsset<ItemDatabase, Item>)(object)val).Objects)
						{
							Add(@object);
						}
					}
					catch (Exception ex2)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogDebug((object)("[GameApi] LoadItems ObjectDatabaseAsset: " + ex2.Message));
						}
					}
					try
					{
						foreach (Item object2 in ((DatabaseAsset<ItemDatabase, Item>)(object)val).Objects)
						{
							Add(object2);
						}
					}
					catch (Exception ex3)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogDebug((object)("[GameApi] LoadItems DatabaseAsset: " + ex3.Message));
						}
					}
				}
				try
				{
					Object[] array = Resources.LoadAll("0_Items", typeof(Item));
					foreach (Object obj2 in array)
					{
						Add((Item)(object)((obj2 is Item) ? obj2 : null));
					}
				}
				catch (Exception ex4)
				{
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogDebug((object)("[GameApi] LoadItems 0_Items: " + ex4.Message));
					}
				}
				try
				{
					Object[] array = Resources.FindObjectsOfTypeAll(typeof(Item));
					foreach (Object obj3 in array)
					{
						Add((Item)(object)((obj3 is Item) ? obj3 : null));
					}
				}
				catch (Exception ex5)
				{
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogDebug((object)("[GameApi] LoadItems all objects: " + ex5.Message));
					}
				}
				_itemsEverLoaded = ItemNames.Count > 0;
				ManualLogSource log6 = Plugin.Log;
				if (log6 != null)
				{
					log6.LogInfo((object)$"[GameApi] items loaded: {ItemNames.Count}");
				}
			}
			catch (Exception ex6)
			{
				ManualLogSource log7 = Plugin.Log;
				if (log7 != null)
				{
					log7.LogWarning((object)("[GameApi] LoadItems: " + ex6.Message));
				}
			}
		}

		public static bool EnsureItemsLoaded()
		{
			if (_itemsEverLoaded && ItemNames.Count > 0)
			{
				return false;
			}
			LoadItems();
			return ItemNames.Count > 0;
		}

		private static void Add(Item it)
		{
			if ((Object)(object)it == (Object)null)
			{
				return;
			}
			try
			{
				if (IsInventorySpawnableItem(it))
				{
					string name = it.GetName();
					if (!string.IsNullOrEmpty(name) && !HasItemAlready(it, name))
					{
						Items.Add(it);
						ItemNames.Add(name);
					}
				}
			}
			catch
			{
			}
		}

		private static bool HasItemAlready(Item item, string displayName)
		{
			for (int i = 0; i < Items.Count; i++)
			{
				Item val = Items[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				try
				{
					if ((Object)(object)val == (Object)(object)item)
					{
						return true;
					}
					if (val.itemID == item.itemID && (Object)(object)((Component)val).gameObject != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null && string.Equals(((Object)((Component)val).gameObject).name, ((Object)((Component)item).gameObject).name, StringComparison.Ordinal))
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return ItemNames.Contains(displayName);
		}

		private static bool IsInventorySpawnableItem(Item item)
		{
			try
			{
				if ((Object)(object)item == (Object)null || item.UIData == null || (Object)(object)((Component)item).gameObject == (Object)null)
				{
					return false;
				}
				if ((((Object)((Component)item).gameObject).name ?? "").IndexOf("BaseConstructable", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
				if (!HasResourcePrefab(item))
				{
					return false;
				}
				if (!item.UIData.canPocket && !item.UIData.canBackpack)
				{
					return false;
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool CanPocketItem(int itemIndex)
		{
			try
			{
				return itemIndex >= 0 && itemIndex < Items.Count && (Object)(object)Items[itemIndex] != (Object)null && Items[itemIndex].UIData != null && Items[itemIndex].UIData.canPocket && HasResourcePrefab(Items[itemIndex]);
			}
			catch
			{
				return false;
			}
		}

		private static bool CanBackpackItem(int itemIndex)
		{
			try
			{
				return itemIndex >= 0 && itemIndex < Items.Count && (Object)(object)Items[itemIndex] != (Object)null && Items[itemIndex].UIData != null && Items[itemIndex].UIData.canBackpack && HasResourcePrefab(Items[itemIndex]);
			}
			catch
			{
				return false;
			}
		}

		private static bool CanBackSlotItem(int itemIndex)
		{
			try
			{
				return itemIndex >= 0 && itemIndex < Items.Count && Items[itemIndex] is Backpack && HasResourcePrefab(Items[itemIndex]);
			}
			catch
			{
				return false;
			}
		}

		private static bool IsScoutAmuletItem(Item item)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)item == (Object)null)
				{
					return false;
				}
				if ((item.itemTags & 0x200) != 0)
				{
					return true;
				}
				string text = (item.GetName() + " " + ((Object)item).name + " " + (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : "")).ToLowerInvariant();
				return text.IndexOf("amulet", StringComparison.Ordinal) >= 0 || text.IndexOf("medallion", StringComparison.Ordinal) >= 0 || text.IndexOf("talisman", StringComparison.Ordinal) >= 0 || text.IndexOf("charm", StringComparison.Ordinal) >= 0 || text.IndexOf("амулет", StringComparison.Ordinal) >= 0 || text.IndexOf("медальон", StringComparison.Ordinal) >= 0 || text.IndexOf("талисман", StringComparison.Ordinal) >= 0;
			}
			catch
			{
				return false;
			}
		}

		private static bool CanTempSlotItem(int itemIndex)
		{
			try
			{
				return itemIndex >= 0 && itemIndex < Items.Count && (Object)(object)Items[itemIndex] != (Object)null && (CanPocketItem(itemIndex) || IsScoutAmuletItem(Items[itemIndex])) && HasResourcePrefab(Items[itemIndex]);
			}
			catch
			{
				return false;
			}
		}

		private static bool HasResourcePrefab(Item item)
		{
			try
			{
				if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null)
				{
					return false;
				}
				return HasResourcePrefabName(((Object)((Component)item).gameObject).name);
			}
			catch
			{
				return false;
			}
		}

		private static bool HasResourcePrefabName(string prefabName)
		{
			try
			{
				if (string.IsNullOrWhiteSpace(prefabName))
				{
					return false;
				}
				return Resources.Load("0_Items/" + prefabName, typeof(GameObject)) != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		private static int RandomItemIndexForSlot(bool backpack)
		{
			EnsureItemsLoaded();
			List<int> list = new List<int>();
			for (int i = 0; i < Items.Count; i++)
			{
				if (backpack ? CanBackpackItem(i) : CanPocketItem(i))
				{
					list.Add(i);
				}
			}
			if (list.Count == 0)
			{
				return -1;
			}
			return list[Random.Range(0, list.Count)];
		}

		public static Texture2D ItemIcon(int index)
		{
			try
			{
				Item val = ((index >= 0 && index < Items.Count) ? Items[index] : null);
				if ((Object)(object)val == (Object)null || val.UIData == null)
				{
					return null;
				}
				Texture2D icon = val.UIData.icon;
				if ((Object)(object)icon != (Object)null)
				{
					return icon;
				}
				return TryTextureMember(val.UIData, "altIcon", "colorBlindIcon", "sprite", "smallIcon", "inventoryIcon");
			}
			catch
			{
				return null;
			}
		}

		public static ushort ItemIdAt(int index)
		{
			try
			{
				return (index >= 0 && index < Items.Count && (Object)(object)Items[index] != (Object)null) ? Items[index].itemID : ushort.MaxValue;
			}
			catch
			{
				return ushort.MaxValue;
			}
		}

		private static Texture2D TryTextureMember(object source, params string[] names)
		{
			if (source == null || names == null)
			{
				return null;
			}
			Type type = source.GetType();
			foreach (string name in names)
			{
				try
				{
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						object value = field.GetValue(source);
						Texture2D val = (Texture2D)((value is Texture2D) ? value : null);
						if (val != null)
						{
							return val;
						}
						Sprite val2 = (Sprite)((value is Sprite) ? value : null);
						if (val2 != null && (Object)(object)val2.texture != (Object)null)
						{
							return val2.texture;
						}
					}
				}
				catch
				{
				}
				try
				{
					PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						object value2 = property.GetValue(source, null);
						Texture2D val3 = (Texture2D)((value2 is Texture2D) ? value2 : null);
						if (val3 != null)
						{
							return val3;
						}
						Sprite val4 = (Sprite)((value2 is Sprite) ? value2 : null);
						if (val4 != null && (Object)(object)val4.texture != (Object)null)
						{
							return val4.texture;
						}
					}
				}
				catch
				{
				}
			}
			return null;
		}

		public static bool SpawnToSlot(int itemIndex, int slot)
		{
			try
			{
				Player localPlayer = Player.localPlayer;
				if ((Object)(object)localPlayer == (Object)null || localPlayer.itemSlots == null)
				{
					return false;
				}
				if (slot < 0 || slot >= localPlayer.itemSlots.Length)
				{
					return false;
				}
				if (itemIndex < 0 || itemIndex >= Items.Count)
				{
					return false;
				}
				if (!CanPocketItem(itemIndex))
				{
					return false;
				}
				SetSlotItem(localPlayer.itemSlots[slot], Items[itemIndex]);
				SyncInventory(localPlayer);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SpawnToSlot: " + ex.Message));
				}
				return false;
			}
		}

		public static bool ClearSlot(int slot)
		{
			try
			{
				Player localPlayer = Player.localPlayer;
				if ((Object)(object)localPlayer == (Object)null || localPlayer.itemSlots == null || slot < 0 || slot >= localPlayer.itemSlots.Length)
				{
					return false;
				}
				ClearItemSlot(localPlayer.itemSlots[slot]);
				SyncInventory(localPlayer);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] ClearSlot: " + ex.Message));
				}
				return false;
			}
		}

		private static void SyncInventory(Player p)
		{
			SyncInventory(p, (RpcTarget)1);
		}

		private static void SetSlotItem(ItemSlot slot, Item prefab)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (slot != null && !((Object)(object)prefab == (Object)null))
			{
				ItemInstanceData val = new ItemInstanceData(Guid.NewGuid());
				BackpackSlot val2 = (BackpackSlot)(object)((slot is BackpackSlot) ? slot : null);
				if (val2 != null)
				{
					Backpack val3 = (Backpack)(object)((prefab is Backpack) ? prefab : null);
					val2.backpackType = (BackpackType)((val3 != null) ? ((int)val3.backpackType) : 0);
				}
				slot.SetItem(prefab, val);
				ItemInstanceDataHandler.AddInstanceData(val);
			}
		}

		private static bool BackpackSlotSupportsStorage(BackpackSlot slot)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			try
			{
				if (slot == null || ((ItemSlot)slot).IsEmpty())
				{
					return false;
				}
				Item obj = SlotPrefab((ItemSlot)(object)slot);
				Backpack val = (Backpack)(object)((obj is Backpack) ? obj : null);
				if (val == null)
				{
					return false;
				}
				if ((int)slot.backpackType == 3 || (int)slot.backpackType == 4)
				{
					return false;
				}
				return val.slotCount > 0;
			}
			catch
			{
				return false;
			}
		}

		private static void ClearItemSlot(ItemSlot slot)
		{
			if (slot != null)
			{
				slot.EmptyOut();
			}
		}

		private static void SyncInventory(Player p, RpcTarget target)
		{
			//IL_0012: 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)
			byte[] array = IBinarySerializable.ToManagedArray<InventorySyncData>(new InventorySyncData(p.itemSlots, p.backpackSlot, p.tempFullSlot));
			((MonoBehaviourPun)p).photonView.RPC("SyncInventoryRPC", target, new object[2] { array, true });
		}

		private static Player PlayerOf(Character c)
		{
			try
			{
				return ((Object)(object)c != (Object)null) ? c.player : null;
			}
			catch
			{
				return null;
			}
		}

		private static bool IsLocal(Character c)
		{
			try
			{
				return (Object)(object)c == (Object)null || c.IsLocal || (Object)(object)c == (Object)(object)Character.localCharacter;
			}
			catch
			{
				return (Object)(object)c == (Object)null || (Object)(object)c == (Object)(object)Character.localCharacter;
			}
		}

		public static bool IsHost()
		{
			try
			{
				return PhotonNetwork.IsMasterClient;
			}
			catch
			{
				return PhotonNetwork.IsMasterClient;
			}
		}

		private static bool RequireHostForRemote(Character target, string action)
		{
			return true;
		}

		private static bool RequireHostAction(string action)
		{
			return true;
		}

		private static void LogHostOnly(string action, Character target)
		{
			string text = (((Object)(object)target != (Object)null) ? SafeCharacterName(target) : "");
			string text2 = (string.IsNullOrWhiteSpace(text) ? (action + ": host only") : (action + ": host only for " + text));
			AddAdminLog(text2);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("[GameApi] " + text2));
			}
		}

		private static CharacterCustomization LocalCustomization()
		{
			try
			{
				return (!((Object)(object)Character.localCharacter != (Object)null)) ? null : Character.localCharacter.refs?.customization;
			}
			catch
			{
				return null;
			}
		}

		private static Customization CustomizationDb()
		{
			try
			{
				if ((Object)(object)_customizationDb != (Object)null && HasAnyCosmeticOptions(_customizationDb))
				{
					return _customizationDb;
				}
				try
				{
					Customization instance = Singleton<Customization>.Instance;
					if ((Object)(object)instance != (Object)null && HasAnyCosmeticOptions(instance))
					{
						_customizationDb = instance;
						return _customizationDb;
					}
				}
				catch
				{
				}
				Customization[] source = Resources.FindObjectsOfTypeAll<Customization>();
				_customizationDb = source.Where(HasAnyCosmeticOptions).OrderByDescending(TotalCosmeticOptionCount).FirstOrDefault() ?? source.FirstOrDefault();
				return _customizationDb;
			}
			catch
			{
				return null;
			}
		}

		private static bool HasAnyCosmeticOptions(Customization db)
		{
			return TotalCosmeticOptionCount(db) > 0;
		}

		private static int TotalCosmeticOptionCount(Customization db)
		{
			if ((Object)(object)db == (Object)null)
			{
				return 0;
			}
			int num = 0;
			try
			{
				int num2 = num;
				CustomizationOption[] skins = db.skins;
				num = num2 + ((skins != null) ? skins.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num3 = num;
				CustomizationOption[] accessories = db.accessories;
				num = num3 + ((accessories != null) ? accessories.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num4 = num;
				CustomizationOption[] eyes = db.eyes;
				num = num4 + ((eyes != null) ? eyes.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num5 = num;
				CustomizationOption[] mouths = db.mouths;
				num = num5 + ((mouths != null) ? mouths.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num6 = num;
				CustomizationOption[] fits = db.fits;
				num = num6 + ((fits != null) ? fits.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num7 = num;
				CustomizationOption[] hats = db.hats;
				num = num7 + ((hats != null) ? hats.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num8 = num;
				CustomizationOption[] sashes = db.sashes;
				num = num8 + ((sashes != null) ? sashes.Length : 0);
			}
			catch
			{
			}
			try
			{
				int num9 = num;
				CustomizationOption[] medals = db.medals;
				num = num9 + ((medals != null) ? medals.Length : 0);
			}
			catch
			{
			}
			return num;
		}

		public static bool SpawnToSlotFor(Character target, int itemIndex, int slot)
		{
			try
			{
				if (IsLocal(target))
				{
					return SpawnToSlot(itemIndex, slot);
				}
				if (!RequireHostForRemote(target, "inventory spawn"))
				{
					return false;
				}
				Player val = PlayerOf(target);
				if ((Object)(object)val == (Object)null || val.itemSlots == null)
				{
					return false;
				}
				if (slot < 0 || slot >= val.itemSlots.Length)
				{
					return false;
				}
				if (itemIndex < 0 || itemIndex >= Items.Count)
				{
					return false;
				}
				if (!CanPocketItem(itemIndex))
				{
					return false;
				}
				SetSlotItem(val.itemSlots[slot], Items[itemIndex]);
				SyncInventory(val, (RpcTarget)0);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SpawnToSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static string SmartSpawnItem(Character target, int itemIndex)
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				EnsureItemsLoaded();
				Character val = target ?? Character.localCharacter;
				Player val2 = TargetPlayer(val);
				if ((Object)(object)val2 == (Object)null || itemIndex < 0 || itemIndex >= Items.Count || (Object)(object)Items[itemIndex] == (Object)null)
				{
					return "failed";
				}
				if (!RequireHostForRemote(val, "smart inventory spawn"))
				{
					return "failed";
				}
				if (CanPocketItem(itemIndex) && val2.itemSlots != null)
				{
					for (int i = 0; i < val2.itemSlots.Length; i++)
					{
						if (val2.itemSlots[i] != null && val2.itemSlots[i].IsEmpty() && SpawnToSlotFor(val, itemIndex, i))
						{
							return "slot:" + (i + 1);
						}
					}
				}
				Vector3 val3 = SafePosition(val) + Vector3.up * 0.7f;
				try
				{
					val3 += ((Component)val).transform.forward * 1.1f;
				}
				catch
				{
				}
				if (SpawnWorldItem(itemIndex, val3, Quaternion.identity))
				{
					return "ground";
				}
			}
			catch (Exception e)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SmartSpawnItem: " + RootExceptionMessage(e)));
				}
			}
			return "failed";
		}

		public static bool ClearSlotFor(Character target, int slot)
		{
			try
			{
				if (IsLocal(target))
				{
					return ClearSlot(slot);
				}
				if (!RequireHostForRemote(target, "inventory clear"))
				{
					return false;
				}
				Player val = PlayerOf(target);
				if ((Object)(object)val == (Object)null || val.itemSlots == null || slot < 0 || slot >= val.itemSlots.Length)
				{
					return false;
				}
				ClearItemSlot(val.itemSlots[slot]);
				SyncInventory(val, (RpcTarget)0);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] ClearSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static int SlotCount()
		{
			return SlotCountFor(Player.localPlayer);
		}

		public static int SlotCountFor(Character c)
		{
			return SlotCountFor(PlayerOf(c));
		}

		private static int SlotCountFor(Player p)
		{
			try
			{
				return ((Object)(object)p != (Object)null && p.itemSlots != null) ? p.itemSlots.Length : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static Player TargetPlayer(Character c)
		{
			return PlayerOf(c) ?? Player.localPlayer;
		}

		public static string DescribeSlot(Character c, int slot)
		{
			try
			{
				Player val = TargetPlayer(c);
				if ((Object)(object)val == (Object)null || val.itemSlots == null || slot < 0 || slot >= val.itemSlots.Length)
				{
					return "";
				}
				Item val2 = SlotPrefab(val.itemSlots[slot]);
				if ((Object)(object)val2 == (Object)null)
				{
					return "-";
				}
				string name = val2.GetName();
				return string.IsNullOrWhiteSpace(name) ? ((Object)val2).name : name;
			}
			catch
			{
				return "";
			}
		}

		public static string DescribeBackSlot(Character c)
		{
			try
			{
				Player val = TargetPlayer(c);
				BackpackSlot val2 = (((Object)(object)val != (Object)null) ? val.backpackSlot : null);
				if (val2 == null || ((ItemSlot)val2).IsEmpty())
				{
					return "-";
				}
				Item val3 = SlotPrefab((ItemSlot)(object)val2);
				string text = (((Object)(object)val3 != (Object)null) ? val3.GetName() : "");
				if (string.IsNullOrWhiteSpace(text) && (Object)(object)val3 != (Object)null)
				{
					text = ((Object)val3).name;
				}
				if (string.IsNullOrWhiteSpace(text))
				{
					text = ((ItemSlot)val2).GetPrefabName();
				}
				return text;
			}
			catch
			{
				return "";
			}
		}

		public static string DescribeTempSlot(Character c)
		{
			try
			{
				Player val = TargetPlayer(c);
				ItemSlot val2 = (((Object)(object)val != (Object)null) ? val.tempFullSlot : null);
				if (val2 == null || val2.IsEmpty())
				{
					return "-";
				}
				Item val3 = SlotPrefab(val2);
				if ((Object)(object)val3 == (Object)null)
				{
					return "-";
				}
				string name = val3.GetName();
				return string.IsNullOrWhiteSpace(name) ? ((Object)val3).name : name;
			}
			catch
			{
				return "";
			}
		}

		public static bool SpawnToBackSlotFor(Character target, int itemIndex)
		{
			try
			{
				if (!RequireHostForRemote(target, "back slot spawn"))
				{
					return false;
				}
				if (!CanBackSlotItem(itemIndex))
				{
					return false;
				}
				Player val = TargetPlayer(target);
				if ((Object)(object)val == (Object)null || val.backpackSlot == null)
				{
					return false;
				}
				SetSlotItem((ItemSlot)(object)val.backpackSlot, Items[itemIndex]);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SpawnToBackSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static bool ClearBackSlotFor(Character target)
		{
			try
			{
				if (!RequireHostForRemote(target, "back slot clear"))
				{
					return false;
				}
				Player val = TargetPlayer(target);
				if ((Object)(object)val == (Object)null || val.backpackSlot == null)
				{
					return false;
				}
				ClearItemSlot((ItemSlot)(object)val.backpackSlot);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] ClearBackSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static void RechargeBackSlotFor(Character target, float value)
		{
			try
			{
				if (RequireHostForRemote(target, "back slot recharge"))
				{
					Player val = TargetPlayer(target);
					if (!((Object)(object)val == (Object)null) && val.backpackSlot != null && !((ItemSlot)val.backpackSlot).IsEmpty())
					{
						RechargeItemSlotData((ItemSlot)(object)val.backpackSlot, value);
						SyncTargetInventory(target);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] RechargeBackSlotFor: " + ex.Message));
				}
			}
		}

		public static bool SpawnToTempSlotFor(Character target, int itemIndex)
		{
			try
			{
				if (!RequireHostForRemote(target, "temporary slot spawn"))
				{
					return false;
				}
				if (!CanTempSlotItem(itemIndex))
				{
					return false;
				}
				Player val = TargetPlayer(target);
				if ((Object)(object)val == (Object)null || val.tempFullSlot == null)
				{
					return false;
				}
				SetSlotItem(val.tempFullSlot, Items[itemIndex]);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SpawnToTempSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static bool ClearTempSlotFor(Character target)
		{
			try
			{
				if (!RequireHostForRemote(target, "temporary slot clear"))
				{
					return false;
				}
				Player val = TargetPlayer(target);
				if ((Object)(object)val == (Object)null || val.tempFullSlot == null)
				{
					return false;
				}
				ClearItemSlot(val.tempFullSlot);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] ClearTempSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static void RechargeTempSlotFor(Character target, float value)
		{
			try
			{
				if (RequireHostForRemote(target, "temporary slot recharge"))
				{
					Player val = TargetPlayer(target);
					if (!((Object)(object)val == (Object)null) && val.tempFullSlot != null && !val.tempFullSlot.IsEmpty())
					{
						RechargeItemSlotData(val.tempFullSlot, value);
						SyncTargetInventory(target);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] RechargeTempSlotFor: " + ex.Message));
				}
			}
		}

		public static bool HasBackpack(Character c)
		{
			try
			{
				Player val = TargetPlayer(c);
				return (Object)(object)val != (Object)null && val.backpackSlot != null && !((ItemSlot)val.backpackSlot).IsEmpty() && ((ItemSlot)val.backpackSlot).data != null;
			}
			catch
			{
				return false;
			}
		}

		public static int BackpackSlotCount(Character c)
		{
			BackpackData val = BackpackDataFor(c, create: false);
			if (val == null || val.itemSlots == null)
			{
				return 0;
			}
			return val.itemSlots.Length;
		}

		public static string DescribeBackpackSlot(Character c, int slot)
		{
			try
			{
				BackpackData val = BackpackDataFor(c, create: false);
				if (val == null || val.itemSlots == null || slot < 0 || slot >= val.itemSlots.Length)
				{
					return "";
				}
				Item val2 = SlotPrefab(val.itemSlots[slot]);
				if ((Object)(object)val2 == (Object)null)
				{
					return "-";
				}
				string name = val2.GetName();
				return string.IsNullOrWhiteSpace(name) ? ((Object)val2).name : name;
			}
			catch
			{
				return "";
			}
		}

		public static bool SpawnToBackpackSlotFor(Character target, int itemIndex, int slot)
		{
			try
			{
				if (!RequireHostForRemote(target, "backpack spawn"))
				{
					return false;
				}
				if (itemIndex < 0 || itemIndex >= Items.Count)
				{
					return false;
				}
				if (!CanBackpackItem(itemIndex))
				{
					return false;
				}
				BackpackData val = BackpackDataFor(target, create: true);
				if (val == null || val.itemSlots == null || slot < 0 || slot >= val.itemSlots.Length)
				{
					return false;
				}
				SetSlotItem(val.itemSlots[slot], Items[itemIndex]);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] SpawnToBackpackSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static bool ClearBackpackSlotFor(Character target, int slot)
		{
			try
			{
				if (!RequireHostForRemote(target, "backpack clear"))
				{
					return false;
				}
				BackpackData val = BackpackDataFor(target, create: false);
				if (val == null || val.itemSlots == null || slot < 0 || slot >= val.itemSlots.Length)
				{
					return false;
				}
				ClearItemSlot(val.itemSlots[slot]);
				SyncTargetInventory(target);
				return true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] ClearBackpackSlotFor: " + ex.Message));
				}
				return false;
			}
		}

		public static void RechargeBackpackSlotFor(Character target, int slot, float value)
		{
			try
			{
				if (RequireHostForRemote(target, "backpack recharge"))
				{
					BackpackData val = BackpackDataFor(target, create: false);
					if (val != null && val.itemSlots != null && slot >= 0 && slot < val.itemSlots.Length)
					{
						RechargeItemSlotData(val.itemSlots[slot], value);
						SyncTargetInventory(target);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[GameApi] RechargeBackpackSlotFor: " + ex.Message));
				}
			}
		}

		private static BackpackData BackpackDataFor(Character c, bool create)
		{
			try
			{
				Player val = TargetPlayer(c);
				if ((Object)(object)val == (Object)null || val.backpackSlot == null || ((ItemSlot)val.backpackSlot).IsEmpty() || ((ItemSlot)val.backpackSlot).data == null)
				{
					return null;
				}
				if (!BackpackSlotSupportsStorage(val.backpackSlot))
				{
					return null;
				}
				BackpackData result = default(BackpackData);
				if (((ItemSlot)val.backpackSlot).data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref result))
				{
					return result;
				}
				return create ? ((ItemSlot)val.backpackSlot).data.RegisterNewEntry<BackpackData>((DataEntryKey)7) : null;
			}
			catch
			{
				return null;
			}
		}

		private static void SyncTargetInventory(Character c)
		{
			Player val = TargetPlayer(c);
			if (!((Object)(object)val == (Object)null))
			{
				SyncInventory(val, (RpcTarget)(IsLocal(c) ? 1 : 0));
			}
		}

		public static CustomizationOption[] GetCosmeticOptions(CosmeticCategory category)
		{
			try
			{
				Customization val = CustomizationDb();
				if ((Object)(object)val == (Object)null)
				{
					return Array.Empty<CustomizationOption>();
				}
				return category switch
				{
					CosmeticCategory.Skin => val.skins ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Accessory => val.accessories ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Eyes => val.eyes ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Mouth => val.mouths ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Outfit => val.fits ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Hat => val.hats ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Sash => val.sashes ?? Array.Empty<CustomizationOption>(), 
					CosmeticCategory.Medal => val.medals ?? Array.Empty<CustomizationOption>(), 
					_ => Array.Empty<CustomizationOption>(), 
				};
			}
			catch
			{
				return Array.Empty<CustomizationOption>();
			}
		}

		public static int GetCurrentCosmeticIndex(CosmeticCategory category)
		{
			try
			{
				CharacterCustomization val = LocalCustomization();
				if ((Object)(object)val == (Object)null)
				{
					return -1;
				}
				CharacterCustomizationData customizationData = GetCustomizationData(val);
				return category switch
				{
					CosmeticCategory.Skin => customizationData.currentSkin, 
					CosmeticCategory.Accessory => customizationData.currentAccessory, 
					CosmeticCategory.Eyes => customizationData.currentEyes, 
					CosmeticCategory.Mouth => customizationData.currentMouth, 
					CosmeticCategory.Outfit => customizationData.currentOutfit, 
					CosmeticCategory.Hat => customizationData.currentHat, 
					CosmeticCategory.Sash => customizationData.currentSash, 
					CosmeticCategory.Medal => customizationData.currentMedal, 
					_ => -1, 
				};
			}
			catch
			{
				return -1;
			}
		}

		public static int FindBlankCosmeticIndex(CosmeticCategory category)
		{
			CustomizationOption[] cosmeticOptions = GetCosmeticOptions(category);
			for (int i = 0; i < cosmeticOptions.Length; i++)
			{
				try
				{
					if ((Object)(object)cosmeticOptions[i] != (Object)null && cosmeticOptions[i].isBlank)
					{
						return i;
					}
				}
				catch
				{
				}
			}
			return -1;
		}

		public static bool IsCosmeticOptionGrantable(CosmeticCategory category, int index)
		{
			CustomizationOption[] cosmeticOptions = GetCosmeticOptions(category);
			if (index >= 0 && index < cosmeticOptions.Length)
			{
				return IsCosmeticOptionGrantable(category, cosmeticOptions[index]);
			}
			return false;
		}

		public static bool IsCosmeticOptionSelectable(CosmeticCategory category, int index)
		{
			CustomizationOption[] cosmeticOptions = GetCosmeticOptions(category);
			if (index >= 0 && index < cosmeticOptions.Length)
			{
				return IsCosmeticOptionSelectable(category, cosmeticOptions[index]);
			}
			return false;
		}

		public static bool IsCosmeticOptionSelectable(CosmeticCategory category, CustomizationOption option)
		{
			return (Object)(object)option != (Object)null;
		}

		public static bool IsCosmeticOptionGrantable(CosmeticCategory category, CustomizationOption option)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)option == (Object)null)
			{
				return false;
			}
			try
			{
				if (option.isBlank)
				{
					return true;
				}
				if (option.type != ExpectedCosmeticType(category))
				{
					return false;
				}
				if (!HasKnownCosmeticRequirement(option))
				{
					return false;
				}
				if (!HasCosmeticUnlockRequirement(option))
				{
					return false;
				}
				return HasCosmeticVisual(category, option);
			}
			catch
			{
				return false;
			}
		}

		private static Type ExpectedCosmeticType(CosmeticCategory category)
		{
			//IL_0029: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004c: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			return (Type)(category switch
			{
				CosmeticCategory.Skin => 0, 
				CosmeticCategory.Accessory => 10, 
				CosmeticCategory.Eyes => 20, 
				CosmeticCategory.Mouth => 30, 
				CosmeticCategory.Outfit => 40, 
				CosmeticCategory.Hat => 50, 
				CosmeticCategory.Sash => 60, 
				CosmeticCategory.Medal => 70, 
				_ => 0, 
			});
		}

		private static bool HasCosmeticVisual(CosmeticCategory category, CustomizationOption option)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)option == (Object)null)
			{
				return false;
			}
			if ((Object)(object)option.texture != (Object)null)
			{
				return true;
			}
			if ((Object)(object)option.fitMesh != (Object)null || (Object)(object)option.fitMaterial != (Object)null || (Object)(object)option.fitMaterialShoes != (Object)null)
			{
				return true;
			}
			if ((Object)(object)option.fitMaterialOverridePants != (Object)null || (Object)(object)option.fitMaterialOverrideHat != (Object)null)
			{
				return true;
			}
			if (category == CosmeticCategory.Skin)
			{
				Color color = option.color;
				if (!(color.a > 0.01f) && !(color.r > 0.01f) && !(color.g > 0.01f))
				{
					return color.b > 0.01f;
				}
				return true;
			}
			return false;
		}

		private static bool HasKnownCosmeticRequirement(CustomizationOption option)
		{
			string a = ((object)Unsafe.As<CUSTOMREQUIREMENT, CUSTOMREQUIREMENT>(ref option.customRequirement)/*cast due to .constrained prefix*/).ToString();
			if (!string.Equals(a, "None", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "Goat", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(a, "Crown", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool HasCosmeticUnlockRequirement(CustomizationOption option)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)option == (Object)null || option.isBlank)
			{
				return false;
			}
			if (option.requiresAscent)
			{
				return true;
			}
			if (option.requiresSteamStat)
			{
				return true;
			}
			if ((int)option.requiredAchievement != 0)
			{
				return true;
			}
			string a = ((object)Unsafe.As<CUSTOMREQUIREMENT, CUSTOMREQUIREMENT>(ref option.customRequirement)/*cast due to .constrained prefix*/).ToString();
			if (!string.Equals(a, "Goat", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(a, "Crown", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static bool SetCosmetic(CosmeticCategory category, int index)
		{
			try
			{
				CharacterCustomization val = LocalCustomization();
				CustomizationOption[] cosmeticOptions = GetCosmeticOptions(category);
				if ((Object)(object)val == (Object)null || index < 0 || index >= cosmeticOptions.Length)
				{
					return false;
				}
				if (!IsCosmeticOptionSelectable(category, cosmeticOptions[index]))
				{
					return false;
				}
				CharacterCustomizationData customizationData = GetCustomizationData(val);
				switch (category)
				{
				case CosmeticCategory.Skin:
					customizationData.currentSkin = index;
					InvokeCustomizationSetter(val, CustomSetSkin, index);
					break;
				case CosmeticCategory.Accessory:
					customizationData.currentAccessory = index;
					InvokeCustomizationSetter(val, CustomSetAccessory, index);
					break;
				case CosmeticCategory.