Decompiled source of PortalAtlas v1.2.5

PortalAtlas.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
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 Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PortalAtlas")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.5.0")]
[assembly: AssemblyInformationalVersion("1.2.5+79c8b42c97fb56ab3bdaec5a60f8e6d41d595556")]
[assembly: AssemblyProduct("PortalAtlas")]
[assembly: AssemblyTitle("PortalAtlas")]
[assembly: AssemblyVersion("1.2.5.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 PortalAtlas
{
	internal static class KnownPortalCache
	{
		[HarmonyPatch(typeof(TeleportWorld), "Interact")]
		private static class TeleportWorld_Interact_Patch
		{
			private static void Postfix(TeleportWorld __instance)
			{
				RecordTeleportWorld(__instance);
			}
		}

		[HarmonyPatch(typeof(TeleportWorld), "GetHoverText")]
		private static class TeleportWorld_GetHoverText_Patch
		{
			private static void Postfix(TeleportWorld __instance)
			{
				RecordTeleportWorld(__instance);
			}
		}

		private const float ApproachPollNearSeconds = 1.5f;

		private const float ApproachPollFarSeconds = 3.5f;

		private const float SaveDebounceSeconds = 4f;

		private const float MoveThresholdMeters = 1.25f;

		private static readonly Dictionary<string, KnownPortalEntry> Entries = new Dictionary<string, KnownPortalEntry>(StringComparer.OrdinalIgnoreCase);

		private static string _worldId = string.Empty;

		private static string _characterId = string.Empty;

		private static float _nextPoll;

		private static float _earliestSaveTime;

		private static bool _dirty;

		private static Vector3 _lastScanPlayerPos;

		private static bool _hasLastScanPos;

		private static int _lastNearbyCount;

		private static float ApproachRange
		{
			get
			{
				if (PortalAtlasPlugin.ApproachRangeMeters == null)
				{
					return 8f;
				}
				return Mathf.Clamp(PortalAtlasPlugin.ApproachRangeMeters.Value, 1f, 50f);
			}
		}

		internal static IReadOnlyCollection<KnownPortalEntry> All => Entries.Values;

		internal static void Tick()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (_dirty && unscaledTime >= _earliestSaveTime)
			{
				Save();
			}
			if (unscaledTime < _nextPoll)
			{
				return;
			}
			float num = ((_lastNearbyCount > 0) ? 1.5f : 3.5f);
			_nextPoll = unscaledTime + num;
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			if (_hasLastScanPos && _lastNearbyCount == 0)
			{
				float num2 = position.x - _lastScanPlayerPos.x;
				float num3 = position.z - _lastScanPlayerPos.z;
				if (num2 * num2 + num3 * num3 < 1.5625f)
				{
					_nextPoll = unscaledTime + 3.5f;
					return;
				}
			}
			EnsureLoaded();
			ScanNearby();
			_lastScanPlayerPos = position;
			_hasLastScanPos = true;
			if (_dirty && _earliestSaveTime <= 0f)
			{
				_earliestSaveTime = unscaledTime + 4f;
			}
		}

		internal static void FlushIfDirty()
		{
			if (_dirty)
			{
				Save();
			}
		}

		internal static void EnsureLoaded()
		{
			string worldId = GetWorldId();
			string characterId = GetCharacterId();
			if (string.Equals(worldId, _worldId, StringComparison.Ordinal) && string.Equals(characterId, _characterId, StringComparison.Ordinal) && !string.IsNullOrEmpty(_worldId))
			{
				return;
			}
			_worldId = worldId;
			_characterId = characterId;
			Entries.Clear();
			_dirty = false;
			string text = PortalPaths.JournalPath(_worldId, _characterId);
			if (!File.Exists(text))
			{
				return;
			}
			try
			{
				KnownPortalFile knownPortalFile = SimpleJson.Deserialize(File.ReadAllText(text, Encoding.UTF8));
				if (knownPortalFile?.Portals == null)
				{
					return;
				}
				foreach (KnownPortalEntry portal in knownPortalFile.Portals)
				{
					if (portal != null && !string.IsNullOrEmpty(portal.Uid))
					{
						Entries[portal.Uid] = portal;
					}
				}
				int num = CompactDuplicates();
				PortalAtlasPlugin.ModLogger.LogInfo((object)$"Loaded {Entries.Count} known portal(s) from {text}");
				PortalAtlasPlugin.Debug($"Journal load world='{_worldId}' character='{_characterId}' count={Entries.Count} " + $"compacted={num} path={text}");
				if (num > 0)
				{
					Save();
				}
			}
			catch (Exception ex)
			{
				PortalAtlasPlugin.ModLogger.LogWarning((object)("Failed to load portal journal: " + ex.Message));
			}
		}

		internal static void Save()
		{
			if (string.IsNullOrEmpty(_worldId))
			{
				EnsureLoaded();
			}
			string text = PortalPaths.JournalPath(_worldId, _characterId);
			try
			{
				KnownPortalFile file = new KnownPortalFile
				{
					WorldId = _worldId,
					CharacterId = _characterId,
					Portals = new List<KnownPortalEntry>(Entries.Values)
				};
				File.WriteAllText(text, SimpleJson.Serialize(file), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				_dirty = false;
				_earliestSaveTime = 0f;
				PortalAtlasPlugin.Debug($"Journal save count={Entries.Count} path={text}");
			}
			catch (Exception ex)
			{
				PortalAtlasPlugin.ModLogger.LogWarning((object)("Failed to save portal journal: " + ex.Message));
			}
		}

		internal unsafe static void RecordZdo(ZDO zdo, string prefabHint = null)
		{
			//IL_0012: 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)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null || !zdo.IsValid())
			{
				return;
			}
			EnsureLoaded();
			Vector3 position = zdo.GetPosition();
			string text = PortalScan.SafeGetString(zdo, "tag");
			ZDOID portalConnectionId = PortalScan.GetPortalConnectionId(zdo);
			float? targetX = null;
			float? targetY = null;
			float? targetZ = null;
			string text2 = ((object)(*(ZDOID*)(&portalConnectionId))/*cast due to .constrained prefix*/).ToString();
			try
			{
				if (!((ZDOID)(ref portalConnectionId)).IsNone())
				{
					ZDO zDO = ZDOMan.instance.GetZDO(portalConnectionId);
					if (zDO != null && zDO.IsValid())
					{
						Vector3 position2 = zDO.GetPosition();
						targetX = position2.x;
						targetY = position2.y;
						targetZ = position2.z;
					}
				}
			}
			catch
			{
			}
			string text3 = ((object)Unsafe.As<ZDOID, ZDOID>(ref zdo.m_uid)/*cast due to .constrained prefix*/).ToString();
			string text4 = prefabHint ?? string.Empty;
			if (string.IsNullOrEmpty(text4))
			{
				try
				{
					ZNetView val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(zdo) : null);
					if ((Object)(object)val != (Object)null)
					{
						text4 = ((Object)((Component)val).gameObject).name.Replace("(Clone)", string.Empty).Trim();
					}
				}
				catch
				{
				}
			}
			if (string.IsNullOrEmpty(text4))
			{
				text4 = ResolvePrefabName(zdo);
			}
			int num = MergeAwayNearbyDuplicates(text3, text, position);
			KnownPortalEntry value;
			bool flag = !Entries.TryGetValue(text3, out value);
			KnownPortalEntry knownPortalEntry = new KnownPortalEntry
			{
				Uid = text3,
				Prefab = text4,
				Tag = text,
				X = position.x,
				Y = position.y,
				Z = position.z,
				TargetUid = text2,
				TargetX = targetX,
				TargetY = targetY,
				TargetZ = targetZ,
				LastSeenUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
			};
			if (flag || num > 0 || !EntriesMatch(value, knownPortalEntry))
			{
				Entries[text3] = knownPortalEntry;
				_dirty = true;
				if (_earliestSaveTime <= 0f)
				{
					_earliestSaveTime = Time.unscaledTime + 4f;
				}
				bool flag2 = PortalAtlasPlugin.AutoPin != null && PortalAtlasPlugin.AutoPin.Value;
				PortalAtlasPlugin.Debug("Record " + (flag ? "new" : "update") + " prefab=" + text4 + " tag='" + text + "' uid=" + text3 + " " + string.Format("pos=({0:0.#},{1:0.#}) link={2} autoPin={3}", position.x, position.z, (!((ZDOID)(ref portalConnectionId)).IsNone()) ? text2 : "none", flag2));
				if (flag2)
				{
					PortalMapPins.UpsertSavedPin(knownPortalEntry);
				}
			}
		}

		private static bool EntriesMatch(KnownPortalEntry a, KnownPortalEntry b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			if (!string.Equals(a.Tag ?? string.Empty, b.Tag ?? string.Empty, StringComparison.Ordinal))
			{
				return false;
			}
			if (!string.Equals(a.Prefab ?? string.Empty, b.Prefab ?? string.Empty, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (!string.Equals(a.TargetUid ?? string.Empty, b.TargetUid ?? string.Empty, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (Mathf.Abs(a.X - b.X) > 0.25f || Mathf.Abs(a.Y - b.Y) > 0.25f || Mathf.Abs(a.Z - b.Z) > 0.25f)
			{
				return false;
			}
			if (!NullableFloatClose(a.TargetX, b.TargetX, 0.25f) || !NullableFloatClose(a.TargetY, b.TargetY, 0.25f) || !NullableFloatClose(a.TargetZ, b.TargetZ, 0.25f))
			{
				return false;
			}
			return true;
		}

		private static bool NullableFloatClose(float? a, float? b, float eps)
		{
			if (!a.HasValue && !b.HasValue)
			{
				return true;
			}
			if (!a.HasValue || !b.HasValue)
			{
				return false;
			}
			return Mathf.Abs(a.Value - b.Value) <= eps;
		}

		internal static bool AddFromRow(PortalRow row, bool alsoAutoPin = false)
		{
			if (row == null || string.IsNullOrEmpty(row.Uid))
			{
				return false;
			}
			EnsureLoaded();
			KnownPortalEntry knownPortalEntry = new KnownPortalEntry
			{
				Uid = row.Uid,
				Prefab = (row.Prefab ?? string.Empty),
				Tag = (row.Tag ?? string.Empty),
				X = row.X,
				Y = row.Y,
				Z = row.Z,
				TargetUid = (row.TargetUid ?? string.Empty),
				TargetX = row.TargetX,
				TargetY = row.TargetY,
				TargetZ = row.TargetZ,
				LastSeenUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
			};
			Entries[row.Uid] = knownPortalEntry;
			_dirty = true;
			Save();
			if (alsoAutoPin || (PortalAtlasPlugin.AutoPin != null && PortalAtlasPlugin.AutoPin.Value))
			{
				PortalMapPins.UpsertSavedPin(knownPortalEntry);
			}
			return true;
		}

		internal static void RecordTeleportWorld(TeleportWorld portal)
		{
			if (!((Object)(object)portal == (Object)null))
			{
				ZNetView val = ((Component)portal).GetComponent<ZNetView>();
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)portal).GetComponentInParent<ZNetView>();
				}
				if ((Object)(object)val == (Object)null || val.GetZDO() == null)
				{
					PortalAtlasPlugin.Debug("RecordTeleportWorld skipped — no ZNetView on '" + ((Object)((Component)portal).gameObject).name + "'");
					return;
				}
				string prefabHint = ((Object)((Component)val).gameObject).name.Replace("(Clone)", string.Empty).Trim();
				RecordZdo(val.GetZDO(), prefabHint);
			}
		}

		private static void ScanNearby()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ZDOMan.instance == null)
			{
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float approachRange = ApproachRange;
			float num = approachRange * approachRange;
			List<ZDO> list = null;
			try
			{
				list = ZDOMan.instance.GetPortalList();
			}
			catch (Exception ex)
			{
				PortalAtlasPlugin.Debug("GetPortalList failed: " + ex.Message);
			}
			if (list != null && list.Count > 0)
			{
				int num2 = 0;
				bool dirty = _dirty;
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				foreach (ZDO item2 in list)
				{
					if (item2 == null || !item2.IsValid())
					{
						continue;
					}
					string item = ((object)Unsafe.As<ZDOID, ZDOID>(ref item2.m_uid)/*cast due to .constrained prefix*/).ToString();
					if (hashSet.Add(item))
					{
						Vector3 position2 = item2.GetPosition();
						float num3 = position.x - position2.x;
						float num4 = position.z - position2.z;
						if (!(num3 * num3 + num4 * num4 > num))
						{
							num2++;
							RecordZdo(item2);
						}
					}
				}
				_lastNearbyCount = num2;
				if (PortalAtlasPlugin.DebugEnabled && num2 > 0 && _dirty && !dirty)
				{
					PortalAtlasPlugin.Debug($"Approach scan via GetPortalList: total={list.Count} nearby={num2} " + $"range={approachRange:0.#}m player=({position.x:0.#},{position.z:0.#})");
				}
				return;
			}
			TeleportWorld[] array = Resources.FindObjectsOfTypeAll<TeleportWorld>();
			int num5 = 0;
			bool dirty2 = _dirty;
			TeleportWorld[] array2 = array;
			foreach (TeleportWorld val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Scene scene = ((Component)val).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					Vector3 portalApproachPoint = GetPortalApproachPoint(val);
					float num6 = position.x - portalApproachPoint.x;
					float num7 = position.z - portalApproachPoint.z;
					if (!(num6 * num6 + num7 * num7 > num))
					{
						num5++;
						RecordTeleportWorld(val);
					}
				}
			}
			_lastNearbyCount = num5;
			if (PortalAtlasPlugin.DebugEnabled && num5 > 0 && _dirty && !dirty2)
			{
				PortalAtlasPlugin.Debug($"Approach scan fallback TeleportWorld[]: instances={array.Length} nearby={num5} range={approachRange:0.#}m");
			}
		}

		private static Vector3 GetPortalApproachPoint(TeleportWorld portal)
		{
			//IL_004a: 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_003c: 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)
			try
			{
				FieldInfo field = typeof(TeleportWorld).GetField("m_proximityRoot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object? value = field.GetValue(portal);
					Transform val = (Transform)((value is Transform) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val.position;
					}
				}
			}
			catch
			{
			}
			return ((Component)portal).transform.position;
		}

		private static string ResolvePrefabName(ZDO zdo)
		{
			if (zdo == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return string.Empty;
			}
			try
			{
				int num = 0;
				FieldInfo field = typeof(ZDO).GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					num = Convert.ToInt32(field.GetValue(zdo), CultureInfo.InvariantCulture);
				}
				if (num == 0)
				{
					return string.Empty;
				}
				string[] array = new string[2] { "portal_wood", "portal_stone" };
				foreach (string text in array)
				{
					if (!((Object)(object)ZNetScene.instance.GetPrefab(text) == (Object)null) && PortalScan.StableHash(text) == num)
					{
						return text;
					}
				}
			}
			catch
			{
			}
			return string.Empty;
		}

		private static int MergeAwayNearbyDuplicates(string keepUid, string tag, Vector3 pos)
		{
			//IL_0063: 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)
			float num = 16f;
			List<string> list = null;
			foreach (KeyValuePair<string, KnownPortalEntry> entry in Entries)
			{
				if (string.Equals(entry.Key, keepUid, StringComparison.OrdinalIgnoreCase) || !string.Equals(entry.Value.Tag ?? string.Empty, tag ?? string.Empty, StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				float num2 = entry.Value.X - pos.x;
				float num3 = entry.Value.Z - pos.z;
				if (!(num2 * num2 + num3 * num3 > num))
				{
					if (list == null)
					{
						list = new List<string>();
					}
					list.Add(entry.Key);
				}
			}
			if (list == null)
			{
				return 0;
			}
			foreach (string item in list)
			{
				Entries.Remove(item);
				PortalAtlasPlugin.Debug("Journal merge: removed duplicate '" + tag + "' uid=" + item + " (keeping " + keepUid + ")");
			}
			return list.Count;
		}

		internal static int CompactDuplicates()
		{
			float num = 16f;
			List<KnownPortalEntry> list = new List<KnownPortalEntry>(Entries.Values);
			list.Sort((KnownPortalEntry a, KnownPortalEntry b) => b.LastSeenUnix.CompareTo(a.LastSeenUnix));
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			List<string> list2 = new List<string>();
			foreach (KnownPortalEntry item in list)
			{
				if (item == null || string.IsNullOrEmpty(item.Uid))
				{
					continue;
				}
				bool flag = false;
				foreach (string item2 in hashSet)
				{
					if (Entries.TryGetValue(item2, out var value) && value != null && string.Equals(value.Tag ?? string.Empty, item.Tag ?? string.Empty, StringComparison.OrdinalIgnoreCase))
					{
						float num2 = value.X - item.X;
						float num3 = value.Z - item.Z;
						if (num2 * num2 + num3 * num3 <= num)
						{
							flag = true;
							break;
						}
					}
				}
				if (flag)
				{
					list2.Add(item.Uid);
				}
				else
				{
					hashSet.Add(item.Uid);
				}
			}
			foreach (string item3 in list2)
			{
				if (Entries.TryGetValue(item3, out var value2))
				{
					PortalAtlasPlugin.Debug($"Journal compact: removed '{value2.Tag}' uid={item3} at ({value2.X:0.#},{value2.Z:0.#})");
				}
				Entries.Remove(item3);
			}
			if (list2.Count > 0)
			{
				_dirty = true;
			}
			return list2.Count;
		}

		private static string GetWorldId()
		{
			try
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					string worldName = ZNet.instance.GetWorldName();
					if (!string.IsNullOrEmpty(worldName))
					{
						return worldName;
					}
				}
			}
			catch
			{
			}
			return "unknown_world";
		}

		private static string GetCharacterId()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					string playerName = localPlayer.GetPlayerName();
					return localPlayer.GetPlayerID().ToString(CultureInfo.InvariantCulture) + "_" + (playerName ?? "player");
				}
			}
			catch
			{
			}
			return "unknown_character";
		}
	}
	internal static class SimpleJson
	{
		internal static string Serialize(KnownPortalFile file)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"WorldId\":").Append(Q(file.WorldId));
			stringBuilder.Append(",\"CharacterId\":").Append(Q(file.CharacterId));
			stringBuilder.Append(",\"Portals\":[");
			bool flag = true;
			foreach (KnownPortalEntry portal in file.Portals)
			{
				if (!flag)
				{
					stringBuilder.Append(',');
				}
				flag = false;
				stringBuilder.Append('{');
				stringBuilder.Append("\"Prefab\":").Append(Q(portal.Prefab)).Append(',');
				stringBuilder.Append("\"Tag\":").Append(Q(portal.Tag)).Append(',');
				stringBuilder.Append("\"Uid\":").Append(Q(portal.Uid)).Append(',');
				stringBuilder.Append("\"X\":").Append(F(portal.X)).Append(',');
				stringBuilder.Append("\"Y\":").Append(F(portal.Y)).Append(',');
				stringBuilder.Append("\"Z\":").Append(F(portal.Z)).Append(',');
				stringBuilder.Append("\"TargetUid\":").Append(Q(portal.TargetUid)).Append(',');
				stringBuilder.Append("\"TargetX\":").Append(portal.TargetX.HasValue ? F(portal.TargetX.Value) : "null").Append(',');
				stringBuilder.Append("\"TargetY\":").Append(portal.TargetY.HasValue ? F(portal.TargetY.Value) : "null").Append(',');
				stringBuilder.Append("\"TargetZ\":").Append(portal.TargetZ.HasValue ? F(portal.TargetZ.Value) : "null").Append(',');
				stringBuilder.Append("\"LastSeenUnix\":").Append(portal.LastSeenUnix.ToString(CultureInfo.InvariantCulture));
				stringBuilder.Append('}');
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		internal static KnownPortalFile Deserialize(string json)
		{
			KnownPortalFile knownPortalFile = new KnownPortalFile
			{
				Portals = new List<KnownPortalEntry>()
			};
			if (string.IsNullOrEmpty(json))
			{
				return knownPortalFile;
			}
			knownPortalFile.WorldId = ExtractString(json, "WorldId");
			knownPortalFile.CharacterId = ExtractString(json, "CharacterId");
			int num = json.IndexOf("\"Portals\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return knownPortalFile;
			}
			int num2 = json.IndexOf('[', num);
			int num3 = json.LastIndexOf(']');
			if (num2 < 0 || num3 <= num2)
			{
				return knownPortalFile;
			}
			foreach (string item in SplitObjects(json.Substring(num2 + 1, num3 - num2 - 1)))
			{
				KnownPortalEntry knownPortalEntry = new KnownPortalEntry
				{
					Prefab = ExtractString(item, "Prefab"),
					Tag = ExtractString(item, "Tag"),
					Uid = ExtractString(item, "Uid"),
					X = ExtractFloat(item, "X"),
					Y = ExtractFloat(item, "Y"),
					Z = ExtractFloat(item, "Z"),
					TargetUid = ExtractString(item, "TargetUid"),
					TargetX = ExtractNullableFloat(item, "TargetX"),
					TargetY = ExtractNullableFloat(item, "TargetY"),
					TargetZ = ExtractNullableFloat(item, "TargetZ"),
					LastSeenUnix = (long)ExtractFloat(item, "LastSeenUnix")
				};
				if (!string.IsNullOrEmpty(knownPortalEntry.Uid))
				{
					knownPortalFile.Portals.Add(knownPortalEntry);
				}
			}
			return knownPortalFile;
		}

		private static IEnumerable<string> SplitObjects(string arrayBody)
		{
			List<string> list = new List<string>();
			int num = 0;
			int num2 = -1;
			for (int i = 0; i < arrayBody.Length; i++)
			{
				switch (arrayBody[i])
				{
				case '{':
					if (num == 0)
					{
						num2 = i;
					}
					num++;
					break;
				case '}':
					num--;
					if (num == 0 && num2 >= 0)
					{
						list.Add(arrayBody.Substring(num2, i - num2 + 1));
						num2 = -1;
					}
					break;
				}
			}
			return list;
		}

		private static string ExtractString(string json, string key)
		{
			string text = "\"" + key + "\"";
			int num = json.IndexOf(text, StringComparison.Ordinal);
			if (num < 0)
			{
				return string.Empty;
			}
			int num2 = json.IndexOf(':', num + text.Length);
			if (num2 < 0)
			{
				return string.Empty;
			}
			int num3 = json.IndexOf('"', num2 + 1);
			if (num3 < 0)
			{
				return string.Empty;
			}
			int i;
			for (i = num3 + 1; i < json.Length && (json[i] != '"' || json[i - 1] == '\\'); i++)
			{
			}
			if (i >= json.Length)
			{
				return string.Empty;
			}
			return json.Substring(num3 + 1, i - num3 - 1).Replace("\\\"", "\"");
		}

		private static float ExtractFloat(string json, string key)
		{
			return ExtractNullableFloat(json, key).GetValueOrDefault();
		}

		private static float? ExtractNullableFloat(string json, string key)
		{
			string text = "\"" + key + "\"";
			int num = json.IndexOf(text, StringComparison.Ordinal);
			if (num < 0)
			{
				return null;
			}
			int num2 = json.IndexOf(':', num + text.Length);
			if (num2 < 0)
			{
				return null;
			}
			int i;
			for (i = num2 + 1; i < json.Length && char.IsWhiteSpace(json[i]); i++)
			{
			}
			if (i + 4 <= json.Length && string.Compare(json, i, "null", 0, 4, StringComparison.OrdinalIgnoreCase) == 0)
			{
				return null;
			}
			int j;
			for (j = i; j < json.Length && (char.IsDigit(json[j]) || json[j] == '-' || json[j] == '+' || json[j] == '.' || json[j] == 'e' || json[j] == 'E'); j++)
			{
			}
			if (j <= i)
			{
				return null;
			}
			if (float.TryParse(json.Substring(i, j - i), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return null;
		}

		private static string Q(string value)
		{
			value = value ?? string.Empty;
			return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
		}

		private static string F(float value)
		{
			return value.ToString("0.###", CultureInfo.InvariantCulture);
		}
	}
	internal static class PortalAccess
	{
		internal static bool CanRefreshWorld()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			if (ZNet.instance.IsServer())
			{
				return true;
			}
			return ServerDevcommandsAccess.IsAdmin();
		}

		internal static string DescribeRefreshAccess()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return "ZNet missing";
			}
			if (ZNet.instance.IsServer())
			{
				return "host/server (local scan allowed)";
			}
			return $"dedicated client ServerDevcommands.IsAdmin={ServerDevcommandsAccess.IsAdmin()} available={ServerDevcommandsAccess.IsAvailable}";
		}

		internal static bool IsPeerAdmin(long sender)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			if (sender == 0L)
			{
				return ZNet.instance.IsServer();
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null)
			{
				return false;
			}
			string peerHostName = GetPeerHostName(peer);
			if (string.IsNullOrEmpty(peerHostName))
			{
				return false;
			}
			try
			{
				return ZNet.instance.IsAdmin(peerHostName);
			}
			catch (Exception ex)
			{
				PortalAtlasPlugin.Debug("ZNet.IsAdmin(" + peerHostName + ") failed: " + ex.Message);
				return false;
			}
		}

		private static string GetPeerHostName(ZNetPeer peer)
		{
			try
			{
				if (peer.m_socket != null)
				{
					return peer.m_socket.GetHostName();
				}
			}
			catch
			{
			}
			return null;
		}
	}
	[BepInPlugin("sonicdm.valheimportallist", "Portal Atlas", "1.2.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class PortalAtlasPlugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__21_0;

			public static ConsoleEvent <>9__21_1;

			internal void <TryRegisterCommands>b__21_0(ConsoleEventArgs args)
			{
				Debug($"Command: portallist canRefresh={PortalAccess.CanRefreshWorld()} isServer={(Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()}");
				if (!PortalAccess.CanRefreshWorld() && (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()))
				{
					args.Context.AddString("Portal Atlas: need Server Devcommands admin (dedicated) or be the world host.");
				}
				else if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					PortalDumpResult portalDumpResult = PortalScan.DumpPortals();
					args.Context.AddString($"Portal Atlas: wrote {portalDumpResult.Rows.Count} portal(s) to {portalDumpResult.CsvPath}");
				}
				else
				{
					args.Context.AddString("Portal Atlas: run portallist on the host, or use Refresh world in the panel.");
				}
			}

			internal void <TryRegisterCommands>b__21_1(ConsoleEventArgs args)
			{
				Debug("Command: portals");
				PortalMapPins.OpenLargeMap();
				PortalPanelUi.Toggle();
			}
		}

		public const string PluginGuid = "sonicdm.valheimportallist";

		public const string PluginName = "Portal Atlas";

		public const string PluginVersion = "1.2.5";

		internal static ManualLogSource ModLogger;

		internal static PortalAtlasPlugin Instance;

		internal static ConfigEntry<bool> AutoPin;

		internal static ConfigEntry<float> ApproachRangeMeters;

		internal static ConfigEntry<bool> DebugLogging;

		private Harmony _harmony;

		private bool _commandRegistered;

		private bool _uiEnabled;

		private bool _subscribedGui;

		private bool _subscribedSdc;

		internal static bool DebugEnabled
		{
			get
			{
				if (DebugLogging != null)
				{
					return DebugLogging.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			Instance = this;
			ModLogger = ((BaseUnityPlugin)this).Logger;
			PortalScan.BindConfig(((BaseUnityPlugin)this).Config);
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "When true, write detailed [DEBUG] lines for all mod systems to BepInEx/LogOutput.log (journal, approach, pins, UI, refresh RPC, access, scans).");
			AutoPin = ((BaseUnityPlugin)this).Config.Bind<bool>("Pins", "AutoPinOnApproach", false, "When enabled, approaching or using a portal also creates a saved map pin (DudeWheresMyPortal-style).");
			ApproachRangeMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Pins", "ApproachRangeMeters", 8f, new ConfigDescription("How close you must be (meters) for a loaded portal to be recorded in the journal (and auto-pinned if AutoPinOnApproach is on).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			_harmony = new Harmony("sonicdm.valheimportallist");
			try
			{
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Harmony patch failed.");
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
			if (GUIManager.IsHeadless())
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Portal Atlas 1.2.5 headless: CSV dump + admin Refresh RPC.");
				Debug("DebugLogging enabled.");
				return;
			}
			_uiEnabled = true;
			GUIManager.OnCustomGUIAvailable += OnCustomGuiAvailable;
			_subscribedGui = true;
			ServerDevcommandsAccess.Subscribe(OnAdminStatusChanged);
			_subscribedSdc = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Portal Atlas 1.2.5 loaded.");
			Debug("DebugLogging enabled.");
		}

		internal static void Debug(string message)
		{
			if (DebugEnabled && ModLogger != null)
			{
				ModLogger.LogInfo((object)("[DEBUG] " + message));
			}
		}

		private void Update()
		{
			PortalRpc.TickRegister();
			PortalScan.TickHostAutoDump();
			if (GUIManager.IsHeadless())
			{
				return;
			}
			if (!_commandRegistered)
			{
				TryRegisterCommands();
			}
			if (_uiEnabled)
			{
				KnownPortalCache.Tick();
				if (PortalMapPins.HasPendingAutoPins)
				{
					PortalMapPins.TickPendingAutoPins();
				}
				PortalPanelUi.Tick();
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			if (_subscribedGui)
			{
				GUIManager.OnCustomGUIAvailable -= OnCustomGuiAvailable;
				_subscribedGui = false;
			}
			if (_subscribedSdc)
			{
				ServerDevcommandsAccess.Unsubscribe();
				_subscribedSdc = false;
			}
			if (_uiEnabled)
			{
				PortalPanelUi.DestroyUi();
				PortalMapPins.ClearOverlay();
			}
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void OnCustomGuiAvailable()
		{
			PortalPanelUi.OnGuiReady();
		}

		private static void OnAdminStatusChanged()
		{
			Debug("OnAdminStatusChanged (" + PortalAccess.DescribeRefreshAccess() + ")");
			PortalPanelUi.OnAdminStatusChanged();
		}

		private void TryRegisterCommands()
		{
			//IL_0033: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			try
			{
				object obj = <>c.<>9__21_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs args)
					{
						Debug($"Command: portallist canRefresh={PortalAccess.CanRefreshWorld()} isServer={(Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()}");
						if (!PortalAccess.CanRefreshWorld() && (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()))
						{
							args.Context.AddString("Portal Atlas: need Server Devcommands admin (dedicated) or be the world host.");
						}
						else if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
						{
							PortalDumpResult portalDumpResult = PortalScan.DumpPortals();
							args.Context.AddString($"Portal Atlas: wrote {portalDumpResult.Rows.Count} portal(s) to {portalDumpResult.CsvPath}");
						}
						else
						{
							args.Context.AddString("Portal Atlas: run portallist on the host, or use Refresh world in the panel.");
						}
					};
					<>c.<>9__21_0 = val;
					obj = (object)val;
				}
				new ConsoleCommand("portallist", "Export every portal to CSV under BepInEx/cache/PortalAtlas (host, or dedicated admin via Server Devcommands)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, true);
				object obj2 = <>c.<>9__21_1;
				if (obj2 == null)
				{
					ConsoleEvent val2 = delegate
					{
						Debug("Command: portals");
						PortalMapPins.OpenLargeMap();
						PortalPanelUi.Toggle();
					};
					<>c.<>9__21_1 = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("portals", "Toggle the Portal Atlas panel", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered commands: portals, portallist");
				_commandRegistered = true;
			}
			catch
			{
			}
		}
	}
	internal static class PortalMapPins
	{
		private const string OwnedMarker = "\u200b";

		private static readonly List<object> OverlayPins = new List<object>();

		private static readonly List<object> SavedPins = new List<object>();

		private static readonly List<KnownPortalEntry> _pendingAutoPins = new List<KnownPortalEntry>();

		internal static Color OverlayTint = new Color(1f, 0.55f, 0.1f, 1f);

		internal static bool HasPendingAutoPins => _pendingAutoPins.Count > 0;

		internal static void ClearOverlay()
		{
			int count = OverlayPins.Count;
			RemoveTrackedPins(OverlayPins);
			if (count > 0)
			{
				PortalAtlasPlugin.Debug($"ClearOverlay removed {count} temp pin(s)");
			}
		}

		internal static void ShowOverlay(IEnumerable<PortalRow> rows)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			ClearOverlay();
			if (rows == null)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				PortalAtlasPlugin.Debug("ShowOverlay skipped — minimap/player missing");
				return;
			}
			object portalPinType = GetPortalPinType();
			long playerID = localPlayer.GetPlayerID();
			int num = 0;
			int num2 = 0;
			Vector3 pos = default(Vector3);
			foreach (PortalRow row in rows)
			{
				((Vector3)(ref pos))..ctor(row.X, row.Y, row.Z);
				string displayTag = row.DisplayTag;
				if (HasPermanentPinNamedNear(displayTag, pos, 1.5f))
				{
					num2++;
					continue;
				}
				object obj = AddMinimapPin(instance, pos, portalPinType, ToOwnedName(displayTag), playerID, save: false);
				if (obj != null)
				{
					OverlayPins.Add(obj);
					num++;
				}
			}
			ForceUpdatePins(instance);
			RetintOverlay();
			PortalAtlasPlugin.Debug($"ShowOverlay placed {num} temp pin(s), skipped {num2} (permanent already there)");
		}

		internal static void RetintOverlay()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			foreach (object overlayPin in OverlayPins)
			{
				TryTintPin(overlayPin, OverlayTint);
			}
		}

		internal static void UpsertSavedPin(KnownPortalEntry entry)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				PortalAtlasPlugin.Debug("Auto-pin deferred — minimap/player missing for tag='" + entry.Tag + "'");
				QueuePendingAutoPin(entry);
				return;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(entry.X, entry.Y, entry.Z);
			string text = (string.IsNullOrEmpty(entry.Tag) ? "(untagged)" : entry.Tag);
			if (HasPermanentPinNamedNear(text, val, 1.5f))
			{
				PortalAtlasPlugin.Debug($"Auto-pin skipped — permanent pin '{text}' already near ({val.x:0.#},{val.z:0.#})");
				return;
			}
			RemoveOwnedNear(val, 1.5f);
			object portalPinType = GetPortalPinType();
			object obj = AddMinimapPin(instance, val, portalPinType, ToOwnedName(text), localPlayer.GetPlayerID(), save: true);
			if (obj != null)
			{
				SavedPins.Add(obj);
				ForceUpdatePins(instance);
				PortalAtlasPlugin.Debug($"Auto-pin placed tag='{text}' prefab={entry.Prefab} at ({val.x:0.#},{val.z:0.#})");
			}
			else
			{
				PortalAtlasPlugin.Debug("Auto-pin failed — AddPin returned null for tag='" + text + "'");
				QueuePendingAutoPin(entry);
			}
		}

		internal static void TickPendingAutoPins()
		{
			if (_pendingAutoPins.Count == 0 || (Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			List<KnownPortalEntry> list = new List<KnownPortalEntry>(_pendingAutoPins);
			_pendingAutoPins.Clear();
			foreach (KnownPortalEntry item in list)
			{
				UpsertSavedPin(item);
			}
		}

		internal static bool TryPinPortal(PortalRow row, out string message)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			message = "Could not pin.";
			if (row == null)
			{
				return false;
			}
			KnownPortalEntry knownPortalEntry = new KnownPortalEntry
			{
				Uid = (row.Uid ?? string.Empty),
				Prefab = (row.Prefab ?? string.Empty),
				Tag = (row.Tag ?? string.Empty),
				X = row.X,
				Y = row.Y,
				Z = row.Z,
				TargetUid = (row.TargetUid ?? string.Empty),
				TargetX = row.TargetX,
				TargetY = row.TargetY,
				TargetZ = row.TargetZ,
				LastSeenUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
			};
			string text = (string.IsNullOrEmpty(knownPortalEntry.Tag) ? "(untagged)" : knownPortalEntry.Tag);
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(knownPortalEntry.X, knownPortalEntry.Y, knownPortalEntry.Z);
			if (HasPermanentPinNamedNear(text, val, 1.5f))
			{
				message = "Already pinned: " + text;
				return false;
			}
			Minimap instance = Minimap.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				QueuePendingAutoPin(knownPortalEntry);
				message = "Pin queued for " + text + " (map not ready yet).";
				return true;
			}
			RemoveOwnedNear(val, 1.5f);
			object portalPinType = GetPortalPinType();
			object obj = AddMinimapPin(instance, val, portalPinType, ToOwnedName(text), localPlayer.GetPlayerID(), save: true);
			if (obj == null)
			{
				QueuePendingAutoPin(knownPortalEntry);
				message = "Pin queued for " + text + " (AddPin failed).";
				return false;
			}
			SavedPins.Add(obj);
			ForceUpdatePins(instance);
			message = "Pinned " + text + ".";
			PortalAtlasPlugin.Debug($"TryPinPortal ok tag='{text}' at ({val.x:0.#},{val.z:0.#})");
			return true;
		}

		internal static void ClearSavedPins()
		{
			int count = SavedPins.Count;
			RemoveTrackedPins(SavedPins);
			RemoveAllOwnedSavedPinsFromMap();
			PortalAtlasPlugin.Debug($"ClearSavedPins trackedRefs={count} (manual pins untouched)");
		}

		internal static void Ping(Vector3 position)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			OpenLargeMap();
			CenterOn(position);
			try
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					MethodInfo method = typeof(Chat).GetMethod("SendPing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (method != null)
					{
						method.Invoke(Chat.instance, new object[1] { position });
					}
				}
			}
			catch
			{
			}
			PortalAtlasPlugin.Debug($"Ping + center map at ({position.x:0.#},{position.z:0.#})");
		}

		internal static void CenterOn(Vector3 position)
		{
			//IL_0054: 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)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			OpenLargeMap();
			try
			{
				MethodInfo method = typeof(Minimap).GetMethod("ShowPointOnMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector3) }, null);
				if (method != null)
				{
					method.Invoke(instance, new object[1] { position });
					return;
				}
			}
			catch
			{
			}
			try
			{
				MethodInfo method2 = typeof(Minimap).GetMethod("CenterMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector3) }, null);
				if (method2 != null)
				{
					method2.Invoke(instance, new object[1] { position });
				}
			}
			catch
			{
			}
		}

		internal static void OpenLargeMap()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				MethodInfo method = typeof(Minimap).GetMethod("SetMapMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method != null)
				{
					Type nestedType = typeof(Minimap).GetNestedType("MapMode", BindingFlags.Public | BindingFlags.NonPublic);
					if (nestedType != null)
					{
						object obj = Enum.Parse(nestedType, "Large", ignoreCase: true);
						method.Invoke(instance, new object[1] { obj });
						return;
					}
				}
			}
			catch
			{
			}
			try
			{
				MethodInfo method2 = typeof(Minimap).GetMethod("ShowMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method2 != null)
				{
					method2.Invoke(instance, null);
				}
			}
			catch
			{
			}
		}

		internal static bool IsLargeMapOpen()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo method = typeof(Minimap).GetMethod("IsOpen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method != null)
				{
					return (bool)method.Invoke(instance, null);
				}
			}
			catch
			{
			}
			try
			{
				FieldInfo field = typeof(Minimap).GetField("m_largeRoot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object? value = field.GetValue(instance);
					GameObject val = (GameObject)((value is GameObject) ? value : null);
					return (Object)(object)val != (Object)null && val.activeInHierarchy;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static Transform GetMapPanelRoot()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			string[] array = new string[2] { "m_mapLarge", "m_largeRoot" };
			foreach (string name in array)
			{
				FieldInfo field = typeof(Minimap).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null))
				{
					object value = field.GetValue(instance);
					GameObject val = (GameObject)((value is GameObject) ? value : null);
					if (val != null && (Object)(object)val != (Object)null)
					{
						return val.transform;
					}
					Transform val2 = (Transform)((value is Transform) ? value : null);
					if (val2 != null && (Object)(object)val2 != (Object)null)
					{
						return val2;
					}
					Component val3 = (Component)((value is Component) ? value : null);
					if (val3 != null && (Object)(object)val3 != (Object)null)
					{
						return val3.transform;
					}
				}
			}
			return GetLargeMapRoot();
		}

		internal static Transform GetLargeMapRoot()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			string[] array = new string[4] { "m_mapLarge", "m_largeRoot", "m_mapRoot", "m_largeMap" };
			foreach (string name in array)
			{
				FieldInfo field = typeof(Minimap).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null))
				{
					object value = field.GetValue(instance);
					GameObject val = (GameObject)((value is GameObject) ? value : null);
					if (val != null)
					{
						return val.transform;
					}
					Transform val2 = (Transform)((value is Transform) ? value : null);
					if (val2 != null)
					{
						return val2;
					}
					Component val3 = (Component)((value is Component) ? value : null);
					if (val3 != null)
					{
						return val3.transform;
					}
				}
			}
			return ((Component)instance).transform;
		}

		internal static bool TryMapClickToWorld(out Vector3 world)
		{
			//IL_0001: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			world = Vector3.zero;
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo method = typeof(Minimap).GetMethod("ScreenToWorldPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method != null && method.Invoke(instance, new object[1] { Input.mousePosition }) is Vector3 val)
				{
					world = val;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static string ToOwnedName(string displayTag)
		{
			return "\u200b" + (displayTag ?? string.Empty);
		}

		private static bool IsOwnedPin(object pin)
		{
			string pinName = GetPinName(pin);
			if (!string.IsNullOrEmpty(pinName))
			{
				return pinName.StartsWith("\u200b", StringComparison.Ordinal);
			}
			return false;
		}

		private static bool HasPermanentPinNamedNear(string displayName, Vector3 pos, float radius)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(displayName))
			{
				return false;
			}
			foreach (object item in SnapshotMapPins())
			{
				if (item == null || (IsOwnedPin(item) && IsPinSaveFlag(item) == false))
				{
					continue;
				}
				string displayPinName = GetDisplayPinName(item);
				if (!string.IsNullOrEmpty(displayPinName) && string.Equals(displayPinName, displayName, StringComparison.OrdinalIgnoreCase))
				{
					Vector3? pinPos = GetPinPos(item);
					if (pinPos.HasValue && Vector3.Distance(pinPos.Value, pos) <= radius)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string GetDisplayPinName(object pin)
		{
			string pinName = GetPinName(pin);
			if (string.IsNullOrEmpty(pinName))
			{
				return string.Empty;
			}
			if (pinName.StartsWith("\u200b", StringComparison.Ordinal))
			{
				return pinName.Substring("\u200b".Length);
			}
			return pinName;
		}

		private static void QueuePendingAutoPin(KnownPortalEntry entry)
		{
			if (entry == null || string.IsNullOrEmpty(entry.Uid))
			{
				return;
			}
			for (int i = 0; i < _pendingAutoPins.Count; i++)
			{
				if (string.Equals(_pendingAutoPins[i].Uid, entry.Uid, StringComparison.OrdinalIgnoreCase))
				{
					_pendingAutoPins[i] = entry;
					return;
				}
			}
			_pendingAutoPins.Add(entry);
		}

		private static void RemoveOwnedNear(Vector3 pos, float radius)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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)
			for (int num = SavedPins.Count - 1; num >= 0; num--)
			{
				object pin = SavedPins[num];
				if (!IsOwnedPin(pin))
				{
					SavedPins.RemoveAt(num);
				}
				else
				{
					Vector3? pinPos = GetPinPos(pin);
					if (pinPos.HasValue && Vector3.Distance(pinPos.Value, pos) <= radius)
					{
						RemoveOneIfOwned(pin);
						SavedPins.RemoveAt(num);
					}
				}
			}
			foreach (object item in SnapshotMapPins())
			{
				if (IsOwnedPin(item))
				{
					Vector3? pinPos2 = GetPinPos(item);
					if (pinPos2.HasValue && Vector3.Distance(pinPos2.Value, pos) <= radius)
					{
						RemoveOneIfOwned(item);
					}
				}
			}
		}

		private static void RemoveAllOwnedSavedPinsFromMap()
		{
			foreach (object item in SnapshotMapPins())
			{
				if (IsOwnedPin(item) && IsPinSaveFlag(item) != false)
				{
					RemoveOneIfOwned(item);
				}
			}
		}

		private static List<object> SnapshotMapPins()
		{
			List<object> list = new List<object>();
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return list;
			}
			try
			{
				FieldInfo field = typeof(Minimap).GetField("m_pins", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (((field != null) ? field.GetValue(instance) : null) is IEnumerable enumerable)
				{
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							list.Add(item);
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static bool? IsPinSaveFlag(object pin)
		{
			if (pin == null)
			{
				return null;
			}
			FieldInfo field = pin.GetType().GetField("m_save", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field.FieldType != typeof(bool))
			{
				return null;
			}
			return (bool)field.GetValue(pin);
		}

		private static string GetPinName(object pin)
		{
			if (pin == null)
			{
				return null;
			}
			FieldInfo field = pin.GetType().GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return null;
			}
			return field.GetValue(pin) as string;
		}

		private static Vector3? GetPinPos(object pin)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (pin == null)
			{
				return null;
			}
			FieldInfo field = pin.GetType().GetField("m_pos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return null;
			}
			object value = field.GetValue(pin);
			if (!(value is Vector3))
			{
				return null;
			}
			return (Vector3)value;
		}

		private static void RemoveTrackedPins(List<object> pins)
		{
			for (int num = pins.Count - 1; num >= 0; num--)
			{
				RemoveOneIfOwned(pins[num]);
				pins.RemoveAt(num);
			}
			pins.Clear();
		}

		private static void RemoveOneIfOwned(object pin)
		{
			if (pin == null || !IsOwnedPin(pin))
			{
				return;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				instance.RemovePin((PinData)((pin is PinData) ? pin : null));
			}
			catch
			{
			}
		}

		private static void TryTintPin(object pin, Color color)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (pin == null || !IsOwnedPin(pin))
			{
				return;
			}
			Type type = pin.GetType();
			try
			{
				string[] array = new string[4] { "m_icon", "m_iconElement", "m_uiElement", "m_pinIcon" };
				foreach (string name in array)
				{
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (!(field == null) && TintGraphic(field.GetValue(pin), color))
					{
						break;
					}
				}
			}
			catch
			{
			}
		}

		private static bool TintGraphic(object value, Color color)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (value == null)
			{
				return false;
			}
			Image val = (Image)((value is Image) ? value : null);
			if (val != null)
			{
				((Graphic)val).color = color;
				return true;
			}
			Component val2 = (Component)((value is Component) ? value : null);
			if (val2 != null)
			{
				Image[] componentsInChildren = val2.GetComponentsInChildren<Image>(true);
				if (componentsInChildren != null && componentsInChildren.Length != 0)
				{
					Image[] array = componentsInChildren;
					foreach (Image val3 in array)
					{
						if ((Object)(object)val3 != (Object)null)
						{
							((Graphic)val3).color = color;
						}
					}
					return true;
				}
			}
			GameObject val4 = (GameObject)((value is GameObject) ? value : null);
			if (val4 != null)
			{
				Image[] componentsInChildren2 = val4.GetComponentsInChildren<Image>(true);
				if (componentsInChildren2 != null && componentsInChildren2.Length != 0)
				{
					Image[] array = componentsInChildren2;
					foreach (Image val5 in array)
					{
						if ((Object)(object)val5 != (Object)null)
						{
							((Graphic)val5).color = color;
						}
					}
					return true;
				}
			}
			return false;
		}

		private static void ForceUpdatePins(Minimap map)
		{
			if ((Object)(object)map == (Object)null)
			{
				return;
			}
			string[] array = new string[2] { "UpdatePins", "UpdateDynamicPins" };
			foreach (string name in array)
			{
				try
				{
					MethodInfo method = typeof(Minimap).GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (!(method == null))
					{
						ParameterInfo[] parameters = method.GetParameters();
						if (parameters.Length == 0)
						{
							method.Invoke(map, null);
							break;
						}
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(float))
						{
							method.Invoke(map, new object[1] { 0f });
							break;
						}
					}
				}
				catch
				{
				}
			}
		}

		private static object GetPortalPinType()
		{
			Type nestedType = typeof(Minimap).GetNestedType("PinType", BindingFlags.Public | BindingFlags.NonPublic);
			if (nestedType == null || !nestedType.IsEnum)
			{
				return 4;
			}
			string[] array = new string[2] { "Portal", "Icon4" };
			foreach (string value in array)
			{
				try
				{
					return Enum.Parse(nestedType, value, ignoreCase: true);
				}
				catch
				{
				}
			}
			return Enum.ToObject(nestedType, 4);
		}

		private static object AddMinimapPin(Minimap map, Vector3 pos, object pinType, string name, long playerId, bool save)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)map == (Object)null)
			{
				return null;
			}
			MethodInfo[] methods = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!string.Equals(methodInfo.Name, "AddPin", StringComparison.Ordinal))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length < 5)
				{
					continue;
				}
				try
				{
					object[] array = new object[parameters.Length];
					array[0] = pos;
					array[1] = pinType;
					array[2] = name ?? string.Empty;
					array[3] = save;
					array[4] = false;
					if (parameters.Length > 5 && (parameters[5].ParameterType == typeof(long) || parameters[5].ParameterType == typeof(int)))
					{
						array[5] = Convert.ChangeType(playerId, parameters[5].ParameterType, CultureInfo.InvariantCulture);
					}
					for (int j = 0; j < array.Length; j++)
					{
						if (array[j] == null)
						{
							Type parameterType = parameters[j].ParameterType;
							array[j] = (parameterType.IsValueType ? Activator.CreateInstance(parameterType) : null);
						}
					}
					return methodInfo.Invoke(map, array);
				}
				catch
				{
				}
			}
			return null;
		}
	}
	internal static class PortalMapZoomGuard
	{
		private static float _largeBefore;

		private static float _smallBefore;

		private static bool _guard;

		private static float _suppressUntil;

		internal static bool HasScrollInput()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Abs(Input.mouseScrollDelta.y) >= 0.01f)
			{
				return true;
			}
			try
			{
				return Mathf.Abs(Input.GetAxis("Mouse ScrollWheel")) >= 0.0001f;
			}
			catch
			{
				return false;
			}
		}

		internal static bool ShouldSuppressZoom()
		{
			if (!PortalPanelUi.IsOpen)
			{
				return false;
			}
			if (HasScrollInput() && PortalPanelUi.ShouldBlockMapZoom())
			{
				_suppressUntil = Time.unscaledTime + 0.12f;
				return true;
			}
			if (Time.unscaledTime < _suppressUntil)
			{
				return PortalPanelUi.ShouldBlockMapZoom();
			}
			return false;
		}

		internal static void BeginFrame(Minimap map)
		{
			_guard = false;
			if ((Object)(object)map == (Object)null || !ShouldSuppressZoom())
			{
				return;
			}
			_guard = true;
			_largeBefore = ReadFloat(map, "m_largeZoom", delegate
			{
				try
				{
					return map.LargeZoom;
				}
				catch
				{
					return 0f;
				}
			});
			_smallBefore = ReadFloat(map, "m_smallZoom", delegate
			{
				try
				{
					return map.SmallZoom;
				}
				catch
				{
					return 0f;
				}
			});
			ClearInertia(map);
		}

		internal static void EndFrame(Minimap map)
		{
			if (!_guard || (Object)(object)map == (Object)null)
			{
				return;
			}
			WriteFloat(map, "m_largeZoom", _largeBefore, delegate(float v)
			{
				try
				{
					map.LargeZoom = v;
				}
				catch
				{
				}
			});
			WriteFloat(map, "m_smallZoom", _smallBefore, delegate(float v)
			{
				try
				{
					map.SmallZoom = v;
				}
				catch
				{
				}
			});
			ClearInertia(map);
		}

		internal static bool AllowZoomSetter()
		{
			return !ShouldSuppressZoom();
		}

		private static void ClearInertia(Minimap map)
		{
			WriteFloat(map, "m_zoomInertia", 0f, null);
			try
			{
				FieldInfo field = typeof(Minimap).GetField("m_startedZooming", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(bool))
				{
					field.SetValue(map, false);
				}
			}
			catch
			{
			}
		}

		private static float ReadFloat(Minimap map, string fieldName, Func<float> fallback)
		{
			try
			{
				FieldInfo field = typeof(Minimap).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(float))
				{
					return (float)field.GetValue(map);
				}
			}
			catch
			{
			}
			return fallback?.Invoke() ?? 0f;
		}

		private static void WriteFloat(Minimap map, string fieldName, float value, Action<float> fallback)
		{
			try
			{
				FieldInfo field = typeof(Minimap).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(float))
				{
					field.SetValue(map, value);
					return;
				}
			}
			catch
			{
			}
			fallback?.Invoke(value);
		}
	}
	[HarmonyPatch(typeof(Minimap), "Update")]
	internal static class Minimap_Update_ZoomGuard
	{
		private static void Prefix(Minimap __instance)
		{
			PortalMapZoomGuard.BeginFrame(__instance);
		}

		private static void Postfix(Minimap __instance)
		{
			PortalMapZoomGuard.EndFrame(__instance);
		}
	}
	[HarmonyPatch(typeof(Minimap), "set_LargeZoom")]
	internal static class Minimap_SetLargeZoom_Block
	{
		private static bool Prefix()
		{
			return PortalMapZoomGuard.AllowZoomSetter();
		}
	}
	[HarmonyPatch(typeof(Minimap), "set_SmallZoom")]
	internal static class Minimap_SetSmallZoom_Block
	{
		private static bool Prefix()
		{
			return PortalMapZoomGuard.AllowZoomSetter();
		}
	}
	internal sealed class PortalRow
	{
		public string Prefab;

		public string Tag;

		public string Uid;

		public float X;

		public float Y;

		public float Z;

		public string TargetUid;

		public bool Connected;

		public float? TargetX;

		public float? TargetY;

		public float? TargetZ;

		public int SameTagCount;

		public string Relationship;

		public string Issues;

		public string DisplayTag
		{
			get
			{
				if (!string.IsNullOrEmpty(Tag))
				{
					return Tag;
				}
				return "(untagged)";
			}
		}

		public string StatusLabel
		{
			get
			{
				if (string.Equals(Relationship, "Mutual pair", StringComparison.OrdinalIgnoreCase) || string.Equals(Relationship, "Connected", StringComparison.OrdinalIgnoreCase))
				{
					return "connected";
				}
				if (string.Equals(Relationship, "One-way link", StringComparison.OrdinalIgnoreCase))
				{
					return "one-way";
				}
				if (string.Equals(Relationship, "Tag twin (not linked yet)", StringComparison.OrdinalIgnoreCase))
				{
					return "tag twin";
				}
				if (Connected)
				{
					return "connected";
				}
				return "unconnected";
			}
		}

		public bool HasPartnerPosition
		{
			get
			{
				if (TargetX.HasValue && TargetY.HasValue && TargetZ.HasValue && !string.IsNullOrEmpty(TargetUid) && !TargetUid.Equals("None", StringComparison.OrdinalIgnoreCase))
				{
					return !TargetUid.Equals("0:0", StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}
		}
	}
	internal sealed class PortalDumpResult
	{
		public List<PortalRow> Rows = new List<PortalRow>();

		public int PortalPrefabCount;

		public string CsvPath;

		public string SummaryCsvPath;

		public string TextPath;
	}
	internal sealed class KnownPortalEntry
	{
		public string Prefab;

		public string Tag;

		public string Uid;

		public float X;

		public float Y;

		public float Z;

		public string TargetUid;

		public float? TargetX;

		public float? TargetY;

		public float? TargetZ;

		public long LastSeenUnix;

		public PortalRow ToRow()
		{
			return new PortalRow
			{
				Prefab = (Prefab ?? string.Empty),
				Tag = (Tag ?? string.Empty),
				Uid = (Uid ?? string.Empty),
				X = X,
				Y = Y,
				Z = Z,
				TargetUid = (TargetUid ?? string.Empty),
				Connected = (!string.IsNullOrEmpty(TargetUid) && !TargetUid.Equals("None", StringComparison.OrdinalIgnoreCase) && !TargetUid.Equals("0:0", StringComparison.OrdinalIgnoreCase) && TargetX.HasValue),
				TargetX = TargetX,
				TargetY = TargetY,
				TargetZ = TargetZ
			};
		}
	}
	internal sealed class KnownPortalFile
	{
		public string WorldId;

		public string CharacterId;

		public List<KnownPortalEntry> Portals = new List<KnownPortalEntry>();
	}
	internal static class PortalPanelUi
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Toggle;

			public static UnityAction <1>__Hide;

			public static UnityAction <2>__OnRefreshClicked;

			public static UnityAction <3>__OnShowKnown;

			public static UnityAction <4>__OnPinSelected;

			public static UnityAction <5>__OnPing;

			public static UnityAction <6>__OnPingExit;

			public static UnityAction <7>__OnAddToJournal;

			public static Action<List<PortalRow>> <8>__OnWorldList;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction<string> <>9__52_0;

			public static UnityAction <>9__52_1;

			public static UnityAction <>9__52_2;

			public static UnityAction <>9__52_3;

			public static UnityAction <>9__52_4;

			public static UnityAction<bool> <>9__52_5;

			internal void <EnsurePanel>b__52_0(string _)
			{
				RebuildList();
			}

			internal void <EnsurePanel>b__52_1()
			{
				_sortByName = true;
				_sortByMapClick = false;
				_awaitingMapClick = false;
				RebuildList();
				RefreshChrome();
			}

			internal void <EnsurePanel>b__52_2()
			{
				_sortByName = false;
				_sortByMapClick = false;
				_mapClickOrigin = null;
				_awaitingMapClick = false;
				RebuildList();
				RefreshChrome();
			}

			internal void <EnsurePanel>b__52_3()
			{
				_sortByName = false;
				_sortByMapClick = true;
				_awaitingMapClick = true;
				SetStatus("Map click — next map click sets sort origin");
				RefreshChrome();
			}

			internal void <EnsurePanel>b__52_4()
			{
				PortalMapPins.ClearSavedPins();
				SetStatus("Cleared this mod's saved portal pins (manual pins untouched).");
			}

			internal void <EnsurePanel>b__52_5(bool v)
			{
				if (PortalAtlasPlugin.AutoPin != null)
				{
					PortalAtlasPlugin.AutoPin.Value = v;
				}
				PortalAtlasPlugin.Debug($"AutoPin toggle → {v}");
			}
		}

		private const string PanelRootName = "PortalAtlas_Panel";

		private const string MapButtonName = "PortalAtlas_MapButton";

		private const float PanelWidth = 440f;

		private const float PanelHeight = 580f;

		private const float HeaderHeight = 220f;

		private const float FooterHeight = 150f;

		private const int UiFontSize = 14;

		private const int UiLayoutVersion = 5;

		private static GameObject _panelRoot;

		private static GameObject _mapButtonRoot;

		private static Text _statusText;

		private static InputField _filterInput;

		private static Transform _listContent;

		private static ScrollRect _listScroll;

		private static RectTransform _listScrollRect;

		private static Text _detailText;

		private static Button _refreshButton;

		private static Button _showKnownButton;

		private static Button _pinButton;

		private static Button _pingExitButton;

		private static Button _addJournalButton;

		private static Toggle _autoPinToggle;

		private static readonly List<GameObject> _rowObjects = new List<GameObject>();

		private static List<PortalRow> _journalRows = new List<PortalRow>();

		private static List<PortalRow> _worldRows;

		private static List<PortalRow> _visible = new List<PortalRow>();

		private static PortalRow _selected;

		private static bool _guiReady;

		private static bool _panelOpen;

		private static bool _showingWorld;

		private static bool _sortByName;

		private static bool _sortByMapClick;

		private static Vector3? _mapClickOrigin;

		private static bool _inputBlocked;

		private static bool _awaitingRefresh;

		private static bool _awaitingMapClick;

		private static Text _sortHintText;

		private static int _builtLayoutVersion;

		private static bool _mapWasOpen;

		private static float _nextRetintTime;

		private static bool _lastCanRefresh;

		internal static bool IsOpen => _panelOpen;

		private static bool FilterFocused
		{
			get
			{
				if ((Object)(object)_filterInput != (Object)null)
				{
					return _filterInput.isFocused;
				}
				return false;
			}
		}

		internal static void OnGuiReady()
		{
			_guiReady = GUIManager.Instance != null && (Object)(object)GUIManager.CustomGUIFront != (Object)null;
			EnsureMapButton();
		}

		internal static void OnAdminStatusChanged()
		{
			if (_panelOpen)
			{
				RefreshChrome();
			}
		}

		internal static void Tick()
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			if (!_guiReady && GUIManager.Instance != null)
			{
				_guiReady = true;
			}
			if (!_guiReady || GUIManager.IsHeadless())
			{
				return;
			}
			bool flag = PortalMapPins.IsLargeMapOpen();
			if (flag != _mapWasOpen)
			{
				_mapWasOpen = flag;
				EnsureMapButton();
				if (_panelOpen && !flag)
				{
					Hide();
				}
			}
			else if (flag && ((Object)(object)_mapButtonRoot == (Object)null || !_mapButtonRoot.activeSelf))
			{
				EnsureMapButton();
			}
			SyncBlockInput();
			if (_panelOpen)
			{
				TryScrollListWithWheel();
				bool flag2 = PortalAccess.CanRefreshWorld();
				if (flag2 != _lastCanRefresh)
				{
					PortalAtlasPlugin.Debug($"Refresh access changed: {flag2} ({PortalAccess.DescribeRefreshAccess()})");
					_lastCanRefresh = flag2;
					RefreshChrome();
				}
			}
			if (_panelOpen && flag)
			{
				if (Time.unscaledTime >= _nextRetintTime)
				{
					_nextRetintTime = Time.unscaledTime + 0.5f;
					PortalMapPins.RetintOverlay();
				}
				if (_awaitingMapClick && Input.GetMouseButtonDown(0) && !IsPointerOverOurPanel() && PortalMapPins.TryMapClickToWorld(out var world))
				{
					_mapClickOrigin = world;
					_sortByMapClick = true;
					_sortByName = false;
					_awaitingMapClick = false;
					PortalAtlasPlugin.Debug($"UI map-click origin=({world.x:0.#},{world.z:0.#})");
					RebuildList();
					UpdateOverlay();
					RefreshChrome();
				}
			}
		}

		internal static void Toggle()
		{
			if (_panelOpen)
			{
				Hide();
			}
			else
			{
				Show();
			}
		}

		internal static void Show()
		{
			if (!GUIManager.IsHeadless())
			{
				PortalAtlasPlugin.Debug("UI Show — journal view (" + PortalAccess.DescribeRefreshAccess() + ")");
				EnsurePanel();
				_panelOpen = true;
				_mapWasOpen = PortalMapPins.IsLargeMapOpen();
				if ((Object)(object)_panelRoot != (Object)null)
				{
					_panelRoot.SetActive(true);
				}
				_showingWorld = false;
				_worldRows = null;
				ReloadJournal();
				RefreshChrome();
				RebuildList();
				UpdateOverlay();
			}
		}

		internal static void Hide()
		{
			PortalAtlasPlugin.Debug("UI Hide");
			_panelOpen = false;
			_awaitingMapClick = false;
			if ((Object)(object)_panelRoot != (Object)null)
			{
				_panelRoot.SetActive(false);
			}
			PortalMapPins.ClearOverlay();
			SetBlockInput(block: false);
			KnownPortalCache.FlushIfDirty();
		}

		internal static void DestroyUi()
		{
			Hide();
			if ((Object)(object)_panelRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_panelRoot);
				_panelRoot = null;
			}
			if ((Object)(object)_mapButtonRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_mapButtonRoot);
				_mapButtonRoot = null;
			}
			_rowObjects.Clear();
			PortalAtlasPlugin.Debug("UI DestroyUi");
		}

		internal static void OnWorldList(List<PortalRow> rows)
		{
			_awaitingRefresh = false;
			if (rows == null)
			{
				PortalAtlasPlugin.Debug("UI OnWorldList failed (null)");
				SetStatus("Refresh failed.");
				return;
			}
			PortalAtlasPlugin.Debug($"UI OnWorldList session rows={rows.Count} (journal untouched)");
			_worldRows = rows;
			_showingWorld = true;
			RefreshChrome();
			RebuildList();
			UpdateOverlay();
			SetStatus($"World list · {rows.Count} portals (not added to journal)");
		}

		private static void EnsureMapButton()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			if (GUIManager.IsHeadless() || GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				return;
			}
			if (!PortalMapPins.IsLargeMapOpen())
			{
				if ((Object)(object)_mapButtonRoot != (Object)null)
				{
					_mapButtonRoot.SetActive(false);
				}
				return;
			}
			Transform val = PortalMapPins.GetMapPanelRoot();
			if ((Object)(object)val == (Object)null)
			{
				val = GUIManager.CustomGUIFront.transform;
			}
			if ((Object)(object)_mapButtonRoot == (Object)null)
			{
				GameObject obj = GUIManager.Instance.CreateButton("Portals", val, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(36f, 36f), 110f, 34f);
				((Object)obj).name = "PortalAtlas_MapButton";
				_mapButtonRoot = obj;
				Button component = obj.GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					ButtonClickedEvent onClick = component.onClick;
					object obj2 = <>O.<0>__Toggle;
					if (obj2 == null)
					{
						UnityAction val2 = Toggle;
						<>O.<0>__Toggle = val2;
						obj2 = (object)val2;
					}
					((UnityEvent)onClick).AddListener((UnityAction)obj2);
				}
				GUIManager.Instance.ApplyButtonStyle(component, 14);
			}
			if ((Object)(object)_mapButtonRoot.transform.parent != (Object)(object)val)
			{
				_mapButtonRoot.transform.SetParent(val, false);
			}
			RectTransform component2 = _mapButtonRoot.GetComponent<RectTransform>();
			if ((Object)(object)component2 != (Object)null)
			{
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(0f, 0f);
				component2.anchorMax = val3;
				component2.anchorMin = val3;
				component2.pivot = new Vector2(0f, 0f);
				component2.anchoredPosition = new Vector2(36f, 36f);
				component2.sizeDelta = new Vector2(110f, 34f);
			}
			_mapButtonRoot.SetActive(true);
			_mapButtonRoot.transform.SetAsLastSibling();
		}

		private static void EnsurePanel()
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Expected O, but got Unknown
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Expected O, but got Unknown
			//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Expected O, but got Unknown
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Expected O, but got Unknown
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_052f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Expected O, but got Unknown
			//IL_07b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_082f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0803: Unknown result type (might be due to invalid IL or missing references)
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_080e: Expected O, but got Unknown
			//IL_0758: Unknown result type (might be due to invalid IL or missing references)
			//IL_0762: Expected O, but got Unknown
			//IL_0867: Unknown result type (might be due to invalid IL or missing references)
			//IL_0844: Unknown result type (might be due to invalid IL or missing references)
			//IL_0849: Unknown result type (might be due to invalid IL or missing references)
			//IL_084f: Expected O, but got Unknown
			//IL_06dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_08b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_087c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0881: Unknown result type (might be due to invalid IL or missing references)
			//IL_0887: Expected O, but got Unknown
			//IL_08f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d2: Expected O, but got Unknown
			//IL_0965: Unknown result type (might be due to invalid IL or missing references)
			//IL_096c: Unknown result type (might be due to invalid IL or missing references)
			//IL_097e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0990: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_090c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0911: Unknown result type (might be due to invalid IL or missing references)
			//IL_0917: Expected O, but got Unknown
			//IL_0a23: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panelRoot != (Object)null && _builtLayoutVersion != 5)
			{
				Object.Destroy((Object)(object)_panelRoot);
				_panelRoot = null;
				_listContent = null;
				_listScroll = null;
				_listScrollRect = null;
				_statusText = null;
				_filterInput = null;
				_detailText = null;
				_sortHintText = null;
				_refreshButton = null;
				_showKnownButton = null;
				_pinButton = null;
				_pingExitButton = null;
				_addJournalButton = null;
				_autoPinToggle = null;
				_rowObjects.Clear();
			}
			if ((Object)(object)_panelRoot != (Object)null || GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				return;
			}
			GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-220f, 20f), 440f, 580f, true);
			((Object)val).name = "PortalAtlas_Panel";
			_panelRoot = val;
			float num = 262f;
			Text obj = CreateLabel(val.transform, "Portals", new Vector2(0f, num), 200f, 28f, bold: true);
			obj.alignment = (TextAnchor)4;
			((Graphic)obj).color = GUIManager.Instance.ValheimOrange;
			ButtonClickedEvent onClick = GUIManager.Instance.CreateButton("X", val.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(192f, num), 32f, 28f).GetComponent<Button>().onClick;
			object obj2 = <>O.<1>__Hide;
			if (obj2 == null)
			{
				UnityAction val2 = Hide;
				<>O.<1>__Hide = val2;
				obj2 = (object)val2;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj2);
			num -= 28f;
			_statusText = CreateLabel(val.transform, "Known portals", new Vector2(0f, num), 400f, 22f, bold: false);
			num -= 32f;
			_refreshButton = GUIManager.Instance.CreateButton("Refresh world", val.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-70f, num), 130f, 28f).GetComponent<Button>();
			ButtonClickedEvent onClick2 = _refreshButton.onClick;
			object obj3 = <>O.<2>__OnRefreshClicked;
			if (obj3 == null)
			{
				UnityAction val3 = OnRefreshClicked;
				<>O.<2>__OnRefreshClicked = val3;
				obj3 = (object)val3;
			}
			((UnityEvent)onClick2).AddListener((UnityAction)obj3);
			GUIManager.Instance.ApplyButtonStyle(_refreshButton, 14);
			_showKnownButton = GUIManager.Instance.CreateButton("Show known", val.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(70f, num), 120f, 28f).GetComponent<Button>();
			ButtonClickedEvent onClick3 = _showKnownButton.onClick;
			object obj4 = <>O.<3>__OnShowKnown;
			if (obj4 == null)
			{
				UnityAction val4 = OnShowKnown;
				<>O.<3>__OnShowKnown = val4;
				obj4 = (object)val4;
			}
			((UnityEvent)onClick3).AddListener((UnityAction)obj4);
			GUIManager.Instance.ApplyButtonStyle(_showKnownButton, 14);
			num -= 34f;
			_filterInput = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, num), (ContentType)0, "Filter tags", 14, 392f, 28f).GetComponent<InputField>();
			GUIManager.Instance.ApplyInputFieldStyle(_filterInput, 14);
			((UnityEvent<string>)(object)_filterInput.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				RebuildList();
			});
			num -= 30f;
			Transform transform = val.transform;
			Vector2 pos = new Vector2(-120f, num);
			object obj5 = <>c.<>9__52_1;
			if (obj5 == null)
			{
				UnityAction val5 = delegate
				{
					_sortByName = true;
					_sortByMapClick = false;
					_awaitingMapClick = false;
					RebuildList();
					RefreshChrome();
				};
				<>c.<>9__52_1 = val5;
				obj5 = (object)val5;
			}
			CreateSortButton(transform, "Name", pos, (UnityAction)obj5);
			Transform transform2 = val.transform;
			Vector2 pos2 = new Vector2(0f, num);
			object obj6 = <>c.<>9__52_2;
			if (obj6 == null)
			{
				UnityAction val6 = delegate
				{
					_sortByName = false;
					_sortByMapClick = false;
					_mapClickOrigin = null;
					_awaitingMapClick = false;
					RebuildList();
					RefreshChrome();
				};
				<>c.<>9__52_2 = val6;
				obj6 = (object)val6;
			}
			CreateSortButton(transform2, "To me", pos2, (UnityAction)obj6);
			Transform transform3 = val.transform;
			Vector2 pos3 = new Vector2(120f, num);
			object obj7 = <>c.<>9__52_3;
			if (obj7 == null)
			{
				UnityAction val7 = delegate
				{
					_sortByName = false;
					_sortByMapClick = true;
					_awaitingMapClick = true;
					SetStatus("Map click — next map click sets sort origin");
					RefreshChrome();
				};
				<>c.<>9__52_3 = val7;
				obj7 = (object)val7;
			}
			CreateSortButton(transform3, "Map click", pos3, (UnityAction)obj7);
			num -= 22f;
			_sortHintText = CreateLabel(val.transform, "Sort: to me", new Vector2(0f, num), 400f, 20f, bold: false);
			_sortHintText.fontSize = 12;
			((Graphic)_sortHintText).color = new Color(0.85f, 0.8f, 0.7f, 1f);
			float num2 = -140f;
			float num3 = 70f - num2;
			float num4 = (70f + num2) * 0.5f;
			ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock;
			((ColorBlock)(ref defaultColorBlock)).normalColor = new Color(0.6f, 0.35f, 0.12f, 1f);
			((ColorBlock)(ref defaultColorBlock)).highlightedColor = GUIManager.Instance.ValheimOrange;
			((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(0.9f, 0.55f, 0.15f, 1f);
			((ColorBlock)(ref defaultColorBlock)).selectedColor = ((ColorBlock)(ref defaultColorBlock)).highlightedColor;
			GameObject val8 = GUIManager.Instance.CreateScrollView(val.transform, false, true, 10f, 3f, defaultColorBlock, new Color(0f, 0f, 0f, 0.45f), 400f, Mathf.Max(120f, num3));
			((Object)val8).name = "PortalListScroll";
			RectTransform component = val8.GetComponent<RectTransform>();
			Vector2 val9 = default(Vector2);
			((Vector2)(ref val9))..ctor(0.5f, 0.5f);
			component.anchorMax = val9;
			component.anchorMin = val9;
			component.pivot = new Vector2(0.5f, 0.5f);
			component.anchoredPosition = new Vector2(0f, num4);
			component.sizeDelta = new Vector2(400f, Mathf.Max(120f, num3));
			val8.transform.SetSiblingIndex(2);
			ScrollRect val10 = val8.GetComponentInChildren<ScrollRect>(true);
			if ((Object)(object)val10 == (Object)null)
			{
				val10 = val8.GetComponent<ScrollRect>();
			}
			if ((Object)(object)val10 != (Object)null)
			{
				val10.horizontal = false;
				val10.vertical = true;
				val10.movementType = (MovementType)2;
				val10.scrollSensitivity = 40f;
				_listScroll = val10;
				_listContent = (Transform)(object)val10.content;
				_listScrollRect = ((Component)val10).GetComponent<RectTransform>();
				if ((Object)(object)_listScrollRect == (Object)null)
				{
					_listScrollRect = component;
				}
				if ((Object)(object)val10.viewport != (Object)null)
				{
					Image val11 = ((Component)val10.viewport).GetComponent<Image>();
					if ((Object)(object)val11 == (Object)null)
					{
						val11 = ((Component)val10.viewport).gameObject.AddComponent<Image>();
					}
					((Graphic)val11).color = new Color(0f, 0f, 0f, 0.01f);
					((Graphic)val11).raycastTarget = true;
				}
			}
			if ((Object)(object)_listContent != (Object)null)
			{
				VerticalLayoutGroup val12 = ((Component)_listContent).GetComponent<VerticalLayoutGroup>();
				if ((Object)(object)val12 == (Object)null)
				{
					val12 = ((Component)_listContent).gameObject.AddComponent<VerticalLayoutGroup>();
				}
				((HorizontalOrVerticalLayoutGroup)val12).childForceExpandHeight = false;
				((HorizontalOrVerticalLayoutGroup)val12).childForceExpandWidth = true;
				((HorizontalOrVerticalLayoutGroup)val12).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)val12).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)val12).spacing = 2f;
				((LayoutGroup)val12).padding = new RectOffset(4, 4, 4, 4);
				ContentSizeFitter val13 = ((Component)_listContent).GetComponent<ContentSizeFitter>();
				if ((Object)(object)val13 == (Object)null)
				{
					val13 = ((Component)_listContent).gameObject.AddComponent<ContentSizeFitter>();
				}
				val13.horizontalFit = (FitMode)0;
				val13.verticalFit = (FitMode)2;
			}
			float num5 = -290f;
			_detailText = CreateLabel(val.transform, string.Empty, new Vector2(0f, num5 + 130f), 400f, 34f, bold: false);
			float num6 = num5 + 92f;
			Transform transform4 = val.transform;
			Vector2 pos4 = new Vector2(-130f, num6);
			object obj8 = <>O.<4>__OnPinSelected;
			if (obj8 == null)
			{
				UnityAction val14 = OnPinSelected;
				<>O.<4>__OnPinSelected = val14;
				obj8 = (object)val14;
			}
			_pinButton = CreateActionButton(transform4, "Pin", pos4, (UnityAction)obj8).GetComponent<Button>();
			Transform transform5 = val.transform;
			Vector2 pos5 = new Vector2(0f, num6);
			object obj9 = <>O.<5>__OnPing;
			if (obj9 == null)
			{
				UnityAction val15 = OnPing;
				<>O.<5>__OnPing = val15;
				obj9 = (object)val15;
			}
			CreateActionButton(transform5, "Ping", pos5, (UnityAction)obj9);
			Transform transform6 = val.transform;
			Vector2 pos6 = new Vector2(130f, num6);
			object obj10 = <>O.<6>__OnPingExit;
			if (obj10 == null)
			{
				UnityAction val16 = OnPingExit;
				<>O.<6>__OnPingExit = val16;
				obj10 = (object)val16;
			}
			_pingExitButton = CreateActionButton(transform6, "Ping exit", pos6, (UnityAction)obj10).GetComponent<Button>();
			float num7 = num5 + 58f;
			Transform transform7 = val.transform;
			Vector2 pos7 = new Vector2(-70f, num7);
			object obj11 = <>O.<7>__OnAddToJournal;
			if (obj11 == null)
			{
				UnityAction val17 = OnAddToJournal;
				<>O.<7>__OnAddToJournal = val17;
				obj11 = (object)val17;
			}
			_addJournalButton = CreateActionButton(transform7, "Add to journal", pos7, (UnityAction)obj11).GetComponent<Button>();
			Transform transform8 = val.transform;
			Vector2 pos8 = new Vector2(70f, num7);
			object obj12 = <>c.<>9__52_4;
			if (obj12 == null)
			{
				UnityAction val18 = delegate
				{
					PortalMapPins.ClearSavedPins();
					SetStatus("Cleared this mod's saved portal pins (manual pins untouched).");
				};
				<>c.<>9__52_4 = val18;
				obj12 = (object)val18;
			}
			CreateActionButton(transform8, "Clear saved", pos8, (UnityAction)obj12);
			float num8 = num5 + 24f;
			GameObject obj13 = GUIManager.Instance.CreateToggle(val.transform, 28f, 28f);
			((Object)obj13).name = "AutoPin";
			RectTransform component2 = obj13.GetComponent<RectTransform>();
			((Vector2)(ref val9))..ctor(0.5f, 0.5f);
			component2.anchorMax = val9;
			component2.anchorMin = val9;
			component2.pivot = new Vector2(0.5f, 0.5f);
			component2.anchoredPosition = new Vector2(-180f, num8);
			component2.sizeDelta = new Vector2(28f, 28f);
			_autoPinToggle = obj13.GetComponent<Toggle>();
			if ((Object)(object)_autoPinToggle != (Object)null)
			{
				_autoPinToggle.isOn = PortalAtlasPlugin.AutoPin != null && PortalAtlasPlugin.AutoPin.Value;
				((UnityEvent<bool>)(object)_autoPinToggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
				{
					if (PortalAtlasPlugin.AutoPin != null)
					{
						PortalAtlasPlugin.AutoPin.Value = v;
					}
					PortalAtlasPlugin.Debug($"AutoPin toggle → {v}");
				});
			}
			CreateLabel(val.transform, "Auto-pin when I approach a portal", new Vector2(18f, num8), 350f, 26f, bold: false).alignment = (TextAnchor)3;
			_panelRoot.SetActive(false);
			_builtLayoutVersion = 5;
			PortalRpc.OnWorldListReceived = OnWorldList;
		}

		private static GameObject CreateActionButton(Transform parent, string text, Vector2 pos, UnityAction action)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateButton(text, parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos, 110f, 28f);
			Button component = obj.GetComponent<Button>();
			((UnityEvent)component.onClick).AddListener(action);
			GUIManager.Instance.ApplyButtonStyle(component, 14);
			return obj;
		}

		private static void CreateSortButton(Transform parent, string text, Vector2 pos, UnityAction action)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateButton(text, parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos, 100f, 26f);
			((UnityEvent)val.GetComponent<Button>().onClick).AddListener(action);
			GUIManager.Instance.ApplyButtonStyle(val.GetComponent<Button>(), 12);
		}

		private static Text CreateLabel(Transform parent, string text, Vector2 pos, float width, float height, bool bold)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Label", new Type[2]
			{
				typeof(RectTransform),
				typeof(Text)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.sizeDelta = new Vector2(width, height);
			component.anchoredPosition = pos;
			Text component2 = val.GetComponent<Text>();
			component2.text = text;
			component2.alignment = (TextAnchor)4;
			component2.font = GUIManager.Instance.AveriaSerifBold;
			if ((Object)(object)component2.font == (Object)null)
			{
				component2.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			}
			component2.fontSize = (bold ? 18 : 14);
			((Graphic)component2).color = Color.white;
			GUIManager.Instance.ApplyTextStyle(component2, bold ? 18 : 14);
			return component2;
		}

		private static void ReloadJournal()
		{
			KnownPortalCache.EnsureLoaded();
			_journalRows = PortalScan.RowsFromKnown(KnownPortalCache.All);
		}

		private static void RebuildList()
		{
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			foreach (GameObject rowObject in _rowObjects)
			{
				if ((Object)(object)rowObject != (Object)null)
				{
					Object.Destroy((Object)(object)rowObject);
				}
			}
			_rowObjects.Clear();
			List<PortalRow> obj = ((_showingWorld && _worldRows != null) ? _worldRows : _journalRows);
			string text = (((Object)(object)_filterInput != (Object)null) ? (_filterInput.text ?? string.Empty).Trim() : string.Empty);
			_visible = new List<PortalRow>();
			foreach (PortalRow item in obj)
			{
				if (string.IsNullOrEmpty(text) || (item.Tag ?? string.Empty).IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 || string.Equals(text, "(untagged)", StringComparison.OrdinalIgnoreCase))
				{
					_visible.Add(item);
				}
			}
			Vector3 origin = GetSortOrigin();
			_visible.Sort(delegate(PortalRow a, PortalRow b)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (_sortByName)
				{
					int num = string.Compare(a.DisplayTag, b.DisplayTag, StringComparison.OrdinalIgnoreCase);
					if (num != 0)
					{
						return num;
					}
				}
				float num2 = DistXz(origin, a);
				float value = DistXz(origin, b);
				return num2.CompareTo(value);
			});
			if (_visible.Count > 0)
			{
				if (_selected == null || !_visible.Contains(_selected))
				{
					_selected = _visible[0];
				}
			}
			else
			{
				_selected = null;
			}
			foreach (PortalRow item2 in _visible)
			{
				CreateRow(item2);
			}
			UpdateDetail();
			RefreshChrome();
		}

		private static void CreateRow(PortalRow row)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (