Decompiled source of BoringCompass v0.5.0

BoringCompass.dll

Decompiled 8 hours 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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.BoringCompass.Core;
using JG224.BoringCompass.UI;
using JG224.ModCore.API;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("BoringCompass")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("BoringCompass")]
[assembly: AssemblyTitle("BoringCompass")]
[assembly: AssemblyVersion("0.5.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 JG224.BoringCompass
{
	internal sealed class BoringCompassConfig
	{
		private readonly ConfigFile _file;

		private readonly List<ConfigEntryBase> _entries = new List<ConfigEntryBase>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<bool> AutoWidth { get; }

		internal ConfigEntry<bool> ShowWhenHudHidden { get; }

		internal ConfigEntry<bool> ShowDeathMarker { get; }

		internal ConfigEntry<bool> ShowMapMarkers { get; }

		internal ConfigEntry<float> MarkerDistance { get; }

		internal ConfigEntry<CompassHeadingSource> HeadingSource { get; }

		internal ConfigEntry<float> Scale { get; }

		internal ConfigEntry<float> OffsetX { get; }

		internal ConfigEntry<float> OffsetY { get; }

		internal ConfigEntry<string> Color { get; }

		internal ConfigEntry<float> Opacity { get; }

		private BoringCompassConfig(ConfigFile file)
		{
			_file = file;
			Enabled = Bind("Enabled", value: true, "Show the scrolling compass at the top centre of the screen.");
			AutoWidth = Bind("AutoWidth", value: true, "Fit the compass and its offsets inside the current screen; Scale remains the maximum size.");
			ShowWhenHudHidden = Bind("ShowWhenHudHidden", value: false, "Keep the compass visible when the HUD is hidden.");
			ShowDeathMarker = Bind("ShowDeathMarker", value: true, "Show your latest death map pin at any distance. Checked or removed death pins stay hidden.");
			ShowMapMarkers = Bind("ShowMapMarkers", value: false, "Show nearby unchecked saved map pins, including pins available through map sharing. Excludes other death pins and live player/event pins.");
			MarkerDistance = BindRange("MarkerDistance", 100f, 1f, 10000f, "Maximum horizontal distance in metres for non-death map markers. Does not limit your latest death marker.");
			HeadingSource = Bind("HeadingSource", CompassHeadingSource.Camera, "Camera, PlayerFacing, or Movement. Movement falls back to player facing while stationary.");
			Scale = BindRange("Scale", 1.3f, 0.5f, 2f, "Compass display scale.");
			OffsetX = BindRange("OffsetX", 0f, -1000f, 1000f, "Horizontal compass offset in screen pixels.");
			OffsetY = BindRange("OffsetY", 8f, -500f, 500f, "Distance down from the top edge in screen pixels.");
			Color = Bind("Color", "#F2E6C9", "Compass HTML color, for example #F2E6C9.");
			Opacity = BindRange("Opacity", 0.95f, 0f, 1f, "Compass opacity.");
		}

		internal static BoringCompassConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyCompassConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			string text2 = Path.Combine(configDirectory, "jg224.BoringCompass.cfg");
			BoringCompassConfig boringCompassConfig = new BoringCompassConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(text2, true, plugin.Info.Metadata)));
			if (text != null)
			{
				boringCompassConfig.ImportLegacyGeneralTweaks(text, log);
			}
			return boringCompassConfig;
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (ConfigEntryBase entry in _entries)
			{
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, entry.Definition.Section, entry.Definition.Key, (ConfigScope)1, (Func<string>)(() => Convert.ToString(entry.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		internal static Color ParseColor(string value, float opacity)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			Color result = default(Color);
			if (!ColorUtility.TryParseHtmlString(value ?? string.Empty, ref result))
			{
				((Color)(ref result))..ctor(0.95f, 0.9f, 0.79f);
			}
			result.a = Mathf.Clamp01(opacity);
			return result;
		}

		private void ImportLegacyGeneralTweaks(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				Enabled.Value = val.Bind<bool>("Compass", "Enabled", Enabled.Value, (ConfigDescription)null).Value;
				HeadingSource.Value = val.Bind<CompassHeadingSource>("Compass", "HeadingSource", HeadingSource.Value, (ConfigDescription)null).Value;
				Scale.Value = val.Bind<float>("Compass", "Scale", Scale.Value, (ConfigDescription)null).Value;
				OffsetX.Value = val.Bind<float>("Compass", "OffsetX", OffsetX.Value, (ConfigDescription)null).Value;
				OffsetY.Value = val.Bind<float>("Compass", "OffsetY", OffsetY.Value, (ConfigDescription)null).Value;
				Color.Value = val.Bind<string>("Compass", "Color", Color.Value, (ConfigDescription)null).Value;
				Opacity.Value = val.Bind<float>("Compass", "Opacity", Opacity.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported compass settings from " + Path.GetFileName(path) + " into jg224.BoringCompass.cfg."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import GeneralTweaks compass settings; using BoringCompass defaults. " + ex.Message));
				}
			}
		}

		private ConfigEntry<T> Bind<T>(string key, T value, string description)
		{
			ConfigEntry<T> val = _file.Bind<T>("Compass", key, value, description);
			_entries.Add((ConfigEntryBase)(object)val);
			return val;
		}

		private ConfigEntry<float> BindRange(string key, float value, float min, float max, string description)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			ConfigEntry<float> val = _file.Bind<float>("Compass", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
			_entries.Add((ConfigEntryBase)(object)val);
			return val;
		}
	}
	[BepInPlugin("jg224.BoringCompass", "BoringCompass", "0.5.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.BoringCompass";

		public const string PluginName = "BoringCompass";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 1;

		public static readonly ModuleId ModuleId = new ModuleId("boringcompass");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static BoringCompassConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = BoringCompassConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before BoringCompass.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyCompassOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains the compass; BoringCompass is paused to prevent duplicate overlays.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 1.0.0 or newer."));
				}
				else
				{
					_harmony = new Harmony("jg224.BoringCompass");
					_harmony.PatchAll(Assembly.GetExecutingAssembly());
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local late-camera compass overlay ready.");
					Game.isModded = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("BoringCompass 0.5.0 loaded; protocol " + 1 + "."));
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("BoringCompass failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("Invalid plugin version.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.BoringCompass", "BoringCompass", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)8, "jg224.boringcompass.", 1, Array.Empty<string>()));
			_registrations.Add(Core.Ui.Reserve(new UiReservation(ModuleId, (UiSurface)6, "top-center.compass", 20, false)));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				CompassOverlay.Destroy();
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				CompassOverlay.Destroy();
			}, 0));
		}

		private static bool TryGetLegacyCompassOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(1, 0, 0)) < 0;
			}
			return false;
		}

		private void Shutdown()
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			CompassOverlay.Destroy();
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}
	}
}
namespace JG224.BoringCompass.UI
{
	internal sealed class CompassMarkers
	{
		private static readonly FieldRef<Minimap, List<PinData>> Pins = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");

