Decompiled source of AdminHelper v1.0.0

BepInEx/plugins/AdminHelper/AdminHelper.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HoldfastGame;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 AdminHelper
{
	[BepInPlugin("com.ryannlt.adminhelper", "AdminHelper", "1.0.0")]
	public class AdminHelperMod : BaseUnityPlugin
	{
		public const string Guid = "com.ryannlt.adminhelper";

		private readonly IsolationTracker _tracker = new IsolationTracker();

		private readonly RingRenderer _rings = new RingRenderer();

		private readonly Hotkey _hotkey = new Hotkey();

		private readonly Hud _hud = new Hud();

		private Driver _driver;

		private float _accumulator;

		private bool _wasInRound;

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Settings.Create(((BaseUnityPlugin)this).Config);
			_hotkey.ResetToDefault();
			SceneManager.sceneLoaded += OnSceneLoaded;
			EnsureDriver();
			Log.Info("Ready. Toggle key " + ((object)Settings.ResolveToggleKey()/*cast due to .constrained prefix*/).ToString() + ", RequireAdminLogin=" + Settings.RequireAdminLogin.Value);
		}

		private void OnDestroy()
		{
			Log.Info("plugin component destroyed");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			Log.Info("scene loaded " + ((Scene)(ref scene)).name + " driverAlive=" + ((Object)(object)_driver != (Object)null));
			EnsureDriver();
			GameAccess.ClearSceneCache();
			_tracker.Reset();
			_rings.Destroy();
			_accumulator = 0f;
			_wasInRound = false;
		}

		private void EnsureDriver()
		{
			if (!((Object)(object)_driver != (Object)null))
			{
				_driver = Driver.Attach(this);
			}
		}

		internal void Tick()
		{
			Settings.PollForExternalEdits();
			if (!Settings.Enabled.Value)
			{
				_rings.HideAll();
				return;
			}
			_hotkey.Poll();
			if (!GameAccess.InRound)
			{
				if (_wasInRound)
				{
					_tracker.Reset();
				}
				_wasInRound = false;
				_rings.HideAll();
				return;
			}
			_wasInRound = true;
			_accumulator += Time.deltaTime;
			float num = 1f / Mathf.Clamp(Settings.TickHz.Value, 1f, 30f);
			if (_accumulator >= num)
			{
				_tracker.Tick(_accumulator);
				_accumulator = 0f;
			}
			if (CanReveal() && _hotkey.Visible && Settings.ShowRings.Value)
			{
				_rings.Draw(_tracker.Watched);
			}
			else
			{
				_rings.HideAll();
			}
		}

		internal void DrawGui()
		{
			if (Settings.Enabled.Value && _hotkey.Visible && GameAccess.InRound)
			{
				_hud.Draw(_tracker, CanReveal());
			}
		}

		private static bool CanReveal()
		{
			return !Settings.RequireAdminLogin.Value || GameAccess.IsLoggedInAdmin;
		}
	}
	internal sealed class Driver : MonoBehaviour
	{
		internal AdminHelperMod Owner;

		internal static Driver Attach(AdminHelperMod owner)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			GameObject val = new GameObject("AdminHelper_Driver");
			Object.DontDestroyOnLoad((Object)(object)val);
			Driver driver = val.AddComponent<Driver>();
			driver.Owner = owner;
			return driver;
		}

		private void Awake()
		{
			Log.Info("driver awake");
		}

		private void OnDestroy()
		{
			Log.Info("driver destroyed");
		}

		private void Update()
		{
			if (Owner != null)
			{
				Owner.Tick();
			}
		}

		private void OnGUI()
		{
			if (Owner != null)
			{
				Owner.DrawGui();
			}
		}
	}
	internal static class FormationDetector
	{
		private const int MaxScratch = 16;

		private static readonly float[] ScratchX = new float[16];

		private static readonly float[] ScratchZ = new float[16];

		private static readonly float[] ScratchDistanceSquared = new float[16];

		public static bool IsInFormation(PlayerSnapshot self, List<PlayerSnapshot> friendlies)
		{
			if (GameAccess.IsInsideOfficerLine(self.Player))
			{
				return true;
			}
			int num = CollectNearest(self, friendlies, Settings.LineRadius.Value, Settings.LineMaxMates.Value);
			if (CountWithin(num, Settings.ClusterFormationRadius.Value) >= Settings.ClusterMinMates.Value)
			{
				return true;
			}
			if (num < Settings.LineMinMates.Value)
			{
				return false;
			}
			return PerpendicularSpread(self, num) <= Settings.LineResidual.Value;
		}

		private static int CollectNearest(PlayerSnapshot self, List<PlayerSnapshot> friendlies, float radius, int max)
		{
			//IL_0041: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			if (max > 16)
			{
				max = 16;
			}
			float num = radius * radius;
			int num2 = 0;
			for (int i = 0; i < friendlies.Count; i++)
			{
				PlayerSnapshot playerSnapshot = friendlies[i];
				if (playerSnapshot.PlayerId == self.PlayerId)
				{
					continue;
				}
				float num3 = playerSnapshot.Position.x - self.Position.x;
				float num4 = playerSnapshot.Position.z - self.Position.z;
				float num5 = num3 * num3 + num4 * num4;
				if (!(num5 > num) && (num2 != max || !(num5 >= ScratchDistanceSquared[num2 - 1])))
				{
					int num6 = ((num2 < max) ? num2 : (num2 - 1));
					while (num6 > 0 && ScratchDistanceSquared[num6 - 1] > num5)
					{
						ScratchDistanceSquared[num6] = ScratchDistanceSquared[num6 - 1];
						ScratchX[num6] = ScratchX[num6 - 1];
						ScratchZ[num6] = ScratchZ[num6 - 1];
						num6--;
					}
					ScratchDistanceSquared[num6] = num5;
					ScratchX[num6] = playerSnapshot.Position.x;
					ScratchZ[num6] = playerSnapshot.Position.z;
					if (num2 < max)
					{
						num2++;
					}
				}
			}
			return num2;
		}

		private static int CountWithin(int count, float radius)
		{
			float num = radius * radius;
			for (int i = 0; i < count; i++)
			{
				if (ScratchDistanceSquared[i] > num)
				{
					return i;
				}
			}
			return count;
		}

		private static float PerpendicularSpread(PlayerSnapshot self, int count)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			int num = count + 1;
			float num2 = self.Position.x;
			float num3 = self.Position.z;
			for (int i = 0; i < count; i++)
			{
				num2 += ScratchX[i];
				num3 += ScratchZ[i];
			}
			float num4 = num2 / (float)num;
			float num5 = num3 / (float)num;
			float xx = 0f;
			float xz = 0f;
			float zz = 0f;
			AccumulateCovariance(self.Position.x - num4, self.Position.z - num5, ref xx, ref xz, ref zz);
			for (int j = 0; j < count; j++)
			{
				AccumulateCovariance(ScratchX[j] - num4, ScratchZ[j] - num5, ref xx, ref xz, ref zz);
			}
			float num6 = xx + zz;
			float num7 = xx - zz;
			float num8 = Mathf.Sqrt(num7 * num7 + 4f * xz * xz);
			float num9 = Mathf.Max(0f, (num6 - num8) * 0.5f);
			return Mathf.Sqrt(num9 / (float)num);
		}

		private static void AccumulateCovariance(float dx, float dz, ref float xx, ref float xz, ref float zz)
		{
			xx += dx * dx;
			xz += dx * dz;
			zz += dz * dz;
		}
	}
	internal static class GameAccess
	{
		private static GameConsolePanel _consolePanel;

		private static float _nextConsoleLookup;

		public static ClientComponentReferenceManager Client => ClientComponentReferenceManager.ClientInstance;

		public static bool IsLoggedInAdmin => ClientRemoteConsoleAccessManager.loggedOn;

		public static bool InRound
		{
			get
			{
				ClientComponentReferenceManager client = Client;
				return (Object)(object)client != (Object)null && (Object)(object)client.clientRoundPlayerManager != (Object)null;
			}
		}

		public static Camera ActiveCamera
		{
			get
			{
				ClientComponentReferenceManager client = Client;
				if ((Object)(object)client == (Object)null || (Object)(object)client.ownerCameraManager == (Object)null)
				{
					return null;
				}
				Camera val = client.ownerCameraManager.ActiveCamera;
				if ((Object)(object)val == (Object)null)
				{
					val = client.ownerCameraManager.ownerCamera;
				}
				return val;
			}
		}

		public static int LocalPlayerId
		{
			get
			{
				ClientComponentReferenceManager client = Client;
				if ((Object)(object)client == (Object)null || (Object)(object)client.clientRoundPlayerManager == (Object)null)
				{
					return -1;
				}
				return ((RoundPlayer)(client.clientRoundPlayerManager.LocalPlayer?)).NetworkPlayerID ?? (-1);
			}
		}

		public static bool IsTyping
		{
			get
			{
				ClientComponentReferenceManager client = Client;
				if ((Object)(object)client != (Object)null && (Object)(object)client.clientChatHandler != (Object)null && client.clientChatHandler.isChatPaneOpened)
				{
					return true;
				}
				if ((Object)(object)_consolePanel == (Object)null && Time.unscaledTime >= _nextConsoleLookup)
				{
					_nextConsoleLookup = Time.unscaledTime + 2f;
					_consolePanel = Object.FindObjectOfType<GameConsolePanel>();
				}
				return (Object)(object)_consolePanel != (Object)null && _consolePanel.Showing;
			}
		}

		public static void ClearSceneCache()
		{
			_consolePanel = null;
			_nextConsoleLookup = 0f;
		}

		public static void CollectPlayers(List<PlayerSnapshot> into)
		{
			into.Clear();
			ClientComponentReferenceManager client = Client;
			if ((Object)(object)client == (Object)null)
			{
				return;
			}
			ClientRoundPlayerManager clientRoundPlayerManager = client.clientRoundPlayerManager;
			if ((Object)(object)clientRoundPlayerManager == (Object)null)
			{
				return;
			}
			List<ClientRoundPlayerProxy> roundPlayersList = clientRoundPlayerManager.roundPlayersList;
			for (int i = 0; i < roundPlayersList.Count; i++)
			{
				if (TrySnapshot((RoundPlayer)(object)roundPlayersList[i], out var snapshot))
				{
					into.Add(snapshot);
				}
			}
			if (TrySnapshot((RoundPlayer)(object)clientRoundPlayerManager.LocalPlayer, out var snapshot2))
			{
				into.Add(snapshot2);
			}
		}

		private static bool TrySnapshot(RoundPlayer player, out PlayerSnapshot snapshot)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			snapshot = default(PlayerSnapshot);
			if (player == null || (Object)(object)player.PlayerBase == (Object)null || !player.PlayerBase.SpawnedAndAlive)
			{
				return false;
			}
			if (player.PlayerTransformData == null || player.PlayerStartData == null)
			{
				return false;
			}
			PlayerBase playerBase = player.PlayerBase;
			snapshot.Player = player;
			snapshot.PlayerId = player.NetworkPlayerID;
			snapshot.Name = ResolveName(player);
			snapshot.Position = player.PlayerTransformData.position;
			snapshot.Faction = player.PlayerStartData.Faction;
			snapshot.Class = player.PlayerStartData.ClassType;
			snapshot.IsCavalry = playerBase.IsCavalry;
			snapshot.IsArtillery = playerBase.IsArty;
			return true;
		}

		private static string ResolveName(RoundPlayer player)
		{
			RoundPlayerInformation playerRoundInformation = player.PlayerRoundInformation;
			if (playerRoundInformation == null || playerRoundInformation.InitialDetails == null)
			{
				return "Player " + player.NetworkPlayerID;
			}
			PlayerInitialDetails initialDetails = playerRoundInformation.InitialDetails;
			string text = (string.IsNullOrEmpty(initialDetails.DisplayName) ? initialDetails.Name : initialDetails.DisplayName);
			if (string.IsNullOrEmpty(text))
			{
				return "Player " + player.NetworkPlayerID;
			}
			return text;
		}

		public static bool IsInsideOfficerLine(RoundPlayer player)
		{
			ClientComponentReferenceManager client = Client;
			if ((Object)(object)client == (Object)null || (Object)(object)client.clientHighCommandOrderManager == (Object)null)
			{
				return false;
			}
			return ((HighCommandOrderManager)client.clientHighCommandOrderManager).IsPlayerInsideAnyOfficerLine(player);
		}
	}
	internal sealed class Hotkey
	{
		private bool _visible;

		public bool Visible => _visible;

		public void ResetToDefault()
		{
			_visible = Settings.StartHudVisible.Value;
		}

		public void Poll()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (!GameAccess.IsTyping && Input.GetKeyDown(Settings.ResolveToggleKey()))
			{
				_visible = !_visible;
			}
		}
	}
	internal sealed class Hud
	{
		private static readonly Color WatchColour = new Color(1f, 0.82f, 0.15f);

		private static readonly Color FlagColour = new Color(1f, 0.35f, 0.25f);

		private static readonly Color InfoColour = new Color(0.85f, 0.88f, 0.92f);

		private static readonly Color PanelColour = new Color(0.05f, 0.05f, 0.06f, 0.78f);

		private readonly List<ScoredPlayer> _sorted = new List<ScoredPlayer>();

		private GUIStyle _labelStyle;

		private GUIStyle _listStyle;

		private Texture2D _panelTexture;

		public void Draw(IsolationTracker tracker, bool revealOthers)
		{
			EnsureStyles();
			_sorted.Clear();
			if (revealOthers)
			{
				SortByHeat(tracker.Watched);
				if (Settings.ShowLabels.Value)
				{
					DrawLabels();
				}
			}
			if (Settings.ShowCornerList.Value)
			{
				DrawCornerList(revealOthers);
			}
			if (Settings.ShowOwnScore.Value && tracker.HasLocalScore)
			{
				DrawOwnScore(tracker.LocalScore);
			}
		}

		private void DrawLabels()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: 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_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			Camera activeCamera = GameAccess.ActiveCamera;
			if ((Object)(object)activeCamera == (Object)null)
			{
				return;
			}
			int num = Mathf.Min(_sorted.Count, Mathf.Max(0, Settings.MaxLabels.Value));
			Rect val4 = default(Rect);
			for (int i = 0; i < num; i++)
			{
				ScoredPlayer scoredPlayer = _sorted[i];
				Vector3 val = activeCamera.WorldToScreenPoint(scoredPlayer.Position + Vector3.up * 2.1f);
				if (!(val.z <= 0f))
				{
					string text = (scoredPlayer.Flagged ? ("RAMBO: " + Mathf.FloorToInt(scoredPlayer.DwellSeconds) + "s") : "ISOLATED");
					string text2 = scoredPlayer.Name + "\n" + text + "\nISO: " + scoredPlayer.Isolation + "  DGR: " + scoredPlayer.Danger;
					GUIContent val2 = new GUIContent(text2);
					Vector2 val3 = _labelStyle.CalcSize(val2);
					val3.y = _labelStyle.CalcHeight(val2, val3.x);
					((Rect)(ref val4))..ctor(val.x - val3.x * 0.5f, (float)Screen.height - val.y - val3.y, val3.x, val3.y);
					GUI.DrawTexture(val4, (Texture)(object)_panelTexture);
					_labelStyle.normal.textColor = (scoredPlayer.Flagged ? FlagColour : WatchColour);
					GUI.Label(val4, text2, _labelStyle);
				}
			}
		}

		private void DrawCornerList(bool revealOthers)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			Camera activeCamera = GameAccess.ActiveCamera;
			Vector3 val = (((Object)(object)activeCamera != (Object)null) ? ((Component)activeCamera).transform.position : Vector3.zero);
			string text = (revealOthers ? ("AdminHelper  " + _sorted.Count + " watched, " + CountFlagged() + " flagged") : "AdminHelper  admin login required");
			float num = 260f;
			float num2 = 18f;
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(12f, 12f, num, num2 * (float)(_sorted.Count + 1) + 10f);
			GUI.DrawTexture(val2, (Texture)(object)_panelTexture);
			_listStyle.normal.textColor = InfoColour;
			GUI.Label(new Rect(((Rect)(ref val2)).x + 6f, ((Rect)(ref val2)).y + 5f, num - 12f, num2), text, _listStyle);
			Rect val3 = default(Rect);
			for (int i = 0; i < _sorted.Count; i++)
			{
				ScoredPlayer scoredPlayer = _sorted[i];
				float num3 = Horizontal(scoredPlayer.Position - val);
				((Rect)(ref val3))..ctor(((Rect)(ref val2)).x + 6f, ((Rect)(ref val2)).y + 5f + num2 * (float)(i + 1), num - 12f, num2);
				_listStyle.normal.textColor = (scoredPlayer.Flagged ? FlagColour : WatchColour);
				GUI.Label(val3, scoredPlayer.Isolation + "/" + scoredPlayer.Danger + "  " + Mathf.RoundToInt(num3) + "m  " + scoredPlayer.Name, _listStyle);
			}
		}

		private void DrawOwnScore(ScoredPlayer local)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			string text = "ISO: " + local.Isolation + "   DGR: " + local.Danger + "\nmates " + Metres(local.MateDistance) + "   enemy " + Metres(local.EnemyDistance) + " x" + local.EnemyCount + (local.InFormation ? "\nin formation" : "\nout of formation") + "   dwell " + local.DwellSeconds.ToString("0.0") + "s";
			Rect val = default(Rect);
			((Rect)(ref val))..ctor((float)Screen.width - 232f, (float)Screen.height - 76f, 220f, 64f);
			GUI.DrawTexture(val, (Texture)(object)_panelTexture);
			_labelStyle.normal.textColor = (local.Flagged ? FlagColour : InfoColour);
			GUI.Label(val, text, _labelStyle);
		}

		private static string Metres(float distance)
		{
			if (distance >= 9000f)
			{
				return "none";
			}
			return distance.ToString("0.0") + "m";
		}

		private void SortByHeat(List<ScoredPlayer> watched)
		{
			_sorted.Clear();
			_sorted.AddRange(watched);
		}

		private int CountFlagged()
		{
			int num = 0;
			for (int i = 0; i < _sorted.Count; i++)
			{
				if (_sorted[i].Flagged)
				{
					num++;
				}
			}
			return num;
		}

		private void EnsureStyles()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panelTexture == (Object)null)
			{
				_panelTexture = new Texture2D(1, 1);
				_panelTexture.SetPixel(0, 0, PanelColour);
				_panelTexture.Apply();
				((Object)_panelTexture).hideFlags = (HideFlags)61;
			}
			if (_labelStyle == null)
			{
				_labelStyle = new GUIStyle(GUI.skin.label);
				_labelStyle.alignment = (TextAnchor)4;
				_labelStyle.fontSize = 13;
				_labelStyle.fontStyle = (FontStyle)1;
				_labelStyle.padding = new RectOffset(6, 6, 3, 3);
				_labelStyle.normal.textColor = FlagColour;
			}
			if (_listStyle == null)
			{
				_listStyle = new GUIStyle(GUI.skin.label);
				_listStyle.alignment = (TextAnchor)3;
				_listStyle.fontSize = 12;
				_listStyle.padding = new RectOffset(0, 0, 0, 0);
				_listStyle.normal.textColor = FlagColour;
			}
		}

		private static float Horizontal(Vector3 delta)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Sqrt(delta.x * delta.x + delta.z * delta.z);
		}
	}
	internal sealed class IsolationTracker
	{
		private sealed class State
		{
			public float Isolation;

			public float Dwell;

			public int LastSeenTick;
		}

		private const float SoloEnemyFloor = 0.5f;

		private const int MaxWatched = 32;

		private readonly Dictionary<int, State> _states = new Dictionary<int, State>();

		private readonly List<PlayerSnapshot> _players = new List<PlayerSnapshot>();

		private readonly Dictionary<FactionCountry, List<PlayerSnapshot>> _byFaction = new Dictionary<FactionCountry, List<PlayerSnapshot>>();

		private readonly List<int> _stale = new List<int>();

		private int _tick;

		public readonly List<ScoredPlayer> Watched = new List<ScoredPlayer>();

		public ScoredPlayer LocalScore;

		public bool HasLocalScore;

		public void Reset()
		{
			_states.Clear();
			_byFaction.Clear();
			Watched.Clear();
			HasLocalScore = false;
		}

		public void Tick(float dt)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			_tick++;
			Watched.Clear();
			HasLocalScore = false;
			GameAccess.CollectPlayers(_players);
			if (_players.Count == 0)
			{
				DropStaleStates();
				return;
			}
			BucketByFaction();
			int localPlayerId = GameAccess.LocalPlayerId;
			ScoredPlayer scoredPlayer = default(ScoredPlayer);
			for (int i = 0; i < _players.Count; i++)
			{
				PlayerSnapshot self = _players[i];
				List<PlayerSnapshot> list = _byFaction[self.Faction];
				if (list.Count >= 3)
				{
					float num = MateDistance(self, list);
					bool flag = FormationDetector.IsInFormation(self, list);
					float num2 = Curve(num);
					if (flag)
					{
						num2 *= 1f - Mathf.Clamp01(Settings.FormationSuppression.Value);
					}
					State state = ResolveState(self.PlayerId);
					Integrate(state, num2, dt);
					MeasureEnemies(self, out var nearestDistance, out var countInRadius);
					int num3 = Mathf.RoundToInt(state.Isolation * 100f);
					int danger = Mathf.RoundToInt((float)num3 * EnemyThreat(nearestDistance, countInRadius));
					bool flag2 = IsScorable(self);
					if (flag2 && num3 >= Settings.RamboThreshold.Value)
					{
						state.Dwell += dt;
					}
					else
					{
						state.Dwell = Mathf.Max(0f, state.Dwell - dt * Mathf.Max(1f, Settings.RecoverMultiplier.Value));
					}
					scoredPlayer.PlayerId = self.PlayerId;
					scoredPlayer.Name = self.Name;
					scoredPlayer.Position = self.Position;
					scoredPlayer.Isolation = num3;
					scoredPlayer.Danger = danger;
					scoredPlayer.DwellSeconds = state.Dwell;
					scoredPlayer.Flagged = flag2 && state.Dwell >= Settings.RamboHoldSeconds.Value;
					scoredPlayer.MateDistance = num;
					scoredPlayer.EnemyDistance = nearestDistance;
					scoredPlayer.EnemyCount = countInRadius;
					scoredPlayer.InFormation = flag;
					if (flag2 && num3 >= Settings.RingThreshold.Value)
					{
						Watched.Add(scoredPlayer);
					}
					if (self.PlayerId == localPlayerId)
					{
						LocalScore = scoredPlayer;
						HasLocalScore = true;
					}
				}
			}
			SortAndCapWatched();
			DropStaleStates();
		}

		private void SortAndCapWatched()
		{
			Watched.Sort((ScoredPlayer a, ScoredPlayer b) => b.Isolation.CompareTo(a.Isolation));
			if (Watched.Count > 32)
			{
				Watched.RemoveRange(32, Watched.Count - 32);
			}
		}

		private bool IsScorable(PlayerSnapshot self)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (self.IsCavalry && !Settings.ScoreCavalry.Value)
			{
				return false;
			}
			if (self.IsArtillery)
			{
				return false;
			}
			return !Settings.IsExempt(self.Class);
		}

		private static float Curve(float distance)
		{
			float value = Settings.ClusterNearMetres.Value;
			float num = Mathf.Max(value + 1f, Settings.ClusterFarMetres.Value);
			return Mathf.Clamp01((distance - value) / (num - value));
		}

		private float MateDistance(PlayerSnapshot self, List<PlayerSnapshot> friendlies)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			float num2 = float.MaxValue;
			Vector3 val = Vector3.zero;
			Vector3 val2 = Vector3.zero;
			for (int i = 0; i < friendlies.Count; i++)
			{
				PlayerSnapshot playerSnapshot = friendlies[i];
				if (playerSnapshot.PlayerId != self.PlayerId)
				{
					float num3 = playerSnapshot.Position.x - self.Position.x;
					float num4 = playerSnapshot.Position.z - self.Position.z;
					float num5 = num3 * num3 + num4 * num4;
					if (num5 < num)
					{
						num2 = num;
						val2 = val;
						num = num5;
						val = playerSnapshot.Position;
					}
					else if (num5 < num2)
					{
						num2 = num5;
						val2 = playerSnapshot.Position;
					}
				}
			}
			if (num2 == float.MaxValue)
			{
				return float.MaxValue;
			}
			float num6 = (val.x + val2.x) * 0.5f;
			float num7 = (val.z + val2.z) * 0.5f;
			return Horizontal(self.Position.x - num6, self.Position.z - num7);
		}

		private void MeasureEnemies(PlayerSnapshot self, out float nearestDistance, out int countInRadius)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			float value = Settings.EnemyRadius.Value;
			float num = value * value;
			float num2 = float.MaxValue;
			countInRadius = 0;
			for (int i = 0; i < _players.Count; i++)
			{
				PlayerSnapshot playerSnapshot = _players[i];
				if (playerSnapshot.Faction != self.Faction)
				{
					float num3 = playerSnapshot.Position.x - self.Position.x;
					float num4 = playerSnapshot.Position.z - self.Position.z;
					float num5 = num3 * num3 + num4 * num4;
					if (num5 < num2)
					{
						num2 = num5;
					}
					if (num5 <= num)
					{
						countInRadius++;
					}
				}
			}
			nearestDistance = ((num2 == float.MaxValue) ? float.MaxValue : Mathf.Sqrt(num2));
		}

		private static float EnemyThreat(float nearestDistance, int countInRadius)
		{
			float value = Settings.EnemyRadius.Value;
			if (value <= 0f || nearestDistance > value)
			{
				return 0f;
			}
			float num = Mathf.Clamp01((value - nearestDistance) / value);
			float num2 = Mathf.Clamp01((float)countInRadius / Mathf.Max(1f, (float)Settings.EnemyCrowd.Value));
			return num * (0.5f + 0.5f * num2);
		}

		private static void Integrate(State state, float target, float dt)
		{
			float num = Mathf.Max(0.05f, Settings.RiseSeconds.Value);
			float num2 = 1f / num;
			if (target < state.Isolation)
			{
				num2 *= Mathf.Max(1f, Settings.RecoverMultiplier.Value);
			}
			state.Isolation += (target - state.Isolation) * (1f - Mathf.Exp((0f - num2) * dt));
		}

		private void BucketByFaction()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<FactionCountry, List<PlayerSnapshot>> item in _byFaction)
			{
				item.Value.Clear();
			}
			for (int i = 0; i < _players.Count; i++)
			{
				FactionCountry faction = _players[i].Faction;
				if (!_byFaction.TryGetValue(faction, out var value))
				{
					value = new List<PlayerSnapshot>(150);
					_byFaction[faction] = value;
				}
				value.Add(_players[i]);
			}
		}

		private State ResolveState(int playerId)
		{
			if (!_states.TryGetValue(playerId, out var value))
			{
				value = new State();
				_states[playerId] = value;
			}
			value.LastSeenTick = _tick;
			return value;
		}

		private void DropStaleStates()
		{
			_stale.Clear();
			foreach (KeyValuePair<int, State> state in _states)
			{
				if (_tick - state.Value.LastSeenTick > 60)
				{
					_stale.Add(state.Key);
				}
			}
			for (int i = 0; i < _stale.Count; i++)
			{
				_states.Remove(_stale[i]);
			}
		}

		private static float Horizontal(float dx, float dz)
		{
			return Mathf.Sqrt(dx * dx + dz * dz);
		}
	}
	internal static class Log
	{
		private static readonly ManualLogSource Source = Logger.CreateLogSource("AdminHelper");

		public static void Info(string message)
		{
			Source.LogInfo((object)message);
		}

		public static void Warn(string message)
		{
			Source.LogWarning((object)message);
		}

		public static void Error(string message)
		{
			Source.LogError((object)message);
		}
	}
	internal struct PlayerSnapshot
	{
		public RoundPlayer Player;

		public int PlayerId;

		public string Name;

		public Vector3 Position;

		public FactionCountry Faction;

		public PlayerClass Class;

		public bool IsCavalry;

		public bool IsArtillery;
	}
	internal struct ScoredPlayer
	{
		public int PlayerId;

		public string Name;

		public Vector3 Position;

		public int Isolation;

		public int Danger;

		public float DwellSeconds;

		public bool Flagged;

		public float MateDistance;

		public float EnemyDistance;

		public int EnemyCount;

		public bool InFormation;
	}
	internal sealed class RingRenderer
	{
		private const int Segments = 48;

		private const float Radius = 1.6f;

		private const float GroundOffset = 0.05f;

		private readonly List<LineRenderer> _pool = new List<LineRenderer>();

		private readonly Vector3[] _points = (Vector3[])(object)new Vector3[49];

		private GameObject _root;

		private Shader _shader;

		public void Draw(List<ScoredPlayer> flagged)
		{
			//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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			EnsureRoot();
			for (int i = 0; i < flagged.Count; i++)
			{
				LineRenderer val = Resolve(i);
				((Component)val).gameObject.SetActive(true);
				Color val2 = ColourFor(flagged[i].Isolation);
				Color startColor = (val.endColor = val2);
				val.startColor = startColor;
				if ((Object)(object)((Renderer)val).sharedMaterial != (Object)null)
				{
					((Renderer)val).sharedMaterial.color = val2;
				}
				BuildCircle(flagged[i].Position);
				val.SetPositions(_points);
			}
			for (int j = flagged.Count; j < _pool.Count; j++)
			{
				if ((Object)(object)_pool[j] != (Object)null)
				{
					((Component)_pool[j]).gameObject.SetActive(false);
				}
			}
		}

		public void HideAll()
		{
			for (int i = 0; i < _pool.Count; i++)
			{
				if ((Object)(object)_pool[i] != (Object)null)
				{
					((Component)_pool[i]).gameObject.SetActive(false);
				}
			}
		}

		public void Destroy()
		{
			_pool.Clear();
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
		}

		private void BuildCircle(Vector3 centre)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			float num = centre.y + 0.05f;
			for (int i = 0; i <= 48; i++)
			{
				float num2 = (float)i * (float)Math.PI * 2f / 48f;
				_points[i] = new Vector3(centre.x + Mathf.Cos(num2) * 1.6f, num, centre.z + Mathf.Sin(num2) * 1.6f);
			}
		}

		private static Color ColourFor(int isolation)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			float num = Settings.RingThreshold.Value;
			float num2 = Mathf.Max(num + 1f, (float)Settings.RamboThreshold.Value);
			float num3 = Mathf.Clamp01(((float)isolation - num) / (num2 - num));
			return Color.Lerp(new Color(1f, 0.82f, 0.15f), new Color(1f, 0.25f, 0.1f), num3);
		}

		private void EnsureRoot()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("AdminHelperRings");
				Object.DontDestroyOnLoad((Object)(object)_root);
				_pool.Clear();
			}
		}

		private LineRenderer Resolve(int index)
		{
			while (_pool.Count <= index)
			{
				_pool.Add(Create());
			}
			LineRenderer val = _pool[index];
			if ((Object)(object)val == (Object)null)
			{
				val = Create();
				_pool[index] = val;
			}
			return val;
		}

		private LineRenderer Create()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			GameObject val = new GameObject("Ring");
			val.transform.SetParent(_root.transform, false);
			LineRenderer val2 = val.AddComponent<LineRenderer>();
			val2.useWorldSpace = true;
			val2.loop = false;
			val2.positionCount = 49;
			float startWidth = (val2.endWidth = 0.14f);
			val2.startWidth = startWidth;
			val2.numCapVertices = 0;
			((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val2).receiveShadows = false;
			((Renderer)val2).sharedMaterial = new Material(ResolveShader());
			return val2;
		}

		private Shader ResolveShader()
		{
			if ((Object)(object)_shader != (Object)null)
			{
				return _shader;
			}
			_shader = Shader.Find("Universal Render Pipeline/Unlit");
			if ((Object)(object)_shader == (Object)null)
			{
				_shader = Shader.Find("Sprites/Default");
			}
			if ((Object)(object)_shader == (Object)null)
			{
				_shader = Shader.Find("Hidden/Internal-Colored");
			}
			return _shader;
		}
	}
	internal static class Settings
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> TickHz;

		public static ConfigEntry<bool> RequireAdminLogin;

		public static ConfigEntry<float> ClusterNearMetres;

		public static ConfigEntry<float> ClusterFarMetres;

		public static ConfigEntry<float> RiseSeconds;

		public static ConfigEntry<float> RecoverMultiplier;

		public static ConfigEntry<float> EnemyRadius;

		public static ConfigEntry<int> EnemyCrowd;

		public static ConfigEntry<float> FormationSuppression;

		public static ConfigEntry<float> LineRadius;

		public static ConfigEntry<int> LineMinMates;

		public static ConfigEntry<int> LineMaxMates;

		public static ConfigEntry<float> LineResidual;

		public static ConfigEntry<int> ClusterMinMates;

		public static ConfigEntry<float> ClusterFormationRadius;

		public static ConfigEntry<int> RingThreshold;

		public static ConfigEntry<int> RamboThreshold;

		public static ConfigEntry<float> RamboHoldSeconds;

		public static ConfigEntry<bool> ScoreCavalry;

		public static ConfigEntry<string> ExemptClasses;

		public static ConfigEntry<bool> ShowRings;

		public static ConfigEntry<bool> ShowLabels;

		public static ConfigEntry<bool> ShowCornerList;

		public static ConfigEntry<bool> ShowOwnScore;

		public static ConfigEntry<int> MaxLabels;

		public static ConfigEntry<string> ToggleKey;

		public static ConfigEntry<bool> StartHudVisible;

		private static readonly HashSet<PlayerClass> ExemptSet = new HashSet<PlayerClass>();

		private static string _exemptSource;

		private static string _toggleKeySource;

		private static KeyCode _toggleKey = (KeyCode)287;

		private static ConfigFile _config;

		private static DateTime _stamp;

		private static float _nextCheck;

		public static void Create(ConfigFile config)
		{
			_config = config;
			Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch. Turning this off stops the scorer as well as the HUD.");
			TickHz = config.Bind<float>("General", "TickHz", 5f, "Scoring ticks per second. Higher costs more and buys nothing.");
			RequireAdminLogin = config.Bind<bool>("General", "RequireAdminLogin", true, "Only show other players once the server has authenticated an 'rc login'. Turning this off makes the mod a wallhack.");
			ClusterNearMetres = config.Bind<float>("Isolation", "ClusterNearMetres", 10f, "Distance from the midpoint of your two nearest mates at which isolation starts counting.");
			ClusterFarMetres = config.Bind<float>("Isolation", "ClusterFarMetres", 30f, "Distance at which isolation is fully saturated.");
			RiseSeconds = config.Bind<float>("Scoring", "RiseSeconds", 6f, "Time constant for the score climbing. Larger is slower to flag.");
			RecoverMultiplier = config.Bind<float>("Scoring", "RecoverMultiplier", 3f, "How much faster the score falls than it climbs when a player rejoins.");
			EnemyRadius = config.Bind<float>("Danger", "EnemyRadius", 30f, "How close an enemy has to be before any isolation counts as danger.");
			EnemyCrowd = config.Bind<int>("Danger", "EnemyCrowd", 3, "Enemies inside the radius that count as being fully inside their formation.");
			FormationSuppression = config.Bind<float>("Formation", "FormationSuppression", 0.9f, "Fraction of the raw isolation signal removed while a player is in formation.");
			LineRadius = config.Bind<float>("Formation", "LineRadius", 10f, "Radius searched for formation mates.");
			LineMinMates = config.Bind<int>("Formation", "LineMinMates", 2, "Mates needed before the line fit is attempted.");
			LineMaxMates = config.Bind<int>("Formation", "LineMaxMates", 6, "Most mates fed into the line fit.");
			LineResidual = config.Bind<float>("Formation", "LineResidual", 2f, "Metres of spread either side of the best-fit line still counted as a line.");
			ClusterMinMates = config.Bind<int>("Formation", "ClusterMinMates", 3, "Mates within ClusterFormationRadius that count as a square or skirmisher knot.");
			ClusterFormationRadius = config.Bind<float>("Formation", "ClusterFormationRadius", 8f, "Radius for the tight cluster test.");
			RingThreshold = config.Bind<int>("Flagging", "RingThreshold", 40, "Isolation score at which a marker appears. Tracks the live score, so it clears as a player returns.");
			RamboThreshold = config.Bind<int>("Flagging", "RamboThreshold", 75, "Isolation score at which the dwell timer starts running.");
			RamboHoldSeconds = config.Bind<float>("Flagging", "RamboHoldSeconds", 5f, "Seconds above the threshold before a player is flagged.");
			ScoreCavalry = config.Bind<bool>("Flagging", "ScoreCavalry", false, "Score cavalry too. Off by default since cavalry operating apart is not a rambo.");
			ExemptClasses = config.Bind<string>("Flagging", "ExemptClasses", "", "Comma-separated PlayerClass names that are never flagged, e.g. Surgeon,Sapper.");
			ShowRings = config.Bind<bool>("Display", "ShowRings", true, "Draw a ground ring under each watched player.");
			ShowLabels = config.Bind<bool>("Display", "ShowLabels", true, "Draw the floating name and score label over each watched player.");
			ShowCornerList = config.Bind<bool>("Display", "ShowCornerList", true, "List watched players in the corner with their scores and distance.");
			ShowOwnScore = config.Bind<bool>("Display", "ShowOwnScore", true, "Your own scores plus the raw distances behind them. The fastest way to pick thresholds.");
			MaxLabels = config.Bind<int>("Display", "MaxLabels", 12, "Most floating labels drawn at once, nearest first.");
			ToggleKey = config.Bind<string>("Display", "ToggleKey", "F6", "Key that hides and shows the HUD. Any UnityEngine.KeyCode name.");
			StartHudVisible = config.Bind<bool>("Display", "StartHudVisible", false, "Whether the HUD starts visible when the game launches. After that the toggle sticks until you quit.");
			_stamp = Stamp();
		}

		public static void PollForExternalEdits()
		{
			if (_config != null && !(Time.unscaledTime < _nextCheck))
			{
				_nextCheck = Time.unscaledTime + 1f;
				DateTime dateTime = Stamp();
				if (!(dateTime == _stamp))
				{
					_stamp = dateTime;
					_config.Reload();
				}
			}
		}

		private static DateTime Stamp()
		{
			try
			{
				return File.GetLastWriteTimeUtc(_config.ConfigFilePath);
			}
			catch (Exception)
			{
				return _stamp;
			}
		}

		public static bool IsExempt(PlayerClass playerClass)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			RefreshExemptSet();
			return ExemptSet.Contains(playerClass);
		}

		private static void RefreshExemptSet()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			string text = ExemptClasses.Value ?? string.Empty;
			if (text == _exemptSource)
			{
				return;
			}
			_exemptSource = text;
			ExemptSet.Clear();
			string[] array = text.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					try
					{
						ExemptSet.Add((PlayerClass)Enum.Parse(typeof(PlayerClass), text2, ignoreCase: true));
					}
					catch (Exception)
					{
						Log.Warn("Unknown PlayerClass in ExemptClasses: " + text2);
					}
				}
			}
		}

		public static KeyCode ResolveToggleKey()
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			string text = ToggleKey.Value ?? string.Empty;
			if (text == _toggleKeySource)
			{
				return _toggleKey;
			}
			_toggleKeySource = text;
			_toggleKey = (KeyCode)287;
			string text2 = text.Trim();
			if (text2.Length == 0)
			{
				return _toggleKey;
			}
			try
			{
				_toggleKey = (KeyCode)Enum.Parse(typeof(KeyCode), text2, ignoreCase: true);
			}
			catch (Exception)
			{
				Log.Warn("ToggleKey '" + text2 + "' is not a KeyCode name. Falling back to F6.");
			}
			return _toggleKey;
		}
	}
}