Decompiled source of FocusFollowedPlayer v1.0.0

FocusFollowedPlayer/FocusFollowedPlayer.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FocusFollowedPlayer.Arrow;
using FocusFollowedPlayer.Glow;
using FocusFollowedPlayer.Gui;
using FocusFollowedPlayer.Portals;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.TextCore.Text;
using UnityEngine.UIElements;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyVersion("0.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 FocusFollowedPlayer
{
	public static class FFLog
	{
		private class Pending
		{
			public string Msg;

			public float At;
		}

		private static ManualLogSource _src;

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

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

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

		private const float DebounceSeconds = 0.4f;

		public static bool IsVerbose => Plugin.VerboseLogging != null && Plugin.VerboseLogging.Value;

		public static void Init(ManualLogSource src)
		{
			_src = src;
		}

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

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

		public static void Error(string msg, Exception ex)
		{
			if (_src != null)
			{
				_src.LogError((object)(msg + " | " + ex));
			}
		}

		public static void ErrorThrottled(string key, string msg, Exception ex, float seconds = 10f)
		{
			if (Allowed("E:" + key, seconds))
			{
				Error(msg, ex);
			}
		}

		public static void WarnThrottled(string key, string msg, float seconds = 30f)
		{
			if (Allowed("W:" + key, seconds))
			{
				Warn(msg);
			}
		}

		public static void Verbose(string msg)
		{
			if (IsVerbose && _src != null)
			{
				_src.LogInfo((object)msg);
			}
		}

		public static void Every(string key, float seconds, string msg)
		{
			if (IsVerbose && Allowed("V:" + key, seconds))
			{
				Verbose(msg);
			}
		}

		public static void Debounced(string key, string msg)
		{
			if (IsVerbose)
			{
				if (!_pending.TryGetValue(key, out var value))
				{
					value = new Pending();
					_pending[key] = value;
				}
				value.Msg = msg;
				value.At = Time.realtimeSinceStartup;
			}
		}

		public static void Flush()
		{
			if (_pending.Count == 0)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			_flushKeys.Clear();
			foreach (KeyValuePair<string, Pending> item in _pending)
			{
				if (realtimeSinceStartup - item.Value.At >= 0.4f)
				{
					_flushKeys.Add(item.Key);
				}
			}
			for (int i = 0; i < _flushKeys.Count; i++)
			{
				string key = _flushKeys[i];
				Verbose(_pending[key].Msg);
				_pending.Remove(key);
			}
		}

		private static bool Allowed(string key, float seconds)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_nextAllowed.TryGetValue(key, out var value) && realtimeSinceStartup < value)
			{
				return false;
			}
			_nextAllowed[key] = realtimeSinceStartup + seconds;
			return true;
		}
	}
	public static class FollowManager
	{
		public enum PositionSource
		{
			None,
			Nearby,
			Map
		}

		public class PlayerStatus
		{
			public string Name;

			public bool Online;

			public PositionSource Source;

			public float Distance = -1f;

			public bool Denied;

			public string Describe()
			{
				if (!Online)
				{
					return "offline";
				}
				if (Denied)
				{
					return "does not allow following";
				}
				if (Distance < 0f && Source != PositionSource.None)
				{
					return (Source == PositionSource.Nearby) ? "nearby" : "map";
				}
				return Source switch
				{
					PositionSource.Nearby => "nearby - " + Distance.ToString("0") + " m", 
					PositionSource.Map => "map - " + Distance.ToString("0") + " m", 
					_ => "position hidden", 
				};
			}
		}

		private const float OnlineCheckInterval = 0.5f;

		private static string _onlineName;

		private static bool _online;

		private static float _nextOnlineCheck;

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

		private const float DeniedShowSeconds = 60f;

		public static bool ShouldHide(Character c)
		{
			if (!Plugin.Active)
			{
				return false;
			}
			if (Plugin.ShowHealthOfAllPlayers.Value)
			{
				return false;
			}
			string value = Plugin.FollowedPlayer.Value;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			Player val = (Player)(object)((c is Player) ? c : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!IsFollowedOnline(value))
			{
				return false;
			}
			string playerName = val.GetPlayerName();
			bool flag = playerName != value;
			if (!FFLog.IsVerbose)
			{
				return flag;
			}
			if (flag)
			{
				FFLog.Every("hide:" + playerName, 10f, "ShouldHide: hiding health bar of '" + playerName + "' (following '" + value + "')");
			}
			else
			{
				FFLog.Every("keep:" + playerName, 10f, "ShouldHide: keeping health bar of followed player '" + playerName + "'");
			}
			return flag;
		}

		private static bool IsFollowedOnline(string name)
		{
			float unscaledTime = Time.unscaledTime;
			if (name == _onlineName && unscaledTime < _nextOnlineCheck)
			{
				return _online;
			}
			_onlineName = name;
			_nextOnlineCheck = unscaledTime + 0.5f;
			bool online = GetStatus(name).Online;
			if (FFLog.IsVerbose)
			{
				FFLog.Every("followed-online", 10f, "ShouldHide: followed player '" + name + "' online=" + online);
			}
			_online = online;
			return online;
		}

		public static List<string> GetOtherPlayerNames()
		{
			//IL_004b: 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_0052: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : "");
			List<string> list = new List<string>();
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null)
			{
				foreach (PlayerInfo player in instance.GetPlayerList())
				{
					if (!string.IsNullOrEmpty(player.m_name) && player.m_name != text)
					{
						list.Add(player.m_name);
					}
				}
			}
			bool flag = false;
			if (list.Count == 0)
			{
				flag = true;
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					if ((Object)(object)allPlayer != (Object)null && (Object)(object)allPlayer != (Object)(object)Player.m_localPlayer)
					{
						list.Add(allPlayer.GetPlayerName());
					}
				}
			}
			List<string> list2 = (from n in list.Distinct()
				orderby n
				select n).ToList();
			if (FFLog.IsVerbose)
			{
				FFLog.Every("names", 5f, "GetOtherPlayerNames: me='" + text + "', znet=" + ((Object)(object)instance != (Object)null) + ", fallbackToNearby=" + flag + ", result=[" + string.Join(", ", list2.ToArray()) + "]");
			}
			return list2;
		}

		public static void MarkDenied(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				_denied[name] = Time.time;
				if (Plugin.FollowedPlayer.Value == name)
				{
					SetFollowed("");
				}
				Plugin.SaveIfMenuClosed();
				Notify(name + " does not allow being followed");
				FFLog.Verbose("FollowManager: '" + name + "' denied being followed");
			}
		}

		public static bool IsDenied(string name)
		{
			if (!_denied.TryGetValue(name, out var value))
			{
				return false;
			}
			if (Time.time - value > 60f)
			{
				_denied.Remove(name);
				return false;
			}
			return true;
		}

		public static PlayerStatus GetStatus(string name)
		{
			//IL_0086: 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_00ef: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			PlayerStatus playerStatus = new PlayerStatus
			{
				Name = name,
				Denied = IsDenied(name)
			};
			Player localPlayer = Player.m_localPlayer;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer == (Object)null || (Object)(object)allPlayer == (Object)(object)localPlayer || allPlayer.GetPlayerName() != name)
				{
					continue;
				}
				playerStatus.Online = true;
				playerStatus.Source = PositionSource.Nearby;
				if ((Object)(object)localPlayer != (Object)null)
				{
					playerStatus.Distance = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)allPlayer).transform.position);
				}
				return playerStatus;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null)
			{
				foreach (PlayerInfo player in instance.GetPlayerList())
				{
					if (player.m_name != name)
					{
						continue;
					}
					playerStatus.Online = true;
					if (player.m_publicPosition)
					{
						playerStatus.Source = PositionSource.Map;
						if ((Object)(object)localPlayer != (Object)null)
						{
							playerStatus.Distance = Vector3.Distance(((Component)localPlayer).transform.position, player.m_position);
						}
					}
					return playerStatus;
				}
			}
			return playerStatus;
		}

		public static void SetFollowed(string name)
		{
			if (!string.IsNullOrEmpty(name) && !Plugin.Active)
			{
				FFLog.Verbose("SetFollowed: refused, mod is switched off (server=" + Plugin.ModActive + ", enabled=" + Plugin.Enabled.Value + ")");
				Notify(Plugin.ModActive ? "FocusFollowedPlayer is switched off (General > Enabled), cannot follow" : "The server admin has switched FocusFollowedPlayer off, cannot follow");
			}
			else
			{
				string value = Plugin.FollowedPlayer.Value;
				Plugin.FollowedPlayer.Value = name ?? "";
				FFLog.Verbose("SetFollowed: '" + value + "' -> '" + name + "'");
			}
		}

		public static void Notify(string msg)
		{
			Player localPlayer = Player.m_localPlayer;
			bool flag = (Object)(object)localPlayer != (Object)null;
			FFLog.Verbose("Notify: " + msg + (flag ? "" : " (no local player, not shown)"));
			if (flag)
			{
				((Character)localPlayer).Message((MessageType)1, msg, 0, (Sprite)null, false);
			}
		}
	}
	[BepInPlugin("kydldev.FocusFollowedPlayer", "FocusFollowedPlayer", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "kydldev.FocusFollowedPlayer";

		public const string Name = "FocusFollowedPlayer";

		public const string Version = "1.0.0";

		public const string ObjPrefix = "FFP_";

		public const float GlowIntensityMin = 0.1f;

		public const float GlowIntensityMax = 1f;

		public const float GlowIntensityDefault = 0.5f;

		public const float RangeMin = 25f;

		public const float RangeMax = 200f;

		public const float RangeDefault = 100f;

		public static ManualLogSource Log;

		public static ConfigEntry<bool> VerboseLogging;

		public static ConfigEntry<bool> ModEnabled;

		public static ConfigEntry<bool> SyncRanges;

		public static ConfigEntry<float> GlowMaxDistance;

		public static ConfigEntry<bool> AllowFollowAndShare;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> ShowHealthOfAllPlayers;

		public static ConfigEntry<string> FollowedPlayer;

		public static ConfigEntry<KeyboardShortcut> ToggleKey;

		public static ConfigEntry<KeyboardShortcut> MenuKey;

		public static ConfigEntry<bool> GlowEnabled;

		public static ConfigEntry<Color> GlowColor;

		public static ConfigEntry<float> GlowIntensity;

		public static ConfigEntry<float> GlowWidth;

		public static ConfigEntry<bool> GlowThroughWalls;

		public static ConfigEntry<bool> AllowGlowAtAnyDistance;

		public static ConfigEntry<bool> ArrowEnabled;

		public static ConfigEntry<bool> ArrowFollowGlowColor;

		public static ConfigEntry<Color> ArrowColor;

		public static ConfigEntry<float> ArrowMinDistance;

		public static ConfigEntry<float> ArrowHeight;

		public static ConfigEntry<float> ArrowSize;

		public static ConfigEntry<bool> ArrowSeeThrough;

		public static ConfigEntry<bool> NotifyPortals;

		public static ConfigEntry<float> NotifyOffsetX;

		public static ConfigEntry<float> NotifyOffsetY;

		public static ConfigEntry<float> NotifyFontSize;

		public static ConfigEntry<float> NotifyDuration;

		public static ConfigEntry<float> ArrowOffset;

		public static ConfigEntry<bool> ArrowFollowVertical;

		public static ConfigEntry<bool> ForcePublicPosition;

		private Harmony _harmony;

		public static bool RangeSyncSuspended;

		public static Plugin Instance { get; private set; }

		public static bool OwnsServerSettings
		{
			get
			{
				ZNet instance = ZNet.instance;
				return (Object)(object)instance == (Object)null || instance.IsServer();
			}
		}

		public static bool ModActive => ModEnabled == null || ModEnabled.Value;

		public static bool Active => ModActive && (Enabled == null || Enabled.Value);

		public static bool IsAdmin
		{
			get
			{
				SynchronizationManager instance = SynchronizationManager.Instance;
				return instance == null || instance.PlayerIsAdmin;
			}
		}

		private void Awake()
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: 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)
			//IL_01b7: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Expected O, but got Unknown
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Expected O, but got Unknown
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Expected O, but got Unknown
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0434: Expected O, but got Unknown
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Expected O, but got Unknown
			//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Expected O, but got Unknown
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Expected O, but got Unknown
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0548: Expected O, but got Unknown
			//IL_059b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a5: Expected O, but got Unknown
			//IL_0604: Unknown result type (might be due to invalid IL or missing references)
			//IL_066d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0677: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			FFLog.Init(((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			BindServerSetting();
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "Verbose", false, "OFF: only warnings/errors are written to the BepInEx log. ON: log everything (very chatty).");
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			FFLog.Verbose("Awake: FocusFollowedPlayer v1.0.0 loading from " + ((BaseUnityPlugin)this).Info.Location);
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turns the whole mod on/off for you: no health bar hiding, no glow, no arrow, you cannot follow anybody and nobody can follow you (follow requests are answered with 'does not allow being followed').");
			ShowHealthOfAllPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowHealthOfAllPlayers", false, "OFF (default): while you follow somebody, the health bars of all OTHER players are hidden. ON: the health bars of all players are shown as usual.");
			FollowedPlayer = ((BaseUnityPlugin)this).Config.Bind<string>("General", "FollowedPlayer", "", "Name of the followed player. Empty = nobody followed, nothing hidden.");
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Toggle", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Turn the mod on/off for you (same as General > Enabled).");
			GlowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "Enabled", true, "Glowing outline around the followed player.");
			GlowColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Glow", "Color", new Color(0f, 1f, 1f, 1f), "Outline color (any). Format: RGBA hex, e.g. #00FFFFFF.");
			GlowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "Intensity", 0.5f, new ConfigDescription("Brightness multiplier of the glow and arrow (0.1 - 1, default 0.5).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			GlowWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "WidthPixels", 2f, new ConfigDescription("Outline thickness in screen pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 12f), Array.Empty<object>()));
			GlowThroughWalls = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "ThroughWalls", true, "Visible through terrain and objects.");
			AllowGlowAtAnyDistance = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "AllowGlowAtAnyDistance", false, "ON: the followed player glows at any distance (ignores MaxDistance and the sync setting). The arrow still uses its own distance. The glow can only be drawn while the player is loaded in your game.");
			GlowMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "MaxDistance", 100f, new ConfigDescription("The glow is only shown up to this distance (m). Further away the arrow takes over. 25 - 200.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(25f, 200f), Array.Empty<object>()));
			SyncRanges = ((BaseUnityPlugin)this).Config.Bind<bool>("Range", "SyncActivationPoint", true, "ON: the glow stops exactly where the arrow starts (one shared distance). OFF: glow and arrow each use their own distance.");
			ArrowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Arrow", "Enabled", true, "Arrow pointing to the followed player when far away.");
			ArrowFollowGlowColor = ((BaseUnityPlugin)this).Config.Bind<bool>("Arrow", "UseGlowColor", true, "ON: the arrow uses the glow color. OFF: it uses Arrow > Color. The glow Intensity setting is applied to the arrow in both cases.");
			ArrowColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Arrow", "Color", new Color(1f, 0.5f, 0f, 1f), "Arrow color when UseGlowColor is false. RGBA hex.");
			ArrowMinDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Arrow", "MinDistance", 100f, new ConfigDescription("Arrow only shows beyond this distance (m). 25 - 200.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(25f, 200f), Array.Empty<object>()));
			SyncRanges.SettingChanged += delegate
			{
				SyncArrowRangeToGlow();
			};
			GlowMaxDistance.SettingChanged += delegate
			{
				SyncArrowRangeToGlow();
			};
			NotifyPortals = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "PortalEnabled", true, "Show a text on the right side of the screen when the followed player takes a portal (needs this mod on their side).");
			NotifyOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Notifications", "DistanceFromRightEdge", 30f, new ConfigDescription("Distance of the notification text from the right screen edge, in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 800f), Array.Empty<object>()));
			NotifyOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Notifications", "HeightOnScreen", 30f, new ConfigDescription("Vertical position of the notification text, in % of the screen height from the top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			NotifyFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Notifications", "TextSize", 20f, new ConfigDescription("Notification text size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(12f, 40f), Array.Empty<object>()));
			NotifyDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Notifications", "ShowSeconds", 10f, new ConfigDescription("How long a notification stays on screen (seconds).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 30f), Array.Empty<object>()));
			ArrowSize = ((BaseUnityPlugin)this).Config.Bind<float>("Arrow", "Size", 1f, new ConfigDescription("Arrow size. 1 = full (default) size, 0.5 = half size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			ArrowSeeThrough = ((BaseUnityPlugin)this).Config.Bind<bool>("Arrow", "ArrowSeeThrough", false, "ON: only a thin border of the arrow is drawn, the inside is see-through. Color and intensity are unchanged.");
			ArrowHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Arrow", "HeightAboveGround", 3f, new ConfigDescription("Hover height above ground (m).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			ArrowFollowVertical = ((BaseUnityPlugin)this).Config.Bind<bool>("Arrow", "FollowVertical", true, "Tilt the arrow up/down toward the target (e.g. target high in the air or far below).");
			ArrowOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Arrow", "DistanceFromPlayer", 5f, new ConfigDescription("How far from you, toward the target (m).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			ForcePublicPosition = ((BaseUnityPlugin)this).Config.Bind<bool>("Arrow", "ForcePublicPosition", true, "Ask the followed player (must have this mod) to turn their map position sharing ON.");
			AllowFollowAndShare = ((BaseUnityPlugin)this).Config.Bind<bool>("Privacy", "AllowFollowAndShare", true, "ON: other players can follow you and turn ON your map position sharing. OFF: nobody can follow you, your map position is never forced.");
			MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Menu", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Open the FocusFollowedPlayer menu.");
			DumpConfig();
			((BaseUnityPlugin)this).Config.Save();
			((Component)this).gameObject.AddComponent<GlowManager>();
			((Component)this).gameObject.AddComponent<ArrowManager>();
			((Component)this).gameObject.AddComponent<MenuManager>();
			((Component)this).gameObject.AddComponent<NotificationHud>();
			FFLog.Verbose("Awake: components added (GlowManager, ArrowManager, MenuManager)");
			try
			{
				_harmony = new Harmony("kydldev.FocusFollowedPlayer");
				_harmony.PatchAll();
				int num = 0;
				foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods())
				{
					num++;
					FFLog.Verbose("Awake: patched " + patchedMethod.DeclaringType?.ToString() + "." + patchedMethod.Name);
				}
				FFLog.Verbose("Awake: Harmony patched " + num + " method(s)");
			}
			catch (Exception ex)
			{
				FFLog.Error("Awake: Harmony PatchAll failed", ex);
			}
			FFLog.Verbose("Awake: done");
		}

		private void BindServerSetting()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "ModEnabled", true, new ConfigDescription("Server admin setting: turns the whole mod off/on for every player on the server. Only admins can change it; it is only saved on the server. [Synced with Server]", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			ModEnabled.SettingChanged += delegate
			{
				if (OwnsServerSettings)
				{
					SaveIfMenuClosed();
				}
			};
		}

		public static float GlowRange()
		{
			return Mathf.Clamp(GlowMaxDistance.Value, 25f, 200f);
		}

		public static float ArrowRange()
		{
			if (SyncRanges.Value)
			{
				return GlowRange();
			}
			return Mathf.Clamp(ArrowMinDistance.Value, 25f, 200f);
		}

		private static void SyncArrowRangeToGlow()
		{
			if (!RangeSyncSuspended && SyncRanges != null && ArrowMinDistance != null && GlowMaxDistance != null && SyncRanges.Value)
			{
				ArrowMinDistance.Value = Mathf.Round(Mathf.Clamp(GlowMaxDistance.Value, 25f, 200f));
			}
		}

		public static void SetGlowRange(float v)
		{
			v = Mathf.Round(Mathf.Clamp(v, 25f, 200f));
			GlowMaxDistance.Value = v;
			if (SyncRanges.Value)
			{
				ArrowMinDistance.Value = v;
			}
		}

		public static void SetArrowRange(float v)
		{
			v = Mathf.Round(Mathf.Clamp(v, 25f, 200f));
			ArrowMinDistance.Value = v;
			if (SyncRanges.Value)
			{
				GlowMaxDistance.Value = v;
			}
		}

		public static void SaveAll()
		{
			try
			{
				((BaseUnityPlugin)Instance).Config.Save();
				FFLog.Verbose("SaveAll: config saved");
			}
			catch (Exception ex)
			{
				FFLog.Error("SaveAll failed", ex);
			}
		}

		public static void SaveIfMenuClosed()
		{
			if (!MenuManager.IsOpen)
			{
				SaveAll();
			}
		}

		public static float GetGlowIntensity()
		{
			return Mathf.Clamp(GlowIntensity.Value, 0.1f, 1f);
		}

		private static void OnSettingChanged(object sender, SettingChangedEventArgs e)
		{
			ConfigEntryBase changedSetting = e.ChangedSetting;
			FFLog.Debounced("cfg:" + changedSetting.Definition.Section + "/" + changedSetting.Definition.Key, "Setting changed: [" + changedSetting.Definition.Section + "] " + changedSetting.Definition.Key + " = " + changedSetting.BoxedValue);
		}

		private void DumpConfig()
		{
			if (!FFLog.IsVerbose)
			{
				return;
			}
			foreach (ConfigDefinition key in ((BaseUnityPlugin)this).Config.Keys)
			{
				FFLog.Verbose("Config: [" + key.Section + "] " + key.Key + " = " + ((BaseUnityPlugin)this).Config[key].BoxedValue);
			}
		}

		private void Update()
		{
			//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_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FFLog.Flush();
				PublicPositionSync.Tick();
				PublicPositionSync.TickFollowed();
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					PortalTracker.Clear();
				}
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				KeyboardShortcut value = MenuKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && (MenuManager.IsOpen || !IsTyping()))
				{
					FFLog.Verbose("Update: menu key pressed (menu open=" + MenuManager.IsOpen + ")");
					MenuManager component = ((Component)this).GetComponent<MenuManager>();
					if ((Object)(object)component != (Object)null)
					{
						component.Toggle();
					}
				}
				else if (!MenuManager.IsOpen && !IsTyping())
				{
					value = ToggleKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						Enabled.Value = !Enabled.Value;
						FFLog.Verbose("Update: toggle key pressed -> Enabled=" + Enabled.Value);
						SaveAll();
						FollowManager.Notify(Enabled.Value ? "FocusFollowedPlayer: ON" : "FocusFollowedPlayer: OFF");
					}
				}
			}
			catch (Exception ex)
			{
				FFLog.ErrorThrottled("Plugin.Update", "Update failed", ex);
			}
		}

		private static bool IsTyping()
		{
			return ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible();
		}

		private void OnDestroy()
		{
			FFLog.Verbose("OnDestroy: unpatching and unloading bundle");
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				ShaderProvider.Unload();
			}
			catch (Exception ex)
			{
				FFLog.Error("OnDestroy failed", ex);
			}
		}
	}
	public static class PublicPositionSync
	{
		private const string RpcName = "FocusFollowedPlayer_Follow";

		private const string DeniedRpcName = "FocusFollowedPlayer_FollowDenied";

		private const float Interval = 5f;

		private const float RestoreAfter = 20f;

		private static bool _weTurnedSharingOn;

		private static float _lastFollowPing;

		private static float _next;

		public static void Register()
		{
			ZRoutedRpc.instance.Register<bool>("FocusFollowedPlayer_Follow", (Action<long, bool>)RPC_Follow);
			ZRoutedRpc.instance.Register("FocusFollowedPlayer_FollowDenied", (Action<long>)RPC_Denied);
			PortalTracker.Register();
			FFLog.Verbose("PublicPositionSync: RPC 'FocusFollowedPlayer_Follow' registered");
		}

		private static void RPC_Follow(long sender, bool wantPublic)
		{
			try
			{
				FFLog.Verbose("PublicPositionSync: follow request from sender " + sender + " (wantPublic=" + wantPublic + ")");
				if (!Plugin.ModActive)
				{
					return;
				}
				if (!Plugin.Enabled.Value || !Plugin.AllowFollowAndShare.Value)
				{
					FFLog.Verbose("PublicPositionSync: refused (Enabled=" + Plugin.Enabled.Value + ", AllowFollowAndShare=" + Plugin.AllowFollowAndShare.Value + ") -> telling the follower");
					ZRoutedRpc instance = ZRoutedRpc.instance;
					if (instance != null)
					{
						instance.InvokeRoutedRPC(sender, "FocusFollowedPlayer_FollowDenied", new object[0]);
					}
					return;
				}
				if (_weTurnedSharingOn)
				{
					_lastFollowPing = Time.time;
				}
				if (wantPublic)
				{
					ZNet instance2 = ZNet.instance;
					if ((Object)(object)instance2 == (Object)null)
					{
						FFLog.Warn("PublicPositionSync: RPC received but ZNet.instance is null");
						return;
					}
					if (instance2.IsReferencePositionPublic())
					{
						FFLog.Verbose("PublicPositionSync: position already public, nothing to do");
						return;
					}
					instance2.SetPublicReferencePosition(true);
					_weTurnedSharingOn = true;
					_lastFollowPing = Time.time;
					FFLog.Verbose("PublicPositionSync: map position sharing turned ON on request of " + sender);
					FollowManager.Notify("FocusFollowedPlayer: map position sharing turned ON (requested)");
				}
			}
			catch (Exception ex)
			{
				FFLog.Error("PublicPositionSync.RPC_Follow failed", ex);
			}
		}

		private static void RPC_Denied(long sender)
		{
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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)
			try
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance == (Object)null || sender == 0)
				{
					return;
				}
				foreach (PlayerInfo player in instance.GetPlayerList())
				{
					ZDOID characterID = player.m_characterID;
					if (((ZDOID)(ref characterID)).UserID != sender)
					{
						continue;
					}
					FollowManager.MarkDenied(player.m_name);
					return;
				}
				FFLog.Verbose("PublicPositionSync: denial from unknown sender " + sender);
			}
			catch (Exception ex)
			{
				FFLog.Error("PublicPositionSync.RPC_Denied failed", ex);
			}
		}

		public static void TickFollowed()
		{
			if (!_weTurnedSharingOn)
			{
				return;
			}
			try
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance == (Object)null)
				{
					_weTurnedSharingOn = false;
				}
				else if (!instance.IsReferencePositionPublic())
				{
					_weTurnedSharingOn = false;
				}
				else if (!Plugin.Active || !Plugin.AllowFollowAndShare.Value || !(Time.time - _lastFollowPing <= 20f))
				{
					_weTurnedSharingOn = false;
					instance.SetPublicReferencePosition(false);
					FFLog.Verbose("PublicPositionSync: nobody follows any more (or not allowed), map position sharing turned OFF again");
					FollowManager.Notify("FocusFollowedPlayer: map position sharing turned OFF again (nobody follows you)");
				}
			}
			catch (Exception ex)
			{
				_weTurnedSharingOn = false;
				FFLog.ErrorThrottled("PublicPositionSync.TickFollowed", "PublicPositionSync.TickFollowed failed", ex);
			}
		}

		public static void Tick()
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: 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_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Active || Time.time < _next)
			{
				return;
			}
			_next = Time.time + 5f;
			string value = Plugin.FollowedPlayer.Value;
			ZNet instance = ZNet.instance;
			ZRoutedRpc instance2 = ZRoutedRpc.instance;
			if (string.IsNullOrEmpty(value) || (Object)(object)instance == (Object)null || instance2 == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				FFLog.Every("pps-skip", 30f, "PublicPositionSync.Tick: skipped (followed='" + value + "', znet=" + ((Object)(object)instance != (Object)null) + ", rpc=" + (instance2 != null) + ", localPlayer=" + ((Object)(object)Player.m_localPlayer != (Object)null) + ")");
				return;
			}
			bool flag = false;
			foreach (PlayerInfo player in instance.GetPlayerList())
			{
				if (player.m_name != value)
				{
					continue;
				}
				flag = true;
				ZDOID characterID = player.m_characterID;
				if (((ZDOID)(ref characterID)).UserID == 0)
				{
					FFLog.Every("pps-noid", 30f, "PublicPositionSync.Tick: '" + value + "' has no character ID yet, not pinging");
					break;
				}
				bool flag2 = Plugin.ForcePublicPosition.Value && !player.m_publicPosition;
				string[] obj = new string[7] { "PublicPositionSync.Tick: ping '", value, "' (user ", null, null, null, null };
				characterID = player.m_characterID;
				obj[3] = ((ZDOID)(ref characterID)).UserID.ToString();
				obj[4] = ", wantPublic=";
				obj[5] = flag2.ToString();
				obj[6] = ")";
				FFLog.Every("pps-send", 30f, string.Concat(obj));
				characterID = player.m_characterID;
				instance2.InvokeRoutedRPC(((ZDOID)(ref characterID)).UserID, "FocusFollowedPlayer_Follow", new object[1] { flag2 });
				break;
			}
			if (!flag)
			{
				FFLog.Every("pps-notfound", 30f, "PublicPositionSync.Tick: followed player '" + value + "' not in server player list");
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetScene_Awake_Patch
	{
		private static void Postfix()
		{
			try
			{
				if (ZRoutedRpc.instance != null)
				{
					PublicPositionSync.Register();
				}
				else
				{
					FFLog.Warn("ZNetScene.Awake: ZRoutedRpc.instance is null, RPC not registered");
				}
			}
			catch (Exception ex)
			{
				FFLog.Error("ZNetScene.Awake postfix failed", ex);
			}
		}
	}
	public struct RangeGate
	{
		private bool _hasState;

		private bool _inside;

		public void Reset()
		{
			_hasState = false;
		}

		public bool Evaluate(float distance, float range)
		{
			if (!_hasState)
			{
				_hasState = true;
				_inside = distance <= range;
				return _inside;
			}
			float num = Mathf.Clamp(range * 0.05f, 1f, 5f);
			if (_inside)
			{
				if (distance > range + num)
				{
					_inside = false;
				}
			}
			else if (distance <= range - num)
			{
				_inside = true;
			}
			return _inside;
		}
	}
}
namespace FocusFollowedPlayer.Testing
{
	public static class TestDummy
	{
		public static ConfigEntry<bool> Wander;

		private static GameObject _go;

		private static Material _mat;

		public static bool Active => (Object)(object)_go != (Object)null;

		public static Transform Transform => ((Object)(object)_go != (Object)null) ? _go.transform : null;

		public static Vector3 Position => ((Object)(object)_go != (Object)null) ? _go.transform.position : Vector3.zero;

		public static void Bind(ConfigFile cfg)
		{
			Wander = cfg.Bind<bool>("Testing", "DummyWanders", false, "TEST ONLY: the dummy walks in a circle (radius 15 m) around its spawn point, to test arrow smoothing.");
		}

		public static void SpawnAtPlayer(float forwardMeters)
		{
			//IL_0028: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				FFLog.Warn("TestDummy: no local player, cannot spawn");
				return;
			}
			Transform transform = ((Component)localPlayer).transform;
			SpawnAt(transform.position + transform.forward * forwardMeters);
		}

		public unsafe static void SpawnAt(Vector3 pos)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			Remove();
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance != (Object)null)
			{
				pos.y = Mathf.Max(pos.y, instance.GetGroundHeight(pos));
			}
			pos.y += 0.9f;
			_go = GameObject.CreatePrimitive((PrimitiveType)1);
			((Object)_go).name = "TestDummy_FFP";
			Component component = _go.GetComponent("Collider");
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			MeshRenderer component2 = _go.GetComponent<MeshRenderer>();
			_mat = ((Renderer)component2).material;
			_mat.color = new Color(1f, 0.2f, 0.8f, 1f);
			_go.transform.position = pos;
			DummyMover dummyMover = _go.AddComponent<DummyMover>();
			dummyMover.Origin = pos;
			Vector3 val = pos;
			FFLog.Verbose("TestDummy: spawned at " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString());
			FollowManager.Notify("Test dummy spawned");
		}

		public static void Remove()
		{
			if ((Object)(object)_go != (Object)null)
			{
				Object.Destroy((Object)(object)_go);
				FFLog.Verbose("TestDummy: removed");
			}
			if ((Object)(object)_mat != (Object)null)
			{
				Object.Destroy((Object)(object)_mat);
			}
			_go = null;
			_mat = null;
		}

		public static void TeleportAwayFromDummy(float meters)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				FollowManager.Notify("Spawn the dummy first");
				return;
			}
			Vector3 val = Quaternion.Euler(0f, 90f, 0f) * Vector3.forward;
			TeleportPlayer(Position + val * meters);
		}

		public static void TeleportToDummy()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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)
			if (!Active)
			{
				FollowManager.Notify("Spawn the dummy first");
			}
			else
			{
				TeleportPlayer(Position + Vector3.back * 3f);
			}
		}

		private unsafe static void TeleportPlayer(Vector3 target)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				ZoneSystem instance = ZoneSystem.instance;
				float num = (((Object)(object)instance != (Object)null) ? instance.GetGroundHeight(target) : target.y);
				target.y = Mathf.Max(num, ((Object)(object)instance != (Object)null) ? instance.m_waterLevel : 0f) + 1f;
				Vector3 val = target;
				FFLog.Verbose("TestDummy: teleporting player to " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString());
				((Character)localPlayer).TeleportTo(target, ((Component)localPlayer).transform.rotation, true);
			}
		}

		public static float DistanceFromPlayer()
		{
			//IL_002a: 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)
			Player localPlayer = Player.m_localPlayer;
			if (!Active || (Object)(object)localPlayer == (Object)null)
			{
				return -1f;
			}
			return Vector3.Distance(((Component)localPlayer).transform.position, Position);
		}
	}
	public class DummyMover : MonoBehaviour
	{
		public Vector3 Origin;

		private const float Radius = 15f;

		private const float Speed = 0.2f;

		private void Update()
		{
			//IL_002c: 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_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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (TestDummy.Wander != null && TestDummy.Wander.Value)
				{
					float num = Time.time * 0.2f;
					Vector3 position = Origin + new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * 15f;
					((Component)this).transform.position = position;
				}
			}
			catch (Exception ex)
			{
				FFLog.ErrorThrottled("DummyMover.Update", "Dummy update failed", ex);
			}
		}
	}
	public static class TestOptions
	{
		public static ConfigEntry<bool> ForceArrow;

		public static ConfigEntry<bool> GlowOnSelf;

		public static ConfigEntry<bool> ShowOwnPortal;

		public static bool ForceArrowOn => ForceArrow != null && ForceArrow.Value;

		public static bool ShowOwnPortalOn => ShowOwnPortal != null && ShowOwnPortal.Value;

		public static bool GlowOnSelfOn => GlowOnSelf != null && GlowOnSelf.Value;

		public static void Bind(ConfigFile cfg)
		{
			ForceArrow = cfg.Bind<bool>("Testing", "AlwaysShowArrow", false, "TEST ONLY: show the arrow all the time (ignores Enabled and MinDistance). Points at the followed player, or slowly spins when nobody is followed.");
			GlowOnSelf = cfg.Bind<bool>("Testing", "GlowOnSelf", false, "TEST ONLY: show the glow around your own character (overrides the followed player's glow while ON).");
			ShowOwnPortal = cfg.Bind<bool>("Testing", "ShowOwnPortal", false, "TEST ONLY: show a notification with the name of the portal YOU took (normally you only see other players' portals).");
			FFLog.Verbose("TestOptions: bound (AlwaysShowArrow=" + ForceArrow.Value + ", GlowOnSelf=" + GlowOnSelf.Value + ")");
		}
	}
}
namespace FocusFollowedPlayer.Portals
{
	public static class PortalTracker
	{
		private const string RpcName = "FocusFollowedPlayer_PortalUsed";

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

		private const int MaxPortalNameLength = 64;

		private const int MaxRawLength = 256;

		private static readonly Regex RichTextTag = new Regex("<[^>]*>", RegexOptions.Compiled);

		public static void Register()
		{
			ZRoutedRpc.instance.Register<string>("FocusFollowedPlayer_PortalUsed", (Action<long, string>)RPC_PortalUsed);
			FFLog.Verbose("PortalTracker: RPC 'FocusFollowedPlayer_PortalUsed' registered");
		}

		public static string Display(string portal)
		{
			return string.IsNullOrEmpty(portal) ? " (unnamed portal) " : (" " + portal);
		}

		public static string LinePrefix(string playerName)
		{
			return "Followed player " + playerName + " used portal ";
		}

		private static void ShowLine(string playerName, string portal)
		{
			NotificationHud.Show(LinePrefix(playerName), Display(portal));
		}

		public static bool TryGetPending(string playerName, out string portal)
		{
			portal = null;
			if (string.IsNullOrEmpty(playerName))
			{
				return false;
			}
			return _pending.TryGetValue(playerName, out portal);
		}

		public static void Clear()
		{
			if (_pending.Count > 0)
			{
				_pending.Clear();
			}
		}

		public static string Sanitize(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			if (s.Length > 256)
			{
				s = s.Substring(0, 256);
			}
			s = RichTextTag.Replace(s, "");
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			string text = s;
			foreach (char c in text)
			{
				if (!char.IsControl(c))
				{
					stringBuilder.Append(c);
				}
			}
			s = stringBuilder.ToString().Trim();
			if (s.Length > 64)
			{
				s = s.Substring(0, 64).TrimEnd();
			}
			return s;
		}

		private static bool SameName(string a, string b)
		{
			return string.Equals((a ?? "").Trim(), (b ?? "").Trim(), StringComparison.OrdinalIgnoreCase);
		}

		public static void OnLocalPortalUsed(string portal)
		{
			portal = Sanitize(portal);
			FFLog.Verbose("PortalTracker: local player took portal '" + portal + "'");
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, string> item in _pending)
			{
				if (SameName(item.Value, portal))
				{
					list.Add(item.Key);
				}
			}
			foreach (string item2 in list)
			{
				_pending.Remove(item2);
				FFLog.Verbose("PortalTracker: '" + item2 + "' portal info cleared (I used the same portal)");
			}
			if (Plugin.Active && Plugin.AllowFollowAndShare.Value)
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(0L, "FocusFollowedPlayer_PortalUsed", new object[1] { portal });
				}
			}
		}

		private static void RPC_PortalUsed(long sender, string portal)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.Active)
				{
					return;
				}
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance == (Object)null || sender == 0)
				{
					return;
				}
				string text = null;
				foreach (PlayerInfo player in instance.GetPlayerList())
				{
					ZDOID characterID = player.m_characterID;
					if (((ZDOID)(ref characterID)).UserID != sender)
					{
						continue;
					}
					text = player.m_name;
					break;
				}
				if (string.IsNullOrEmpty(text))
				{
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !(text == localPlayer.GetPlayerName()))
				{
					portal = Sanitize(portal);
					_pending[text] = portal;
					FFLog.Verbose("PortalTracker: '" + text + "' took portal '" + portal + "'");
					if (Plugin.NotifyPortals.Value && text == (Plugin.FollowedPlayer.Value ?? ""))
					{
						ShowLine(text, portal);
					}
				}
			}
			catch (Exception ex)
			{
				FFLog.Error("PortalTracker.RPC_PortalUsed failed", ex);
			}
		}
	}
}
namespace FocusFollowedPlayer.Patches
{
	[HarmonyPatch(typeof(EnemyHud), "TestShow")]
	internal static class EnemyHud_TestShow_Patch
	{
		private static void Postfix(Character c, ref bool __result)
		{
			try
			{
				if (__result && (Object)(object)c != (Object)null && FollowManager.ShouldHide(c))
				{
					__result = false;
				}
			}
			catch (Exception ex)
			{
				FFLog.ErrorThrottled("EnemyHud.TestShow", "EnemyHud.TestShow postfix failed", ex);
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
	internal static class TeleportWorld_Teleport_Patch
	{
		private static void Prefix(Player __0, out bool __state)
		{
			__state = (Object)(object)__0 != (Object)null && ((Character)__0).IsTeleporting();
		}

		private static void Postfix(TeleportWorld __instance, Player __0, bool __state)
		{
			try
			{
				if (!__state && !((Object)(object)__0 == (Object)null) && !((Object)(object)__0 != (Object)(object)Player.m_localPlayer) && ((Character)__0).IsTeleporting())
				{
					string s = "";
					ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
					ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
					if (val != null)
					{
						s = val.GetString(ZDOVars.s_tag, "");
					}
					s = PortalTracker.Sanitize(s);
					PortalTracker.OnLocalPortalUsed(s);
				}
			}
			catch (Exception ex)
			{
				FFLog.ErrorThrottled("PortalPatch", "TeleportWorld.Teleport postfix failed", ex);
			}
		}
	}
	[HarmonyPatch]
	internal static class ZInput_MouseScrollWheel_Patch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo wrapper = AccessTools.Method(typeof(ZInput), "GetMouseScrollWheel", (Type[])null, (Type[])null);
			if (wrapper != null)
			{
				yield return wrapper;
			}
			MethodInfo worker = AccessTools.Method(typeof(ZInput), "Internal_GetMouseScrollWheel", (Type[])null, (Type[])null);
			if (worker != null)
			{
				yield return worker;
			}
		}

		private static void Postfix(ref float __result)
		{
			if (MenuManager.IsOpen)
			{
				__result = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "IsVisible")]
	internal static class TextInput_IsVisible_Patch
	{
		private static void Postfix(ref bool __result)
		{
			if (MenuManager.IsOpen)
			{
				__result = true;
			}
		}
	}
}
namespace FocusFollowedPlayer.Gui
{
	public class MenuManager : MonoBehaviour
	{
		private class RowStatus
		{
			public string Name;

			public Label Label;

			public Label Portal;
		}

		private const float RefreshEvery = 0.2f;

		private GameObject _go;

		private PanelSettings _panel;

		private VisualElement _root;

		private VisualElement _playerList;

		private VisualElement _arrowColorBox;

		private const int AlertsTabIndex = 4;

		private VisualElement _viewport;

		private VisualElement _body;

		private VisualElement _scrollTrack;

		private VisualElement _scrollThumb;

		private float _scroll;

		private float _thumbDragStartY;

		private float _thumbDragStartScroll;

		private const float WheelStep = 18f;

		private const float WindowHeightPercent = 70f;

		private Label _followingLabel;

		private VisualElement _stopButton;

		private VisualElement _revertButton;

		private Label _footLabel;

		private Label _disabledBanner;

		private readonly Dictionary<ConfigEntryBase, object> _saved = new Dictionary<ConfigEntryBase, object>();

		private const int PlayersPerPage = 7;

		private int _page;

		private readonly List<RowStatus> _rowStatus = new List<RowStatus>();

		private readonly List<Action> _refresh = new List<Action>();

		private readonly List<VisualElement> _tabs = new List<VisualElement>();

		private readonly List<VisualElement> _pages = new List<VisualElement>();

		private readonly List<Label> _tabLabels = new List<Label>();

		private float _nextRefresh;

		private string _playersKey = "";

		private int _selected = -1;

		public static bool IsOpen { get; private set; }

		public static bool AlertsTabOpen { get; private set; }

		public void Toggle()
		{
			if (IsOpen)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		public void Open()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Build())
				{
					FFLog.Error("Menu: Open failed, UI could not be built");
					return;
				}
				TakeSnapshot();
				_root.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)0);
				IsOpen = true;
				FFLog.Verbose("Menu: opened");
				RefreshAll();
			}
			catch (Exception ex)
			{
				FFLog.Error("Menu: Open failed", ex);
			}
		}

		public void Close()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				FFLog.Verbose("Menu: closed, saving config");
				IsOpen = false;
				AlertsTabOpen = false;
				Plugin.SaveAll();
			}
			if (_root != null)
			{
				_root.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			}
			IsOpen = false;
		}

		private static IEnumerable<ConfigEntryBase> AllEntries()
		{
			Plugin inst = Plugin.Instance;
			if (!((Object)(object)inst != (Object)null))
			{
				yield break;
			}
			foreach (ConfigDefinition def in ((BaseUnityPlugin)inst).Config.Keys)
			{
				yield return ((BaseUnityPlugin)inst).Config[def];
			}
		}

		private static bool Tracked(ConfigEntryBase e)
		{
			if ((object)e == Plugin.FollowedPlayer)
			{
				return false;
			}
			if ((object)e == Plugin.ModEnabled && !Plugin.IsAdmin)
			{
				return false;
			}
			return true;
		}

		private void TakeSnapshot()
		{
			_saved.Clear();
			foreach (ConfigEntryBase item in AllEntries())
			{
				if (Tracked(item))
				{
					_saved[item] = item.BoxedValue;
				}
			}
			FFLog.Verbose("Menu: snapshot of " + _saved.Count + " config values taken");
		}

		private bool IsDirty()
		{
			foreach (KeyValuePair<ConfigEntryBase, object> item in _saved)
			{
				if (!Tracked(item.Key) || object.Equals(item.Key.BoxedValue, item.Value))
				{
					continue;
				}
				return true;
			}
			return false;
		}

		private void RevertChanges()
		{
			int num = 0;
			Plugin.RangeSyncSuspended = true;
			try
			{
				foreach (KeyValuePair<ConfigEntryBase, object> item in _saved)
				{
					if (Tracked(item.Key) && !object.Equals(item.Key.BoxedValue, item.Value))
					{
						item.Key.BoxedValue = item.Value;
						num++;
					}
				}
			}
			finally
			{
				Plugin.RangeSyncSuspended = false;
			}
			FFLog.Verbose("Menu: reverted " + num + " value(s) to the saved config");
			RefreshAll();
		}

		private void Update()
		{
			if (!IsOpen)
			{
				return;
			}
			try
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null || Input.GetKeyDown((KeyCode)27))
				{
					FFLog.Verbose("Menu: closing (" + (((Object)(object)Player.m_localPlayer == (Object)null) ? "no local player" : "Escape pressed") + ")");
					Close();
				}
				else if (Time.unscaledTime >= _nextRefresh)
				{
					_nextRefresh = Time.unscaledTime + 0.2f;
					RefreshAll();
				}
			}
			catch (Exception ex)
			{
				FFLog.ErrorThrottled("Menu.Update", "Menu update failed", ex);
			}
		}

		private void LateUpdate()
		{
			if (IsOpen)
			{
				Cursor.visible = true;
				Cursor.lockState = (CursorLockMode)0;
			}
		}

		private void RefreshAll()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			foreach (Action item in _refresh)
			{
				item();
			}
			RebuildPlayersIfChanged();
			UpdatePlayerStatuses();
			if (_arrowColorBox != null)
			{
				_arrowColorBox.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(Plugin.ArrowFollowGlowColor.Value ? 1 : 0));
			}
			if (_disabledBanner != null)
			{
				if (!Plugin.ModActive)
				{
					((TextElement)_disabledBanner).text = "The server admin has switched this mod off - nothing is active.";
				}
				else if (!Plugin.Enabled.Value)
				{
					((TextElement)_disabledBanner).text = "The mod is switched off for you (Options > Enabled) - nothing is active.";
				}
				((VisualElement)_disabledBanner).style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(Plugin.Active ? 1 : 0));
			}
			bool flag = IsDirty();
			if (_revertButton != null)
			{
				_revertButton.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(!flag));
			}
			if (_footLabel != null)
			{
				((TextElement)_footLabel).text = (flag ? "Preview only - saved when you close (F6 or Esc)" : "F6 or Esc to close");
			}
		}

		private static bool Plugin_IsAdmin()
		{
			return Plugin.IsAdmin;
		}

		private bool Build()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: 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)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (_root != null)
			{
				return true;
			}
			FFLog.Verbose("Menu: building UI");
			_panel = ScriptableObject.CreateInstance<PanelSettings>();
			_panel.scaleMode = (PanelScaleMode)2;
			_panel.referenceResolution = new Vector2Int(1920, 1080);
			_panel.screenMatchMode = (PanelScreenMatchMode)0;
			_panel.match = 0.5f;
			_panel.sortingOrder = 32000f;
			ThemeStyleSheet val = ShaderProvider.LoadOptional<ThemeStyleSheet>("Assets/FocusFollowedPlayer/FocusFollowedPlayerTheme.tss");
			if ((Object)(object)val != (Object)null)
			{
				_panel.themeStyleSheet = val;
			}
			else
			{
				FFLog.Verbose("Menu: no theme stylesheet in bundle, using code styling only");
			}
			_go = new GameObject("FFP_Menu");
			Object.DontDestroyOnLoad((Object)(object)_go);
			_go.SetActive(false);
			UIDocument val2 = _go.AddComponent<UIDocument>();
			val2.panelSettings = _panel;
			_go.SetActive(true);
			_root = val2.rootVisualElement;
			if (_root == null)
			{
				FFLog.Error("Menu: UI Toolkit root not available.");
				return false;
			}
			_root.style.position = StyleEnum<Position>.op_Implicit((Position)1);
			_root.style.left = StyleLength.op_Implicit(0f);
			_root.style.top = StyleLength.op_Implicit(0f);
			_root.style.right = StyleLength.op_Implicit(0f);
			_root.style.bottom = StyleLength.op_Implicit(0f);
			_root.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			_root.style.justifyContent = StyleEnum<Justify>.op_Implicit((Justify)1);
			_root.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			_root.pickingMode = (PickingMode)1;
			Theme.SetFont(_root, bold: false);
			_root.Add(BuildWindow());
			FFLog.Verbose("Menu: UI built (" + _pages.Count + " tabs)");
			return true;
		}

		private VisualElement BuildWindow()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//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)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: 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_015a: 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_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Expected O, but got Unknown
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			val.style.width = StyleLength.op_Implicit(600f);
			val.style.height = StyleLength.op_Implicit(Length.Percent(70f));
			val.style.backgroundColor = StyleColor.op_Implicit(Theme.Panel);
			Theme.Radius(val, 8f);
			Theme.Borders(val, 2f, Theme.Border);
			val.style.borderTopColor = StyleColor.op_Implicit(Theme.Gold);
			((CallbackEventHandler)val).RegisterCallback<PointerDownEvent>((EventCallback<PointerDownEvent>)delegate(PointerDownEvent e)
			{
				((EventBase)e).StopPropagation();
			}, (TrickleDown)0);
			VisualElement val2 = BuildHeader();
			val2.style.flexShrink = StyleFloat.op_Implicit(0f);
			val.Add(val2);
			VisualElement val3 = BuildTabBar();
			val3.style.flexShrink = StyleFloat.op_Implicit(0f);
			val.Add(val3);
			_disabledBanner = Theme.MakeLabel("The server admin has switched this mod off - nothing is active.", 14f, new Color(1f, 0.35f, 0.3f, 1f));
			((VisualElement)_disabledBanner).style.alignSelf = StyleEnum<Align>.op_Implicit((Align)2);
			Theme.Margin((VisualElement)(object)_disabledBanner, 6f, 0f);
			((VisualElement)_disabledBanner).style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			((VisualElement)_disabledBanner).style.flexShrink = StyleFloat.op_Implicit(0f);
			val.Add((VisualElement)(object)_disabledBanner);
			_viewport = new VisualElement();
			_viewport.style.overflow = StyleEnum<Overflow>.op_Implicit((Overflow)1);
			_viewport.style.flexBasis = StyleLength.op_Implicit(0f);
			_viewport.style.flexGrow = StyleFloat.op_Implicit(1f);
			_viewport.style.flexShrink = StyleFloat.op_Implicit(1f);
			_viewport.style.minHeight = StyleLength.op_Implicit(0f);
			val.Add(_viewport);
			_body = new VisualElement();
			Theme.Padding(_body, 14f, 18f);
			_body.style.flexShrink = StyleFloat.op_Implicit(0f);
			_viewport.Add(_body);
			BuildScrollBar();
			((CallbackEventHandler)_viewport).RegisterCallback<GeometryChangedEvent>((EventCallback<GeometryChangedEvent>)delegate
			{
				UpdateScroll();
			}, (TrickleDown)0);
			((CallbackEventHandler)_body).RegisterCallback<GeometryChangedEvent>((EventCallback<GeometryChangedEvent>)delegate
			{
				UpdateScroll();
			}, (TrickleDown)0);
			((CallbackEventHandler)val).RegisterCallback<WheelEvent>((EventCallback<WheelEvent>)OnWheel, (TrickleDown)0);
			AddPage(_body, BuildPlayersPage());
			AddPage(_body, BuildOptionsPage());
			AddPage(_body, BuildGlowPage());
			AddPage(_body, BuildArrowPage());
			AddPage(_body, BuildAlertsPage());
			SelectTab(0);
			VisualElement val4 = new VisualElement();
			val4.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			val4.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			val4.style.justifyContent = StyleEnum<Justify>.op_Implicit((Justify)1);
			Theme.Margin(val4, 0f, 10f);
			val4.style.flexShrink = StyleFloat.op_Implicit(0f);
			_revertButton = Widgets.Button("Revert changes", RevertChanges);
			_revertButton.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			_revertButton.style.marginRight = StyleLength.op_Implicit(12f);
			val4.Add(_revertButton);
			_footLabel = Theme.MakeLabel("F6 or Esc to close", 12f, Theme.TextDim);
			val4.Add((VisualElement)(object)_footLabel);
			val.Add(val4);
			return val;
		}

		private VisualElement BuildHeader()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			val.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			val.style.justifyContent = StyleEnum<Justify>.op_Implicit((Justify)3);
			val.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			Theme.Padding(val, 12f, 18f);
			val.style.borderBottomWidth = StyleFloat.op_Implicit(1f);
			val.style.borderBottomColor = StyleColor.op_Implicit(Theme.Border);
			Label val2 = Theme.MakeLabel("FOCUS FOLLOWED PLAYER", 26f, Theme.Gold, title: true);
			((VisualElement)val2).style.letterSpacing = StyleLength.op_Implicit(2f);
			val.Add((VisualElement)(object)val2);
			val.Add(Widgets.Button("X", Close));
			return val;
		}

		private VisualElement BuildTabBar()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_00aa: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			val.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			Theme.Padding(val, 0f, 12f);
			val.style.backgroundColor = StyleColor.op_Implicit(new Color(0f, 0f, 0f, 0.25f));
			string[] array = new string[5] { "Players", "Options", "Glow", "Arrow", "Alerts" };
			for (int i = 0; i < array.Length; i++)
			{
				int idx = i;
				VisualElement tab = new VisualElement();
				tab.style.flexGrow = StyleFloat.op_Implicit(1f);
				tab.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
				Theme.Padding(tab, 9f, 8f);
				tab.style.borderBottomWidth = StyleFloat.op_Implicit(2f);
				tab.style.borderBottomColor = StyleColor.op_Implicit(Color.clear);
				Theme.Fade(tab, "background-color", "border-bottom-color");
				Label val2 = Theme.MakeLabel(array[i].ToUpperInvariant(), 15f, Theme.TextDim, title: true);
				((VisualElement)val2).style.letterSpacing = StyleLength.op_Implicit(1f);
				tab.Add((VisualElement)(object)val2);
				((CallbackEventHandler)tab).RegisterCallback<PointerEnterEvent>((EventCallback<PointerEnterEvent>)delegate
				{
					//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)
					if (!IsSelected(idx))
					{
						tab.style.backgroundColor = StyleColor.op_Implicit(Theme.PanelLight);
					}
				}, (TrickleDown)0);
				((CallbackEventHandler)tab).RegisterCallback<PointerLeaveEvent>((EventCallback<PointerLeaveEvent>)delegate
				{
					//IL_000b: 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)
					tab.style.backgroundColor = StyleColor.op_Implicit(Color.clear);
				}, (TrickleDown)0);
				((CallbackEventHandler)tab).RegisterCallback<ClickEvent>((EventCallback<ClickEvent>)delegate
				{
					SelectTab(idx);
				}, (TrickleDown)0);
				_tabs.Add(tab);
				_tabLabels.Add(val2);
				val.Add(tab);
			}
			return val;
		}

		private bool IsSelected(int i)
		{
			return _selected == i;
		}

		private void SelectTab(int idx)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			FFLog.Verbose("Menu: tab selected " + idx);
			_selected = idx;
			AlertsTabOpen = idx == 4;
			for (int i = 0; i < _pages.Count; i++)
			{
				bool flag = i == idx;
				_pages[i].style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(!flag));
				((VisualElement)_tabLabels[i]).style.color = StyleColor.op_Implicit(flag ? Theme.Gold : Theme.TextDim);
				_tabs[i].style.borderBottomColor = StyleColor.op_Implicit(flag ? Theme.Gold : Color.clear);
				_tabs[i].style.backgroundColor = StyleColor.op_Implicit(Color.clear);
			}
			_scroll = 0f;
			UpdateScroll();
		}

		private void BuildScrollBar()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0018: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_00fb: 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_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			_scrollTrack = new VisualElement();
			_scrollTrack.style.position = StyleEnum<Position>.op_Implicit((Position)1);
			_scrollTrack.style.right = StyleLength.op_Implicit(4f);
			_scrollTrack.style.top = StyleLength.op_Implicit(6f);
			_scrollTrack.style.bottom = StyleLength.op_Implicit(6f);
			_scrollTrack.style.width = StyleLength.op_Implicit(8f);
			_scrollTrack.style.backgroundColor = StyleColor.op_Implicit(Theme.Track);
			Theme.Radius(_scrollTrack, 4f);
			_scrollTrack.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			_viewport.Add(_scrollTrack);
			_scrollThumb = new VisualElement();
			_scrollThumb.style.position = StyleEnum<Position>.op_Implicit((Position)1);
			_scrollThumb.style.left = StyleLength.op_Implicit(0f);
			_scrollThumb.style.right = StyleLength.op_Implicit(0f);
			Theme.Radius(_scrollThumb, 4f);
			_scrollThumb.style.backgroundColor = StyleColor.op_Implicit(Theme.GoldDim);
			_scrollTrack.Add(_scrollThumb);
			((CallbackEventHandler)_scrollTrack).RegisterCallback<GeometryChangedEvent>((EventCallback<GeometryChangedEvent>)delegate
			{
				UpdateScroll();
			}, (TrickleDown)0);
			((CallbackEventHandler)_scrollTrack).RegisterCallback<PointerDownEvent>((EventCallback<PointerDownEvent>)delegate(PointerDownEvent e)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: 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)
				//IL_0037: 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)
				Rect layout = _scrollTrack.layout;
				float height = ((Rect)(ref layout)).height;
				layout = _scrollThumb.layout;
				float num = height - ((Rect)(ref layout)).height;
				if (num > 1f)
				{
					float y = ((PointerEventBase<PointerDownEvent>)(object)e).localPosition.y;
					layout = _scrollThumb.layout;
					SetScroll((y - ((Rect)(ref layout)).height * 0.5f) / num * MaxScroll());
				}
				((EventBase)e).StopPropagation();
			}, (TrickleDown)0);
			((CallbackEventHandler)_scrollThumb).RegisterCallback<PointerDownEvent>((EventCallback<PointerDownEvent>)delegate(PointerDownEvent e)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				PointerCaptureHelper.CapturePointer((IEventHandler)(object)_scrollThumb, ((PointerEventBase<PointerDownEvent>)(object)e).pointerId);
				_thumbDragStartY = ((PointerEventBase<PointerDownEvent>)(object)e).position.y;
				_thumbDragStartScroll = _scroll;
				((EventBase)e).StopPropagation();
			}, (TrickleDown)0);
			((CallbackEventHandler)_scrollThumb).RegisterCallback<PointerMoveEvent>((EventCallback<PointerMoveEvent>)delegate(PointerMoveEvent e)
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				if (PointerCaptureHelper.HasPointerCapture((IEventHandler)(object)_scrollThumb, ((PointerEventBase<PointerMoveEvent>)(object)e).pointerId))
				{
					Rect layout = _scrollTrack.layout;
					float height = ((Rect)(ref layout)).height;
					layout = _scrollThumb.layout;
					float num = height - ((Rect)(ref layout)).height;
					float num2 = MaxScroll();
					if (!(num <= 1f) && !(num2 <= 0f))
					{
						SetScroll(_thumbDragStartScroll + (((PointerEventBase<PointerMoveEvent>)(object)e).position.y - _thumbDragStartY) * num2 / num);
					}
				}
			}, (TrickleDown)0);
			((CallbackEventHandler)_scrollThumb).RegisterCallback<PointerUpEvent>((EventCallback<PointerUpEvent>)delegate(PointerUpEvent e)
			{
				if (PointerCaptureHelper.HasPointerCapture((IEventHandler)(object)_scrollThumb, ((PointerEventBase<PointerUpEvent>)(object)e).pointerId))
				{
					PointerCaptureHelper.ReleasePointer((IEventHandler)(object)_scrollThumb, ((PointerEventBase<PointerUpEvent>)(object)e).pointerId);
				}
			}, (TrickleDown)0);
		}

		private float MaxScroll()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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)
			if (_body == null || _viewport == null)
			{
				return 0f;
			}
			Rect layout = _body.layout;
			float height = ((Rect)(ref layout)).height;
			layout = _viewport.layout;
			float num = height - ((Rect)(ref layout)).height;
			return (float.IsNaN(num) || num < 0f) ? 0f : num;
		}

		private void SetScroll(float value)
		{
			_scroll = Mathf.Clamp(value, 0f, MaxScroll());
			UpdateScroll();
		}

		private void OnWheel(WheelEvent e)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!(MaxScroll() <= 0.5f))
			{
				SetScroll(_scroll + e.delta.y * 18f);
				((EventBase)e).StopPropagation();
			}
		}

		private void UpdateScroll()
		{
			//IL_005f: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			if (_body == null || _viewport == null || _scrollTrack == null || _scrollThumb == null)
			{
				return;
			}
			float num = MaxScroll();
			if (_scroll > num)
			{
				_scroll = num;
			}
			_body.style.top = StyleLength.op_Implicit(0f - _scroll);
			bool flag = num > 0.5f;
			_scrollTrack.style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(!flag));
			if (flag)
			{
				Rect layout = _viewport.layout;
				float height = ((Rect)(ref layout)).height;
				layout = _body.layout;
				float height2 = ((Rect)(ref layout)).height;
				layout = _scrollTrack.layout;
				float num2 = ((Rect)(ref layout)).height;
				if (float.IsNaN(num2) || num2 <= 1f)
				{
					num2 = height - 12f;
				}
				if (!float.IsNaN(height) && !float.IsNaN(height2) && !(height2 <= 1f) && !(num2 <= 1f))
				{
					float num3 = Mathf.Max(24f, num2 * height / height2);
					float num4 = Mathf.Max(0f, num2 - num3);
					_scrollThumb.style.height = StyleLength.op_Implicit(num3);
					_scrollThumb.style.top = StyleLength.op_Implicit(num4 * (_scroll / num));
				}
			}
		}

		private void AddPage(VisualElement body, VisualElement page)
		{
			_pages.Add(page);
			body.Add(page);
		}

		private VisualElement BuildPlayersPage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			VisualElement val2 = Widgets.Section("Following");
			VisualElement val3 = new VisualElement();
			val3.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			val3.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			val3.style.justifyContent = StyleEnum<Justify>.op_Implicit((Justify)3);
			Theme.Padding(val3, 2f, 8f);
			_followingLabel = Theme.MakeLabel("Nobody", 18f, Theme.TextDim);
			val3.Add((VisualElement)(object)_followingLabel);
			_stopButton = Widgets.Button("Stop following", delegate
			{
				FFLog.Verbose("Menu: 'Stop following' clicked");
				FollowManager.SetFollowed("");
				RefreshAll();
			});
			val3.Add(_stopButton);
			val2.Add(val3);
			val.Add(val2);
			VisualElement val4 = Widgets.Section("Players online");
			_playerList = new VisualElement();
			val4.Add(_playerList);
			Label val5 = Theme.MakeLabel("Click a player to follow them. Click again to stop.", 12f, Theme.TextDim);
			Theme.Margin((VisualElement)(object)val5, 6f, 0f);
			val4.Add((VisualElement)(object)val5);
			val.Add(val4);
			return val;
		}

		private VisualElement BuildOptionsPage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			VisualElement val = new VisualElement();
			VisualElement val2 = Widgets.Section("Follow");
			val2.Add(Widgets.Toggle("Enabled", Plugin.Enabled, _refresh, "Off: the whole mod is off for you - no glow, arrow or hiding, you cannot follow or be followed"));
			val2.Add(Widgets.Toggle("Show health of all players", Plugin.ShowHealthOfAllPlayers, _refresh, "Off: while following, only the followed player keeps a health bar"));
			val2.Add(Widgets.Toggle("Force map position sharing", Plugin.ForcePublicPosition, _refresh, "Needs this mod on their side"));
			val2.Add(Widgets.Toggle("Allow others to follow me", Plugin.AllowFollowAndShare, _refresh, "Off: nobody can follow you or turn your map position on"));
			val.Add(val2);
			VisualElement val3 = Widgets.Section("Server");
			val3.Add(Widgets.Toggle("Mod enabled for everybody", Plugin.ModEnabled, _refresh, "Server admins only. Off: the mod does nothing for anyone", Plugin_IsAdmin));
			val.Add(val3);
			VisualElement val4 = Widgets.Section("Logging");
			val4.Add(Widgets.Toggle("Verbose logging", Plugin.VerboseLogging, _refresh, "Off: only warnings and errors in the BepInEx log"));
			val.Add(val4);
			return val;
		}

		private VisualElement BuildAlertsPage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			VisualElement val2 = Widgets.Section("Portal notification (right side of the screen)");
			val2.Add(Widgets.Toggle("Show when the followed player takes a portal", Plugin.NotifyPortals, _refresh, "Shows the portal name. Needs this mod on their side"));
			val2.Add(Widgets.Slider("Distance from right edge (px)", Plugin.NotifyOffsetX, "0", _refresh));
			val2.Add(Widgets.Slider("Height on screen (%)", Plugin.NotifyOffsetY, "0", _refresh));
			val2.Add(Widgets.Slider("Text size", Plugin.NotifyFontSize, "0", _refresh));
			val2.Add(Widgets.Slider("Show for (seconds)", Plugin.NotifyDuration, "0", _refresh));
			val.Add(val2);
			Label val3 = Theme.MakeLabel("While this tab is open an example line is shown so you can place it.", 12f, Theme.TextDim);
			Theme.Margin((VisualElement)(object)val3, 6f, 0f);
			val.Add((VisualElement)(object)val3);
			return val;
		}

		private VisualElement BuildGlowPage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			VisualElement val = new VisualElement();
			VisualElement val2 = Widgets.Section("Outline");
			val2.Add(Widgets.Toggle("Enabled", Plugin.GlowEnabled, _refresh));
			val2.Add(Widgets.Toggle("Visible through walls", Plugin.GlowThroughWalls, _refresh));
			val2.Add(Widgets.Slider("Intensity", Plugin.GlowIntensity, "0.0", _refresh));
			val2.Add(Widgets.Slider("Width (px)", Plugin.GlowWidth, "0.0", _refresh));
			val.Add(val2);
			VisualElement val3 = Widgets.Section("Range");
			val3.Add(Widgets.Toggle("Sync with arrow", Plugin.SyncRanges, _refresh, "On: the glow stops exactly where the arrow starts"));
			val3.Add(Widgets.Toggle("Glow at any distance", Plugin.AllowGlowAtAnyDistance, _refresh, "On: ignores the distance below (and Sync). The arrow keeps its distance. Needs the player to be loaded"));
			val3.Add(Widgets.Slider("Glow visible up to (m)", () => Plugin.GlowRange(), Plugin.SetGlowRange, 25f, 200f, (float v) => v.ToString("0"), _refresh));
			val.Add(val3);
			VisualElement val4 = Widgets.Section("Color");
			val4.Add(Widgets.ColorEditor(Plugin.GlowColor, _refresh));
			val.Add(val4);
			return val;
		}

		private VisualElement BuildArrowPage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			VisualElement val = new VisualElement();
			VisualElement val2 = Widgets.Section("Arrow");
			val2.Add(Widgets.Toggle("Enabled", Plugin.ArrowEnabled, _refresh));
			val2.Add(Widgets.Toggle("Follow vertical axis", Plugin.ArrowFollowVertical, _refresh, "Tilt up/down toward targets above or below you"));
			val2.Add(Widgets.Toggle("See-through arrow", Plugin.ArrowSeeThrough, _refresh, "Only a thin border is drawn, the inside is see-through"));
			val2.Add(Widgets.Toggle("Use glow color", Plugin.ArrowFollowGlowColor, _refresh));
			val2.Add(Widgets.Toggle("Sync with glow", Plugin.SyncRanges, _refresh, "On: the arrow starts exactly where the glow stops"));
			val2.Add(Widgets.Slider("Show beyond (m)", () => Plugin.ArrowRange(), Plugin.SetArrowRange, 25f, 200f, (float v) => v.ToString("0"), _refresh));
			val2.Add(Widgets.Slider("Size", () => Plugin.ArrowSize.Value, delegate(float v)
			{
				Plugin.ArrowSize.Value = v;
			}, 0.5f, 1f, (float v) => Mathf.RoundToInt(v * 100f) + "%", _refresh, 100f));
			val2.Add(Widgets.Slider("Height (m)", Plugin.ArrowHeight, "0.0", _refresh));
			val2.Add(Widgets.Slider("Distance from you (m)", Plugin.ArrowOffset, "0.0", _refresh));
			val.Add(val2);
			_arrowColorBox = Widgets.Section("Arrow color");
			_arrowColorBox.Add(Widgets.ColorEditor(Plugin.ArrowColor, _refresh));
			val.Add(_arrowColorBox);
			return val;
		}

		private void RebuildPlayersIfChanged()
		{
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			if (_playerList == null)
			{
				return;
			}
			List<string> otherPlayerNames = FollowManager.GetOtherPlayerNames();
			string text = Plugin.FollowedPlayer.Value ?? "";
			int num = Mathf.Max(1, (otherPlayerNames.Count + 7 - 1) / 7);
			if (_page >= num)
			{
				_page = num - 1;
			}
			if (_page < 0)
			{
				_page = 0;
			}
			string text2 = text + "|" + _page + "|" + string.Join(",", otherPlayerNames.ToArray());
			if (!(text2 == _playersKey))
			{
				_playersKey = text2;
				FFLog.Verbose("Menu: rebuilding player list, followed='" + text + "', page " + (_page + 1) + "/" + num + ", online=[" + string.Join(", ", otherPlayerNames.ToArray()) + "]");
				_playerList.Clear();
				_rowStatus.Clear();
				if (otherPlayerNames.Count == 0)
				{
					Label val = Theme.MakeLabel("No other players online", 14f, Theme.TextDim);
					Theme.Padding((VisualElement)(object)val, 6f, 8f);
					_playerList.Add((VisualElement)(object)val);
				}
				int num2 = _page * 7;
				int num3 = Mathf.Min(otherPlayerNames.Count, num2 + 7);
				for (int i = num2; i < num3; i++)
				{
					_playerList.Add(PlayerRow(otherPlayerNames[i], otherPlayerNames[i] == text));
				}
				if (text != "" && !otherPlayerNames.Contains(text))
				{
					_playerList.Add(PlayerRow(text, selected: true));
				}
				if (num > 1)
				{
					_playerList.Add(BuildPager(num));
				}
			}
		}

		private VisualElement BuildPager(int pages)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			VisualElement val = new VisualElement();
			val.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			val.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			val.style.justifyContent = StyleEnum<Justify>.op_Implicit((Justify)3);
			Theme.Margin(val, 6f, 0f);
			val.Add(Widgets.Button("< Prev", delegate
			{
				ChangePage(-1);
			}));
			val.Add((VisualElement)(object)Theme.MakeLabel("Page " + (_page + 1) + " / " + pages, 14f, Theme.TextDim));
			val.Add(Widgets.Button("Next >", delegate
			{
				ChangePage(1);
			}));
			return val;
		}

		private void ChangePage(int delta)
		{
			_page += delta;
			if (_page < 0)
			{
				_page = 0;
			}
			FFLog.Verbose("Menu: player list page -> " + (_page + 1));
			_playersKey = "";
			RefreshAll();
		}

		private VisualElement PlayerRow(string name, bool selected)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			VisualElement row = new VisualElement();
			row.style.flexDirection = StyleEnum<FlexDirection>.op_Implicit((FlexDirection)2);
			row.style.alignItems = StyleEnum<Align>.op_Implicit((Align)2);
			Theme.Padding(row, 7f, 10f);
			Theme.Radius(row, 4f);
			row.style.marginBottom = StyleLength.op_Implicit(3f);
			row.style.backgroundColor = StyleColor.op_Implicit((Color)(selected ? new Color(1f, 0.72f, 0.36f, 0.18f) : Theme.PanelLight));
			Theme.Borders(row, 1f, selected ? Theme.Gold : Color.clear);
			Theme.Fade(row, "background-color");
			VisualElement val = new VisualElement();
			val.pickingMode = (PickingMode)1;
			val.style.width = StyleLength.op_Implicit(8f);
			val.style.height = StyleLength.op_Implicit(8f);
			val.style.marginRight = StyleLength.op_Implicit(10f);
			val.style.flexShrink = StyleFloat.op_Implicit(0f);
			Theme.Radius(val, 4f);
			val.style.backgroundColor = StyleColor.op_Implicit(selected ? Theme.Gold : Theme.Border);
			row.Add(val);
			Label val2 = Theme.MakeLabel(name, 17f, selected ? Theme.Gold : Theme.Text);
			((VisualElement)val2).style.flexGrow = StyleFloat.op_Implicit(1f);
			row.Add((VisualElement)(object)val2);
			Label val3 = Theme.MakeLabel("", 13f, new Color(0.45f, 0.8f, 1f, 1f));
			((VisualElement)val3).style.flexShrink = StyleFloat.op_Implicit(1f);
			((VisualElement)val3).style.maxWidth = StyleLength.op_Implicit(230f);
			((VisualElement)val3).style.marginRight = StyleLength.op_Implicit(12f);
			((VisualElement)val3).style.overflow = StyleEnum<Overflow>.op_Implicit((Overflow)1);
			((VisualElement)val3).style.whiteSpace = StyleEnum<WhiteSpace>.op_Implicit((WhiteSpace)1);
			((VisualElement)val3).style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)1);
			row.Add((VisualElement)(object)val3);
			Label val4 = Theme.MakeLabel("", 13f, selected ? Theme.GoldDim : Theme.TextDim);
			((VisualElement)val4).style.flexShrink = StyleFloat.op_Implicit(0f);
			row.Add((VisualElement)(object)val4);
			_rowStatus.Add(new RowStatus
			{
				Name = name,
				Label = val4,
				Portal = val3
			});
			StyleColor backgroundColor = row.style.backgroundColor;
			Color normal = ((StyleColor)(ref backgroundColor)).value;
			((CallbackEventHandler)row).RegisterCallback<PointerEnterEvent>((EventCallback<PointerEnterEvent>)delegate
			{
				//IL_000b: 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)
				row.style.backgroundColor = StyleColor.op_Implicit(Theme.PanelHover);
			}, (TrickleDown)0);
			((CallbackEventHandler)row).RegisterCallback<PointerLeaveEvent>((EventCallback<PointerLeaveEvent>)delegate
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				row.style.backgroundColor = StyleColor.op_Implicit(normal);
			}, (TrickleDown)0);
			((CallbackEventHandler)row).RegisterCallback<ClickEvent>((EventCallback<ClickEvent>)delegate
			{
				FFLog.Verbose("Menu: player row clicked '" + name + "' (currently followed=" + selected + ")");
				FollowManager.SetFollowed(selected ? "" : name);
				RefreshAll();
			}, (TrickleDown)0);
			return row;
		}

		private void UpdatePlayerStatuses()
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: 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)
			for (int i = 0; i < _rowStatus.Count; i++)
			{
				RowStatus rowStatus = _rowStatus[i];
				bool flag = rowStatus.Name == (Plugin.FollowedPlayer.Value ?? "");
				string text = FollowManager.GetStatus(rowStatus.Name).Describe();
				((TextElement)rowStatus.Label).text = (flag ? ("FOLLOWING  |  " + text) : text);
				string portal;
				bool flag2 = Plugin.Active && PortalTracker.TryGetPending(rowStatus.Name, out portal);
				if (rowStatus.Portal != null)
				{
					((VisualElement)rowStatus.Portal).style.display = StyleEnum<DisplayStyle>.op_Implicit((DisplayStyle)(!flag2));
					if (flag2)
					{
						PortalTracker.TryGetPending(rowStatus.Name, out portal);
						((TextEle