		private readonly Transform _parent;

		private readonly List<PinData> _candidates = new List<PinData>();

		private readonly List<Image> _images = new List<Image>();

		private Minimap _map;

		private PinData _latestDeath;

		private float _refreshAt;

		private bool _showDeath;

		private bool _showOthers;

		private float _range;

		internal CompassMarkers(Transform parent)
		{
			_parent = parent;
		}

		internal void Update(Player player, float heading, Color color, BoringCompassConfig config)
		{
			//IL_001e: 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_00a3: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0111: 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_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: 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)
			Minimap instance = Minimap.instance;
			float num = CompassMarkerRules.DistanceLimit(config.MarkerDistance.Value);
			Vector3 position = ((Component)player).transform.position;
			if ((Object)(object)instance != (Object)(object)_map || Time.unscaledTime >= _refreshAt || config.ShowDeathMarker.Value != _showDeath || config.ShowMapMarkers.Value != _showOthers || num != _range)
			{
				_map = instance;
				_showDeath = config.ShowDeathMarker.Value;
				_showOthers = config.ShowMapMarkers.Value;
				_range = num;
				Refresh(position);
				_refreshAt = Time.unscaledTime + 0.25f;
			}
			int num2 = 0;
			if (Object.op_Implicit((Object)(object)instance))
			{
				foreach (PinData candidate in _candidates)
				{
					Vector3 val = candidate.m_pos - position;
					if (ShouldShow(candidate, val) && Object.op_Implicit((Object)(object)candidate.m_icon))
					{
						float relativeDegrees = CompassMarkerRules.RelativeBearing(val.x, val.z, heading);
						float num3 = CompassLayoutRules.EdgeOpacity(relativeDegrees);
						if (!(num3 <= 0f))
						{
							Image image = GetImage(num2++);
							image.sprite = candidate.m_icon;
							((Graphic)image).color = new Color(1f, 1f, 1f, color.a * num3);
							((Graphic)image).rectTransform.anchoredPosition = new Vector2(CompassLayoutRules.HorizontalOffset(relativeDegrees), -68f);
							((Component)image).gameObject.SetActive(true);
						}
					}
				}
			}
			for (int i = num2; i < _images.Count; i++)
			{
				((Component)_images[i]).gameObject.SetActive(false);
			}
		}

