Decompiled source of DrakesTelemarker v0.1.0

DrakesTelemarker.dll

Decompiled a month 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.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("DrakesTelemarker")]
[assembly: AssemblyDescription("Per-world console warp bookmarks (10 slots).")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("DrakeMods")]
[assembly: AssemblyProduct("DrakesTelemarker")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5DF97E86-BF46-46C5-894F-6B2D80EA6645")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 DrakesTelemarker
{
	[BepInPlugin("com.drakemods.DrakesTelemarker", "DrakesTelemarker", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class DrakesTelemarkerPlugin : BaseUnityPlugin
	{
		private sealed class TelemarkCommand : ConsoleCommand
		{
			private readonly DrakesTelemarkerPlugin _plugin;

			public override string Name => "telemark";

			public override string Help => "Not a cheat: prints usage. Subcommands list/set/recall/clear require cheat access — run with no args.";

			public override bool IsCheat => false;

			public TelemarkCommand(DrakesTelemarkerPlugin plugin)
			{
				_plugin = plugin;
			}

			public override List<string> CommandOptionList()
			{
				return BuildTelemarkTabCompletions();
			}

			public override void Run(string[] args)
			{
				RouteTelemark(_plugin, args);
			}
		}

		private sealed class SetMarkCommand : ConsoleCommand
		{
			private readonly DrakesTelemarkerPlugin _plugin;

			public override string Name => "set";

			public override string Help => $"Shortcut for telemark set mark <1-{10}>";

			public override bool IsCheat => false;

			public SetMarkCommand(DrakesTelemarkerPlugin plugin)
			{
				_plugin = plugin;
			}

			public override List<string> CommandOptionList()
			{
				return BuildMarkAliasTabCompletions();
			}

			public override void Run(string[] args)
			{
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				if (!CanUseTelemarkerCheats())
				{
					Notify("Need cheat access (devcommands or server admin). Run telemark for help.");
					return;
				}
				if (!TryParseMarkSlotArgs(args, out int slot, out string usageError))
				{
					Notify(usageError);
					return;
				}
				Player localPlayerOrNotify = GetLocalPlayerOrNotify();
				if (localPlayerOrNotify != null)
				{
					Vector3 position = ((Component)localPlayerOrNotify).transform.position;
					_plugin.TrySetSlot(slot, position, out string _);
					Notify($"Saved mark {slot} at {FormatVec(position)}.");
				}
			}
		}

		private sealed class RecallMarkCommand : ConsoleCommand
		{
			private readonly DrakesTelemarkerPlugin _plugin;

			public override string Name => "recall";

			public override string Help => $"Shortcut for telemark recall mark <1-{10}>";

			public override bool IsCheat => false;

			public RecallMarkCommand(DrakesTelemarkerPlugin plugin)
			{
				_plugin = plugin;
			}

			public override List<string> CommandOptionList()
			{
				return BuildMarkAliasTabCompletions();
			}

			public override void Run(string[] args)
			{
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
				if (!CanUseTelemarkerCheats())
				{
					Notify("Need cheat access (devcommands or server admin). Run telemark for help.");
					return;
				}
				if (!TryParseMarkSlotArgs(args, out int slot, out string usageError))
				{
					Notify(usageError);
					return;
				}
				Player localPlayerOrNotify = GetLocalPlayerOrNotify();
				if (localPlayerOrNotify != null)
				{
					if (!_plugin.TryRecallSlot(slot, out Vector3 position, out string error))
					{
						Notify(string.IsNullOrEmpty(error) ? $"Mark {slot} is empty." : error);
						return;
					}
					((Component)localPlayerOrNotify).transform.position = position;
					Physics.SyncTransforms();
					Notify($"Recalled mark {slot} -> {FormatVec(position)}.");
				}
			}
		}

		public const string ModName = "DrakesTelemarker";

		public const string Version = "0.1.0";

		public const string GUID = "com.drakemods.DrakesTelemarker";

		internal const int SlotCount = 10;

		private void Awake()
		{
			TelemarkerClearDialog.RegisterForGuiRebuild();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TelemarkCommand(this));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SetMarkCommand(this));
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RecallMarkCommand(this));
			TelemarkerMinimap.Init(this);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DrakesTelemarker v0.1.0 loaded — type 'telemark' in console for commands.");
		}

		internal static string GetMarksFilePath()
		{
			string text = Path.Combine(Paths.ConfigPath, "DrakesTelemarker");
			Directory.CreateDirectory(text);
			return Path.Combine(text, "TelemarkerMarks.json");
		}

		internal static string GetWorldKey()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				string worldName = ZNet.instance.GetWorldName();
				if (!string.IsNullOrEmpty(worldName))
				{
					return worldName;
				}
			}
			return "noworld";
		}

		internal static string SanitizeWorldKey(string worldName)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				worldName = worldName.Replace(oldChar, '_');
			}
			return string.IsNullOrEmpty(worldName) ? "world" : worldName;
		}

		internal static Player? GetLocalPlayerOrNotify()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Notify("No local player (join a world first).");
			}
			return localPlayer;
		}

		internal static void Notify(string message)
		{
			PrintToDeveloperConsole("[DrakesTelemarker] " + message);
		}

		private static void PrintToDeveloperConsole(string line)
		{
			try
			{
				object obj = TryGetValheimConsoleInstance();
				if (obj == null)
				{
					Debug.Log((object)line);
					return;
				}
				MethodInfo method = obj.GetType().GetMethod("Print", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
				if (method == null)
				{
					Debug.Log((object)line);
					return;
				}
				method.Invoke(obj, new object[1] { line });
			}
			catch
			{
				Debug.Log((object)line);
			}
		}

		private static object? TryGetValheimConsoleInstance()
		{
			try
			{
				Type type = typeof(Player).Assembly.GetType("Console");
				if (type == null)
				{
					return null;
				}
				PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
				if (property != null)
				{
					return property.GetValue(null);
				}
				FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public);
				if (field != null)
				{
					return field.GetValue(null);
				}
				return type.GetField("m_instance", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
			}
			catch
			{
				return null;
			}
		}

		private static void PrintMultilineToDeveloperConsole(string text)
		{
			string[] array = text.Split(new string[2] { "\r\n", "\n" }, StringSplitOptions.None);
			foreach (string text2 in array)
			{
				if (text2.Length > 0)
				{
					PrintToDeveloperConsole(text2);
				}
			}
		}

		internal bool TrySetSlot(int slot, Vector3 position, out string error)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			error = "";
			string marksFilePath = GetMarksFilePath();
			string worldKey = SanitizeWorldKey(GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			telemarkerMarksDocument.UpsertSlot(worldKey, slot, position);
			TelemarkerMarksDocument.Save(marksFilePath, telemarkerMarksDocument);
			TelemarkerMinimap.RequestSync();
			return true;
		}

		internal bool TryRecallSlot(int slot, out Vector3 position, out string error)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			error = "";
			string marksFilePath = GetMarksFilePath();
			string worldKey = SanitizeWorldKey(GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			return telemarkerMarksDocument.TryGetSlot(worldKey, slot, out position, out error);
		}

		internal bool TryClearSlot(int slot, out string error)
		{
			error = "";
			string marksFilePath = GetMarksFilePath();
			string worldKey = SanitizeWorldKey(GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			if (!telemarkerMarksDocument.ClearSlot(worldKey, slot))
			{
				error = $"Mark {slot} was already empty.";
				return false;
			}
			TelemarkerMarksDocument.Save(marksFilePath, telemarkerMarksDocument);
			TelemarkerMinimap.RequestSync();
			return true;
		}

		internal bool TryClearAllSlots(out int clearedCount, out string error)
		{
			clearedCount = 0;
			error = "";
			string marksFilePath = GetMarksFilePath();
			string worldKey = SanitizeWorldKey(GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			clearedCount = telemarkerMarksDocument.ClearAllSlotsForWorld(worldKey);
			if (clearedCount == 0)
			{
				error = "No marks to clear.";
				return false;
			}
			TelemarkerMarksDocument.Save(marksFilePath, telemarkerMarksDocument);
			TelemarkerMinimap.RequestSync();
			return true;
		}

		internal static bool CanUseTelemarkerCheats()
		{
			if (IsDevcommandsClientFlagOn())
			{
				return true;
			}
			try
			{
				SynchronizationManager instance = SynchronizationManager.Instance;
				if (instance != null && instance.PlayerIsAdmin)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool IsDevcommandsClientFlagOn()
		{
			return ReadTerminalCheatsEnabledWithFallback();
		}

		private static bool ReadTerminalCheatsEnabledWithFallback()
		{
			try
			{
				MethodInfo[] methods = typeof(Terminal).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.Name != "IsCheatsEnabled" || methodInfo.ReturnType != typeof(bool) || methodInfo.GetParameters().Length != 0)
					{
						continue;
					}
					if (methodInfo.IsStatic)
					{
						if (methodInfo.Invoke(null, null) is bool result)
						{
							return result;
						}
						continue;
					}
					object obj = TryGetValheimConsoleInstance();
					if (obj != null && methodInfo.Invoke(obj, null) is bool result2)
					{
						return result2;
					}
				}
			}
			catch
			{
			}
			return ReadTerminalCheatFlagViaReflection();
		}

		private static bool ReadTerminalCheatFlagViaReflection()
		{
			try
			{
				object obj = TryGetValheimConsoleInstance();
				FieldInfo field = typeof(Terminal).GetField("m_cheat", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && obj != null && field.GetValue(obj) is bool result)
				{
					return result;
				}
				field = typeof(Terminal).GetField("m_cheat", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.IsStatic)
				{
					object value = field.GetValue(null);
					if (value is bool)
					{
						bool result2 = (bool)value;
						if (true)
						{
							return result2;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static string[] ArgsTail(string[] args, int start)
		{
			int num = args.Length - start;
			if (num <= 0)
			{
				return Array.Empty<string>();
			}
			string[] array = new string[num];
			Array.Copy(args, start, array, 0, num);
			return array;
		}

		internal static string FormatVec(Vector3 p)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			return string.Format(CultureInfo.InvariantCulture, "({0:F1}, {1:F1}, {2:F1})", p.x, p.y, p.z);
		}

		internal static List<string> BuildTelemarkTabCompletions()
		{
			List<string> list = new List<string> { "help", "?", "list", "set", "recall", "clear", "mark", "all" };
			for (int i = 1; i <= 10; i++)
			{
				list.Add(i.ToString(CultureInfo.InvariantCulture));
			}
			return list;
		}

		internal static List<string> BuildMarkAliasTabCompletions()
		{
			List<string> list = new List<string> { "mark" };
			for (int i = 1; i <= 10; i++)
			{
				list.Add(i.ToString(CultureInfo.InvariantCulture));
			}
			return list;
		}

		internal static bool TryParseMarkSlotArgs(string[] args, out int slot, out string usageError)
		{
			slot = 0;
			usageError = "";
			if (args == null || args.Length < 2 || !args[0].Equals("mark", StringComparison.OrdinalIgnoreCase))
			{
				usageError = $"Usage: telemark set mark <1-{10}> | telemark recall mark <1-{10}> (aliases: set mark …, recall mark …)";
				return false;
			}
			int num = 1;
			if (num < args.Length && args[num].Equals("mark", StringComparison.OrdinalIgnoreCase))
			{
				num++;
			}
			if (num >= args.Length || !int.TryParse(args[num], NumberStyles.Integer, CultureInfo.InvariantCulture, out slot) || slot < 1 || slot > 10)
			{
				usageError = $"Slot must be 1-{10}.";
				return false;
			}
			return true;
		}

		private static void PrintTelemarkUsage()
		{
			string text = "[DrakesTelemarker] Cheat vs help:\r\n  • Running telemark alone (or telemark help) is NOT a cheat — it only prints this guide.\r\n  • telemark list / set / recall / clear ARE cheats — need devcommands or server admin.\r\n  • Same for aliases: set mark … and recall mark … (cheat access required).\r\n\r\nCommands:\r\n  telemark\r\n      Show this help (always allowed).\r\n  telemark list\r\n      List every slot that has a saved position for this world (cheat).\r\n" + $"  telemark set mark <1-{10}>\r\n" + "      Save your current position into a slot (cheat).\r\n" + $"  telemark recall mark <1-{10}>\r\n" + "      Warp to a saved slot (cheat).\r\n" + $"  telemark clear <1-{10}>\r\n" + "      Opens a confirmation dialog — click Yes to erase that slot, No to cancel.\r\n  telemark clear all\r\n      Same dialog for wiping every mark in this world.\r\nAliases:\r\n  set mark <n>        Same as telemark set mark … (cheat).\r\n  recall mark <n>    Same as telemark recall mark … (cheat).\r\n\r\nCheat access = devcommands OR server admin (adminlist; Server Devcommands works well here).\r\nJotunn syncs admin status for PlayerIsAdmin. Open console (F5): devcommands\r\n\r\nMinimap:\r\n" + $"  Numbered pins labeled Mark 1–Mark {10}. Right-click (or Ctrl+left-click) a pin to recall when cheat access matches console (devcommands / Server Devcommands admin, same as telemark recall).\r\n";
			PrintMultilineToDeveloperConsole(text);
		}

		private static void RunTelemarkList()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			string marksFilePath = GetMarksFilePath();
			string text = SanitizeWorldKey(GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			for (int i = 1; i <= 10; i++)
			{
				if (telemarkerMarksDocument.TryGetSlot(text, i, out Vector3 position, out string _))
				{
					num++;
					stringBuilder.AppendLine($"  slot {i}: {FormatVec(position)}");
				}
			}
			if (num == 0)
			{
				Notify("No active marks for this world.");
				return;
			}
			PrintMultilineToDeveloperConsole(string.Format("[{0}] Active marks ({1}) for world \"{2}\":\n{3}", "DrakesTelemarker", num, text, stringBuilder.ToString().TrimEnd(Array.Empty<char>())));
		}

		private static void RouteTelemark(DrakesTelemarkerPlugin plugin, string[] args)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_01ec: Unknown result type (might be due to invalid IL or missing references)
			if (args == null || args.Length == 0)
			{
				PrintTelemarkUsage();
				return;
			}
			string text = args[0].ToLowerInvariant();
			if ((text == "help" || text == "?") ? true : false)
			{
				PrintTelemarkUsage();
				return;
			}
			if (!CanUseTelemarkerCheats())
			{
				Notify("That needs cheat access (devcommands or server admin). telemark alone only prints help — run it for the full list.");
				return;
			}
			switch (text)
			{
			case "list":
				RunTelemarkList();
				break;
			case "set":
			{
				if (!TryParseMarkSlotArgs(ArgsTail(args, 1), out int slot, out string usageError))
				{
					Notify(usageError);
					break;
				}
				Player localPlayerOrNotify = GetLocalPlayerOrNotify();
				if (localPlayerOrNotify != null)
				{
					Vector3 position = ((Component)localPlayerOrNotify).transform.position;
					plugin.TrySetSlot(slot, position, out string _);
					Notify($"Saved mark {slot} at {FormatVec(position)}.");
				}
				break;
			}
			case "recall":
			{
				if (!TryParseMarkSlotArgs(ArgsTail(args, 1), out int slot2, out string usageError2))
				{
					Notify(usageError2);
					break;
				}
				Player localPlayerOrNotify2 = GetLocalPlayerOrNotify();
				if (localPlayerOrNotify2 != null)
				{
					if (!plugin.TryRecallSlot(slot2, out Vector3 position2, out string error2))
					{
						Notify(string.IsNullOrEmpty(error2) ? $"Mark {slot2} is empty." : error2);
						break;
					}
					((Component)localPlayerOrNotify2).transform.position = position2;
					Physics.SyncTransforms();
					Notify($"Recalled mark {slot2} -> {FormatVec(position2)}.");
				}
				break;
			}
			case "clear":
			{
				if (args.Length < 2)
				{
					Notify($"Usage: telemark clear <1-{10}>  |  telemark clear all");
					break;
				}
				string text2 = args[1];
				int result;
				if (text2.Equals("all", StringComparison.OrdinalIgnoreCase))
				{
					if (args.Length > 2)
					{
						Notify("Usage: telemark clear all");
					}
					else
					{
						TelemarkerClearDialog.RequestConfirmation(plugin, clearAll: true, 0);
					}
				}
				else if (!int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result) || result < 1 || result > 10)
				{
					Notify($"Usage: telemark clear <1-{10}>  |  telemark clear all");
				}
				else if (args.Length > 2)
				{
					Notify($"Usage: telemark clear <1-{10}>");
				}
				else
				{
					TelemarkerClearDialog.RequestConfirmation(plugin, clearAll: false, result);
				}
				break;
			}
			default:
				Notify("Unknown telemark subcommand '" + args[0] + "'. Check console log for full usage.");
				PrintTelemarkUsage();
				break;
			}
		}
	}
	[DataContract]
	internal sealed class TelemarkerMarksDocument
	{
		[DataMember(Name = "schemaVersion")]
		public int SchemaVersion { get; set; } = 1;


		[DataMember(Name = "worlds")]
		public Dictionary<string, WorldMarkSlots> Worlds { get; set; } = new Dictionary<string, WorldMarkSlots>();


		internal static TelemarkerMarksDocument Load(string path)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (!File.Exists(path))
			{
				return new TelemarkerMarksDocument();
			}
			try
			{
				using FileStream fileStream = File.OpenRead(path);
				DataContractJsonSerializer val = new DataContractJsonSerializer(typeof(TelemarkerMarksDocument));
				if (((XmlObjectSerializer)val).ReadObject((Stream)fileStream) is TelemarkerMarksDocument telemarkerMarksDocument)
				{
					TelemarkerMarksDocument telemarkerMarksDocument2 = telemarkerMarksDocument;
					if (telemarkerMarksDocument2.Worlds == null)
					{
						Dictionary<string, WorldMarkSlots> dictionary2 = (telemarkerMarksDocument2.Worlds = new Dictionary<string, WorldMarkSlots>());
					}
					foreach (WorldMarkSlots value in telemarkerMarksDocument.Worlds.Values)
					{
						WorldMarkSlots worldMarkSlots = value;
						if (worldMarkSlots.Slots == null)
						{
							Dictionary<string, MarkCoordinates> dictionary4 = (worldMarkSlots.Slots = new Dictionary<string, MarkCoordinates>());
						}
					}
					return telemarkerMarksDocument;
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[DrakesTelemarker] Could not read " + path + ", starting fresh: " + ex.Message));
			}
			return new TelemarkerMarksDocument();
		}

		internal static void Save(string path, TelemarkerMarksDocument doc)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			if (doc.Worlds == null)
			{
				Dictionary<string, WorldMarkSlots> dictionary2 = (doc.Worlds = new Dictionary<string, WorldMarkSlots>());
			}
			string text = path + ".tmp";
			using (FileStream fileStream = File.Create(text))
			{
				DataContractJsonSerializer val = new DataContractJsonSerializer(typeof(TelemarkerMarksDocument));
				((XmlObjectSerializer)val).WriteObject((Stream)fileStream, (object)doc);
			}
			if (File.Exists(path))
			{
				File.Delete(path);
			}
			File.Move(text, path);
		}

		internal void UpsertSlot(string worldKey, int slot1Based, Vector3 position)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Worlds.TryGetValue(worldKey, out WorldMarkSlots value);
			if (value == null)
			{
				value = new WorldMarkSlots();
				Worlds[worldKey] = value;
			}
			WorldMarkSlots worldMarkSlots = value;
			if (worldMarkSlots.Slots == null)
			{
				Dictionary<string, MarkCoordinates> dictionary2 = (worldMarkSlots.Slots = new Dictionary<string, MarkCoordinates>());
			}
			string key = slot1Based.ToString(CultureInfo.InvariantCulture);
			value.Slots[key] = new MarkCoordinates
			{
				X = position.x,
				Y = position.y,
				Z = position.z
			};
		}

		internal bool TryGetSlot(string worldKey, int slot1Based, out Vector3 position, out string error)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			error = "";
			if (!Worlds.TryGetValue(worldKey, out WorldMarkSlots value) || value?.Slots == null)
			{
				error = $"Mark {slot1Based} is empty.";
				return false;
			}
			string key = slot1Based.ToString(CultureInfo.InvariantCulture);
			if (!value.Slots.TryGetValue(key, out MarkCoordinates value2))
			{
				error = $"Mark {slot1Based} is empty.";
				return false;
			}
			position = new Vector3(value2.X, value2.Y, value2.Z);
			return true;
		}

		internal bool ClearSlot(string worldKey, int slot1Based)
		{
			if (!Worlds.TryGetValue(worldKey, out WorldMarkSlots value) || value.Slots == null)
			{
				return false;
			}
			string key = slot1Based.ToString(CultureInfo.InvariantCulture);
			return value.Slots.Remove(key);
		}

		internal int ClearAllSlotsForWorld(string worldKey)
		{
			if (!Worlds.TryGetValue(worldKey, out WorldMarkSlots value) || value.Slots == null || value.Slots.Count == 0)
			{
				return 0;
			}
			int count = value.Slots.Count;
			value.Slots.Clear();
			Worlds.Remove(worldKey);
			return count;
		}
	}
	[DataContract]
	internal sealed class WorldMarkSlots
	{
		[DataMember(Name = "slots")]
		public Dictionary<string, MarkCoordinates>? Slots { get; set; }
	}
	[DataContract]
	internal sealed class MarkCoordinates
	{
		[DataMember(Name = "x")]
		public float X { get; set; }

		[DataMember(Name = "y")]
		public float Y { get; set; }

		[DataMember(Name = "z")]
		public float Z { get; set; }
	}
	internal static class TelemarkerClearDialog
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Close;
		}

		[CompilerGenerated]
		private sealed class <CoRetryOpen>d__11 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DrakesTelemarkerPlugin plugin;

			public bool clearAll;

			public int slot1Based;

			private int <i>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CoRetryOpen>d__11(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<i>5__1 = 0;
					break;
				case 1:
					<>1__state = -1;
					<i>5__1++;
					break;
				}
				if (<i>5__1 < 240)
				{
					if (TryOpenNow(plugin, clearAll, slot1Based))
					{
						return false;
					}
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				DrakesTelemarkerPlugin.Notify("Confirmation UI is not ready yet. Try again in a moment.");
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const int TryOpenFrames = 240;

		private static GameObject? _root;

		private static Text? _questionText;

		private static Button? _buttonYes;

		private static Button? _buttonNo;

		private static bool _inputBlocked;

		private static bool _pendingAll;

		private static int _pendingSlot;

		private static string _lockedWorldKey = "";

		internal static void RegisterForGuiRebuild()
		{
			GUIManager.OnCustomGUIAvailable += delegate
			{
				TearDownPanel();
			};
		}

		internal static void RequestConfirmation(DrakesTelemarkerPlugin plugin, bool clearAll, int slot1Based)
		{
			if (!TryOpenNow(plugin, clearAll, slot1Based))
			{
				((MonoBehaviour)plugin).StartCoroutine(CoRetryOpen(plugin, clearAll, slot1Based));
			}
		}

		[IteratorStateMachine(typeof(<CoRetryOpen>d__11))]
		private static IEnumerator CoRetryOpen(DrakesTelemarkerPlugin plugin, bool clearAll, int slot1Based)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CoRetryOpen>d__11(0)
			{
				plugin = plugin,
				clearAll = clearAll,
				slot1Based = slot1Based
			};
		}

		internal static bool TryOpenNow(DrakesTelemarkerPlugin plugin, bool clearAll, int slot1Based)
		{
			GUIManager instance = GUIManager.Instance;
			if (instance == null || !Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront))
			{
				return false;
			}
			EnsurePanelBuilt(instance);
			if ((Object)(object)_root == (Object)null || (Object)(object)_questionText == (Object)null || (Object)(object)_buttonYes == (Object)null || (Object)(object)_buttonNo == (Object)null)
			{
				return false;
			}
			_pendingAll = clearAll;
			_pendingSlot = slot1Based;
			_lockedWorldKey = DrakesTelemarkerPlugin.SanitizeWorldKey(DrakesTelemarkerPlugin.GetWorldKey());
			_questionText.text = (clearAll ? "Are you sure you want to clear ALL saved marks for this world?" : ("Are you sure you want to clear mark " + slot1Based.ToString(CultureInfo.InvariantCulture) + "?"));
			WireButtonsOnce(plugin);
			_root.SetActive(true);
			if (!_inputBlocked)
			{
				GUIManager.BlockInput(true);
				_inputBlocked = true;
			}
			return true;
		}

		private static void WireButtonsOnce(DrakesTelemarkerPlugin plugin)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			DrakesTelemarkerPlugin plugin2 = plugin;
			((UnityEventBase)_buttonYes.onClick).RemoveAllListeners();
			((UnityEventBase)_buttonNo.onClick).RemoveAllListeners();
			((UnityEvent)_buttonYes.onClick).AddListener((UnityAction)delegate
			{
				OnYes(plugin2);
			});
			ButtonClickedEvent onClick = _buttonNo.onClick;
			object obj = <>O.<0>__Close;
			if (obj == null)
			{
				UnityAction val = Close;
				<>O.<0>__Close = val;
				obj = (object)val;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
		}

		private static void OnYes(DrakesTelemarkerPlugin plugin)
		{
			string b = DrakesTelemarkerPlugin.SanitizeWorldKey(DrakesTelemarkerPlugin.GetWorldKey());
			if (!string.Equals(_lockedWorldKey, b, StringComparison.Ordinal))
			{
				DrakesTelemarkerPlugin.Notify("World changed before confirmation — nothing was cleared. Run telemark clear … again.");
				Close();
				return;
			}
			string error2;
			if (_pendingAll)
			{
				if (!plugin.TryClearAllSlots(out int clearedCount, out string error))
				{
					DrakesTelemarkerPlugin.Notify(string.IsNullOrEmpty(error) ? "Nothing to clear." : error);
				}
				else
				{
					DrakesTelemarkerPlugin.Notify($"Cleared all {clearedCount} marks for this world.");
				}
			}
			else if (!plugin.TryClearSlot(_pendingSlot, out error2))
			{
				DrakesTelemarkerPlugin.Notify(string.IsNullOrEmpty(error2) ? ("Mark " + _pendingSlot.ToString(CultureInfo.InvariantCulture) + " was already empty.") : error2);
			}
			else
			{
				DrakesTelemarkerPlugin.Notify("Cleared mark " + _pendingSlot.ToString(CultureInfo.InvariantCulture) + ".");
			}
			Close();
		}

		private static void Close()
		{
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
			if (_inputBlocked)
			{
				GUIManager.BlockInput(false);
				_inputBlocked = false;
			}
		}

		private static void TearDownPanel()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
			_questionText = null;
			_buttonYes = null;
			_buttonNo = null;
			if (_inputBlocked)
			{
				try
				{
					GUIManager.BlockInput(false);
				}
				catch
				{
				}
				_inputBlocked = false;
			}
		}

		private static void EnsurePanelBuilt(GUIManager gui)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root != (Object)null))
			{
				Transform transform = GUIManager.CustomGUIFront.transform;
				_root = gui.CreateWoodpanel(transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 380f, 200f, false);
				gui.CreateText("DrakesTelemarker", _root.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -36f), gui.AveriaSerifBold, 18, gui.ValheimOrange, true, Color.black, 340f, 28f, false);
				GameObject val = gui.CreateText("", _root.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 12f), gui.AveriaSerifBold, 15, Color.white, true, Color.black, 340f, 70f, false);
				_questionText = val.GetComponent<Text>();
				_questionText.alignment = (TextAnchor)4;
				_questionText.horizontalOverflow = (HorizontalWrapMode)0;
				_buttonYes = gui.CreateButton("Yes", _root.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(-70f, 36f), 110f, 32f).GetComponent<Button>();
				_buttonNo = gui.CreateButton("No", _root.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(70f, 36f), 110f, 32f).GetComponent<Button>();
				_root.SetActive(false);
			}
		}
	}
	internal static class TelemarkerMinimap
	{
		[CompilerGenerated]
		private sealed class <DelayedSyncRoutine>d__12 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			private int <i>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DelayedSyncRoutine>d__12(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<i>5__1 = 0;
					break;
				case 2:
					<>1__state = -1;
					<i>5__1++;
					break;
				}
				if (<i>5__1 < 30 && (Object)(object)Minimap.instance == (Object)null)
				{
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				SyncPins();
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <SyncNextFrame>d__14 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SyncNextFrame>d__14(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					SyncPins();
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static MethodInfo? ScreenToWorldPointMethod;

		private static MethodInfo? GetClosestPinMethod;

		private static MethodInfo? CreateMapNamePinMethod;

		private static readonly Dictionary<int, PinData> TrackedBySlot = new Dictionary<int, PinData>();

		private static readonly Sprite?[] SlotSpriteCache = (Sprite?[])(object)new Sprite[11];

		private static DrakesTelemarkerPlugin? _plugin;

		private static Harmony? _harmony;

		private static bool _loggedCreateMapNamePinFailure;

		internal static void Init(DrakesTelemarkerPlugin plugin)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			//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_01c7: Expected O, but got Unknown
			_plugin = plugin;
			ResolveMinimapReflection();
			_harmony = new Harmony("com.drakemods.DrakesTelemarker.Minimap");
			MethodInfo methodInfo = FindParameterlessInstanceMethod(typeof(Minimap), "LoadMapData");
			if (methodInfo != null)
			{
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(TelemarkerMinimap), "AfterMinimap_LoadMapData", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				Debug.LogWarning((object)"[DrakesTelemarker] Minimap.LoadMapData() not found — pins may not refresh when the map reloads.");
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Minimap), "OnMapRightClick", new Type[1] { typeof(UIInputHandler) }, (Type[])null);
			if (methodInfo2 != null && ScreenToWorldPointMethod != null)
			{
				_harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(TelemarkerMinimap), "OnMapRightClick_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(Minimap), "OnMapLeftDown", new Type[1] { typeof(UIInputHandler) }, (Type[])null);
			if (methodInfo3 != null && ScreenToWorldPointMethod != null)
			{
				_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(TelemarkerMinimap), "OnMapLeftDown_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (methodInfo2 == null && methodInfo3 == null)
			{
				Debug.LogWarning((object)"[DrakesTelemarker] Could not attach map click hooks — use telemark recall from console.");
			}
			MethodInfo methodInfo4 = AccessTools.Method(typeof(Minimap), "UpdatePins", (Type[])null, (Type[])null);
			if (methodInfo4 != null && CreateMapNamePinMethod != null)
			{
				HarmonyMethod val = new HarmonyMethod(typeof(TelemarkerMinimap), "After_UpdatePins_Postfix", (Type[])null)
				{
					priority = 0
				};
				_harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			((MonoBehaviour)plugin).StartCoroutine(DelayedSyncRoutine());
		}

		private static void ResolveMinimapReflection()
		{
			ScreenToWorldPointMethod = AccessTools.Method(typeof(Minimap), "ScreenToWorldPoint", new Type[1] { typeof(Vector3) }, (Type[])null);
			GetClosestPinMethod = AccessTools.Method(typeof(Minimap), "GetClosestPin", new Type[3]
			{
				typeof(Vector3),
				typeof(float),
				typeof(bool)
			}, (Type[])null);
			CreateMapNamePinMethod = AccessTools.Method(typeof(Minimap), "CreateMapNamePin", new Type[2]
			{
				typeof(PinData),
				typeof(RectTransform)
			}, (Type[])null);
		}

		private static MethodInfo? FindParameterlessInstanceMethod(Type type, string name)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == name && methodInfo.GetParameters().Length == 0)
				{
					return methodInfo;
				}
			}
			return null;
		}

		private static void AfterMinimap_LoadMapData()
		{
			SyncPins();
		}

		[IteratorStateMachine(typeof(<DelayedSyncRoutine>d__12))]
		private static IEnumerator DelayedSyncRoutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DelayedSyncRoutine>d__12(0);
		}

		internal static void RequestSync()
		{
			if (!((Object)(object)_plugin == (Object)null))
			{
				((MonoBehaviour)_plugin).StartCoroutine(SyncNextFrame());
			}
		}

		[IteratorStateMachine(typeof(<SyncNextFrame>d__14))]
		private static IEnumerator SyncNextFrame()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SyncNextFrame>d__14(0);
		}

		internal static void SyncPins()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_plugin == (Object)null || (Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			foreach (PinData value in TrackedBySlot.Values)
			{
				if (value != null)
				{
					instance.RemovePin(value);
				}
			}
			TrackedBySlot.Clear();
			_loggedCreateMapNamePinFailure = false;
			string marksFilePath = DrakesTelemarkerPlugin.GetMarksFilePath();
			string worldKey = DrakesTelemarkerPlugin.SanitizeWorldKey(DrakesTelemarkerPlugin.GetWorldKey());
			TelemarkerMarksDocument telemarkerMarksDocument = TelemarkerMarksDocument.Load(marksFilePath);
			long uID = ZNet.GetUID();
			PlatformUserID val = default(PlatformUserID);
			for (int i = 1; i <= 10; i++)
			{
				if (telemarkerMarksDocument.TryGetSlot(worldKey, i, out Vector3 position, out string _))
				{
					string text = $"Mark {i}";
					PinData val2 = instance.AddPin(position, (PinType)3, text, false, false, uID, val);
					if (val2 != null)
					{
						val2.m_icon = GetOrCreateSlotSprite(i);
						val2.m_animate = false;
						val2.m_doubleSize = true;
						TrackedBySlot[i] = val2;
					}
				}
			}
		}

		private static RectTransform ResolvePinNameRoot(Minimap mm)
		{
			if (mm.m_mapLarge.gameObject.activeInHierarchy && mm.m_mapLarge.gameObject.activeSelf)
			{
				return mm.m_pinNameRootLarge;
			}
			return mm.m_pinNameRootSmall;
		}

		private static void After_UpdatePins_Postfix(Minimap __instance)
		{
			if ((Object)(object)__instance == (Object)null || TrackedBySlot.Count == 0 || CreateMapNamePinMethod == null)
			{
				return;
			}
			RectTransform val = ResolvePinNameRoot(__instance);
			foreach (PinData value in TrackedBySlot.Values)
			{
				if (value == null || (Object)(object)value.m_uiElement == (Object)null || (value.m_NamePinData != null && (Object)(object)value.m_NamePinData.PinNameGameObject != (Object)null))
				{
					continue;
				}
				try
				{
					CreateMapNamePinMethod.Invoke(__instance, new object[2] { value, val });
				}
				catch (Exception ex)
				{
					if (!_loggedCreateMapNamePinFailure)
					{
						_loggedCreateMapNamePinFailure = true;
						Debug.LogWarning((object)("[DrakesTelemarker] CreateMapNamePin failed (further errors suppressed): " + ex.Message));
					}
					break;
				}
			}
		}

		private static bool OnMapRightClick_Prefix(Minimap __instance, UIInputHandler handler)
		{
			return HandleMapRecallInput(__instance, "right-click map pin");
		}

		private static bool OnMapLeftDown_Prefix(Minimap __instance, UIInputHandler handler)
		{
			if (!Input.GetKey((KeyCode)306) && !Input.GetKey((KeyCode)305))
			{
				return true;
			}
			return HandleMapRecallInput(__instance, "Ctrl+click map pin");
		}

		private static bool HandleMapRecallInput(Minimap minimapInstance, string viaPhrase)
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_plugin == (Object)null || (Object)(object)minimapInstance == (Object)null || TrackedBySlot.Count == 0)
			{
				return true;
			}
			if (ScreenToWorldPointMethod == null)
			{
				return true;
			}
			if (!DrakesTelemarkerPlugin.CanUseTelemarkerCheats())
			{
				return true;
			}
			if (!TryGetTrackedSlotUnderCursor(minimapInstance, out var slot))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			if (!_plugin.TryRecallSlot(slot, out Vector3 position, out string error))
			{
				DrakesTelemarkerPlugin.Notify(string.IsNullOrEmpty(error) ? $"Mark {slot} is empty." : error);
				return true;
			}
			((Component)localPlayer).transform.position = position;
			Physics.SyncTransforms();
			DrakesTelemarkerPlugin.Notify($"Recalled mark {slot} ({viaPhrase}) -> {DrakesTelemarkerPlugin.FormatVec(position)}.");
			return false;
		}

		private static Camera? CanvasUiCamera(RectTransform rt)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Canvas componentInParent = ((Component)rt).GetComponentInParent<Canvas>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return null;
			}
			return ((int)componentInParent.renderMode == 0) ? null : componentInParent.worldCamera;
		}

		private static float ScreenPivotSlackPixels(RectTransform rt)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = rt.rect;
			float num = Mathf.Abs(((Rect)(ref rect)).width * ((Transform)rt).lossyScale.x);
			rect = rt.rect;
			float num2 = Mathf.Abs(((Rect)(ref rect)).height * ((Transform)rt).lossyScale.y);
			float num3 = Mathf.Max(24f, Mathf.Min(num, num2));
			return Mathf.Clamp(num3 * 0.65f + 32f, 40f, 140f);
		}

		private static bool TryLooseRectHit(RectTransform rt, Vector2 screen)
		{
			//IL_0009: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_005b: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			Camera val = CanvasUiCamera(rt);
			if (RectTransformUtility.RectangleContainsScreenPoint(rt, screen, val))
			{
				return true;
			}
			Rect rect = rt.rect;
			Vector3 val2 = Vector2.op_Implicit(RectTransformUtility.WorldToScreenPoint(val, ((Transform)rt).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))));
			float num = ScreenPivotSlackPixels(rt);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(val2.x, val2.y);
			Vector2 val4 = screen - val3;
			return ((Vector2)(ref val4)).sqrMagnitude <= num * num;
		}

		private static bool TryGetTrackedSlotUnderCursor(Minimap minimap, out int slot)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: 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_00b6: 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_00db: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			slot = 0;
			Vector3 mousePosition = Input.mousePosition;
			Vector2 val = Vector2.op_Implicit(mousePosition);
			int num = 0;
			float num2 = float.MaxValue;
			foreach (KeyValuePair<int, PinData> item in TrackedBySlot)
			{
				PinData value = item.Value;
				if (value == null)
				{
					continue;
				}
				RectTransform[] array = new RectTransform[3];
				Image iconElement = value.m_iconElement;
				array[0] = ((iconElement != null) ? ((Graphic)iconElement).rectTransform : null);
				array[1] = value.m_uiElement;
				PinNameData namePinData = value.m_NamePinData;
				array[2] = ((namePinData != null) ? namePinData.PinNameRectTransform : null);
				RectTransform[] array2 = (RectTransform[])(object)array;
				RectTransform[] array3 = array2;
				foreach (RectTransform val2 in array3)
				{
					if (!((Object)(object)val2 == (Object)null) && TryLooseRectHit(val2, val))
					{
						Camera val3 = CanvasUiCamera(val2);
						Rect rect = val2.rect;
						Vector3 val4 = Vector2.op_Implicit(RectTransformUtility.WorldToScreenPoint(val3, ((Transform)val2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))));
						Vector2 val5 = new Vector2(val4.x, val4.y) - val;
						float sqrMagnitude = ((Vector2)(ref val5)).sqrMagnitude;
						if (!(sqrMagnitude >= num2))
						{
							num2 = sqrMagnitude;
							num = item.Key;
						}
					}
				}
			}
			if (num != 0)
			{
				slot = num;
				return true;
			}
			MethodInfo screenToWorldPointMethod = ScreenToWorldPointMethod;
			if (screenToWorldPointMethod == null)
			{
				return false;
			}
			Vector3 val7;
			try
			{
				if (!(screenToWorldPointMethod.Invoke(minimap, new object[1] { mousePosition }) is Vector3 val6) || 1 == 0)
				{
					return false;
				}
				val7 = val6;
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[DrakesTelemarker] ScreenToWorldPoint reflex failed: " + ex.Message));
				return false;
			}
			MethodInfo getClosestPinMethod = GetClosestPinMethod;
			if (getClosestPinMethod != null)
			{
				try
				{
					float num3 = Mathf.Max(minimap.m_removeRadius * 48f, 640f);
					bool[] array4 = new bool[2] { false, true };
					foreach (bool flag in array4)
					{
						object obj = getClosestPinMethod.Invoke(minimap, new object[3] { val7, num3, flag });
						PinData val8 = (PinData)((obj is PinData) ? obj : null);
						if (val8 == null)
						{
							continue;
						}
						foreach (KeyValuePair<int, PinData> item2 in TrackedBySlot)
						{
							if (item2.Value == val8)
							{
								slot = item2.Key;
								return true;
							}
						}
					}
				}
				catch (Exception ex2)
				{
					Debug.LogWarning((object)("[DrakesTelemarker] GetClosestPin reflex failed: " + ex2.Message));
				}
			}
			float num4 = Mathf.Max(minimap.m_removeRadius * 40f, 450f);
			float num5 = num4 * num4;
			float num6 = float.PositiveInfinity;
			int num7 = 0;
			foreach (KeyValuePair<int, PinData> item3 in TrackedBySlot)
			{
				if (item3.Value != null)
				{
					Vector3 pos = item3.Value.m_pos;
					float num8 = val7.x - pos.x;
					float num9 = val7.z - pos.z;
					float num10 = num8 * num8 + num9 * num9;
					if (num10 < num6)
					{
						num6 = num10;
						num7 = item3.Key;
					}
				}
			}
			if (num7 == 0 || num6 > num5)
			{
				return false;
			}
			slot = num7;
			return true;
		}

		private static Sprite GetOrCreateSlotSprite(int slot)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (slot < 1 || slot > 10)
			{
				slot = Mathf.Clamp(slot, 1, 10);
			}
			Sprite val = SlotSpriteCache[slot];
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Texture2D val2 = TelemarkerPinTexture.Build(slot);
			((Texture)val2).wrapMode = (TextureWrapMode)1;
			((Texture)val2).filterMode = (FilterMode)2;
			Sprite val3 = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.12f), Mathf.Max((float)((Texture)val2).width / 120f * 96f, 48f));
			SlotSpriteCache[slot] = val3;
			return val3;
		}
	}
	internal static class TelemarkerPinTexture
	{
		private const int Res = 128;

		private static readonly string[][] DigitRows = new string[10][]
		{
			new string[5] { "01110", "10001", "10001", "10001", "01110" },
			new string[5] { "00100", "01100", "00100", "00100", "01110" },
			new string[5] { "01110", "10001", "00110", "01000", "11111" },
			new string[5] { "11110", "00001", "01110", "00001", "11110" },
			new string[5] { "10001", "10001", "11111", "00001", "00001" },
			new string[5] { "11111", "10000", "11110", "00001", "11110" },
			new string[5] { "01110", "10000", "11110", "10001", "01110" },
			new string[5] { "11111", "00001", "00010", "00100", "01000" },
			new string[5] { "01110", "10001", "01110", "10001", "01110" },
			new string[5] { "01110", "10001", "01111", "00001", "01110" }
		};

		public static Texture2D Build(int slotNumber)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: 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_01f0: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false);
			((Object)val).name = $"TelemarkerPin_{slotNumber}";
			Color32[] array = (Color32[])(object)new Color32[16384];
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.06f);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.35180002f, 0.52f);
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.6482f, 0.52f);
			int holeCx = Mathf.RoundToInt(63.5f);
			int holeCy = Mathf.RoundToInt(93.98f);
			int holeR = Mathf.RoundToInt(11.52f);
			for (int i = 0; i < 128; i++)
			{
				float num = (float)i / 127f;
				for (int j = 0; j < 128; j++)
				{
					float num2 = (float)j / 127f;
					bool flag = InsideEllipse(num2, num, 0.5f, 0.74f, 0.19f, 0.24f);
					bool flag2 = InsideTriangle(new Vector2(num2, num), val2, val3, val4);
					bool flag3 = flag || flag2;
					bool flag4 = InsideEllipse(num2, num, 0.5f, 0.74f, 0.1f, 0.11f);
					if (!flag3)
					{
						array[i * 128 + j] = new Color32((byte)0, (byte)0, (byte)0, (byte)0);
						continue;
					}
					if (flag4)
					{
						array[i * 128 + j] = new Color32((byte)32, (byte)38, (byte)48, byte.MaxValue);
						continue;
					}
					byte b = (byte)((num2 < 0.5f) ? byte.MaxValue : 228);
					bool flag5 = OutlineSample(num2, num, 0.5f, 0.74f, 0.19f, 0.24f, val2, val3, val4);
					byte b2 = ((b >= 247) ? b : ((byte)(b + 8)));
					array[i * 128 + j] = (flag5 ? new Color32((byte)200, (byte)200, (byte)210, byte.MaxValue) : new Color32(b, b, b2, byte.MaxValue));
				}
			}
			BlitDigits(array, holeCx, holeCy, holeR, slotNumber);
			val.SetPixels32(array);
			val.Apply(false, false);
			return val;
		}

		private static bool OutlineSample(float ux, float uy, float cx, float cyHead, float headRx, float headRy, Vector2 tip, Vector2 shoulderL, Vector2 shoulderR)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (!InsidePin(ux, uy, cx, cyHead, headRx, headRy, tip, shoulderL, shoulderR))
			{
				return false;
			}
			float[] array = new float[3]
			{
				-1f / 127f,
				0f,
				1f / 127f
			};
			foreach (float num in array)
			{
				float[] array2 = new float[3]
				{
					-1f / 127f,
					0f,
					1f / 127f
				};
				foreach (float num2 in array2)
				{
					if ((!(Mathf.Abs(num) < 0.0001f) || !(Mathf.Abs(num2) < 0.0001f)) && !InsidePin(ux + num, uy + num2, cx, cyHead, headRx, headRy, tip, shoulderL, shoulderR))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool InsidePin(float ux, float uy, float cx, float cyHead, float headRx, float headRy, Vector2 tip, Vector2 shoulderL, Vector2 shoulderR)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			return InsideEllipse(ux, uy, cx, cyHead, headRx, headRy) || InsideTriangle(new Vector2(ux, uy), tip, shoulderL, shoulderR);
		}

		private static bool InsideEllipse(float ux, float uy, float cx, float cy, float halfW, float halfH)
		{
			float num = (ux - cx) / halfW;
			float num2 = (uy - cy) / halfH;
			return num * num + num2 * num2 <= 1f;
		}

		private static bool InsideTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			float num = Sign(p, a, b);
			float num2 = Sign(p, b, c);
			float num3 = Sign(p, c, a);
			bool flag = num < 0f || num2 < 0f || num3 < 0f;
			bool flag2 = num > 0f || num2 > 0f || num3 > 0f;
			return !(flag && flag2);
			static float Sign(Vector2 p1, Vector2 p2, Vector2 p3)
			{
				//IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
			}
		}

		private static void BlitDigits(Color32[] pix, int holeCx, int holeCy, int holeR, int value)
		{
			string text = Mathf.Clamp(value, 1, 10).ToString(CultureInfo.InvariantCulture);
			float num = ((text.Length >= 2) ? 1.35f : 1f);
			int num2 = Mathf.Max(8, Mathf.RoundToInt((float)holeR * num * 0.55f));
			int num3 = Mathf.Max(10, Mathf.RoundToInt((float)holeR * num * 0.62f));
			int num4 = num2 * text.Length + Mathf.Max(0, text.Length - 1);
			int num5 = holeCx - num4 / 2;
			int oy = holeCy - num3 / 2;
			string text2 = text;
			foreach (char digit in text2)
			{
				DrawGlyph(pix, num5, oy, num2, num3, digit);
				num5 += num2 + 1;
			}
		}

		private static void DrawGlyph(Color32[] pix, int ox, int oy, int gw, int gh, char digit)
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if (!char.IsDigit(digit))
			{
				return;
			}
			int num = digit - 48;
			string[] array = DigitRows[num];
			int num2 = Mathf.Max(1, gh / 5);
			for (int i = 0; i < 5 && i < array.Length; i++)
			{
				string text = array[i];
				int num3 = Mathf.Max(1, gw / 5);
				for (int j = 0; j < 5 && j < text.Length; j++)
				{
					if (text[j] != '1')
					{
						continue;
					}
					int num4 = ox + j * num3;
					int num5 = oy + gh - (i + 1) * num2;
					for (int k = num5; k < num5 + num2 && k < 128; k++)
					{
						if (k < 0)
						{
							continue;
						}
						for (int l = num4; l < num4 + num3 && l < 128; l++)
						{
							if (l >= 0)
							{
								pix[k * 128 + l] = new Color32((byte)246, (byte)248, byte.MaxValue, byte.MaxValue);
							}
						}
					}
				}
			}
		}
	}
}