Decompiled source of RunicExploration v1.0.0

RunicExploration.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RunicExploration.Core;
using RunicExploration.Integration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Runic Exploration")]
[assembly: AssemblyDescription("Bounded navigation through already-known Valheim map state.")]
[assembly: AssemblyCompany("Chazman")]
[assembly: AssemblyProduct("Runic Exploration")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: InternalsVisibleTo("RunicExploration.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RunicExploration
{
	public enum ExplorationPanelSide
	{
		Left,
		Right
	}
	internal static class ExplorationConfig
	{
		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> ShowKnownPinBrowser { get; private set; }

		internal static ConfigEntry<bool> ShowNavigationReadout { get; private set; }

		internal static ConfigEntry<bool> ShowSailingReadout { get; private set; }

		internal static ConfigEntry<bool> IncludeSharedPins { get; private set; }

		internal static ConfigEntry<float> RefreshInterval { get; private set; }

		internal static ConfigEntry<int> MaximumResults { get; private set; }

		internal static ConfigEntry<float> UiScale { get; private set; }

		internal static ConfigEntry<float> ControllerScaleMultiplier { get; private set; }

		internal static ConfigEntry<ExplorationPanelSide> PanelSide { get; private set; }

		internal static ConfigEntry<bool> VerboseLogging { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable the known-world navigation overlay. It never changes map/world data.");
			ShowKnownPinBrowser = config.Bind<bool>("Panels", "KnownPinBrowser", true, "Show bounded search/filter results for saved pins on already explored map cells.");
			ShowNavigationReadout = config.Bind<bool>("Panels", "SelectedPinNavigation", true, "Show straight-line distance and direction to the selected known pin.");
			ShowSailingReadout = config.Bind<bool>("Panels", "SailingReadout", true, "Show current local ship, wind, current biome, and selected-known-pin distance.");
			IncludeSharedPins = config.Bind<bool>("KnownMap", "IncludeSharedPins", true, "Include saved shared pins only when their map cell is already explored locally/shared.");
			RefreshInterval = config.Bind<float>("Performance", "RefreshIntervalSeconds", 0.5f, new ConfigDescription("Bounded known-pin fingerprint and local navigation refresh interval.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), Array.Empty<object>()));
			MaximumResults = config.Bind<int>("Display", "MaximumVisibleResults", 12, new ConfigDescription("Maximum visible search results. The hard ceiling is 24.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 24), Array.Empty<object>()));
			UiScale = config.Bind<float>("Display", "UiScale", 1f, new ConfigDescription("Known-map panel scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.75f, 1.75f), Array.Empty<object>()));
			ControllerScaleMultiplier = config.Bind<float>("Display", "ControllerScaleMultiplier", 1.15f, new ConfigDescription("Additional readability scale while Valheim reports a gamepad active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.5f), Array.Empty<object>()));
			PanelSide = config.Bind<ExplorationPanelSide>("Display", "PanelSide", ExplorationPanelSide.Left, "Place the panel on the left or right safe-area edge.");
			VerboseLogging = config.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log index rebuild counts only; never logs pin names, coordinates, or hidden state.");
		}
	}
	[BepInPlugin("chazman.RunicExploration", "Runic Exploration", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chazman.RunicExploration";

		public const string Name = "Runic Exploration";

		public const string Version = "1.0.0";

		private Harmony _harmony;

		private ExplorationRuntime _runtime;

		internal static Plugin Instance { get; private set; }

		internal ExplorationRuntime Runtime => _runtime;

		private void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			Instance = this;
			ExplorationConfig.Bind(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			try
			{
				ValheimContracts.VerifyInstalledSignatures();
				_runtime = new ExplorationRuntime(((BaseUnityPlugin)this).Logger, Application.isBatchMode);
				if (!Application.isBatchMode)
				{
					_harmony = new Harmony("chazman.RunicExploration");
					_harmony.PatchAll(typeof(Plugin).Assembly);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Exploration v1.0.0 ready: bounded search and navigation for already-explored saved pins only.");
				if (Application.isBatchMode)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Dedicated/batch process detected; Exploration is intentionally inert and unpatched.");
				}
			}
			catch (Exception ex)
			{
				Cleanup();
				((BaseUnityPlugin)this).Logger.LogError((object)("Runic Exploration failed closed during startup; the vanilla map and world remain unchanged. " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private void Update()
		{
			try
			{
				_runtime?.Update();
			}
			catch (Exception exception)
			{
				_runtime?.FailClosed("update", exception);
			}
		}

		private void OnGUI()
		{
			try
			{
				_runtime?.Draw();
			}
			catch (Exception exception)
			{
				_runtime?.FailClosed("draw", exception);
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs arguments)
		{
			try
			{
				ExplorationRuntime runtime = _runtime;
				if (runtime != null)
				{
					object definition;
					if (arguments == null)
					{
						definition = null;
					}
					else
					{
						ConfigEntryBase changedSetting = arguments.ChangedSetting;
						definition = ((changedSetting != null) ? changedSetting.Definition : null);
					}
					runtime.OnConfigurationChanged((ConfigDefinition)definition);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Exploration configuration refresh failed closed: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			Cleanup();
			Instance = null;
		}

		private void Cleanup()
		{
			_runtime?.Dispose();
			_runtime = null;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
		}
	}
}
namespace RunicExploration.Integration
{
	internal sealed class ExplorationRuntime : IDisposable
	{
		private const string SearchControlName = "RunicExploration.Search";

		private readonly ManualLogSource _log;

		private readonly bool _inert;

		private readonly List<PinEvidence> _evidence = new List<PinEvidence>(256);

		private readonly KnownPinRebuildGate _rebuildGate = new KnownPinRebuildGate();

		private readonly OptionalPortalAdapter _portals;

		private KnownPinIndex _index = new KnownPinIndex(KnownPinIndexStatus.Ready, Array.Empty<KnownPinRecord>(), 0, 0, 0);

		private KnownPinSearchResult _search = new KnownPinSearchResult(Array.Empty<KnownPinRecord>(), 0, truncated: false);

		private GUIContent[] _resultContent = Array.Empty<GUIContent>();

		private GUIStyle _boxStyle;

		private GUIStyle _titleStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _smallStyle;

		private GUIStyle _buttonStyle;

		private GUIStyle _textFieldStyle;

		private Minimap _map;

		private int _playerInstanceId;

		private ulong _fingerprint;

		private bool _hasFingerprint;

		private bool _visible;

		private bool _failed;

		private bool _sensitiveCleared;

		private bool _searchFocused;

		private bool _panelRectValid;

		private Rect _panelRect;

		private float _nextRefresh;

		private float _nextPortalRefresh;

		private int _styleFontSize;

		private int _softFailureCount;

		private string _query = string.Empty;

		private string _summary = "Known pins: waiting for the local map.";

		private string _problem = string.Empty;

		private string _navigation = string.Empty;

		private string _sailing = string.Empty;

		private string _portalStatus = string.Empty;

		private string _categoryButtonText = "Category: all";

		private string _scopeButtonText = "Source: all";

		private KnownPinCategoryFilter _categoryFilter;

		private KnownPinScopeFilter _scopeFilter;

		private KnownPinRecord _selected;

		private bool _hasSelected;

		private ulong _navigationSignature;

		private ulong _sailingSignature;

		private bool _hasNavigationSignature;

		private bool _hasSailingSignature;

		internal bool BlocksMapInput
		{
			get
			{
				if (!_inert && !_failed && _visible && _searchFocused)
				{
					return ExplorationConfig.Enabled?.Value ?? false;
				}
				return false;
			}
		}

		internal ExplorationRuntime(ManualLogSource log, bool inert)
		{
			_log = log ?? throw new ArgumentNullException("log");
			_inert = inert;
			_portals = (inert ? null : new OptionalPortalAdapter());
		}

		internal bool BlocksMapPointer(Vector2 guiPosition)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!_inert && !_failed && _visible && _panelRectValid)
			{
				ConfigEntry<bool> enabled = ExplorationConfig.Enabled;
				if (enabled != null && enabled.Value)
				{
					return ((Rect)(ref _panelRect)).Contains(guiPosition);
				}
			}
			return false;
		}

		internal void Update()
		{
			if (_inert || _failed)
			{
				return;
			}
			ConfigEntry<bool> enabled = ExplorationConfig.Enabled;
			if (enabled == null || !enabled.Value || Game.m_noMap)
			{
				ClearSensitive();
				return;
			}
			if (!Minimap.IsOpen())
			{
				HidePanel();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Minimap instance = Minimap.instance;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null)
			{
				ClearSensitive();
				return;
			}
			_sensitiveCleared = false;
			int instanceID = ((Object)localPlayer).GetInstanceID();
			if (_map != instance || _playerInstanceId != instanceID)
			{
				ClearIndex();
				_map = instance;
				_playerInstanceId = instanceID;
			}
			_visible = true;
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _nextRefresh && unscaledTime >= 0f)
			{
				return;
			}
			_nextRefresh = unscaledTime + FiniteClamp(ExplorationConfig.RefreshInterval.Value, 0.25f, 3f, 0.5f);
			bool value = ExplorationConfig.IncludeSharedPins.Value;
			int sourceCount;
			ulong fingerprint;
			KnownPinCaptureStatus knownPinCaptureStatus = KnownPinSource.Capture(instance, value, _evidence, out fingerprint, out sourceCount);
			if (knownPinCaptureStatus != KnownPinCaptureStatus.Ready)
			{
				string text = ((knownPinCaptureStatus == KnownPinCaptureStatus.Oversized) ? "Known pin browser withheld: the local pin list exceeds the hard 10,000-pin ceiling." : "Known pin browser unavailable: explored-map evidence is incompatible.");
				KnownPinIndexStatus knownPinIndexStatus = ((knownPinCaptureStatus == KnownPinCaptureStatus.Oversized) ? KnownPinIndexStatus.Oversized : KnownPinIndexStatus.InvalidSource);
				if (_index.Status != knownPinIndexStatus || !string.Equals(_problem, text, StringComparison.Ordinal))
				{
					_problem = text;
					_index = new KnownPinIndex(knownPinIndexStatus, Array.Empty<KnownPinRecord>(), 0, 0, 0);
					RefreshSearch();
					ClearSelection();
				}
				_hasFingerprint = false;
				_rebuildGate.Reset();
			}
			else
			{
				_problem = string.Empty;
				fingerprint = Mix(fingerprint, localPlayer.GetPlayerID());
				fingerprint = Mix(fingerprint, (long)(value ? 1 : 0));
				if (_rebuildGate.ShouldRebuild(fingerprint, _fingerprint, _hasFingerprint, unscaledTime))
				{
					_index = KnownPinIndexer.Build(_evidence, localPlayer.GetPlayerID(), value);
					_fingerprint = fingerprint;
					_hasFingerprint = true;
					_rebuildGate.MarkPublished(unscaledTime);
					RestoreOrClearSelection();
					RefreshSearch();
					if (ExplorationConfig.VerboseLogging.Value)
					{
						_log.LogInfo((object)("Exploration rebuilt its bounded known-pin index: known-evidence=" + _index.Inspected + ", accepted=" + _index.Accepted + ", visible=" + _index.Records.Length + ", merged=" + _index.Merged + "."));
					}
				}
			}
			UpdateReadouts(localPlayer, instance, value);
			if (_portals != null && ExplorationConfig.ShowKnownPinBrowser.Value && (unscaledTime >= _nextPortalRefresh || unscaledTime < 0f))
			{
				_nextPortalRefresh = unscaledTime + 5f;
				_portals.Refresh(unscaledTime);
				_portalStatus = _portals.StatusLine;
			}
			else if (!ExplorationConfig.ShowKnownPinBrowser.Value)
			{
				_portalStatus = string.Empty;
			}
		}

		internal void Draw()
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: 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_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Unknown result type (might be due to invalid IL or missing references)
			//IL_0633: Unknown result type (might be due to invalid IL or missing references)
			//IL_0486: Unknown result type (might be due to invalid IL or missing references)
			//IL_068b: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_071f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0725: Invalid comparison between Unknown and I4
			//IL_072c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0733: Invalid comparison between Unknown and I4
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e2: Unknown result type (might be due to invalid IL or missing references)
			if (!_inert && !_failed && _visible && !Game.m_noMap)
			{
				ConfigEntry<bool> enabled = ExplorationConfig.Enabled;
				if (enabled != null && enabled.Value && Event.current != null)
				{
					bool value = ExplorationConfig.ShowKnownPinBrowser.Value;
					bool flag = ExplorationConfig.ShowNavigationReadout.Value && !string.IsNullOrEmpty(_navigation);
					bool flag2 = ExplorationConfig.ShowSailingReadout.Value && !string.IsNullOrEmpty(_sailing);
					bool flag3 = value && !string.IsNullOrEmpty(_portalStatus);
					if (!value && !flag && !flag2 && !flag3)
					{
						_searchFocused = false;
						_panelRectValid = false;
						return;
					}
					bool flag4 = false;
					try
					{
						flag4 = ZInput.IsGamepadActive();
					}
					catch (Exception)
					{
					}
					float num = Mathf.Clamp(FiniteClamp(ExplorationConfig.UiScale.Value, 0.75f, 1.75f, 1f) * (flag4 ? FiniteClamp(ExplorationConfig.ControllerScaleMultiplier.Value, 1f, 1.5f, 1.15f) : 1f), 0.75f, 2.625f);
					int fontSize = Mathf.Clamp(Mathf.RoundToInt(14f * num), 11, 28);
					EnsureStyles(fontSize);
					Rect safeArea = Screen.safeArea;
					float num2 = Mathf.Max(8f, 10f * num);
					float num3 = ((Rect)(ref safeArea)).width - num2 * 2f;
					float num4 = ((Rect)(ref safeArea)).height - num2 * 2f;
					if (!Finite(num3) || !Finite(num4) || num3 < 260f || num4 < 150f * num)
					{
						_searchFocused = false;
						_panelRectValid = false;
						return;
					}
					float num5 = Mathf.Min(520f * num, num3);
					float num6 = Mathf.Max(24f, 28f * num);
					int val = (value ? Math.Min(_search.Records.Length, Math.Max(5, Math.Min(24, ExplorationConfig.MaximumResults.Value))) : 0);
					float num7 = 118f * num + (flag ? (66f * num) : 0f) + (flag2 ? (66f * num) : 0f) + (flag3 ? (34f * num) : 0f);
					int val2 = Math.Max(0, Mathf.FloorToInt((((Rect)(ref safeArea)).height - num2 * 2f - num7) / num6));
					int num8 = Math.Min(val, val2);
					float num9 = Mathf.Min(num4, num7 + (float)num8 * num6);
					num9 = Mathf.Max(Mathf.Min(150f * num, num4), num9);
					float num10 = ((ExplorationConfig.PanelSide.Value == ExplorationPanelSide.Right) ? (((Rect)(ref safeArea)).xMax - num5 - num2) : (((Rect)(ref safeArea)).xMin + num2));
					_panelRect = new Rect(num10, ((Rect)(ref safeArea)).yMin + num2, num5, num9);
					_panelRectValid = true;
					GUI.Box(_panelRect, GUIContent.none, _boxStyle);
					float num11 = 12f * num;
					float num12 = ((Rect)(ref _panelRect)).y + num11;
					float num13 = ((Rect)(ref _panelRect)).width - num11 * 2f;
					GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 25f * num), "Runic Exploration - known world only", _titleStyle);
					num12 += 27f * num;
					if (value)
					{
						GUI.SetNextControlName("RunicExploration.Search");
						string text = BoundedText.Sanitize(GUI.TextField(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 27f * num), _query, 48, _textFieldStyle), 48, 1);
						if (!string.Equals(_query, text, StringComparison.Ordinal))
						{
							_query = text;
							RefreshSearch();
						}
						num12 += 31f * num;
						float num14 = (num13 - 6f * num) / 2f;
						if (GUI.Button(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num14, 25f * num), _categoryButtonText, _buttonStyle))
						{
							_categoryFilter = (KnownPinCategoryFilter)((int)(_categoryFilter + 1) % Enum.GetValues(typeof(KnownPinCategoryFilter)).Length);
							UpdateFilterButtonText();
							RefreshSearch();
						}
						if (GUI.Button(new Rect(((Rect)(ref _panelRect)).x + num11 + num14 + 6f * num, num12, num14, 25f * num), _scopeButtonText, _buttonStyle))
						{
							_scopeFilter = (KnownPinScopeFilter)((int)(_scopeFilter + 1) % Enum.GetValues(typeof(KnownPinScopeFilter)).Length);
							UpdateFilterButtonText();
							RefreshSearch();
						}
						num12 += 29f * num;
						string text2 = ((!string.IsNullOrEmpty(_problem)) ? _problem : _summary);
						GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 30f * num), text2, _smallStyle);
						num12 += 31f * num;
						int num15 = ClampVisibleRows(num8, _search.Records.Length, _resultContent.Length);
						for (int i = 0; i < num15; i++)
						{
							if (GUI.Button(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, num6 - 3f), _resultContent[i], _buttonStyle))
							{
								SelectAndCenter(_search.Records[i]);
							}
							num12 += num6;
						}
						if (_search.Records.Length == 0 && string.IsNullOrEmpty(_problem))
						{
							GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, num6), "No explored saved pins match this filter.", _smallStyle);
							num12 += num6;
						}
					}
					if (flag && num12 + 45f * num < ((Rect)(ref _panelRect)).yMax)
					{
						GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 62f * num), _navigation, _labelStyle);
						num12 += 66f * num;
					}
					if (flag2 && num12 + 45f * num < ((Rect)(ref _panelRect)).yMax)
					{
						GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 62f * num), _sailing, _labelStyle);
						num12 += 66f * num;
					}
					if (flag3 && num12 + 20f < ((Rect)(ref _panelRect)).yMax)
					{
						GUI.Label(new Rect(((Rect)(ref _panelRect)).x + num11, num12, num13, 30f * num), _portalStatus, _smallStyle);
					}
					_searchFocused = value && string.Equals(GUI.GetNameOfFocusedControl(), "RunicExploration.Search", StringComparison.Ordinal);
					if (_searchFocused && (int)Event.current.type == 4 && (int)Event.current.keyCode == 27)
					{
						GUI.FocusControl((string)null);
						_searchFocused = false;
						Event.current.Use();
					}
					return;
				}
			}
			_searchFocused = false;
			_panelRectValid = false;
		}

		internal void OnConfigurationChanged(ConfigDefinition definition)
		{
			_nextRefresh = 0f;
			_styleFontSize = 0;
			_hasFingerprint = false;
			_rebuildGate.Reset();
			_hasNavigationSignature = false;
			_hasSailingSignature = false;
			ConfigEntry<bool> enabled = ExplorationConfig.Enabled;
			if (enabled == null || !enabled.Value)
			{
				ClearSensitive();
			}
			else if (definition != (ConfigDefinition)null && string.Equals(definition.Section, "KnownMap", StringComparison.Ordinal) && string.Equals(definition.Key, "IncludeSharedPins", StringComparison.Ordinal))
			{
				ClearIndex();
			}
			else
			{
				RefreshSearch();
			}
			ConfigEntry<bool> verboseLogging = ExplorationConfig.VerboseLogging;
			if (verboseLogging != null && verboseLogging.Value)
			{
				_log.LogInfo((object)("Exploration configuration refreshed: " + ((definition == (ConfigDefinition)null) ? "unknown" : (definition.Section + "/" + definition.Key))));
			}
		}

		internal static int ClampVisibleRows(int requested, int records, int content)
		{
			if (requested <= 0 || records <= 0 || content <= 0)
			{
				return 0;
			}
			return Math.Min(requested, Math.Min(records, content));
		}

		internal void FailClosed(string operation, Exception exception)
		{
			if (!_failed)
			{
				_failed = true;
				ClearSensitive();
				_log.LogError((object)("Runic Exploration stopped its display after " + operation + "; the vanilla map and world remain unchanged. " + exception.GetType().Name + ": " + exception.Message));
			}
		}

		public void Dispose()
		{
			_portals?.Dispose();
			ClearSensitive();
			_boxStyle = (_titleStyle = (_labelStyle = (_smallStyle = (_buttonStyle = (_textFieldStyle = null)))));
		}

		private unsafe void UpdateReadouts(Player player, Minimap minimap, bool includeShared)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: 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)
			//IL_0163: 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_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 position = ((Component)player).transform.position;
				Vector3 position2 = default(Vector3);
				bool flag = _hasSelected && KnownPinSource.TryResolve(minimap, includeShared, _selected, out position2);
				if (_hasSelected && !flag)
				{
					ClearSelection();
				}
				if (flag && ExplorationConfig.ShowNavigationReadout.Value)
				{
					ulong num = Signature(_selected.EvidenceHash, Quantize(position.x, 5f), Quantize(position.y, 5f), Quantize(position.z, 5f));
					if (!_hasNavigationSignature || num != _navigationSignature)
					{
						_navigation = NavigationFormatter.FormatSelected(_selected, position.x, position.y, position.z);
						_navigationSignature = num;
						_hasNavigationSignature = true;
					}
				}
				else
				{
					_navigation = string.Empty;
					_hasNavigationSignature = false;
				}
				Ship val = (ExplorationConfig.ShowSailingReadout.Value ? player.GetControlledShip() : null);
				EnvMan instance = EnvMan.instance;
				if ((Object)(object)val != (Object)null && (Object)(object)instance != (Object)null)
				{
					float windAngleFactor = val.GetWindAngleFactor();
					float windIntensity = instance.GetWindIntensity();
					Speed speedSetting = val.GetSpeedSetting();
					bool flag2 = val.IsSailUp();
					Biome currentBiome = player.GetCurrentBiome();
					double? selectedDistance = (flag ? new double?(Math.Sqrt((double)(position2.x - position.x) * (double)(position2.x - position.x) + (double)(position2.z - position.z) * (double)(position2.z - position.z))) : ((double?)null));
					ulong hash = Signature((ulong)(long)speedSetting, flag2 ? 1 : 0, Quantize(windAngleFactor, 0.01f), Quantize(windIntensity, 0.01f));
					hash = Mix(hash, (long)currentBiome);
					hash = Mix(hash, selectedDistance.HasValue ? ((long)Math.Round(selectedDistance.Value / 10.0, MidpointRounding.AwayFromZero)) : (-1));
					if (!_hasSailingSignature || hash != _sailingSignature)
					{
						_sailing = NavigationFormatter.FormatSailing(((object)(*(Speed*)(&speedSetting))/*cast due to .constrained prefix*/).ToString(), flag2, windAngleFactor, windIntensity, ((object)(*(Biome*)(&currentBiome))/*cast due to .constrained prefix*/).ToString(), selectedDistance);
						_sailingSignature = hash;
						_hasSailingSignature = true;
					}
				}
				else
				{
					_sailing = string.Empty;
					_hasSailingSignature = false;
				}
			}
			catch (Exception ex)
			{
				_navigation = string.Empty;
				_sailing = string.Empty;
				_hasNavigationSignature = false;
				_hasSailingSignature = false;
				if (_softFailureCount < 3)
				{
					_softFailureCount++;
					_log.LogWarning((object)("Exploration ignored a local readout failure: " + ex.GetType().Name + "."));
				}
			}
		}

		private void SelectAndCenter(KnownPinRecord record)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_map == (Object)null || Game.m_noMap || !KnownPinSource.TryResolve(_map, ExplorationConfig.IncludeSharedPins.Value, record, out var position))
			{
				_hasFingerprint = false;
				_rebuildGate.Reset();
				ClearSelection();
				return;
			}
			_selected = record;
			_hasSelected = true;
			_hasNavigationSignature = false;
			_hasSailingSignature = false;
			_map.ShowPointOnMap(position);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				UpdateReadouts(localPlayer, _map, ExplorationConfig.IncludeSharedPins.Value);
			}
		}

		private void RefreshSearch()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			_search = KnownPinSearch.Filter(_index.Records, _query, _categoryFilter, _scopeFilter, Math.Max(5, Math.Min(24, ExplorationConfig.MaximumResults?.Value ?? 12)));
			_resultContent = (GUIContent[])(object)new GUIContent[_search.Records.Length];
			for (int i = 0; i < _search.Records.Length; i++)
			{
				_resultContent[i] = new GUIContent(_search.Records[i].DisplayRow);
			}
			_summary = "Known explored pins: " + _index.Records.Length.ToString(CultureInfo.InvariantCulture) + " | matches: " + _search.Records.Length.ToString(CultureInfo.InvariantCulture) + (_search.Truncated ? "+" : string.Empty) + " | merged duplicates: " + _index.Merged.ToString(CultureInfo.InvariantCulture);
		}

		private void RestoreOrClearSelection()
		{
			if (!_hasSelected)
			{
				return;
			}
			for (int i = 0; i < _index.Records.Length; i++)
			{
				KnownPinRecord selected = _index.Records[i];
				if (selected.EvidenceHash == _selected.EvidenceHash)
				{
					_selected = selected;
					return;
				}
			}
			ClearSelection();
		}

		private void ClearSelection()
		{
			_selected = default(KnownPinRecord);
			_hasSelected = false;
			_navigation = string.Empty;
			_hasNavigationSignature = false;
			_sailingSignature = 0uL;
			_hasSailingSignature = false;
		}

		private void ClearIndex()
		{
			_evidence.Clear();
			_index = new KnownPinIndex(KnownPinIndexStatus.Ready, Array.Empty<KnownPinRecord>(), 0, 0, 0);
			_hasFingerprint = false;
			_fingerprint = 0uL;
			_rebuildGate.Reset();
			_problem = string.Empty;
			ClearSelection();
			RefreshSearch();
		}

		private void ClearSensitive()
		{
			HidePanel();
			if (!_sensitiveCleared)
			{
				_sensitiveCleared = true;
				_query = string.Empty;
				ClearIndex();
				_map = null;
				_playerInstanceId = 0;
				_portalStatus = string.Empty;
				_navigation = string.Empty;
				_sailing = string.Empty;
			}
		}

		private void HidePanel()
		{
			_visible = false;
			_searchFocused = false;
			_panelRectValid = false;
		}

		private void EnsureStyles(int fontSize)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_00bb: 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_00d5: Expected O, but got Unknown
			//IL_00f4: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			//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_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			if (_boxStyle == null || _styleFontSize != fontSize)
			{
				_boxStyle = new GUIStyle(GUI.skin.box);
				_titleStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = fontSize + 2,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)3,
					wordWrap = false
				};
				_titleStyle.normal.textColor = new Color(0.96f, 0.82f, 0.4f, 1f);
				_labelStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = fontSize,
					alignment = (TextAnchor)0,
					wordWrap = true,
					richText = false
				};
				_smallStyle = new GUIStyle(_labelStyle)
				{
					fontSize = Math.Max(10, fontSize - 2)
				};
				_smallStyle.normal.textColor = new Color(0.78f, 0.86f, 0.9f, 1f);
				_buttonStyle = new GUIStyle(GUI.skin.button)
				{
					fontSize = Math.Max(10, fontSize - 1),
					alignment = (TextAnchor)3,
					wordWrap = false
				};
				_textFieldStyle = new GUIStyle(GUI.skin.textField)
				{
					fontSize = fontSize,
					alignment = (TextAnchor)3
				};
				_styleFontSize = fontSize;
			}
		}

		private static string CategoryFilterLabel(KnownPinCategoryFilter filter)
		{
			return filter switch
			{
				KnownPinCategoryFilter.TaggedAssets => "tagged assets", 
				KnownPinCategoryFilter.Tombstones => "tombstones", 
				KnownPinCategoryFilter.Beds => "beds", 
				KnownPinCategoryFilter.Custom => "custom", 
				KnownPinCategoryFilter.Bosses => "bosses", 
				_ => "all", 
			};
		}

		private static string ScopeFilterLabel(KnownPinScopeFilter filter)
		{
			return filter switch
			{
				KnownPinScopeFilter.Personal => "personal", 
				KnownPinScopeFilter.Shared => "shared", 
				_ => "all", 
			};
		}

		private void UpdateFilterButtonText()
		{
			_categoryButtonText = "Category: " + CategoryFilterLabel(_categoryFilter);
			_scopeButtonText = "Source: " + ScopeFilterLabel(_scopeFilter);
		}

		private static int Quantize(float value, float unit)
		{
			if (float.IsNaN(value) || float.IsInfinity(value) || unit <= 0f)
			{
				return 0;
			}
			double num = Math.Round(value / unit, MidpointRounding.AwayFromZero);
			if (!(num > 2147483647.0))
			{
				if (!(num < -2147483648.0))
				{
					return (int)num;
				}
				return int.MinValue;
			}
			return int.MaxValue;
		}

		private static float FiniteClamp(float value, float minimum, float maximum, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				value = fallback;
			}
			return Math.Max(minimum, Math.Min(maximum, value));
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static ulong Signature(ulong first, int second, int third, int fourth)
		{
			return Mix(Mix(Mix((0x14650FB0739D0383L ^ first) * 1099511628211L, second), third), fourth);
		}

		private static ulong Signature(ulong first, int second, int third, int fourth, int fifth)
		{
			return Mix(Signature(first, second, third, fourth), fifth);
		}

		private static ulong Mix(ulong hash, long value)
		{
			hash ^= (ulong)value;
			return hash * 1099511628211L;
		}

		private static ulong Mix(ulong hash, int value)
		{
			return Mix(hash, (long)value);
		}
	}
	[HarmonyPatch(typeof(Minimap), "InTextInput", new Type[] { })]
	internal static class MinimapTextInputPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(ref bool __result)
		{
			if (Plugin.Instance?.Runtime?.BlocksMapInput == true)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapLeftDown", new Type[] { typeof(UIInputHandler) })]
	internal static class MinimapLeftDownPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			return !BlocksPointer();
		}

		private static bool BlocksPointer()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) == true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapLeftUp", new Type[] { typeof(UIInputHandler) })]
	internal static class MinimapLeftUpPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) != true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapLeftClick", new Type[] { })]
	internal static class MinimapLeftClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) != true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapDblClick", new Type[] { })]
	internal static class MinimapDoubleClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) != true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapMiddleClick", new Type[] { typeof(UIInputHandler) })]
	internal static class MinimapMiddleClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) != true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	[HarmonyPatch(typeof(Minimap), "OnMapRightClick", new Type[] { typeof(UIInputHandler) })]
	internal static class MinimapRightClickPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Instance?.Runtime?.BlocksMapPointer(CurrentGuiPointer()) != true;
		}

		private static Vector2 CurrentGuiPointer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 mousePosition = Input.mousePosition;
			return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
		}
	}
	internal enum KnownPinCaptureStatus
	{
		Ready,
		Oversized,
		InvalidMap
	}
	internal static class KnownPinSource
	{
		internal const int HardMaximumTextureSize = 4096;

		internal static KnownPinCaptureStatus Capture(Minimap minimap, bool includeSharedCoverage, List<PinEvidence> destination, out ulong fingerprint, out int sourceCount)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected I4, but got Unknown
			fingerprint = 1469598103934665603uL;
			sourceCount = 0;
			destination?.Clear();
			if (destination == null || !ValheimContracts.TryRead(minimap, out var view) || !ValidCoverage(view, includeSharedCoverage))
			{
				return KnownPinCaptureStatus.InvalidMap;
			}
			sourceCount = view.Pins.Count;
			fingerprint = Mix(fingerprint, includeSharedCoverage ? 1 : 0);
			if (sourceCount > 10000)
			{
				return KnownPinCaptureStatus.Oversized;
			}
			for (int i = 0; i < sourceCount; i++)
			{
				PinData val = view.Pins[i];
				if (val != null)
				{
					Vector3 pos = val.m_pos;
					bool save = val.m_save;
					bool flag = IsKnown(view, pos, includeSharedCoverage);
					if (save && flag)
					{
						fingerprint = Mix(fingerprint, Quantize(pos.x));
						fingerprint = Mix(fingerprint, Quantize(pos.y));
						fingerprint = Mix(fingerprint, Quantize(pos.z));
						PinEvidence item = new PinEvidence(i, val.m_name, (int)val.m_type, pos.x, pos.y, pos.z, saved: true, known: true, val.m_checked, val.m_ownerID);
						destination.Add(item);
						fingerprint ^= KnownPinIndexer.EvidenceHash(item);
						fingerprint *= 1099511628211uL;
					}
				}
			}
			return KnownPinCaptureStatus.Ready;
		}

		internal static bool TryResolve(Minimap minimap, bool includeSharedCoverage, KnownPinRecord record, out Vector3 position)
		{
			//IL_0001: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected I4, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			if (!ValheimContracts.TryRead(minimap, out var view) || !ValidCoverage(view, includeSharedCoverage) || record.SourceIndex < 0 || record.SourceIndex >= view.Pins.Count)
			{
				return false;
			}
			PinData val = view.Pins[record.SourceIndex];
			if (val == null || !val.m_save || !IsKnown(view, val.m_pos, includeSharedCoverage))
			{
				return false;
			}
			if (KnownPinIndexer.EvidenceHash(new PinEvidence(record.SourceIndex, val.m_name, (int)val.m_type, val.m_pos.x, val.m_pos.y, val.m_pos.z, saved: true, known: true, val.m_checked, val.m_ownerID)) != record.EvidenceHash)
			{
				return false;
			}
			position = val.m_pos;
			return IsFinite(position);
		}

		internal static bool IsKnown(MinimapReadView view, Vector3 position, bool includeSharedCoverage)
		{
			//IL_0000: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			if (!IsFinite(position) || !Finite(view.PixelSize) || view.PixelSize <= 0f || view.TextureSize <= 0)
			{
				return false;
			}
			float num = (float)view.TextureSize / 2f;
			float num2 = position.x / view.PixelSize + num;
			float num3 = position.z / view.PixelSize + num;
			if (!Finite(num2) || !Finite(num3) || num2 < -2.1474836E+09f || num2 > 2.1474836E+09f || num3 < -2.1474836E+09f || num3 > 2.1474836E+09f)
			{
				return false;
			}
			int num4 = Mathf.RoundToInt(num2);
			int num5 = Mathf.RoundToInt(num3);
			if (num4 < 0 || num4 >= view.TextureSize || num5 < 0 || num5 >= view.TextureSize)
			{
				return false;
			}
			int num6 = num5 * view.TextureSize + num4;
			if (!view.Explored[num6])
			{
				if (includeSharedCoverage && view.ExploredOthers != null)
				{
					return view.ExploredOthers[num6];
				}
				return false;
			}
			return true;
		}

		private static bool ValidCoverage(MinimapReadView view, bool includeSharedCoverage)
		{
			int textureSize = view.TextureSize;
			if (textureSize <= 0 || textureSize > 4096 || !Finite(view.PixelSize) || view.PixelSize <= 0f)
			{
				return false;
			}
			long num = (long)textureSize * (long)textureSize;
			if (num > int.MaxValue || view.Explored == null || view.Explored.Length < num)
			{
				return false;
			}
			if (includeSharedCoverage)
			{
				if (view.ExploredOthers != null)
				{
					return view.ExploredOthers.Length >= num;
				}
				return false;
			}
			return true;
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			if (Finite(value.x) && Finite(value.y))
			{
				return Finite(value.z);
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static int Quantize(float value)
		{
			if (!Finite(value))
			{
				return int.MinValue;
			}
			double num = Math.Round(value, MidpointRounding.AwayFromZero);
			if (!(num > 2147483647.0))
			{
				if (!(num < -2147483648.0))
				{
					return (int)num;
				}
				return int.MinValue;
			}
			return int.MaxValue;
		}

		private static ulong Mix(ulong hash, int value)
		{
			hash ^= (uint)value;
			return hash * 1099511628211L;
		}
	}
	internal sealed class OptionalPortalAdapter : IDisposable
	{
		private const float RetrySeconds = 10f;

		private BaseUnityPlugin _plugin;

		private ConfigEntry<bool> _enabled;

		private float _nextBindAt;

		private bool _assemblyDirty = true;

		private string _statusLine = string.Empty;

		internal string StatusLine => _statusLine;

		internal OptionalPortalAdapter()
		{
			AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
		}

		internal void Refresh(float now)
		{
			if (!TryBind(now))
			{
				_statusLine = string.Empty;
				return;
			}
			try
			{
				_statusLine = ((_enabled.Value && (Object)(object)_plugin != (Object)null) ? "Runic Portals: authorized directory remains available in portal context." : string.Empty);
			}
			catch
			{
				ClearBinding();
				_nextBindAt = now + 10f;
			}
		}

		public void Dispose()
		{
			AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
			ClearBinding();
		}

		private bool TryBind(float now)
		{
			if ((Object)(object)_plugin != (Object)null && _enabled != null)
			{
				return true;
			}
			if (!_assemblyDirty && now < _nextBindAt)
			{
				return false;
			}
			_assemblyDirty = false;
			_nextBindAt = now + 10f;
			try
			{
				if (Chainloader.PluginInfos.TryGetValue("chazman.RunicPortals", out var value))
				{
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BaseUnityPlugin instance = value.Instance;
						obj = ((instance != null) ? instance.Config : null);
					}
					ConfigEntry<bool> enabled = default(ConfigEntry<bool>);
					if (obj != null && value.Instance.Config.TryGetEntry<bool>("General", "Enabled", ref enabled))
					{
						_plugin = value.Instance;
						_enabled = enabled;
						return true;
					}
				}
				return false;
			}
			catch
			{
				ClearBinding();
				return false;
			}
		}

		private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs arguments)
		{
			if (string.Equals(arguments?.LoadedAssembly?.GetName().Name, "RunicPortals", StringComparison.Ordinal))
			{
				_assemblyDirty = true;
			}
		}

		private void ClearBinding()
		{
			_plugin = null;
			_enabled = null;
			_statusLine = string.Empty;
		}
	}
	internal readonly struct MinimapReadView
	{
		internal List<PinData> Pins { get; }

		internal bool[] Explored { get; }

		internal bool[] ExploredOthers { get; }

		internal int TextureSize { get; }

		internal float PixelSize { get; }

		internal MinimapReadView(List<PinData> pins, bool[] explored, bool[] exploredOthers, int textureSize, float pixelSize)
		{
			Pins = pins;
			Explored = explored;
			ExploredOthers = exploredOthers;
			TextureSize = textureSize;
			PixelSize = pixelSize;
		}
	}
	internal static class ValheimContracts
	{
		private const BindingFlags InstanceFields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static FieldInfo _pins;

		private static FieldInfo _explored;

		private static FieldInfo _exploredOthers;

		private static FieldInfo _textureSize;

		private static FieldInfo _pixelSize;

		private static bool _verified;

		internal static void VerifyInstalledSignatures()
		{
			if (!_verified)
			{
				RequireMethod(typeof(Minimap), "IsOpen", typeof(bool), true);
				RequireMethod(typeof(Minimap), "ShowPointOnMap", typeof(void), false, typeof(Vector3));
				RequireMethod(typeof(Minimap), "WorldToPixel", typeof(void), false, typeof(Vector3), typeof(int).MakeByRefType(), typeof(int).MakeByRefType());
				RequireMethod(typeof(Minimap), "IsExplored", typeof(bool), false, typeof(Vector3));
				RequireMethod(typeof(Minimap), "InTextInput", typeof(bool), true);
				RequireMethod(typeof(Minimap), "OnMapLeftDown", typeof(void), false, typeof(UIInputHandler));
				RequireMethod(typeof(Minimap), "OnMapLeftUp", typeof(void), false, typeof(UIInputHandler));
				RequireMethod(typeof(Minimap), "OnMapLeftClick", typeof(void), false);
				RequireMethod(typeof(Minimap), "OnMapDblClick", typeof(void), false);
				RequireMethod(typeof(Minimap), "OnMapMiddleClick", typeof(void), false, typeof(UIInputHandler));
				RequireMethod(typeof(Minimap), "OnMapRightClick", typeof(void), false, typeof(UIInputHandler));
				RequireMethod(typeof(ZInput), "IsGamepadActive", typeof(bool), true);
				RequireMethod(typeof(Player), "GetCurrentBiome", typeof(Biome), false);
				RequireMethod(typeof(Player), "GetControlledShip", typeof(Ship), false);
				RequireMethod(typeof(Player), "GetPlayerID", typeof(long), false);
				RequireMethod(typeof(Ship), "GetWindAngleFactor", typeof(float), false);
				RequireMethod(typeof(Ship), "IsSailUp", typeof(bool), false);
				RequireMethod(typeof(Ship), "GetSpeedSetting", typeof(Speed), false);
				RequireMethod(typeof(EnvMan), "GetWindIntensity", typeof(float), false);
				RequireField(typeof(Player), "m_localPlayer", typeof(Player), BindingFlags.Static | BindingFlags.Public);
				RequireProperty(typeof(Minimap), "instance", typeof(Minimap), isStatic: true);
				RequireProperty(typeof(EnvMan), "instance", typeof(EnvMan), isStatic: true);
				RequireField(typeof(Game), "m_noMap", typeof(bool), BindingFlags.Static | BindingFlags.Public);
				_pins = RequireField(typeof(Minimap), "m_pins", typeof(List<PinData>), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_explored = RequireField(typeof(Minimap), "m_explored", typeof(bool[]), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_exploredOthers = RequireField(typeof(Minimap), "m_exploredOthers", typeof(bool[]), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_textureSize = RequireField(typeof(Minimap), "m_textureSize", typeof(int), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_pixelSize = RequireField(typeof(Minimap), "m_pixelSize", typeof(float), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				RequirePublicPinField("m_name", typeof(string));
				RequirePublicPinField("m_type", typeof(PinType));
				RequirePublicPinField("m_pos", typeof(Vector3));
				RequirePublicPinField("m_save", typeof(bool));
				RequirePublicPinField("m_ownerID", typeof(long));
				RequirePublicPinField("m_checked", typeof(bool));
				_verified = true;
			}
		}

		internal static bool TryRead(Minimap minimap, out MinimapReadView view)
		{
			view = default(MinimapReadView);
			if (!_verified || (Object)(object)minimap == (Object)null)
			{
				return false;
			}
			try
			{
				List<PinData> list = _pins.GetValue(minimap) as List<PinData>;
				bool[] array = _explored.GetValue(minimap) as bool[];
				bool[] exploredOthers = _exploredOthers.GetValue(minimap) as bool[];
				if (list == null || array == null)
				{
					return false;
				}
				view = new MinimapReadView(list, array, exploredOthers, (int)_textureSize.GetValue(minimap), (float)_pixelSize.GetValue(minimap));
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void RequirePublicPinField(string name, Type type)
		{
			FieldInfo field = typeof(PinData).GetField(name, BindingFlags.Instance | BindingFlags.Public);
			if (field == null || field.FieldType != type)
			{
				throw new MissingFieldException(typeof(PinData).FullName, name);
			}
		}

		private static MethodInfo RequireMethod(Type type, string name, Type returnType, bool isStatic, params Type[] parameters)
		{
			MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameters ?? Type.EmptyTypes, null);
			if (method == null || method.ReturnType != returnType || method.IsStatic != isStatic)
			{
				throw new MissingMethodException(type.FullName, name);
			}
			return method;
		}

		private static FieldInfo RequireField(Type type, string name, Type fieldType, BindingFlags flags)
		{
			FieldInfo field = type.GetField(name, flags);
			if (field == null || field.FieldType != fieldType)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return field;
		}

		private static PropertyInfo RequireProperty(Type type, string name, Type propertyType, bool isStatic)
		{
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo methodInfo = property?.GetMethod;
			if (property == null || property.PropertyType != propertyType || methodInfo == null || methodInfo.IsStatic != isStatic || property.GetIndexParameters().Length != 0)
			{
				throw new MissingMemberException(type.FullName, name);
			}
			return property;
		}
	}
}
namespace RunicExploration.Core
{
	internal static class BoundedText
	{
		internal const int MaximumLabelCharacters = 64;

		internal const int MaximumQueryCharacters = 48;

		internal const int MaximumRawInspectionCharacters = 256;

		internal static string Label(string value)
		{
			return Sanitize(value, 64, 1);
		}

		internal static string Query(string value)
		{
			return Sanitize(value, 48, 1).ToLowerInvariant();
		}

		internal static string Sanitize(string value, int maximumCharacters, int maximumLines)
		{
			maximumCharacters = Math.Max(1, maximumCharacters);
			maximumLines = Math.Max(1, maximumLines);
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			int num = Math.Min(value.Length, Math.Min(256, Math.Max(64, maximumCharacters * 4)));
			StringBuilder stringBuilder = new StringBuilder(Math.Min(maximumCharacters, num));
			int num2 = 1;
			bool flag = false;
			for (int i = 0; i < num && stringBuilder.Length < maximumCharacters; i++)
			{
				char c = value[i];
				switch (c)
				{
				case '<':
					c = '[';
					break;
				case '>':
					c = ']';
					break;
				}
				switch (c)
				{
				case '\n':
					if (num2 < maximumLines)
					{
						TrimSpace(stringBuilder);
						if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '\n')
						{
							stringBuilder.Append('\n');
							num2++;
						}
						flag = false;
						continue;
					}
					break;
				default:
				{
					UnicodeCategory unicodeCategory = char.GetUnicodeCategory(c);
					if (unicodeCategory == UnicodeCategory.Control || unicodeCategory == UnicodeCategory.Format || unicodeCategory == UnicodeCategory.Surrogate || IsBidi(c))
					{
						continue;
					}
					if (c == '\t' || char.IsWhiteSpace(c))
					{
						if (!flag && stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '\n')
						{
							stringBuilder.Append(' ');
							flag = true;
						}
					}
					else
					{
						stringBuilder.Append(c);
						flag = false;
					}
					continue;
				}
				case '\r':
					continue;
				}
				break;
			}
			TrimSpace(stringBuilder);
			while (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] == '\n')
			{
				stringBuilder.Length--;
			}
			if (num < value.Length || (stringBuilder.Length == maximumCharacters && value.Length > maximumCharacters))
			{
				AddEllipsis(stringBuilder, maximumCharacters);
			}
			return stringBuilder.ToString();
		}

		internal static ulong Hash(string value, int maximumCharacters)
		{
			ulong num = 1469598103934665603uL;
			if (value == null)
			{
				return num;
			}
			int num2 = Math.Min(value.Length, Math.Max(0, Math.Min(256, maximumCharacters)));
			for (int i = 0; i < num2; i++)
			{
				num ^= value[i];
				num *= 1099511628211L;
			}
			num ^= (ulong)value.Length;
			return num * 1099511628211L;
		}

		private static bool IsBidi(char value)
		{
			if ((value < '\u202a' || value > '\u202e') && (value < '\u2066' || value > '\u2069') && (value < '\u200b' || value > '\u200f') && value != '\u061c')
			{
				return value == '\ufeff';
			}
			return true;
		}

		private static void TrimSpace(StringBuilder builder)
		{
			while (builder.Length > 0 && builder[builder.Length - 1] == ' ')
			{
				builder.Length--;
			}
		}

		private static void AddEllipsis(StringBuilder builder, int maximumCharacters)
		{
			if (maximumCharacters == 1)
			{
				if (builder.Length == 0)
				{
					builder.Append('…');
				}
				else
				{
					builder[0] = '…';
				}
			}
			else
			{
				builder.Length = Math.Min(builder.Length, maximumCharacters - 1);
				builder.Append('…');
			}
		}
	}
	internal static class KnownPinIndexer
	{
		private readonly struct Candidate
		{
			internal int SourceIndex { get; }

			internal string Label { get; }

			internal string NameKey { get; }

			internal int Type { get; }

			internal float X { get; }

			internal float Y { get; }

			internal float Z { get; }

			internal int CellX { get; }

			internal int CellY { get; }

			internal int CellZ { get; }

			internal ulong RawNameHash { get; }

			internal bool ExactName { get; }

			internal ulong EvidenceHash { get; }

			internal KnownPinCategory Category { get; }

			internal KnownAssetKind AssetKind { get; }

			internal KnownPinScope Scope { get; }

			internal bool IsChecked { get; }

			internal Candidate(int sourceIndex, string label, string nameKey, int type, float x, float y, float z, int cellX, int cellY, int cellZ, ulong rawNameHash, bool exactName, ulong evidenceHash, KnownPinCategory category, KnownAssetKind assetKind, KnownPinScope scope, bool isChecked)
			{
				SourceIndex = sourceIndex;
				Label = label;
				NameKey = nameKey;
				Type = type;
				X = x;
				Y = y;
				Z = z;
				CellX = cellX;
				CellY = cellY;
				CellZ = cellZ;
				RawNameHash = rawNameHash;
				ExactName = exactName;
				EvidenceHash = evidenceHash;
				Category = category;
				AssetKind = assetKind;
				Scope = scope;
				IsChecked = isChecked;
			}
		}

		private sealed class CandidateComparer : IComparer<Candidate>
		{
			internal static readonly CandidateComparer Instance = new CandidateComparer();

			public int Compare(Candidate left, Candidate right)
			{
				int num = string.CompareOrdinal(left.NameKey, right.NameKey);
				if (num != 0)
				{
					return num;
				}
				num = left.Type.CompareTo(right.Type);
				if (num != 0)
				{
					return num;
				}
				num = left.CellX.CompareTo(right.CellX);
				if (num != 0)
				{
					return num;
				}
				num = left.CellZ.CompareTo(right.CellZ);
				if (num != 0)
				{
					return num;
				}
				num = left.CellY.CompareTo(right.CellY);
				if (num != 0)
				{
					return num;
				}
				num = left.RawNameHash.CompareTo(right.RawNameHash);
				if (num == 0)
				{
					return left.SourceIndex.CompareTo(right.SourceIndex);
				}
				return num;
			}
		}

		internal const int HardMaximumPins = 10000;

		internal const int PositionCellMeters = 1;

		internal static KnownPinIndex Build(IReadOnlyList<PinEvidence> evidence, long localPlayerId, bool includeShared)
		{
			if (evidence == null)
			{
				return new KnownPinIndex(KnownPinIndexStatus.InvalidSource, Array.Empty<KnownPinRecord>(), 0, 0, 0);
			}
			int count = evidence.Count;
			if (count > 10000)
			{
				return new KnownPinIndex(KnownPinIndexStatus.Oversized, Array.Empty<KnownPinRecord>(), 0, 0, 0);
			}
			List<Candidate> list = new List<Candidate>(Math.Min(count, 10000));
			for (int i = 0; i < count; i++)
			{
				PinEvidence item = evidence[i];
				if (!item.Saved || !item.Known || !Finite(item.X) || !Finite(item.Y) || !Finite(item.Z))
				{
					continue;
				}
				KnownPinScope knownPinScope = ((item.OwnerId != 0L && item.OwnerId != localPlayerId) ? KnownPinScope.Shared : KnownPinScope.Personal);
				if (includeShared || knownPinScope != KnownPinScope.Shared)
				{
					string text = BoundedText.Label(item.RawName);
					if (string.IsNullOrEmpty(text))
					{
						text = TypeLabel(item.Type);
					}
					string nameKey = text.ToLowerInvariant();
					KnownAssetKind knownAssetKind = AssetKind(nameKey, item.Type);
					KnownPinCategory category = Category(item.Type, knownAssetKind);
					int num = Quantize(item.X);
					int num2 = Quantize(item.Y);
					int num3 = Quantize(item.Z);
					ulong rawNameHash = BoundedText.Hash(item.RawName, 256);
					bool exactName = item.RawName == null || item.RawName.Length <= 256;
					ulong evidenceHash = EvidenceHash(item, rawNameHash, num, num2, num3);
					list.Add(new Candidate(item.SourceIndex, text, nameKey, item.Type, item.X, item.Y, item.Z, num, num2, num3, rawNameHash, exactName, evidenceHash, category, knownAssetKind, knownPinScope, item.IsChecked));
				}
			}
			list.Sort(CandidateComparer.Instance);
			List<KnownPinRecord> list2 = new List<KnownPinRecord>(list.Count);
			int num4 = 0;
			int num5 = 0;
			while (num5 < list.Count)
			{
				Candidate left = list[num5];
				int j = num5 + 1;
				KnownPinScope knownPinScope2 = left.Scope;
				bool flag = left.IsChecked;
				for (; j < list.Count && SameKnownPin(left, list[j]); j++)
				{
					Candidate candidate = list[j];
					if (candidate.Scope != knownPinScope2)
					{
						knownPinScope2 = KnownPinScope.Mixed;
					}
					flag |= candidate.IsChecked;
				}
				int num6 = j - num5;
				num4 += num6 - 1;
				string searchKey = left.NameKey + " " + CategoryLabel(left.Category).ToLowerInvariant() + " " + AssetLabel(left.AssetKind).ToLowerInvariant();
				string displayRow = DisplayRow(left.Label, left.Category, left.AssetKind, knownPinScope2, num6, flag);
				list2.Add(new KnownPinRecord(left.SourceIndex, left.Label, searchKey, left.Type, left.X, left.Y, left.Z, left.CellX, left.CellY, left.CellZ, left.RawNameHash, left.EvidenceHash, left.Category, left.AssetKind, knownPinScope2, num6, flag, displayRow));
				num5 = j;
			}
			return new KnownPinIndex(KnownPinIndexStatus.Ready, list2.ToArray(), count, list.Count, num4);
		}

		internal static ulong EvidenceHash(PinEvidence item)
		{
			int x = (Finite(item.X) ? Quantize(item.X) : int.MinValue);
			int y = (Finite(item.Y) ? Quantize(item.Y) : int.MinValue);
			int z = (Finite(item.Z) ? Quantize(item.Z) : int.MinValue);
			return EvidenceHash(item, BoundedText.Hash(item.RawName, 256), x, y, z);
		}

		internal static string TypeLabel(int type)
		{
			return type switch
			{
				0 => "Icon 0", 
				1 => "Icon 1", 
				2 => "Icon 2", 
				3 => "Icon 3", 
				4 => "Tombstone", 
				5 => "Bed", 
				6 => "Icon 4", 
				9 => "Boss", 
				_ => "Pin type " + type.ToString(CultureInfo.InvariantCulture), 
			};
		}

		internal static string CategoryLabel(KnownPinCategory category)
		{
			return category switch
			{
				KnownPinCategory.TaggedAsset => "tagged asset", 
				KnownPinCategory.Tombstone => "tombstone", 
				KnownPinCategory.Bed => "bed", 
				KnownPinCategory.Boss => "boss", 
				_ => "custom pin", 
			};
		}

		internal static string AssetLabel(KnownAssetKind asset)
		{
			return asset switch
			{
				KnownAssetKind.Boat => "boat", 
				KnownAssetKind.Cart => "cart", 
				KnownAssetKind.TamedAnimal => "tamed animal", 
				KnownAssetKind.Portal => "portal", 
				KnownAssetKind.Bed => "bed", 
				KnownAssetKind.Tombstone => "tombstone", 
				_ => string.Empty, 
			};
		}

		private static KnownAssetKind AssetKind(string nameKey, int type)
		{
			switch (type)
			{
			case 4:
				return KnownAssetKind.Tombstone;
			case 5:
				return KnownAssetKind.Bed;
			default:
				if (nameKey.StartsWith("[boat]", StringComparison.Ordinal))
				{
					return KnownAssetKind.Boat;
				}
				if (nameKey.StartsWith("[cart]", StringComparison.Ordinal))
				{
					return KnownAssetKind.Cart;
				}
				if (nameKey.StartsWith("[animal]", StringComparison.Ordinal) || nameKey.StartsWith("[tame]", StringComparison.Ordinal))
				{
					return KnownAssetKind.TamedAnimal;
				}
				if (nameKey.StartsWith("[portal]", StringComparison.Ordinal))
				{
					return KnownAssetKind.Portal;
				}
				if (nameKey.StartsWith("[bed]", StringComparison.Ordinal))
				{
					return KnownAssetKind.Bed;
				}
				if (nameKey.StartsWith("[tombstone]", StringComparison.Ordinal) || nameKey.StartsWith("[grave]", StringComparison.Ordinal))
				{
					return KnownAssetKind.Tombstone;
				}
				return KnownAssetKind.None;
			}
		}

		private static KnownPinCategory Category(int type, KnownAssetKind asset)
		{
			if (type == 4 || asset == KnownAssetKind.Tombstone)
			{
				return KnownPinCategory.Tombstone;
			}
			if (type == 5 || asset == KnownAssetKind.Bed)
			{
				return KnownPinCategory.Bed;
			}
			if (type == 9)
			{
				return KnownPinCategory.Boss;
			}
			if (asset == KnownAssetKind.None)
			{
				return KnownPinCategory.Custom;
			}
			return KnownPinCategory.TaggedAsset;
		}

		private static string DisplayRow(string label, KnownPinCategory category, KnownAssetKind asset, KnownPinScope scope, int sources, bool isChecked)
		{
			string text = ((asset == KnownAssetKind.None) ? CategoryLabel(category) : AssetLabel(asset));
			string text2 = scope switch
			{
				KnownPinScope.Shared => "shared", 
				KnownPinScope.Personal => "personal", 
				_ => "personal + shared", 
			};
			return label + "  |  " + text + "  |  LAST KNOWN, " + text2 + ((sources > 1) ? (", " + sources.ToString(CultureInfo.InvariantCulture) + " merged sources") : string.Empty) + (isChecked ? ", checked" : string.Empty);
		}

		private static bool SameKnownPin(Candidate left, Candidate right)
		{
			if (left.Type == right.Type && left.CellX == right.CellX && left.CellY == right.CellY && left.CellZ == right.CellZ && left.RawNameHash == right.RawNameHash && left.ExactName && right.ExactName)
			{
				return string.Equals(left.NameKey, right.NameKey, StringComparison.Ordinal);
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static int Quantize(float value)
		{
			double num = Math.Round(value / 1f, MidpointRounding.AwayFromZero);
			if (!(num > 2147483647.0))
			{
				if (!(num < -2147483648.0))
				{
					return (int)num;
				}
				return int.MinValue;
			}
			return int.MaxValue;
		}

		private static ulong EvidenceHash(PinEvidence item, ulong rawNameHash, int x, int y, int z)
		{
			return (((Mix(Mix(Mix(Mix(Mix(Mix(Mix(Mix(1469598103934665603uL, item.SourceIndex), item.Type), x), y), z), item.Saved ? 1 : 0), item.Known ? 1 : 0), item.IsChecked ? 1 : 0) ^ (ulong)item.OwnerId) * 1099511628211L) ^ rawNameHash) * 1099511628211L;
		}

		private static ulong Mix(ulong hash, int value)
		{
			hash ^= (uint)value;
			return hash * 1099511628211L;
		}
	}
	internal enum KnownPinCategory
	{
		Custom,
		TaggedAsset,
		Tombstone,
		Bed,
		Boss
	}
	internal enum KnownAssetKind
	{
		None,
		Boat,
		Cart,
		TamedAnimal,
		Portal,
		Bed,
		Tombstone
	}
	internal enum KnownPinScope
	{
		Personal,
		Shared,
		Mixed
	}
	internal enum KnownPinCategoryFilter
	{
		All,
		TaggedAssets,
		Tombstones,
		Beds,
		Custom,
		Bosses
	}
	internal enum KnownPinScopeFilter
	{
		All,
		Personal,
		Shared
	}
	internal enum KnownPinIndexStatus
	{
		Ready,
		Oversized,
		InvalidSource
	}
	internal readonly struct PinEvidence
	{
		internal int SourceIndex { get; }

		internal string RawName { get; }

		internal int Type { get; }

		internal float X { get; }

		internal float Y { get; }

		internal float Z { get; }

		internal bool Saved { get; }

		internal bool Known { get; }

		internal bool IsChecked { get; }

		internal long OwnerId { get; }

		internal PinEvidence(int sourceIndex, string rawName, int type, float x, float y, float z, bool saved, bool known, bool isChecked, long ownerId)
		{
			SourceIndex = sourceIndex;
			RawName = rawName;
			Type = type;
			X = x;
			Y = y;
			Z = z;
			Saved = saved;
			Known = known;
			IsChecked = isChecked;
			OwnerId = ownerId;
		}
	}
	internal readonly struct KnownPinRecord
	{
		internal int SourceIndex { get; }

		internal string Label { get; }

		internal string SearchKey { get; }

		internal int Type { get; }

		internal float X { get; }

		internal float Y { get; }

		internal float Z { get; }

		internal int CellX { get; }

		internal int CellY { get; }

		internal int CellZ { get; }

		internal ulong RawNameHash { get; }

		internal ulong EvidenceHash { get; }

		internal KnownPinCategory Category { get; }

		internal KnownAssetKind AssetKind { get; }

		internal KnownPinScope Scope { get; }

		internal int SourceCount { get; }

		internal bool AnyChecked { get; }

		internal string DisplayRow { get; }

		internal KnownPinRecord(int sourceIndex, string label, string searchKey, int type, float x, float y, float z, int cellX, int cellY, int cellZ, ulong rawNameHash, ulong evidenceHash, KnownPinCategory category, KnownAssetKind assetKind, KnownPinScope scope, int sourceCount, bool anyChecked, string displayRow)
		{
			SourceIndex = sourceIndex;
			Label = label;
			SearchKey = searchKey;
			Type = type;
			X = x;
			Y = y;
			Z = z;
			CellX = cellX;
			CellY = cellY;
			CellZ = cellZ;
			RawNameHash = rawNameHash;
			EvidenceHash = evidenceHash;
			Category = category;
			AssetKind = assetKind;
			Scope = scope;
			SourceCount = sourceCount;
			AnyChecked = anyChecked;
			DisplayRow = displayRow;
		}
	}
	internal sealed class KnownPinIndex
	{
		internal KnownPinIndexStatus Status { get; }

		internal KnownPinRecord[] Records { get; }

		internal int Inspected { get; }

		internal int Accepted { get; }

		internal int Merged { get; }

		internal KnownPinIndex(KnownPinIndexStatus status, KnownPinRecord[] records, int inspected, int accepted, int merged)
		{
			Status = status;
			Records = records ?? Array.Empty<KnownPinRecord>();
			Inspected = inspected;
			Accepted = accepted;
			Merged = merged;
		}
	}
	internal sealed class KnownPinSearchResult
	{
		internal KnownPinRecord[] Records { get; }

		internal int Inspected { get; }

		internal bool Truncated { get; }

		internal KnownPinSearchResult(KnownPinRecord[] records, int inspected, bool truncated)
		{
			Records = records ?? Array.Empty<KnownPinRecord>();
			Inspected = inspected;
			Truncated = truncated;
		}
	}
	internal sealed class KnownPinRebuildGate
	{
		internal const float QuietPeriodSeconds = 0.75f;

		internal const float MaximumDeferralSeconds = 2f;

		private bool _hasPending;

		private ulong _pendingFingerprint;

		private float _pendingSince;

		private float _lastPublishedAt;

		private bool _hasPublishedTime;

		internal bool ShouldRebuild(ulong candidateFingerprint, ulong publishedFingerprint, bool hasPublishedFingerprint, float now)
		{
			if (!Finite(now) || now < 0f)
			{
				now = 0f;
			}
			if (!hasPublishedFingerprint)
			{
				ClearPending();
				return true;
			}
			if (candidateFingerprint == publishedFingerprint)
			{
				ClearPending();
				return false;
			}
			if (!_hasPending || _pendingFingerprint != candidateFingerprint)
			{
				_hasPending = true;
				_pendingFingerprint = candidateFingerprint;
				_pendingSince = now;
			}
			bool num = Elapsed(now, _pendingSince) >= 0.75f;
			bool flag = !_hasPublishedTime || Elapsed(now, _lastPublishedAt) >= 2f;
			return num || flag;
		}

		internal void MarkPublished(float now)
		{
			_lastPublishedAt = ((Finite(now) && now >= 0f) ? now : 0f);
			_hasPublishedTime = true;
			ClearPending();
		}

		internal void Reset()
		{
			_lastPublishedAt = 0f;
			_hasPublishedTime = false;
			ClearPending();
		}

		private void ClearPending()
		{
			_hasPending = false;
			_pendingFingerprint = 0uL;
			_pendingSince = 0f;
		}

		private static float Elapsed(float now, float then)
		{
			if (!(now >= then))
			{
				return float.MaxValue;
			}
			return now - then;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class KnownPinSearch
	{
		internal const int HardMaximumResults = 24;

		internal static KnownPinSearchResult Filter(IReadOnlyList<KnownPinRecord> records, string query, KnownPinCategoryFilter category, KnownPinScopeFilter scope, int maximumResults)
		{
			if (records == null || records.Count > 10000)
			{
				return new KnownPinSearchResult(Array.Empty<KnownPinRecord>(), 0, truncated: false);
			}
			string value = BoundedText.Query(query);
			int num = Math.Max(1, Math.Min(24, maximumResults));
			List<KnownPinRecord> list = new List<KnownPinRecord>(num);
			int num2 = 0;
			bool truncated = false;
			for (int i = 0; i < records.Count; i++)
			{
				KnownPinRecord item = records[i];
				num2++;
				if (CategoryMatches(item.Category, category) && ScopeMatches(item.Scope, scope) && (string.IsNullOrEmpty(value) || item.SearchKey.IndexOf(value, StringComparison.Ordinal) >= 0))
				{
					if (list.Count < num)
					{
						list.Add(item);
					}
					else
					{
						truncated = true;
					}
				}
			}
			return new KnownPinSearchResult(list.ToArray(), num2, truncated);
		}

		private static bool CategoryMatches(KnownPinCategory category, KnownPinCategoryFilter filter)
		{
			return filter switch
			{
				KnownPinCategoryFilter.TaggedAssets => category == KnownPinCategory.TaggedAsset, 
				KnownPinCategoryFilter.Tombstones => category == KnownPinCategory.Tombstone, 
				KnownPinCategoryFilter.Beds => category == KnownPinCategory.Bed, 
				KnownPinCategoryFilter.Custom => category == KnownPinCategory.Custom, 
				KnownPinCategoryFilter.Bosses => category == KnownPinCategory.Boss, 
				_ => true, 
			};
		}

		private static bool ScopeMatches(KnownPinScope scope, KnownPinScopeFilter filter)
		{
			switch (filter)
			{
			case KnownPinScopeFilter.Personal:
				if (scope != KnownPinScope.Personal)
				{
					return scope == KnownPinScope.Mixed;
				}
				return true;
			case KnownPinScopeFilter.Shared:
				if (scope != KnownPinScope.Shared)
				{
					return scope == KnownPinScope.Mixed;
				}
				return true;
			default:
				return true;
			}
		}
	}
	internal static class NavigationFormatter
	{
		private static readonly string[] Directions = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };

		internal static string FormatSelected(KnownPinRecord pin, float playerX, float playerY, float playerZ)
		{
			if (!Finite(playerX) || !Finite(playerY) || !Finite(playerZ))
			{
				return string.Empty;
			}
			float num = pin.X - playerX;
			float num2 = pin.Z - playerZ;
			double meters = Math.Sqrt((double)num * (double)num + (double)num2 * (double)num2);
			string value = Direction(num, num2);
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("LAST KNOWN PIN: ").Append(pin.Label).Append("\nStraight-line: ")
				.Append(FormatDistance(meters))
				.Append(" ")
				.Append(value);
			float num3 = pin.Y - playerY;
			if (Math.Abs(num3) >= 10f)
			{
				stringBuilder.Append(" | elevation ").Append((num3 > 0f) ? "+" : string.Empty).Append(num3.ToString("0", CultureInfo.InvariantCulture))
					.Append(" m");
			}
			if (pin.Category == KnownPinCategory.Tombstone)
			{
				stringBuilder.Append("\nTopology warning: route, shore, and passability are not inferred; no remote recovery.");
			}
			return BoundedText.Sanitize(stringBuilder.ToString(), 384, 3);
		}

		internal static string FormatSailing(string speed, bool sailUp, float windFactor, float windIntensity, string currentBiome, double? selectedDistance)
		{
			if (!Finite(windFactor) || !Finite(windIntensity))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("LIVE LOCAL SHIP: ").Append(BoundedText.Label(speed)).Append(sailUp ? " | sail up" : " | sail down")
				.Append("\nWind power: ")
				.Append(Math.Max(0f, Math.Min(1f, windFactor)).ToString("0%", CultureInfo.InvariantCulture))
				.Append(" | intensity ")
				.Append(Math.Max(0f, Math.Min(1f, windIntensity)).ToString("0%", CultureInfo.InvariantCulture))
				.Append(" | current biome ")
				.Append(BoundedText.Label(currentBiome));
			if (selectedDistance.HasValue && !double.IsNaN(selectedDistance.Value) && !double.IsInfinity(selectedDistance.Value) && selectedDistance.Value >= 0.0)
			{
				stringBuilder.Append("\nSelected known pin: ").Append(FormatDistance(selectedDistance.Value));
			}
			return BoundedText.Sanitize(stringBuilder.ToString(), 384, 3);
		}

		internal static string Direction(float deltaX, float deltaZ)
		{
			if (!Finite(deltaX) || !Finite(deltaZ))
			{
				return string.Empty;
			}
			if (Math.Abs(deltaX) < 0.001f && Math.Abs(deltaZ) < 0.001f)
			{
				return "here";
			}
			double num = Math.Atan2(deltaX, deltaZ) * 180.0 / Math.PI;
			if (num < 0.0)
			{
				num += 360.0;
			}
			int num2 = (int)Math.Floor((num + 22.5) / 45.0) & 7;
			return Directions[num2];
		}

		internal static string FormatDistance(double meters)
		{
			if (double.IsNaN(meters) || double.IsInfinity(meters) || meters < 0.0)
			{
				return "unknown";
			}
			if (meters < 1000.0)
			{
				return Math.Round(meters, MidpointRounding.AwayFromZero).ToString("0", CultureInfo.InvariantCulture) + " m";
			}
			return (meters / 1000.0).ToString((meters < 10000.0) ? "0.0" : "0", CultureInfo.InvariantCulture) + " km";
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
}