		private void Refresh(Vector3 position)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			_candidates.Clear();
			_latestDeath = null;
			if (!Object.op_Implicit((Object)(object)_map) || (!_showDeath && !_showOthers))
			{
				return;
			}
			PlayerProfile val = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.GetPlayerProfile() : null);
			List<PinData> list = Pins.Invoke(_map);
			if (_showDeath && val != null && val.HaveDeathPoint())
			{
				Vector3 deathPoint = val.GetDeathPoint();
				foreach (PinData item in list)
				{
					Vector3 val2 = item.m_pos - deathPoint;
					if (CompassMarkerRules.MatchesLatestDeath((int)item.m_type == 4, item.m_ownerID, val2.x, val2.y, val2.z))
					{
						_latestDeath = item;
					}
				}
			}
			foreach (PinData item2 in list)
			{
				if (item2 != _latestDeath && ShouldShow(item2, item2.m_pos - position))
				{
					_candidates.Add(item2);
				}
			}
			if (_latestDeath != null && ShouldShow(_latestDeath, _latestDeath.m_pos - position))
			{
				_candidates.Add(_latestDeath);
			}
		}

		private bool ShouldShow(PinData pin, Vector3 delta)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0032: 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)
			if (!pin.m_shouldDelete)
			{
				return CompassMarkerRules.ShouldShow((int)pin.m_type == 4, pin == _latestDeath, pin.m_save, pin.m_checked, _showDeath, _showOthers, delta.x, delta.z, _range);
			}
			return false;
		}

		private Image GetImage(int index)
		{
			//IL_003f: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (index < _images.Count)
			{
				return _images[index];
			}
			GameObject val = new GameObject("MapMarker" + index, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_parent, false);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).raycastTarget = false;
			val2.preserveAspect = true;
			RectTransform rectTransform = ((Graphic)val2).rectTransform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 1f);
			rectTransform.anchorMax = val3;
			rectTransform.anchorMin = val3;
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.sizeDelta = new Vector2(24f, 24f);
			_images.Add(val2);
			return val2;
		}
	}
	internal static class CompassOverlay
	{
		private const int TickCount = 25;

		private const float TickDegrees = 5f;

		private const float CompassOutlineWidth = 3f / 14f;

		private const float CompassFaceDilate = 0.2f;

		private const float PointerOutlineWidth = 5f / 52f;

		private const float PointerFaceDilate = 0.05f;

		private static readonly List<TextMeshProUGUI> CompassTicks = new List<TextMeshProUGUI>();

		private static readonly List<Material> FontMaterials = new List<Material>();

		private static GameObject _root;

		private static RectTransform _compass;

		private static Image _compassRail;

		private static TextMeshProUGUI _pointer;

		private static TMP_FontAsset _compassFont;

		private static CompassMarkers _markers;

		private static int _renderedCompassCentre = int.MinValue;

		private static float _lastHeading = float.NaN;

		private static float _retryAt;

		private static bool _reportedFailure;

		internal static void TickAfterCamera()
		{
			//IL_010e: 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)
			if (Application.isBatchMode || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				if (Object.op_Implicit((Object)(object)_root))
				{
					_root.SetActive(false);
				}
			}
			else
			{
				if (Time.realtimeSinceStartup < _retryAt)
				{
					return;
				}
				try
				{
					EnsureCreated();
					if (!Object.op_Implicit((Object)(object)_root))
					{
						return;
					}
					_root.SetActive(true);
					UpdateCompass(Player.m_localPlayer);
					if (_reportedFailure)
					{
						_reportedFailure = false;
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"Compass overlay recovered after its HUD initialization failure.");
						}
						ICoreServices core = Plugin.Core;
						if (core != null)
						{
							core.Modules.SetState(Plugin.ModuleId, (ModuleRuntimeState)2, "Client-local late-camera compass overlay ready.");
						}
					}
				}
				catch (Exception ex)
				{
					ReleaseObjects();
					_retryAt = Time.realtimeSinceStartup + 5f;
					if (!_reportedFailure)
					{
						_reportedFailure = true;
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogError((object)("Compass overlay failed; retrying in five seconds: " + ex));
						}
						ICoreServices core2 = Plugin.Core;
						if (core2 != null)
						{
							core2.Modules.SetState(Plugin.ModuleId, (ModuleRuntimeState)3, "Compass overlay initialization/update failed; waiting to retry.");
						}
					}
				}
			}
		}

		internal static void Destroy()
		{
			ReleaseObjects();
			_retryAt = 0f;
		}

		private static void ReleaseObjects()
		{
			if (Object.op_Implicit((Object)(object)_root))
			{
				_root.SetActive(false);
				Object.Destroy((Object)(object)_root);
			}
			foreach (Material fontMaterial in FontMaterials)
			{
				if (Object.op_Implicit((Object)(object)fontMaterial))
				{
					Object.Destroy((Object)(object)fontMaterial);
				}
			}
			FontMaterials.Clear();
			_root = null;
			_compass = null;
			_compassRail = null;
			_pointer = null;
			_compassFont = null;
			_markers = null;
			_renderedCompassCentre = int.MinValue;
			_lastHeading = float.NaN;
			CompassTicks.Clear();
		}

		private static void EnsureCreated()
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_root))
			{
				return;
			}
			ReleaseObjects();
			if (Object.op_Implicit((Object)(object)Hud.instance) && Object.op_Implicit((Object)(object)Hud.instance.m_healthText))
			{
				_compassFont = Hud.instance.m_healthText.font;
			}
			if (!Object.op_Implicit((Object)(object)_compassFont) && Object.op_Implicit((Object)(object)MessageHud.instance) && Object.op_Implicit((Object)(object)MessageHud.instance.m_messageText))
			{
				_compassFont = MessageHud.instance.m_messageText.font;
			}
			if (!Object.op_Implicit((Object)(object)_compassFont))
			{
				_compassFont = TMP_Settings.defaultFontAsset;
			}
			if (!Object.op_Implicit((Object)(object)_compassFont) || !Object.op_Implicit((Object)(object)((TMP_Asset)_compassFont).material) || !Object.op_Implicit((Object)(object)_compassFont.atlasTexture))
			{
				return;
			}
			_root = new GameObject("JG224.BoringCompass.Overlay");
			_root.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_root);
			Canvas obj = _root.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 200;
			obj.pixelPerfect = false;
			_root.AddComponent<CanvasScaler>().uiScaleMode = (ScaleMode)0;
			((Behaviour)_root.AddComponent<GraphicRaycaster>()).enabled = false;
			GameObject val = new GameObject("Compass", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_root.transform, false);
			_compass = val.GetComponent<RectTransform>();
			_compass.anchorMin = new Vector2(0.5f, 1f);
			_compass.anchorMax = new Vector2(0.5f, 1f);
			_compass.pivot = new Vector2(0.5f, 1f);
			_compass.sizeDelta = new Vector2(900f, 96f);
			val.AddComponent<RectMask2D>();
			_compassRail = CreateRule("TopRail", (Transform)(object)_compass, new Vector2(896f, 2f), new Vector2(0f, -5f));
			Vector2 val3 = default(Vector2);
			for (int i = 0; i < 25; i++)
			{
				TextMeshProUGUI val2 = CreateCompassText("Tick" + i, (Transform)(object)_compass);
				RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
				((Vector2)(ref val3))..ctor(0.5f, 1f);
				rectTransform.anchorMax = val3;
				rectTransform.anchorMin = val3;
				rectTransform.pivot = new Vector2(0.5f, 1f);
				rectTransform.sizeDelta = new Vector2(58f, 58f);
				CompassTicks.Add(val2);
			}
			_pointer = CreateCompassPointer((Transform)(object)_compass);
			_markers = new CompassMarkers((Transform)(object)_compass);
			_root.SetActive(true);
			foreach (TextMeshProUGUI compassTick in CompassTicks)
			{
				ApplyMaterialStyle(compassTick, 3f / 14f, 0.2f);
			}
			ApplyMaterialStyle(_pointer, 5f / 52f, 0.05f);
		}

		private static TextMeshProUGUI CreateCompassText(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			if (Object.op_Implicit((Object)(object)_compassFont))
			{
				((TMP_Text)val2).font = _compassFont;
			}
			((TMP_Text)val2).fontSize = 14f;
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((TMP_Text)val2).fontWeight = (FontWeight)900;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)258;
			((TMP_Text)val2).richText = true;
			((TMP_Text)val2).enableAutoSizing = false;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)0;
			((Graphic)val2).raycastTarget = false;
			return val2;
		}

		private static void ApplyMaterialStyle(TextMeshProUGUI text, float outlineWidth, float faceDilate)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Behaviour)text).isActiveAndEnabled || !Object.op_Implicit((Object)(object)((TMP_Text)text).fontSharedMaterial))
			{
				throw new InvalidOperationException("Compass text has no active font material.");
			}
			Material fontMaterial = ((TMP_Text)text).fontMaterial;
			if (!Object.op_Implicit((Object)(object)fontMaterial))
			{
				throw new InvalidOperationException("Compass font material could not be initialized.");
			}
			FontMaterials.Add(fontMaterial);
			((TMP_Text)text).outlineColor = Color32.op_Implicit(Color.black);
			((TMP_Text)text).outlineWidth = outlineWidth;
			if (fontMaterial.HasProperty(ShaderUtilities.ID_FaceDilate))
			{
				fontMaterial.SetFloat(ShaderUtilities.ID_FaceDilate, faceDilate);
			}
			((TMP_Text)text).UpdateMeshPadding();
		}

		private static TextMeshProUGUI CreateCompassPointer(Transform parent)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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)
			TextMeshProUGUI obj = CreateCompassText("Pointer", parent);
			((TMP_Text)obj).text = "<size=13>▼</size>";
			((TMP_Text)obj).fontWeight = (FontWeight)700;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)258;
			RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 1f);
			rectTransform.anchorMax = val;
			rectTransform.anchorMin = val;
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.anchoredPosition = new Vector2(0f, -1f);
			rectTransform.sizeDelta = new Vector2(22f, 17f);
			return obj;
		}

		private static Image CreateRule(string name, Transform parent, Vector2 size, Vector2 position)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).raycastTarget = false;
			RectTransform rectTransform = ((Graphic)obj).rectTransform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 1f);
			rectTransform.anchorMax = val2;
			rectTransform.anchorMin = val2;
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.anchoredPosition = position;
			rectTransform.sizeDelta = size;
			return obj;
		}

		private static void UpdateCompass(Player player)
		{
			//IL_009f: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: 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_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: 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)
			BoringCompassConfig settings = Plugin.Settings;
			bool flag = settings != null && settings.Enabled.Value && (settings.ShowWhenHudHidden.Value || (Object.op_Implicit((Object)(object)Hud.instance) && !Hud.IsUserHidden() && Object.op_Implicit((Object)(object)Hud.instance.m_rootObject) && Hud.instance.m_rootObject.activeInHierarchy));
			((Component)_compass).gameObject.SetActive(flag);
			if (!flag)
			{
				return;
			}
			float num = CompassFitRules.Scale(settings.Scale.Value, Screen.width, Screen.height, settings.AutoWidth.Value);
			((Transform)_compass).localScale = Vector3.one * num;
			float num2 = (settings.AutoWidth.Value ? CompassFitRules.OffsetX(settings.OffsetX.Value, Screen.width, num) : settings.OffsetX.Value);
			float num3 = (settings.AutoWidth.Value ? Mathf.Clamp(settings.OffsetY.Value, 0f, Mathf.Max(0f, (float)Screen.height - 96f * num)) : settings.OffsetY.Value);
			_compass.anchoredPosition = new Vector2(num2, 0f - num3);
			Color val = BoringCompassConfig.ParseColor(settings.Color.Value, settings.Opacity.Value);
			((Graphic)_pointer).color = val;
			Color color = val;
			color.a *= 0.75f;
			((Graphic)_compassRail).color = color;
			float num4 = Heading(player, settings.HeadingSource.Value);
			int num5 = Mathf.RoundToInt(num4 / 5f) * 5;
			bool flag2 = CompassLayoutRules.ShouldRefreshText(_renderedCompassCentre, num5);
			int num6 = 12;
			for (int i = 0; i < 25; i++)
			{
				int num7 = num5 + (i - num6) * 5;
				float num8 = HeadingRules.Normalize(num7);
				float relativeDegrees = Mathf.DeltaAngle(num4, num8);
				TextMeshProUGUI val2 = CompassTicks[i];
				CompassTickKind compassTickKind = HeadingRules.TickKindFor(num7);
				if (flag2)
				{
					string bearingText = HeadingRules.BearingText(num7);
					string text = CompassLayoutRules.TickMarkFor(compassTickKind);
					string text2 = CompassLayoutRules.BearingNumberFor(compassTickKind, bearingText);
					switch (compassTickKind)
					{
					case CompassTickKind.Cardinal:
						((TMP_Text)val2).text = "<size=16>" + text + "</size>\n<size=24>" + HeadingRules.LabelFor(num8) + "</size>";
						break;
					case CompassTickKind.Ordinal:
						((TMP_Text)val2).text = "<size=16>" + text + "</size>\n<size=18>" + HeadingRules.LabelFor(num8) + "</size>";
						break;
					default:
						((TMP_Text)val2).text = "<size=16>" + text + "</size>\n<size=14>" + text2 + "</size>";
						break;
					}
				}
				Color color2 = val;
				color2.a *= CompassLayoutRules.EdgeOpacity(relativeDegrees);
				((Graphic)val2).color = color2;
				((TMP_Text)val2).rectTransform.anchoredPosition = new Vector2(CompassLayoutRules.HorizontalOffset(relativeDegrees), CompassLayoutRules.VerticalOffset(compassTickKind));
			}
			if (flag2)
			{
				_renderedCompassCentre = num5;
			}
			_markers.Update(player, num4, val, settings);
		}

		private static float Heading(Player player, CompassHeadingSource source)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val;
			switch (source)
			{
			case CompassHeadingSource.PlayerFacing:
				val = ((Component)player).transform.forward;
				break;
			case CompassHeadingSource.Movement:
				val = ((Character)player).GetMoveDir();
				if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
				{
					val = ((Component)player).transform.forward;
				}
				break;
			default:
				val = (Object.op_Implicit((Object)(object)GameCamera.instance) ? ((Component)GameCamera.instance).transform.forward : ((Component)player).transform.forward);
				break;
			}
			Vector3 forward = ((Component)player).transform.forward;
			_lastHeading = CompassFitRules.Heading(val.x, val.z, forward.x, forward.z, _lastHeading);
			return _lastHeading;
		}
	}
}
namespace JG224.BoringCompass.Patches
{
	[HarmonyPatch(typeof(GameCamera), "LateUpdate")]
	internal static class CompassCameraLateUpdatePatch
	{
		private static void Postfix()
		{
			CompassOverlay.TickAfterCamera();
		}
	}
}
namespace JG224.BoringCompass.Core
{
	internal static class CompassFitRules
	{
		internal static float Heading(float x, float z, float facingX, float facingZ, float previous)
		{
			if (Finite(x) && Finite(z) && x * x + z * z >= 0.0001f)
			{
				return HeadingRules.Normalize((float)(Math.Atan2(x, z) * 180.0 / Math.PI));
			}
			if (Finite(previous))
			{
				return HeadingRules.Normalize(previous);
			}
			if (Finite(facingX) && Finite(facingZ) && facingX * facingX + facingZ * facingZ >= 0.0001f)
			{
				return HeadingRules.Normalize((float)(Math.Atan2(facingX, facingZ) * 180.0 / Math.PI));
			}
			return 0f;
		}

		internal static float Scale(float requested, int width, int height, bool autoWidth)
		{
			requested = (Finite(requested) ? Math.Max(0.5f, Math.Min(2f, requested)) : 1f);
			if (!autoWidth)
			{
				return requested;
			}
			return Math.Min(requested, Math.Max(0.01f, Math.Min((float)Math.Max(1, width - 16) / 900f, (float)Math.Max(1, height) / 96f)));
		}

		internal static float OffsetX(float requested, int width, float scale)
		{
			float num = Math.Max(0f, ((float)width - 900f * scale) / 2f);
			if (!Finite(requested))
			{
				return 0f;
			}
			return Math.Max(0f - num, Math.Min(num, requested));
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	public enum CompassHeadingSource
	{
		Camera,
		PlayerFacing,
		Movement
	}
	internal enum CompassTickKind
	{
		Minor,
		Ordinal,
		Cardinal
	}
	internal static class HeadingRules
	{
		private static readonly string[] Labels = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };

		internal static float Normalize(float degrees)
		{
			degrees %= 360f;
			if (!(degrees < 0f))
			{
				return degrees;
			}
			return degrees + 360f;
		}

		internal static string LabelFor(float degrees)
		{
			int num = (int)Math.Floor((Normalize(degrees) + 22.5f) / 45f) % Labels.Length;
			return Labels[num];
		}

		internal static bool IsNamedBearing(int degrees)
		{
			return (degrees % 45 + 45) % 45 == 0;
		}

		internal static bool IsCardinalBearing(int degrees)
		{
			return (degrees % 90 + 90) % 90 == 0;
		}

		internal static CompassTickKind TickKindFor(int degrees)
		{
			if (IsCardinalBearing(degrees))
			{
				return CompassTickKind.Cardinal;
			}
			if (!IsNamedBearing(degrees))
			{
				return CompassTickKind.Minor;
			}
			return CompassTickKind.Ordinal;
		}

		internal static string BearingText(int degrees)
		{
			return ((degrees % 360 + 360) % 360).ToString("000", CultureInfo.InvariantCulture);
		}
	}
	internal static class CompassLayoutRules
	{
		internal const float ViewportHeight = 96f;

		internal const float TickTextHeight = 58f;

		private const float PixelsPerDegree = 10.625f;

		private const float OpaqueHalfDegrees = 34f;

		private const float VisibleHalfDegrees = 42f;

		internal static string TickMarkFor(CompassTickKind kind)
		{
			return "|";
		}

		internal static string BearingNumberFor(CompassTickKind kind, string bearingText)
		{
			if (kind != CompassTickKind.Minor)
			{
				return string.Empty;
			}
			return bearingText;
		}

		internal static float HorizontalOffset(float relativeDegrees)
		{
			return relativeDegrees * 10.625f;
		}

		internal static float VerticalOffset(CompassTickKind kind)
		{
			return -6f;
		}

		internal static bool ShouldRefreshText(int renderedCentre, int currentCentre)
		{
			return renderedCentre != currentCentre;
		}

		internal static float EdgeOpacity(float relativeDegrees)
		{
			if (float.IsNaN(relativeDegrees) || float.IsInfinity(relativeDegrees))
			{
				return 0f;
			}
			float num = Math.Abs(relativeDegrees);
			if (num <= 34f)
			{
				return 1f;
			}
			if (num >= 42f)
			{
				return 0f;
			}
			return (42f - num) / 8f;
		}
	}
	internal static class CompassMarkerRules
	{
		internal const float DefaultDistance = 100f;

		internal static bool MatchesLatestDeath(bool isDeath, long ownerId, float deltaX, float deltaY, float deltaZ)
		{
			if (isDeath && ownerId == 0L && deltaX == 0f && deltaY == 0f)
			{
				return deltaZ == 0f;
			}
			return false;
		}

		internal static float DistanceLimit(float configured)
		{
			if (!Finite(configured))
			{
				return 100f;
			}
			return Math.Max(1f, Math.Min(10000f, configured));
		}

		internal static bool ShouldShow(bool isDeath, bool isLatestDeath, bool saved, bool isChecked, bool showDeath, bool showOthers, float deltaX, float deltaZ, float range)
		{
			if (isChecked || !Finite(deltaX) || !Finite(deltaZ))
			{
				return false;
			}
			if (isDeath)
			{
				return showDeath && isLatestDeath;
			}
			double num = DistanceLimit(range);
			if (showOthers && saved)
			{
				return (double)deltaX * (double)deltaX + (double)deltaZ * (double)deltaZ <= num * num;
			}
			return false;
		}

		internal static float RelativeBearing(float deltaX, float deltaZ, float heading)
		{
			if (deltaX == 0f && deltaZ == 0f)
			{
				return 0f;
			}
			return HeadingRules.Normalize((float)(Math.Atan2(deltaX, deltaZ) * 180.0 / Math.PI) - heading + 180f) - 180f;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class LegacyCompassConfigRules
	{
		internal const string CurrentFileName = "jg224.BoringCompass.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.BoringCompass.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
}