Decompiled source of structure finder v1.0.2

BepInEx/plugins/StructureFinder/StructureFinder.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using ValheimClientTools.Features;
using ValheimClientTools.Ui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StructureFinder")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StructureFinder")]
[assembly: AssemblyTitle("StructureFinder")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimClientTools
{
	internal static class Config
	{
		private static bool _initialized;

		private static ConfigFile _file;

		private static ConfigEntry<string> _toggleKey;

		private static ConfigEntry<int> _count;

		private static ConfigEntry<float> _radius;

		private static ConfigEntry<string> _pinIcon;

		private static ConfigEntry<string> _target;

		private static ConfigEntry<string> _locale;

		private static ConfigEntry<bool> _showArrow;

		private static ConfigEntry<string> _uiKey;

		private static string _lastToggleKeyRaw;

		private static KeyCode _toggleKeyParsed = (KeyCode)289;

		private static string _lastUiKeyRaw;

		private static KeyCode _uiKeyParsed = (KeyCode)288;

		private static string _lastPinIconRaw;

		private static PinType _pinIconParsed = (PinType)2;

		internal static KeyCode ToggleKeyCode => GetCachedKey(_toggleKey, ref _lastToggleKeyRaw, ref _toggleKeyParsed, (KeyCode)289, "ToggleKey");

		internal static KeyCode UiKeyCode => GetCachedKey(_uiKey, ref _lastUiKeyRaw, ref _uiKeyParsed, (KeyCode)288, "UiKey");

		internal static int Count => Math.Max(1, _count.Value);

		internal static float RadiusMeters
		{
			get
			{
				if (!(_radius.Value > 0f))
				{
					return 2500f;
				}
				return _radius.Value;
			}
		}

		internal static string Target
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(_target.Value))
				{
					return _target.Value.Trim();
				}
				return "SunkenCrypt4";
			}
		}

		internal static bool LocaleIsFr => string.Equals(_locale.Value, "fr", StringComparison.OrdinalIgnoreCase);

		internal static bool ShowArrowEnabled => _showArrow.Value;

		internal static PinType PinIcon
		{
			get
			{
				//IL_0056: 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_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				string value = _pinIcon.Value;
				if (value != _lastPinIconRaw)
				{
					_lastPinIconRaw = value;
					if (TryParsePinType(value, out var type))
					{
						_pinIconParsed = type;
					}
					else
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("Invalid PinIcon '" + value + "', falling back to Icon2 (one warning per value)"));
						}
						_pinIconParsed = (PinType)2;
					}
				}
				return _pinIconParsed;
			}
		}

		internal static void Initialize(ConfigFile file)
		{
			if (!_initialized)
			{
				_file = file;
				_toggleKey = file.Bind<string>("StructureFinder", "ToggleKey", "F8", "Hotkey to toggle pins (Unity KeyCode, e.g. F8, K, M)");
				_count = file.Bind<int>("StructureFinder", "Count", 3, "Number of structures to pin (minimum 1)");
				_radius = file.Bind<float>("StructureFinder", "Radius", 2500f, "Search radius in meters");
				_pinIcon = file.Bind<string>("StructureFinder", "PinIcon", "Icon2", "Minimap.PinType used for our pins (e.g. Icon0..Icon4)");
				_target = file.Bind<string>("StructureFinder", "Target", "SunkenCrypt4", "Default location type searched by the hotkey and by commands without arguments");
				_locale = file.Bind<string>("StructureFinder", "Locale", "en", "HUD message language: en | fr");
				_uiKey = file.Bind<string>("StructureFinder", "UiKey", "F7", "Hotkey to open the Structure Finder window (Unity KeyCode)");
				_showArrow = file.Bind<bool>("StructureFinder", "ShowArrow", true, "Show the directional HUD arrow toward the nearest pinned structure");
				_initialized = true;
			}
		}

		private static KeyCode GetCachedKey(ConfigEntry<string> entry, ref string lastRaw, ref KeyCode parsed, KeyCode fallback, string label)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected I4, but got Unknown
			//IL_0036: 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_0049: Expected I4, but got Unknown
			string value = entry.Value;
			if (value != lastRaw)
			{
				lastRaw = value;
				if (TryParseKeyCode(value, out var key))
				{
					parsed = (KeyCode)(int)key;
				}
				else
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"Invalid {label} '{value}', falling back to {fallback} (one warning per value)");
					}
					parsed = (KeyCode)(int)fallback;
				}
			}
			return parsed;
		}

		internal static void SetLocale(string locale)
		{
			_locale.Value = locale;
			ConfigFile file = _file;
			if (file != null)
			{
				file.Save();
			}
		}

		private static bool TryParseKeyCode(string raw, out KeyCode key)
		{
			if (Enum.TryParse<KeyCode>(raw, ignoreCase: true, out key) && key)
			{
				return Enum.IsDefined(typeof(KeyCode), key);
			}
			return false;
		}

		private static bool TryParsePinType(string raw, out PinType type)
		{
			if (Enum.TryParse<PinType>(raw, ignoreCase: true, out type) && (int)type != 8)
			{
				return Enum.IsDefined(typeof(PinType), type);
			}
			return false;
		}
	}
	internal static class Messages
	{
		internal static string Pinned1(int d)
		{
			if (!Config.LocaleIsFr)
			{
				return $"Structure pinned — {d} m";
			}
			return $"Structure épinglée — {d} m";
		}

		internal static string PinnedN(int n, int d)
		{
			if (!Config.LocaleIsFr)
			{
				return $"{n} structures pinned (nearest: {d} m)";
			}
			return $"Structures épinglées — {n} (plus proche : {d} m)";
		}

		internal static string None(string nom, int r)
		{
			if (!Config.LocaleIsFr)
			{
				return $"No '{nom}' found within {r} m";
			}
			return $"Aucune '{nom}' trouvée dans {r} m";
		}

		internal static string Removed(int n)
		{
			if (!Config.LocaleIsFr)
			{
				return $"{n} markers removed";
			}
			return $"Marqueurs retirés ({n})";
		}

		internal static string Scanning()
		{
			if (!Config.LocaleIsFr)
			{
				return "Scan in progress — try again in a moment";
			}
			return "Analyse des cartes en cours — réessaie dans un instant";
		}

		internal static string NotFound(string x, int total)
		{
			if (!Config.LocaleIsFr)
			{
				return $"Structure '{x}' not found (0/{total})";
			}
			return $"Structure '{x}' introuvable (0/{total})";
		}

		internal static string DidYouMean(string suggestion)
		{
			if (!Config.LocaleIsFr)
			{
				return "Did you mean '" + suggestion + "'?";
			}
			return "Voulais-tu dire '" + suggestion + "' ?";
		}

		internal static string GuiTitle()
		{
			return "Structure Finder";
		}

		internal static string GuiCatalogCount(int types, int instances)
		{
			return $"{types} types · {instances} instances";
		}

		internal static string GuiRadius()
		{
			if (!Config.LocaleIsFr)
			{
				return "Radius";
			}
			return "Rayon";
		}

		internal static string GuiPin()
		{
			if (!Config.LocaleIsFr)
			{
				return "Pin";
			}
			return "Épingler";
		}

		internal static string GuiPinned()
		{
			if (!Config.LocaleIsFr)
			{
				return "✓ Pinned";
			}
			return "✓ Épinglée";
		}

		internal static string GuiPinAll()
		{
			if (!Config.LocaleIsFr)
			{
				return "Pin all";
			}
			return "Tout épingler";
		}

		internal static string GuiUnpinAll()
		{
			if (!Config.LocaleIsFr)
			{
				return "Unpin all";
			}
			return "Tout retirer";
		}

		internal static string GuiEmpty()
		{
			if (!Config.LocaleIsFr)
			{
				return "No match";
			}
			return "Aucun résultat";
		}

		internal static string GuiHint()
		{
			if (!Config.LocaleIsFr)
			{
				return "F7 / Esc to close";
			}
			return "F7 / Échap pour fermer";
		}

		internal static string GuiFilter()
		{
			if (!Config.LocaleIsFr)
			{
				return "Filter";
			}
			return "Filtre";
		}

		internal static string GuiLocations()
		{
			if (!Config.LocaleIsFr)
			{
				return "Locations";
			}
			return "Structures";
		}
	}
	[BepInPlugin("structure_finder", "Structure Finder", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__OnFindLoc;
		}

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

			public static ConsoleEvent <>9__8_0;

			public static Func<string, string> <>9__10_1;

			internal void <Awake>b__8_0(ConsoleEventArgs _)
			{
				SunkenCryptLocator.Toggle(Player.m_localPlayer, Config.Target, null);
			}

			internal string <PrintCatalog>b__10_1(string n)
			{
				return n;
			}
		}

		public const string PluginGuid = "structure_finder";

		public const string PluginName = "Structure Finder";

		public const string PluginVersion = "1.0.2";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_008d: 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_00a9: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Structure Finder loaded");
			Config.Initialize(((BaseUnityPlugin)this).Config);
			object obj = <>O.<0>__OnFindLoc;
			if (obj == null)
			{
				ConsoleEvent val = OnFindLoc;
				<>O.<0>__OnFindLoc = val;
				obj = (object)val;
			}
			new ConsoleCommand("findloc", "Find structures (wildcard * allowed). Usage: findloc <name|pattern> [radius] | findloc list [filter] | findloc ui | findloc (toggle default target)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__8_0;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate
				{
					SunkenCryptLocator.Toggle(Player.m_localPlayer, Config.Target, null);
				};
				<>c.<>9__8_0 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("suncrypt", "Toggle pins for the configured target (default SunkenCrypt4).", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			SunkenCryptLocator.PurgeOrphanPins();
			_harmony = new Harmony("structure_finder");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
		}

		private static void OnFindLoc(ConsoleEventArgs args)
		{
			string[] array = (args.ArgsAll ?? string.Empty).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				SunkenCryptLocator.Toggle(Player.m_localPlayer, Config.Target, null);
				return;
			}
			if (string.Equals(array[0], "ui", StringComparison.OrdinalIgnoreCase))
			{
				FinderWindow.Toggle();
				return;
			}
			if (string.Equals(array[0], "list", StringComparison.OrdinalIgnoreCase))
			{
				PrintCatalog(args, (array.Length > 1) ? array[1] : null);
				return;
			}
			string target = array[0];
			float? radiusOverride = null;
			if (array.Length > 1)
			{
				if (float.TryParse(array[1].Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f)
				{
					radiusOverride = result;
				}
				else
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)$"[Finder] invalid radius '{array[1]}', falling back to config Radius ({Config.RadiusMeters:0} m)");
					}
				}
			}
			SunkenCryptLocator.Toggle(Player.m_localPlayer, target, radiusOverride);
		}

		private static void PrintCatalog(ConsoleEventArgs args, string filter)
		{
			List<string> list = (from n in SunkenCryptLocator.GetCatalogNames()
				where filter == null || n.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0
				select n).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToList();
			for (int num = 0; num < list.Count; num += 6)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString(string.Join(", ", list.Skip(num).Take(6)));
				}
			}
			Terminal context2 = args.Context;
			if (context2 != null)
			{
				context2.AddString($"{list.Count} locations");
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			try
			{
				FinderWindow.Close();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("FinderWindow close failed during teardown: " + ex.Message));
				}
			}
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"Structure Finder unloaded (patches removed)");
			}
		}

		private void OnGUI()
		{
			FinderWindow.OnGUI();
			ArrowHud.OnGUI();
		}
	}
}
namespace ValheimClientTools.Ui
{
	internal static class ArrowHud
	{
		private static Texture2D _arrow;

		private static GUIStyle _style;

		internal static void OnGUI()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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 (!Config.ShowArrowEnabled)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && SunkenCryptLocator.NearestTrackedPin(out var target, out var dist))
			{
				Camera main = Camera.main;
				if (!((Object)(object)main == (Object)null))
				{
					Vector3 val = target - ((Component)localPlayer).transform.position;
					float num = Mathf.Atan2(val.x, val.z) * 57.29578f;
					float num2 = Mathf.DeltaAngle(((Component)main).transform.eulerAngles.y, num);
					float num3 = (float)Screen.width / 2f;
					float num4 = 96f;
					Vector2 val2 = default(Vector2);
					((Vector2)(ref val2))..ctor(num3, num4);
					Matrix4x4 matrix = GUI.matrix;
					GUIUtility.RotateAroundPivot(num2, val2);
					GUI.DrawTexture(new Rect(num3 - 24f, num4 - 24f, 48f, 48f), (Texture)(object)Arrow());
					GUI.matrix = matrix;
					GUI.Label(new Rect(num3 - 60f, num4 + 26f, 120f, 24f), $"{Mathf.RoundToInt(dist)} m", Style());
				}
			}
		}

		private static GUIStyle Style()
		{
			//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_001d: 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_0031: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (_style == null)
			{
				_style = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 13,
					fontStyle = (FontStyle)1
				};
				_style.normal.textColor = new Color(1f, 0.92f, 0.25f);
			}
			return _style;
		}

		private static Texture2D Arrow()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_arrow != (Object)null)
			{
				return _arrow;
			}
			_arrow = new Texture2D(48, 48, (TextureFormat)4, false);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(24f, 46f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(3f, 10f);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(45f, 10f);
			Vector2 p = default(Vector2);
			for (int i = 0; i < 48; i++)
			{
				for (int j = 0; j < 48; j++)
				{
					((Vector2)(ref p))..ctor((float)j + 0.5f, (float)i + 0.5f);
					bool flag = SameSide(p, val, val2, val3) && SameSide(p, val2, val, val3) && SameSide(p, val3, val, val2);
					_arrow.SetPixel(j, i, flag ? new Color(1f, 0.92f, 0.25f, 1f) : new Color(0f, 0f, 0f, 0f));
				}
			}
			_arrow.Apply();
			return _arrow;
		}

		private static bool SameSide(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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_0008: 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_000f: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_001e: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			Vector2 val2 = p - a;
			Vector2 val3 = c - a;
			float num = val.x * val2.y - val.y * val2.x;
			float num2 = val.x * val3.y - val.y * val3.x;
			return num * num2 >= 0f;
		}
	}
	internal static class CategoryFilter
	{
		internal const string OtherKey = "Other";

		private const string PrefKey = "VHT.Cats";

		internal static readonly (string Parent, string[] Subs)[] Tree = new(string, string[])[10]
		{
			("Boss and Progression", new string[3] { "Major Bosses", "Starting Point", "Hildir Quests" }),
			("Merchants and NPCs", new string[1] { "Merchant Camps" }),
			("Dungeons and Underground", new string[6] { "Black Forest", "Swamps", "Mountains", "Mistlands", "Ashlands", "Deep North" }),
			("Enemy Camps and Spawners", new string[5] { "Greydwarfs", "Fulings", "Ashlands and Charred", "Dvergr", "Deep North Trolls" }),
			("Surface Structures and Ruins", new string[8] { "Meadows and Forest Wood", "Stone Houses and Towers", "Swamps", "Mountains", "Mistlands", "Ashlands", "Deep North", "Generic" }),
			("Monuments, Stones and Graves", new string[4] { "Runestones", "Megaliths and Circles", "Graves and Burials", "Statues and Giant Remains" }),
			("Natural POIs and Resources", new string[5] { "Tar Pits", "Nests and Wildlife", "Geological Features and Water", "Shipwrecks", "Mysteries" }),
			("Developer and Testing Tools", new string[2] { "Dev Rooms and Buildings", "Dev Arenas and Surfaces" }),
			("Seasonal", new string[1] { "Events" }),
			("Other", Array.Empty<string>())
		};

		private static readonly Dictionary<string, (string Parent, string Sub)> s_exact = BuildExact();

		private static readonly HashSet<string> s_selected = new HashSet<string>(StringComparer.Ordinal);

		private static readonly HashSet<string> s_expanded = new HashSet<string>(StringComparer.Ordinal);

		private static Dictionary<(string Parent, string Sub), List<string>> s_leaves;

		private static bool s_loaded;

		internal static int SelectedCount => s_selected.Count;

		internal static void EnsureLoaded()
		{
			if (s_loaded)
			{
				return;
			}
			s_loaded = true;
			string[] array = PlayerPrefs.GetString("VHT.Cats", "").Split(new char[1] { ';' });
			foreach (string text in array)
			{
				if (text.Length > 0)
				{
					s_selected.Add(text);
				}
			}
		}

		internal static void Save()
		{
			PlayerPrefs.SetString("VHT.Cats", string.Join(";", s_selected));
			PlayerPrefs.Save();
		}

		internal static bool IsSelected(string key)
		{
			return s_selected.Contains(key);
		}

		internal static void ToggleParent(string parent)
		{
			if (!s_selected.Remove(parent))
			{
				s_selected.Add(parent);
				s_selected.RemoveWhere((string k) => k.StartsWith(parent + "/", StringComparison.Ordinal));
				s_selected.RemoveWhere((string k) => k.StartsWith("excl:" + parent + "/", StringComparison.Ordinal));
			}
			Save();
		}

		internal static bool EffectiveSubOn(string parent, string sub)
		{
			if (s_selected.Contains(parent + "/" + sub) || s_selected.Contains(parent))
			{
				return !s_selected.Contains("excl:" + parent + "/" + sub);
			}
			return false;
		}

		internal static bool EffectiveIdOn(string id)
		{
			(string, string) tuple = Classify(id);
			bool result = (tuple.Item2 == null || !s_selected.Contains("excl:" + tuple.Item1 + "/" + tuple.Item2)) && (s_selected.Contains(tuple.Item1) || (tuple.Item2 != null && s_selected.Contains(tuple.Item1 + "/" + tuple.Item2)));
			if (s_selected.Contains("excl:" + id))
			{
				return false;
			}
			if (s_selected.Contains("id:" + id))
			{
				return true;
			}
			return result;
		}

		internal static void ToggleSub(string parent, string sub)
		{
			string text = parent + "/" + sub;
			string item = "excl:" + text;
			if (EffectiveSubOn(parent, sub))
			{
				s_selected.Remove(text);
				if (s_selected.Contains(parent))
				{
					s_selected.Add(item);
				}
				else
				{
					s_selected.Remove(item);
				}
			}
			else
			{
				s_selected.Remove(item);
				if (!s_selected.Contains(parent))
				{
					s_selected.Add(text);
				}
			}
			Save();
		}

		internal static void ClearAll()
		{
			s_selected.Clear();
			Save();
		}

		internal static bool Passes(string name)
		{
			if (s_selected.Count == 0)
			{
				return true;
			}
			return EffectiveIdOn(name);
		}

		internal static void ToggleId(string id)
		{
			if (EffectiveIdOn(id))
			{
				s_selected.Remove("id:" + id);
				s_selected.Add("excl:" + id);
			}
			else
			{
				s_selected.Remove("excl:" + id);
				(string, string) tuple = Classify(id);
				if ((tuple.Item2 != null && s_selected.Contains("excl:" + tuple.Item1 + "/" + tuple.Item2)) || (!s_selected.Contains(tuple.Item1) && (tuple.Item2 == null || !s_selected.Contains(tuple.Item1 + "/" + tuple.Item2))))
				{
					s_selected.Add("id:" + id);
				}
			}
			Save();
		}

		internal static bool IsExpanded(string parent, string sub)
		{
			return s_expanded.Contains(parent + "/" + sub);
		}

		internal static void ToggleExpanded(string parent, string sub)
		{
			string item = parent + "/" + sub;
			if (!s_expanded.Remove(item))
			{
				s_expanded.Add(item);
			}
		}

		internal static List<string> IdsOf(string parent, string sub)
		{
			if (s_leaves == null)
			{
				s_leaves = new Dictionary<(string, string), List<string>>();
				foreach (KeyValuePair<string, (string, string)> item in s_exact)
				{
					(string, string) key = (item.Value.Item1, item.Value.Item2);
					if (!s_leaves.TryGetValue(key, out var value))
					{
						value = new List<string>();
						s_leaves[key] = value;
					}
					value.Add(item.Key);
				}
				foreach (List<string> value3 in s_leaves.Values)
				{
					value3.Sort(StringComparer.OrdinalIgnoreCase);
				}
			}
			if (!s_leaves.TryGetValue((parent, sub), out var value2))
			{
				return null;
			}
			return value2;
		}

		internal static (string Parent, string Sub) Classify(string name)
		{
			if (!s_exact.TryGetValue(name, out (string, string) value))
			{
				return (Parent: "Other", Sub: null);
			}
			return value;
		}

		private static Dictionary<string, (string Parent, string Sub)> BuildExact()
		{
			Dictionary<string, (string, string)> d = new Dictionary<string, (string, string)>(StringComparer.Ordinal);
			A("Boss and Progression", "Major Bosses", new string[8] { "Eikthyrnir", "GDKing", "Bonemass", "Dragonqueen", "GoblinKing", "Mistlands_DvergrBossEntrance1", "FaderLocation", "DN_Bossroom" });
			A("Boss and Progression", "Starting Point", new string[1] { "StartTemple" });
			A("Boss and Progression", "Hildir Quests", new string[3] { "Hildir_crypt", "Hildir_cave", "Hildir_plainsfortress" });
			A("Merchants and NPCs", "Merchant Camps", new string[3] { "Vendor_BlackForest", "Hildir_camp", "BogWitch_Camp" });
			A("Dungeons and Underground", "Black Forest", new string[6] { "Crypt2", "Crypt3", "Crypt4", "HalfBurried_ForestCrypt", "TrollCave", "TrollCave02" });
			A("Dungeons and Underground", "Swamps", new string[4] { "SunkenCrypt1", "SunkenCrypt2", "SunkenCrypt3", "SunkenCrypt4" });
			A("Dungeons and Underground", "Mountains", new string[2] { "MountainCave01", "MountainCave02" });
			A("Dungeons and Underground", "Mistlands", new string[2] { "Mistlands_DvergrTownEntrance1", "Mistlands_DvergrTownEntrance2" });
			A("Dungeons and Underground", "Ashlands", new string[5] { "MorgenHole1", "MorgenHole2", "MorgenHole3", "TheDarkestHole", "TheHole01" });
			A("Dungeons and Underground", "Deep North", new string[1] { "BearCave" });
			A("Enemy Camps and Spawners", "Greydwarfs", new string[3] { "Greydwarf_camp1", "Greydwarf_camp2", "Greydwarf_camp3" });
			A("Enemy Camps and Spawners", "Fulings", new string[6] { "GoblinCamp1", "GoblinCamp2", "GoblinCamp2_1", "GoblinHut01", "GoblinHut02", "GoblinHut03" });
			A("Enemy Camps and Spawners", "Ashlands and Charred", new string[3] { "CharredStone_Spawner", "CharredFortress", "Fort1" });
			A("Enemy Camps and Spawners", "Dvergr", new string[8] { "Mistlands_GuardTower1_new", "Mistlands_GuardTower2_new", "Mistlands_GuardTower3_new", "Mistlands_Harbour1", "Mistlands_Excavation1", "Mistlands_Excavation2", "Mistlands_Excavation3", "CharredTowerRuins1_dvergr" });
			A("Enemy Camps and Spawners", "Deep North Trolls", new string[2] { "DN_gammeltrollFrac01", "DN_gammeltrollFrac02" });
			A("Surface Structures and Ruins", "Meadows and Forest Wood", new string[20]
			{
				"AbandonedLogCabin02", "AbandonedLogCabin03", "AbandonedLogCabin04", "LumberCamp", "WoodFarm1", "WoodHouse1", "WoodHouse2", "WoodHouse3", "WoodHouse4", "WoodHouse5",
				"WoodHouse6", "WoodHouse7", "WoodHouse8", "WoodHouse9", "WoodHouse10", "WoodHouse11", "WoodHouse12", "WoodHouse13", "WoodVillage1", "WoodVillage2"
			});
			A("Surface Structures and Ruins", "Stone Houses and Towers", new string[25]
			{
				"Castle", "StoneHouse1", "StoneHouse1_heath", "StoneHouse2", "StoneHouse2_heath", "StoneHouse3", "StoneHouse4", "StoneHouse5", "StoneHouse5_heath", "StoneTower1",
				"StoneTower2", "StoneTower3", "StoneTower4", "StoneTowerRuins03", "StoneTowerRuins04", "StoneTowerRuins05", "StoneTowerRuins05_leet", "StoneTowerRuins07", "StoneTowerRuins07_sunk", "StoneTowerRuins08",
				"StoneTowerRuins08_sunk", "StoneTowerRuins09", "StoneTowerRuins09_sunk", "StoneTowerRuins10", "StoneTowerRuins10_sunk"
			});
			A("Surface Structures and Ruins", "Swamps", new string[11]
			{
				"SwampHut1", "SwampHut1_1", "SwampHut2", "SwampHut2_1", "SwampHut3", "SwampHut3_1", "SwampHut4", "SwampHut5", "SwampRuin1", "SwampRuin2",
				"SwampWell1"
			});
			A("Surface Structures and Ruins", "Mountains", new string[1] { "MountainWell1" });
			A("Surface Structures and Ruins", "Mistlands", new string[6] { "Mistlands_GuardTower1_ruined_new", "Mistlands_GuardTower1_ruined_new2", "Mistlands_GuardTower3_ruined_new", "Mistlands_RoadPost1", "Mistlands_Viaduct1", "Mistlands_Viaduct2" });
			A("Surface Structures and Ruins", "Ashlands", new string[11]
			{
				"AshlandRuins", "CharredRuins1", "CharredRuins2", "CharredRuins3", "CharredRuins4", "CharredTowerRuins1", "CharredTowerRuins2", "CharredTowerRuins3", "CombatRuins1", "FortressRuins",
				"MorkBorg"
			});
			A("Surface Structures and Ruins", "Deep North", new string[3] { "DN_huto1", "NorthMemorialPlace", "NorthVillage" });
			A("Surface Structures and Ruins", "Generic", new string[3] { "Ruin1", "Ruin2", "Ruin3" });
			A("Monuments, Stones and Graves", "Runestones", new string[12]
			{
				"Runestone_Ashlands", "Runestone_BlackForest", "Runestone_Boars", "Runestone_DeepNorth", "Runestone_Draugr", "Runestone_Greydwarfs", "Runestone_Meadows", "Runestone_Mistlands", "Runestone_Mountains", "Runestone_Plains",
				"Runestone_Swamps", "DrakeLorestone"
			});
			A("Monuments, Stones and Graves", "Megaliths and Circles", new string[12]
			{
				"Dolmen01", "Dolmen02", "Dolmen03", "Pillar1", "Pillar2", "StoneCircle", "StoneHenge1", "StoneHenge2", "StoneHenge3", "StoneHenge4",
				"StoneHenge5", "StoneHenge6"
			});
			A("Monuments, Stones and Graves", "Graves and Burials", new string[5] { "Grave1", "MountainGrave01", "ShipSetting01", "ShipSetting02", "ShipSetting03" });
			A("Monuments, Stones and Graves", "Statues and Giant Remains", new string[10] { "Mistlands_Giant1", "Mistlands_Giant2", "Mistlands_Statue1", "Mistlands_Statue2", "Mistlands_StatueGroup1", "Mistlands_Swords1", "Mistlands_Swords2", "Mistlands_Swords3", "Waymarker01", "Waymarker02" });
			A("Natural POIs and Resources", "Tar Pits", new string[6] { "TarPit1", "TarPit1_1", "TarPit2", "TarPit2_1", "TarPit3", "TarPit3_1" });
			A("Natural POIs and Resources", "Nests and Wildlife", new string[2] { "DrakeNest01", "VoltureNest" });
			A("Natural POIs and Resources", "Geological Features and Water", new string[10] { "BigRockClearing", "FireHole", "HotSpring1", "HotSpring2", "HotSpring3", "IcePond1", "InfestedTree01", "LeviathanLava", "Mistlands_RockSpire1", "SulfurArch" });
			A("Natural POIs and Resources", "Shipwrecks", new string[9] { "ShipWreck01", "ShipWreck01_DN", "ShipWreck02", "ShipWreck02_DN", "ShipWreck03", "ShipWreck04", "FrozenShip01_DN", "FrozenShip02_DN", "FrozenShip03_DN" });
			A("Natural POIs and Resources", "Mysteries", new string[5] { "PlaceofMystery1", "PlaceofMystery2", "PlaceofMystery3", "FimbulLocation01", "AncientUpgradeStation" });
			A("Developer and Testing Tools", "Dev Rooms and Buildings", new string[12]
			{
				"DevBedchamber", "DevDressingRoom", "DevForge", "DevGarden", "DevHouse1", "DevHouse2", "DevHouse3", "DevHouse4", "DevHouse5", "DevHouseStart",
				"DevKitchen", "DevMageRoom"
			});
			A("Developer and Testing Tools", "Dev Arenas and Surfaces", new string[10] { "DevCombatRange", "DevCombatRing", "DevFloor1", "DevGround1", "DevGround2", "DevSoundTest", "DevWall1", "DevWall2", "DevWallAsh", "Hugintest" });
			A("Seasonal", "Events", new string[1] { "xmastree" });
			return d;
			void A(string parent, string sub, string[] names)
			{
				foreach (string key in names)
				{
					d[key] = (parent, sub);
				}
			}
		}
	}
	internal static class FinderWindow
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static WindowFunction <0>__DrawWindow;
		}

		private const string RectPrefKey = "VHT.UiRect";

		private const int WindowId = 5654612;

		private const int MaxRenderedRows = 300;

		private static bool _open;

		private static Rect _rect = new Rect(80f, 80f, 780f, 560f);

		private static bool _rectLoaded;

		private static CursorLockMode _prevLock = (CursorLockMode)1;

		private static bool _prevVisible;

		private static string _filter = "";

		private static string _radiusText = "";

		private static string _lastBadRadius;

		private static List<(string Name, Vector3 Pos, float Dist)> _results = new List<(string, Vector3, float)>();

		private static int _matchedTypes;

		private static int _totalPositions;

		private static int _catalogTypes;

		private static string _source = "-";

		private static bool _scanning = true;

		private static string _tipDraw;

		private static bool _tipHoverLogged;

		private static Vector2 _scroll;

		private static Vector2 _catScroll;

		private static int _rawCount;

		private static Texture2D _bgTex;

		private static GUIStyle _headerStyle;

		private static GUIStyle _whiteLabel;

		private static readonly Dictionary<string, string> s_dispCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static float _staleAt = -1f;

		internal static bool IsOpen => _open;

		internal static void Toggle()
		{
			if (_open)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		internal static void Open()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!_open && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				_open = true;
				_prevLock = Cursor.lockState;
				_prevVisible = Cursor.visible;
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				LoadRect();
				CategoryFilter.EnsureLoaded();
				Refresh();
			}
		}

		internal static void Close()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (_open)
			{
				_open = false;
				Cursor.lockState = _prevLock;
				Cursor.visible = _prevVisible;
				SaveRect();
				GUI.FocusControl("");
			}
		}

		internal static void HandleHotkey()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (ZInput.GetKeyDown(Config.UiKeyCode, true))
			{
				if (_open)
				{
					Close();
				}
				else if (!((Object)(object)Player.m_localPlayer == (Object)null) && !Console.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()))
				{
					Open();
				}
			}
		}

		internal static void OnGUI()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Invalid comparison between Unknown and I4
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Invalid comparison between Unknown and I4
			//IL_00a2: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			if (!_open)
			{
				return;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				Close();
				return;
			}
			if (_scanning && SunkenCryptLocator.EnsureGenerated())
			{
				Refresh();
			}
			Event current = Event.current;
			if ((int)current.type == 4 && (int)current.keyCode == 27 && string.IsNullOrEmpty(GUI.GetNameOfFocusedControl()))
			{
				Close();
				return;
			}
			if ((int)Event.current.type == 7)
			{
				_tipDraw = null;
				if (_staleAt >= 0f && Time.unscaledTime >= _staleAt)
				{
					_staleAt = -1f;
					Refresh();
				}
			}
			LoadRect();
			ClampRect();
			Rect rect = _rect;
			object obj = <>O.<0>__DrawWindow;
			if (obj == null)
			{
				WindowFunction val = DrawWindow;
				<>O.<0>__DrawWindow = val;
				obj = (object)val;
			}
			_rect = GUI.Window(5654612, rect, (WindowFunction)obj, Messages.GuiTitle());
		}

		private static void DrawWindow(int id)
		{
			//IL_001e: 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_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_047b: Unknown result type (might be due to invalid IL or missing references)
			//IL_071b: Unknown result type (might be due to invalid IL or missing references)
			//IL_072f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0734: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_07bf: Invalid comparison between Unknown and I4
			//IL_0515: Unknown result type (might be due to invalid IL or missing references)
			//IL_0532: Expected O, but got Unknown
			//IL_0533: Unknown result type (might be due to invalid IL or missing references)
			//IL_0538: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Invalid comparison between Unknown and I4
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_054e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0805: Unknown result type (might be due to invalid IL or missing references)
			//IL_0817: Expected O, but got Unknown
			//IL_081e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0823: Unknown result type (might be due to invalid IL or missing references)
			//IL_0886: Unknown result type (might be due to invalid IL or missing references)
			//IL_08af: Unknown result type (might be due to invalid IL or missing references)
			GUI.DrawTexture(new Rect(0f, 0f, ((Rect)(ref _rect)).width, ((Rect)(ref _rect)).height), (Texture)(object)BgTex());
			GUILayout.BeginArea(new Rect(8f, 8f, ((Rect)(ref _rect)).width - 16f, ((Rect)(ref _rect)).height - 16f));
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Messages.GuiTitle(), HeaderStyle(), Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(Config.LocaleIsFr ? "FR" : "EN", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) }))
			{
				Config.SetLocale(Config.LocaleIsFr ? "en" : "fr");
			}
			if (GUILayout.Button("x", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
			{
				Close();
				GUILayout.EndHorizontal();
				GUILayout.EndArea();
				return;
			}
			GUILayout.EndHorizontal();
			GUILayout.Label(_scanning ? Messages.Scanning() : $"{Messages.GuiCatalogCount(_catalogTypes, _totalPositions)} · {_source} · {_matchedTypes} matched", WhiteLabel(), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Messages.GuiRadius(), WhiteLabel(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
			GUI.SetNextControlName("VHT.Radius");
			string text = GUILayout.TextField(_radiusText, Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			if (text != _radiusText)
			{
				_radiusText = text;
				MarkStale();
			}
			if (GUILayout.Button("*", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
			{
				Refresh();
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((((Rect)(ref _rect)).width - 34f) * 0.58f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Messages.GuiFilter(), WhiteLabel(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			GUI.SetNextControlName("VHT.Filter");
			string text2 = GUILayout.TextField(_filter, Array.Empty<GUILayoutOption>());
			if (text2 != _filter)
			{
				_filter = text2;
				MarkStale();
			}
			GUILayout.EndHorizontal();
			if (GUI.GetNameOfFocusedControl() == "VHT.Filter" && !string.IsNullOrWhiteSpace(_filter))
			{
				foreach (string item in (from n in SunkenCryptLocator.GetCatalogNames().Distinct<string>(StringComparer.Ordinal)
					where n.IndexOf(_filter.Trim(), StringComparison.OrdinalIgnoreCase) >= 0
					select n).Take(8).ToList())
				{
					string text3 = Disp(item);
					if (text3 != item)
					{
						text3 = text3 + " (" + item + ")";
					}
					if (GUILayout.Button(text3, WhiteLabel(), Array.Empty<GUILayoutOption>()))
					{
						_filter = item;
						GUI.FocusControl("");
						Refresh();
						break;
					}
				}
			}
			_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			if (_scanning)
			{
				GUILayout.Label(Messages.Scanning(), WhiteLabel(), Array.Empty<GUILayoutOption>());
			}
			else if (_matchedTypes == 0)
			{
				GUILayout.Label(Messages.GuiEmpty(), WhiteLabel(), Array.Empty<GUILayoutOption>());
			}
			else if (_rawCount == 0)
			{
				GUILayout.Label(Messages.None(CurrentPattern(), Mathf.RoundToInt(ParseRadius())), WhiteLabel(), Array.Empty<GUILayoutOption>());
			}
			else if (_results.Count == 0)
			{
				GUILayout.Label(Messages.GuiEmpty(), WhiteLabel(), Array.Empty<GUILayoutOption>());
			}
			else
			{
				int num = Mathf.Min(_results.Count, 300);
				for (int num2 = 0; num2 < num; num2++)
				{
					(string, Vector3, float) tuple = _results[num2];
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label($"{Disp(tuple.Item1)} — {Mathf.RoundToInt(tuple.Item3)} m", WhiteLabel(), Array.Empty<GUILayoutOption>());
					bool flag = SunkenCryptLocator.IsPinned(tuple.Item2);
					if (GUILayout.Button(flag ? Messages.GuiPinned() : Messages.GuiPin(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }))
					{
						if (flag)
						{
							SunkenCryptLocator.UnpinSingle(tuple.Item2);
						}
						else
						{
							SunkenCryptLocator.PinSingle(tuple.Item1, tuple.Item2, tuple.Item3);
						}
					}
					string text4 = Lore.Desc(tuple.Item1, Config.LocaleIsFr);
					if (!string.IsNullOrEmpty(text4))
					{
						GUILayout.Button(new GUIContent("i", tuple.Item1 + "\n" + text4), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) });
						Rect lastRect = GUILayoutUtility.GetLastRect();
						if ((int)Event.current.type == 7 && ((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
						{
							if (!_tipHoverLogged)
							{
								_tipHoverLogged = true;
								ManualLogSource log = Plugin.Log;
								if (log != null)
								{
									log.LogInfo((object)$"[Finder] tooltip hover OK ({text4.Length} car.)");
								}
							}
							_tipDraw = text4;
						}
					}
					GUILayout.EndHorizontal();
				}
				if (_results.Count > num)
				{
					GUILayout.Label($"… +{_results.Count - num}", WhiteLabel(), Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.EndScrollView();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.enabled = !_scanning && _results.Count > 0;
			if (GUILayout.Button(Messages.GuiPinAll(), Array.Empty<GUILayoutOption>()))
			{
				SunkenCryptLocator.PinAll(_results.Where(((string Name, Vector3 Pos, float Dist) r) => !SunkenCryptLocator.IsPinned(r.Pos)));
			}
			if (GUILayout.Button(Messages.GuiUnpinAll(), Array.Empty<GUILayoutOption>()))
			{
				SunkenCryptLocator.ClearAll(Player.m_localPlayer);
			}
			GUI.enabled = true;
			GUILayout.FlexibleSpace();
			GUILayout.Label(Messages.GuiHint(), WhiteLabel(), Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUILayout.Space(6f);
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((((Rect)(ref _rect)).width - 34f) * 0.42f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Messages.GuiLocations(), HeaderStyle(), Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (CategoryFilter.SelectedCount > 0 && GUILayout.Button("Clear", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
			{
				CategoryFilter.ClearAll();
				Refresh();
			}
			GUILayout.EndHorizontal();
			_catScroll = GUILayout.BeginScrollView(_catScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			(string, string[])[] tree = CategoryFilter.Tree;
			for (int num3 = 0; num3 < tree.Length; num3++)
			{
				var (parent, array) = tree[num3];
				DrawCatRow(parent, null, 0f);
				string[] array2 = array;
				foreach (string sub in array2)
				{
					DrawCatRow(parent, sub, 20f);
				}
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
			GUILayout.EndArea();
			if ((int)Event.current.type == 7 && !string.IsNullOrEmpty(_tipDraw))
			{
				GUIStyle val = WhiteLabel();
				val.wordWrap = true;
				float num5 = Mathf.Min(340f, ((Rect)(ref _rect)).width - 20f);
				float num6 = val.CalcHeight(new GUIContent(_tipDraw), num5 - 16f);
				Vector2 mousePosition = Event.current.mousePosition;
				float num7 = Mathf.Clamp(mousePosition.x + 14f, 4f, ((Rect)(ref _rect)).width - num5 - 4f);
				float num8 = Mathf.Clamp(mousePosition.y + 14f, 4f, ((Rect)(ref _rect)).height - num6 - 16f);
				GUI.DrawTexture(new Rect(num7, num8, num5, num6 + 12f), (Texture)(object)BgTex());
				GUI.Label(new Rect(num7 + 8f, num8 + 6f, num5 - 16f, num6), _tipDraw, val);
			}
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _rect)).width, 24f));
		}

		private static void DrawCatRow(string parent, string sub, float indent)
		{
			bool flag = sub == null;
			string text = (flag ? parent : sub);
			List<string> list = CategoryFilter.IdsOf(parent, sub);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Space(indent);
			bool flag2 = list != null && CategoryFilter.IsExpanded(parent, sub);
			if (list != null)
			{
				if (GUILayout.Button(flag2 ? "v" : ">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(18f) }))
				{
					CategoryFilter.ToggleExpanded(parent, sub);
				}
			}
			else
			{
				GUILayout.Space(18f);
			}
			bool flag3 = (flag ? CategoryFilter.IsSelected(parent) : CategoryFilter.EffectiveSubOn(parent, sub));
			if (GUILayout.Toggle(flag3, text, Array.Empty<GUILayoutOption>()) != flag3)
			{
				if (flag)
				{
					CategoryFilter.ToggleParent(parent);
				}
				else
				{
					CategoryFilter.ToggleSub(parent, sub);
				}
				Refresh();
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			if (!(list != null && flag2))
			{
				return;
			}
			foreach (string item in list)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.Space(indent + 40f);
				bool flag4 = CategoryFilter.EffectiveIdOn(item);
				if (GUILayout.Toggle(flag4, Disp(item), Array.Empty<GUILayoutOption>()) != flag4)
				{
					CategoryFilter.ToggleId(item);
					Refresh();
				}
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
			}
		}

		private static void Refresh()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_scanning = false;
				_results.Clear();
				return;
			}
			_scanning = !SunkenCryptLocator.EnsureGenerated();
			_catalogTypes = SunkenCryptLocator.GetCatalogNames().Distinct<string>(StringComparer.Ordinal).Count();
			if (_scanning)
			{
				_results.Clear();
				_matchedTypes = 0;
				_totalPositions = 0;
				_source = "-";
				return;
			}
			float radius = ParseRadius();
			SunkenCryptLocator.SearchResult searchResult = SunkenCryptLocator.Search(localPlayer, CurrentPattern(), radius);
			_matchedTypes = searchResult.MatchedTypes;
			_totalPositions = searchResult.TotalPositions;
			_source = searchResult.Source;
			_rawCount = searchResult.Items.Count;
			_results = searchResult.Items.Where(((string Name, Vector3 Pos, float Dist) r) => CategoryFilter.Passes(r.Name)).ToList();
		}

		private static string Disp(string id)
		{
			if (s_dispCache.TryGetValue(id, out var value))
			{
				return value;
			}
			string text = id;
			Localization instance = Localization.instance;
			if (instance != null)
			{
				string text2 = Lore.Token(id);
				if (!string.IsNullOrEmpty(text2))
				{
					string text3 = instance.Localize(text2);
					if (IsRealTranslation(text3, text2))
					{
						text = text3;
					}
				}
				if (text == id)
				{
					string text4 = id.ToLowerInvariant();
					string text5 = text4;
					int num = text5.Length;
					while (num > 0 && char.IsDigit(text5[num - 1]))
					{
						num--;
					}
					text5 = ((num > 0) ? text5.Substring(0, num) : text4);
					string text6 = "$location_" + text4;
					string text7 = instance.Localize(text6);
					if (IsRealTranslation(text7, text6))
					{
						text = text7;
					}
					else if (text5 != text4)
					{
						text6 = "$location_" + text5;
						text7 = instance.Localize(text6);
						if (IsRealTranslation(text7, text6))
						{
							text = text7;
						}
					}
				}
			}
			if (text == id)
			{
				text = Lore.Name(id, Config.LocaleIsFr);
			}
			s_dispCache[id] = text;
			return text;
		}

		private static bool IsRealTranslation(string t, string token)
		{
			if (string.IsNullOrEmpty(t) || t == token)
			{
				return false;
			}
			if (t.Length >= 2 && t[0] == '[' && t[t.Length - 1] == ']')
			{
				return false;
			}
			return true;
		}

		private static void MarkStale()
		{
			_staleAt = Time.unscaledTime + 0.15f;
		}

		private static string CurrentPattern()
		{
			if (!string.IsNullOrWhiteSpace(_filter))
			{
				return _filter.Trim();
			}
			return "*";
		}

		private static float ParseRadius()
		{
			if (string.IsNullOrWhiteSpace(_radiusText))
			{
				return Config.RadiusMeters;
			}
			if (float.TryParse(_radiusText.Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f)
			{
				return result;
			}
			if (_lastBadRadius != _radiusText)
			{
				_lastBadRadius = _radiusText;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"[Finder] invalid UI radius '{_radiusText}', using config Radius ({Config.RadiusMeters:0} m)");
				}
			}
			return Config.RadiusMeters;
		}

		private static void LoadRect()
		{
			//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)
			if (_rectLoaded)
			{
				return;
			}
			_rectLoaded = true;
			string text = PlayerPrefs.GetString("VHT.UiRect", "");
			if (!string.IsNullOrEmpty(text))
			{
				string[] array = text.Split(new char[1] { ';' });
				if (array.Length == 4 && float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && float.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
				{
					_rect = new Rect(result, result2, result3, result4);
				}
			}
			ClampRect();
		}

		private static void ClampRect()
		{
			((Rect)(ref _rect)).width = Mathf.Clamp(((Rect)(ref _rect)).width, 740f, Mathf.Max(740f, (float)Screen.width));
			((Rect)(ref _rect)).height = Mathf.Clamp(((Rect)(ref _rect)).height, 360f, Mathf.Max(360f, (float)Screen.height));
			((Rect)(ref _rect)).x = Mathf.Clamp(((Rect)(ref _rect)).x, 0f, Mathf.Max(0f, (float)Screen.width - 100f));
			((Rect)(ref _rect)).y = Mathf.Clamp(((Rect)(ref _rect)).y, 0f, Mathf.Max(0f, (float)Screen.height - 60f));
		}

		private static void SaveRect()
		{
			PlayerPrefs.SetString("VHT.UiRect", string.Join(";", ((Rect)(ref _rect)).x.ToString(CultureInfo.InvariantCulture), ((Rect)(ref _rect)).y.ToString(CultureInfo.InvariantCulture), ((Rect)(ref _rect)).width.ToString(CultureInfo.InvariantCulture), ((Rect)(ref _rect)).height.ToString(CultureInfo.InvariantCulture)));
			PlayerPrefs.Save();
		}

		private static Texture2D BgTex()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_bgTex == (Object)null)
			{
				_bgTex = new Texture2D(1, 1);
				_bgTex.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.85f));
				_bgTex.Apply();
			}
			return _bgTex;
		}

		private static GUIStyle HeaderStyle()
		{
			//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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (_headerStyle == null)
			{
				_headerStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					fontSize = 14
				};
				_headerStyle.normal.textColor = Color.white;
			}
			return _headerStyle;
		}

		private static GUIStyle WhiteLabel()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (_whiteLabel == null)
			{
				_whiteLabel = new GUIStyle(GUI.skin.label);
				_whiteLabel.normal.textColor = Color.white;
				_whiteLabel.hover.textColor = Color.white;
			}
			return _whiteLabel;
		}
	}
	internal static class Lore
	{
		internal static readonly Dictionary<string, (string Token, string NameEn, string NameFr, string DescEn, string DescFr)> ById = new Dictionary<string, (string, string, string, string, string)>(StringComparer.Ordinal)
		{
			["Eikthyrnir"] = ("$enemy_eikthyr", "Eikthyr", "Eikthyr", "Sacrificial altar of the Meadows boss.", "Autel sacrificiel du boss des Prairies."),
			["GDKing"] = ("$enemy_gdking", "The Elder", "L'Aîné", "Ancient fire-bowl altar of the Black Forest Greydwarf king.", "Autel à vasque de feu du roi Nain-Gris en Forêt Noire."),
			["Bonemass"] = ("$enemy_bonemass", "Bonemass", "Masse d'Os", "Skull-shaped sacrificial altar in the Swamps.", "Autel sacrificiel en forme de crâne dans les Marais."),
			["Dragonqueen"] = ("$enemy_dragonqueen", "Moder", "Moder", "High mountain altar summoning the mother wyvern.", "Autel de haute montagne pour invoquer la reine wyverne."),
			["GoblinKing"] = ("$enemy_goblinking", "Yagluth", "Yagluth", "Stone fingers ring shrine summoning the Fuling skeleton king.", "Cercle de doigts de pierre invoquant le roi squelette Fuling."),
			["Mistlands_DvergrBossEntrance1"] = ("$location_citadel", "The Queen", "La Reine", "Sealed gateway leading into the Infested Citadel in the Mistlands.", "Porte scellée menant à la Citadelle Infestée des Terres Brumeuses."),
			["FaderLocation"] = ("$enemy_fader", "Fader", "Fader", "Ancient ruin altar summoning the Ashlands boss.", "Autel en ruine pour invoquer le boss des Ashlands."),
			["DN_Bossroom"] = ("$location_dnboss", "Deep North Boss Arena", "Arène de boss du Grand Nord", "Arena chamber for the Deep North biome boss.", "Arène de combat du boss du Grand Nord."),
			["StartTemple"] = ("$location_starttemple", "Sacrificial Stones", "Pierres sacrificielles", "Starting spawn location featuring the trophy hook pillars.", "Point d'apparition de départ avec les piliers à trophées."),
			["Hildir_crypt"] = ("$location_smoulderingtomb", "Smouldering Tomb", "Tombe fumante", "Swamp crypt location containing one of Hildir's brass chests.", "Crypte des Marais abritant l'un des coffres en laiton d'Hildir."),
			["Hildir_cave"] = ("$location_howlingcavern", "Howling Cavern", "Caverne hurlante", "Mountain frost cave location housing one of Hildir's brass chests.", "Grotte de givre abritant l'un des coffres en laiton d'Hildir."),
			["Hildir_plainsfortress"] = ("$location_sealedtower", "Sealed Tower", "Tour scellée", "Fortified Plains tower holding one of Hildir's brass chests.", "Tour fortifiée des Plaines contenant un coffre en laiton d'Hildir."),
			["Vendor_BlackForest"] = ("$npc_haldor", "Haldor", "Haldor", "Black Forest encampment of Haldor the dwarven merchant.", "Campement de Haldor, le marchand nain de la Forêt Noire."),
			["Hildir_camp"] = ("$npc_hildir", "Hildir", "Hildir", "Meadows encampment belonging to Hildir the trader.", "Campement d'Hildir la marchande dans les Prairies."),
			["BogWitch_Camp"] = ("$npc_bogwitch", "Bog Witch", "Sorcière des marais", "Swamp refuge of the mysterious Bog Witch vendor.", "Refuge de la Sorcière des marais dans les Marais."),
			["Crypt2"] = ("$location_forestcrypt", "Burial Chambers (Layout 1)", "Chambres funéraires (Plan 1)", "Subterranean tomb skeleton dungeon in the Black Forest.", "Donjon souterrain rempli de squelettes en Forêt Noire."),
			["Crypt3"] = ("$location_forestcrypt", "Burial Chambers (Layout 2)", "Chambres funéraires (Plan 2)", "Subterranean tomb skeleton dungeon variant.", "Variante de chambres funéraires sous la Forêt Noire."),
			["Crypt4"] = ("$location_forestcrypt", "Burial Chambers (Layout 3)", "Chambres funéraires (Plan 3)", "Subterranean tomb skeleton dungeon variant.", "Variante de chambres funéraires sous la Forêt Noire."),
			["HalfBurried_ForestCrypt"] = ("$location_forestcrypt", "Buried Skeleton Crypt", "Crypte à demi-enfouie", "Partially buried stone crypt entrance in the Black Forest.", "Entrée de crypte en pierre partiellement enfouie sous terre."),
			["TrollCave"] = ("$location_trollcave", "Troll Cave", "Grotte de troll", "Cavern lair housing a forest troll.", "Grotte servant de repaire à un troll de la forêt."),
			["TrollCave02"] = ("$location_trollcave", "Troll Cave (Variant)", "Grotte de troll (Variante)", "Alternate interior variant of a troll cave.", "Variante d'agencement intérieur d'une grotte de troll."),
			["SunkenCrypt1"] = ("$location_sunkencrypt", "Sunken Crypt 1", "Crypte enfouie 1", "Flooded iron-filled crypt guarded by Draugrs and Blobs.", "Crypte inondée riche en fer, gardée par des Draugrs et des Blobs."),
			["SunkenCrypt2"] = ("$location_sunkencrypt", "Sunken Crypt 2", "Crypte enfouie 2", "Flooded iron-filled crypt entrance variant.", "Variante d'entrée de crypte inondée des marais."),
			["SunkenCrypt3"] = ("$location_sunkencrypt", "Sunken Crypt 3", "Crypte enfouie 3", "Flooded iron-filled crypt entrance variant.", "Variante d'entrée de crypte inondée des marais."),
			["SunkenCrypt4"] = ("$location_sunkencrypt", "Sunken Crypt 4", "Crypte enfouie 4", "Flooded iron-filled crypt entrance variant.", "Variante d'entrée de crypte inondée des marais."),
			["MountainCave01"] = ("$location_mountaincave", "Frost Cave 1", "Grotte de givre 1", "Icy cavern network inhabited by Bats and Cultists.", "Réseau de cavernes glacées abritant chauves-souris et cultistes."),
			["MountainCave02"] = ("$location_mountaincave", "Frost Cave 2", "Grotte de givre 2", "Alternative entryway variant into a mountain frost cave.", "Variante d'accès à une grotte de givre de montagne."),
			["Mistlands_DvergrTownEntrance1"] = ("$location_dvergrtown", "Infested Mine 1", "Mine infestée 1", "Underground dungeon entry infested with Seekers and Broods.", "Donjon souterrain infesté de Chercheurs et de Couveuses."),
			["Mistlands_DvergrTownEntrance2"] = ("$location_dvergrtown", "Infested Mine 2", "Mine infestée 2", "Alternative tunnel entrance to an Infested Mine.", "Entrée alternative menant à une mine infestée."),
			["MorgenHole1"] = ("$location_putridhole", "Putrid Hole 1", "Trou putride 1", "Subterranean den housing a ravenous Morgen.", "Fosse souterraine abritant un Morgen vorace."),
			["MorgenHole2"] = ("$location_putridhole", "Putrid Hole 2", "Trou putride 2", "Variant cave den inhabited by a Morgen.", "Variante de fosse abritant un Morgen."),
			["MorgenHole3"] = ("$location_putridhole", "Putrid Hole 3", "Trou putride 3", "Variant cave den inhabited by a Morgen.", "Variante de fosse abritant un Morgen."),
			["TheDarkestHole"] = ("$location_ashlandshole", "The Darkest Hole", "Le trou le plus sombre", "Deep chasm dungeon in the volcanic Ashlands.", "Gouffre profond formant un donjon dans les Ashlands."),
			["TheHole01"] = ("$location_ashlandshole", "Ashlands Hole", "Fosse des Terres de cendres", "Underground cavern opening in the Ashlands.", "Ouverture souterraine béante dans les Terres de cendres."),
			["BearCave"] = ("$location_bearcave", "Bear Cave", "Grotte d'ours", "Cold glacial cave den found in the Deep North.", "Tanière de glace située dans le biome du Grand Nord."),
			["Greydwarf_camp1"] = ("$piece_greydwarfspawner", "Greydwarf Nest 1", "Nid de Nains-Gris 1", "Greydwarf spawner encircled by runic stones.", "Générateur de Nains-Gris entouré de pierres runiques."),
			["Greydwarf_camp2"] = ("$piece_greydwarfspawner", "Greydwarf Nest 2", "Nid de Nains-Gris 2", "Greydwarf spawner clearing variant.", "Variante de clairière avec nid de Nains-Gris."),
			["Greydwarf_camp3"] = ("$piece_greydwarfspawner", "Greydwarf Nest 3", "Nid de Nains-Gris 3", "Greydwarf spawner clearing variant.", "Variante de clairière avec nid de Nains-Gris."),
			["GoblinCamp1"] = ("$location_goblincamp", "Fuling Village 1", "Village Fuling 1", "Fortified Goblin camp containing huts, totems, and crop farms.", "Village de gobelins fortifié avec huttes, totems et cultures."),
			["GoblinCamp2"] = ("$location_goblincamp", "Fuling Village 2", "Village Fuling 2", "Medium-sized Goblin settlement with watchtowers.", "Campement gobelin de taille moyenne avec tours de guet."),
			["GoblinCamp2_1"] = ("$location_goblincamp", "Fuling Village 2.1", "Village Fuling 2.1", "Alternate layout of a fortified Goblin outpost.", "Agencement alternatif d'avant-poste Fuling fortifié."),
			["GoblinHut01"] = ("$piece_goblinhut", "Fuling Hut 1", "Hutte Fuling 1", "Isolated tanned-leather Fuling shelter.", "Hutte de gobelin isolée en peau tannée."),
			["GoblinHut02"] = ("$piece_goblinhut", "Fuling Hut 2", "Hutte Fuling 2", "Isolated tanned-leather Fuling shelter variant.", "Variante de hutte de gobelin isolée."),
			["GoblinHut03"] = ("$piece_goblinhut", "Fuling Hut 3", "Hutte Fuling 3", "Isolated tanned-leather Fuling shelter variant.", "Variante de hutte de gobelin isolée."),
			["CharredStone_Spawner"] = ("$piece_spawner_charred", "Charred Spawner", "Générateur Calciné", "Mystical monument continuously raising skeletal Charred warriors.", "Monument mystique invoquant en continu des guerriers Calcinés."),
			["CharredFortress"] = ("$location_charredfortress", "Charred Fortress", "Forteresse Calcinée", "Massive walled citadel guarded by elite Charred forces and ballistas.", "Citadelle fortifiée gardée par des troupes d'élite Calcinées."),
			["Fort1"] = ("$location_ashlandfort", "Ashlands Fort", "Fort des Terres de cendres", "Defensive bastion situated across the volcanic wastes.", "Bastion défensif érigé au milieu des terres volcaniques."),
			["Mistlands_GuardTower1_new"] = ("$location_guardtower", "Dvergr Guard Tower 1", "Tour de guet Dvergr 1", "Active outpost garrisoned by friendly or hostile Dvergr rogues.", "Poste de garde actif défendu par des arbalétriers Dvergr."),
			["Mistlands_GuardTower2_new"] = ("$location_guardtower", "Dvergr Guard Tower 2", "Tour de guet Dvergr 2", "Reinforced marble watchtower manned by Dvergr guards.", "Tour de garde fortifiée en marbre noir occupée par des Dvergrs."),
			["Mistlands_GuardTower3_new"] = ("$location_guardtower", "Dvergr Guard Tower 3", "Tour de guet Dvergr 3", "Large Dvergr fortress tower guarding an extraction crate.", "Grande tour Dvergr abritant une caisse d'extracteur Dvergr."),
			["Mistlands_Harbour1"] = ("$location_dvergrharbour", "Dvergr Harbour", "Port Dvergr", "Coastal dock structure constructed by Dvergr expeditions.", "Quai côtier fortifié construit par les expéditions Dvergr."),
			["Mistlands_Lighthouse1_new"] = ("$location_lighthouse", "Dvergr Lighthouse", "Phare Dvergr", "Tall black marble beacon lighting misty shorelines.", "Haute balise de marbre noir éclairant les côtes brumeuses."),
			["Mistlands_Excavation1"] = ("$location_excavation", "Dvergr Excavation 1", "Excavation Dvergr 1", "Dvergr mining operation searching for soft tissue and petrified bones.", "Site minier Dvergr extrayant des tissus mous et os pétrifiés."),
			["Mistlands_Excavation2"] = ("$location_excavation", "Dvergr Excavation 2", "Excavation Dvergr 2", "Active excavation trench maintained by Dvergr workers.", "Tranchée de fouille active gérée par des ouvriers Dvergr."),
			["Mistlands_Excavation3"] = ("$location_excavation", "Dvergr Excavation 3", "Excavation Dvergr 3", "Dvergr extraction outpost focused on ancient giant remains.", "Poste de forage Dvergr creusé autour de restes de géants."),
			["CharredTowerRuins1_dvergr"] = ("$location_charredoutpost", "Charred Dvergr Outpost", "Avant-poste Dvergr calciné", "Dvergr expedition outpost holding out inside the Ashlands.", "Camp retranché d'une expédition Dvergr dans les Terres de cendres."),
			["DN_gammeltrollFrac01"] = ("$location_trollsettlement", "Ancient Troll Camp 1", "Camp de troll ancien 1", "Primitive camp inhabited by ancient Deep North trolls.", "Campement de trolls ancestraux dans le Grand Nord."),
			["DN_gammeltrollFrac02"] = ("$location_trollsettlement", "Ancient Troll Camp 2", "Camp de troll ancien 2", "Deep North glacial camp housing ancient trolls.", "Camp glaciaire habité par de vieux trolls du Grand Nord."),
			["AbandonedLogCabin02"] = ("$location_woodshack", "Abandoned Log Cabin 2", "Cabane en rondins 2", "Derelict wooden hut in the Black Forest.", "Cabane en bois en ruine dans la Forêt Noire."),
			["AbandonedLogCabin03"] = ("$location_woodshack", "Abandoned Log Cabin 3", "Cabane en rondins 3", "Weathered log cabin ruin containing chests or bees.", "Ruine de cabane pouvant abriter un coffre ou une ruche."),
			["AbandonedLogCabin04"] = ("$location_woodshack", "Abandoned Log Cabin 4", "Cabane en rondins 4", "Damaged forest shack with collapsed timbers.", "Cabane forestière aux poutres écroulées."),
			["LumberCamp"] = ("$location_lumbercamp", "Lumber Camp", "Camp de bûcherons", "Clearing with chopped logs and makeshift shelters.", "Clairière aménagée avec troncs coupés et abris de fortune."),
			["WoodFarm1"] = ("$location_farm", "Abandoned Farmhouse", "Ferme abandonnée", "Old farmstead ruin with fencing in the Meadows.", "Vestiges d'une ancienne ferme avec clôtures dans les Prairies."),
			["WoodHouse1"] = ("$location_woodhouse", "Wood House 1", "Maison en bois 1", "Small derelict cabin often housing a beehive.", "Petite masure en bois abritant fréquemment une ruche d'abeilles."),
			["WoodHouse2"] = ("$location_woodhouse", "Wood House 2", "Maison en bois 2", "Single-room decayed wooden cabin.", "Abri en bois à pièce unique délabré."),
			["WoodHouse3"] = ("$location_woodhouse", "Wood House 3", "Maison en bois 3", "Wrecked wooden cottage with missing walls.", "Cottage en bois aux pans de murs arrachés."),
			["WoodHouse4"] = ("$location_woodhouse", "Wood House 4", "Maison en bois 4", "Partially collapsed timber dwelling.", "Habitation en bois en partie effondrée."),
			["WoodHouse5"] = ("$location_woodhouse", "Wood House 5", "Maison en bois 5", "Abandoned wooden shack.", "Petite bicoque délaissée en bois."),
			["WoodHouse6"] = ("$location_woodhouse", "Wood House 6", "Maison en bois 6", "Overgrown hut in the Meadows.", "Cabane envahie par la végétation dans les Prairies."),
			["WoodHouse7"] = ("$location_woodhouse", "Wood House 7", "Maison en bois 7", "Shelter ruin containing common forage loot.", "Ruine d'abri abritant du butin commun."),
			["WoodHouse8"] = ("$location_woodhouse", "Wood House 8", "Maison en bois 8", "Decaying wooden shelter frame.", "Charpente en bois dégradée."),
			["WoodHouse9"] = ("$location_woodhouse", "Wood House 9", "Maison en bois 9", "Shattered cabin foundation.", "Fondation de cabane en bois écroulée."),
			["WoodHouse10"] = ("$location_woodhouse", "Wood House 10", "Maison en bois 10", "Small cottage ruin with preserved floorboards.", "Vestiges de maisonnette avec plancher préservé."),
			["WoodHouse11"] = ("$location_woodhouse", "Wood House 11", "Maison en bois 11", "Old woodland hut ruin.", "Ruine d'un ancien pavillon forestier."),
			["WoodHouse12"] = ("$location_woodhouse", "Wood House 12", "Maison en bois 12", "Rustic cabin shell.", "Structure de cabane rustique."),
			["WoodHouse13"] = ("$location_woodhouse", "Wood House 13", "Maison en bois 13", "Timber farmhouse structure in decay.", "Bâtisse de ferme décrépite."),
			["WoodVillage1"] = ("$location_woodvillage", "Abandoned Village 1", "Village abandonné 1", "Hamlet cluster of ruined homes inhabited by Draugrs.", "Hameau de maisons dévastées investi par des Draugrs."),
			["WoodVillage2"] = ("$location_woodvillage", "Abandoned Village 2", "Village abandonné 2", "Large derelict village cluster in the Meadows.", "Grand ensemble de bâtisses abandonnées dans les Prairies."),
			["Castle"] = ("$location_castle", "Ruined Castle", "Château en ruine", "Expansive stone fortification in ruins.", "Vaste forteresse médiévale en ruines."),
			["StoneHouse1"] = ("$location_stonehouse", "Stone House 1", "Maison en pierre 1", "Crumbling stone foundation in the Black Forest.", "Murs de pierre en ruines en Forêt Noire."),
			["StoneHouse1_heath"] = ("$location_stonehouse", "Plains Stone House 1", "Maison en pierre des plaines 1", "Stone building foundation in the Plains.", "Bâtisse en pierre ruinée sur les plaines."),
			["StoneHouse2"] = ("$location_stonehouse", "Stone House 2", "Maison en pierre 2", "Rectangular stone room ruins.", "Ruine d'une pièce rectangulaire maçonnée."),
			["StoneHouse2_heath"] = ("$location_stonehouse", "Plains Stone House 2", "Maison en pierre des plaines 2", "Plains variant of a ruined stone cottage.", "Variante de maisonnette en pierre dans les Plaines."),
			["StoneHouse3"] = ("$location_stonehouse", "Stone House 3", "Maison en pierre 3", "Stone masonry walls partially taken by vegetation.", "Murs en pierres de taille envahis par la mousse."),
			["StoneHouse4"] = ("$location_stonehouse", "Stone House 4", "Maison en pierre 4", "Weathered stone shelter footprint.", "Vestiges érodés d'un abri de pierre."),
			["StoneHouse5"] = ("$location_stonehouse", "Stone House 5", "Maison en pierre 5", "Large stone manor wall remnants.", "Ruines des grands murs d'un manoir de pierre."),
			["StoneHouse5_heath"] = ("$location_stonehouse", "Plains Stone House 5", "Maison en pierre des plaines 5", "Open stone shell in the Plains.", "Enceinte en pierre ouverte dans les Plaines."),
			["StoneTower1"] = ("$location_stonetower", "Stone Tower 1", "Tour en pierre 1", "Intact multi-floor stone watchtower guarded by Skeletons.", "Tour de garde à étages gardée par des squelettes."),
			["StoneTower2"] = ("$location_stonetower", "Stone Tower 2", "Tour en pierre 2", "Round stone watchtower occupied by Greydwarfs.", "Tour de pierre ronde occupée par des Nains-Gris."),
			["StoneTower3"] = ("$location_stonetower", "Stone Tower 3", "Tour en pierre 3", "Tower ruin with timber flooring and treasure chest.", "Tour délabrée avec plancher en bois et coffre au trésor."),
			["StoneTower4"] = ("$location_stonetower", "Stone Tower 4", "Tour en pierre 4", "Fortified defensive tower in the mountains or forest.", "Tour de guet défensive en montagne ou en forêt."),
			["StoneTowerRuins03"] = ("$location_stonetowerruin", "Stone Tower Ruin 3", "Ruine de tour 3", "Damaged lookout outpost.", "Vestiges de poste d'observation en pierre."),
			["StoneTowerRuins04"] = ("$location_stonetowerruin", "Stone Tower Ruin 4", "Ruine de tour 4", "Crumbling tower battlement.", "Créneaux de tour effondrés."),
			["StoneTowerRuins05"] = ("$location_stonetowerruin", "Stone Tower Ruin 5", "Ruine de tour 5", "Half-collapsed stone outpost.", "Avant-poste de pierre à demi effondré."),
			["StoneTowerRuins05_leet"] = ("$location_stonetowerruin", "Stone Tower Ruin 5 (Elite)", "Ruine de tour 5 (Élite)", "Stone tower ruins guarded by elite variants.", "Ruine de tour gardée par des ennemis d'élite."),
			["StoneTowerRuins07"] = ("$location_stonetowerruin", "Stone Tower Ruin 7", "Ruine de tour 7", "Eroded stone tower turret.", "Tourelle de guet érodée."),
			["StoneTowerRuins07_sunk"] = ("$location_stonetowerruin", "Sunken Tower Ruin 7", "Tour enfouie 7", "Stone tower base partially buried in the ground.", "Base de tour en pierre enfoncée dans le sol."),
			["StoneTowerRuins08"] = ("$location_stonetowerruin", "Stone Tower Ruin 8", "Ruine de tour 8", "Ancient lookout tower footprint.", "Empreinte d'une ancienne tour de vigie."),
			["StoneTowerRuins08_sunk"] = ("$location_stonetowerruin", "Sunken Tower Ruin 8", "Tour enfouie 8", "Depressed stone tower ruins in wet soil.", "Ruines de tour tassées dans un terrain meuble."),
			["StoneTowerRuins09"] = ("$location_stonetowerruin", "Stone Tower Ruin 9", "Ruine de tour 9", "Broken stone sentinel tower.", "Tour sentinelle brisée."),
			["StoneTowerRuins09_sunk"] = ("$location_stonetowerruin", "Sunken Tower Ruin 9", "Tour enfouie 9", "Collapsed tower settling deep into the earth.", "Tour de pierre affaissée dans le sol."),
			["StoneTowerRuins10"] = ("$location_stonetowerruin", "Stone Tower Ruin 10", "Ruine de tour 10", "Tall decaying stone spiral tower.", "Haute tour délabrée à escalier en colimaçon."),
			["StoneTowerRuins10_sunk"] = ("$location_stonetowerruin", "Sunken Tower Ruin 10", "Tour enfouie 10", "Submerged stone tower base.", "Base de tour submergée dans la terre."),
			["SwampHut1"] = ("$location_swamphut", "Swamp Stilt Hut 1", "Cabane sur pilotis 1", "Decaying wooden stilt cabin above stagnant water.", "Cabane en bois sur pilotis au-dessus des eaux stagnantes."),
			["SwampHut1_1"] = ("$location_swamphut", "Swamp Stilt Hut 1.1", "Cabane sur pilotis 1.1", "Submerged shack populated by Draugrs.", "Cabane immergée gardée par des Draugrs."),
			["SwampHut2"] = ("$location_swamphut", "Swamp Stilt Hut 2", "Cabane sur pilotis 2", "Waterlogged timber cabin ruin.", "Vestiges de cabane en bois gorgée d'eau."),
			["SwampHut2_1"] = ("$location_swamphut", "Swamp Stilt Hut 2.1", "Cabane sur pilotis 2.1", "Draugr-patrolled swamp shack.", "Cabane des marais patrouillée par des Draugrs."),
			["SwampHut3"] = ("$location_swamphut", "Swamp Stilt Hut 3", "Cabane sur pilotis 3", "Elevated wooden stilt shack with broken floors.", "Abri sur pilotis surélevé au plancher défoncé."),
			["SwampHut3_1"] = ("$location_swamphut", "Swamp Stilt Hut 3.1", "Cabane sur pilotis 3.1", "Variant stilt shack in muddy pools.", "Variante de cabane sur pilotis dans la fange."),
			["SwampHut4"] = ("$location_swamphut", "Swamp Stilt Hut 4", "Cabane sur pilotis 4", "Old stilt shack guarded by Draugr archers.", "Cabane sur pilotis gardée par des archers Draugrs."),
			["SwampHut5"] = ("$location_swamphut", "Swamp Stilt Hut 5", "Cabane sur pilotis 5", "Ruined wetland wooden dwelling.", "Cabane délabrée au cœur du marais."),
			["SwampRuin1"] = ("$location_swampruin", "Swamp Stone Ruin 1", "Ruine en pierre du marais 1", "Ancient sunken stone masonry in the mire.", "Ouvrage de pierre antique englouti dans la tourbe."),
			["SwampRuin2"] = ("$location_swampruin", "Swamp Stone Ruin 2", "Ruine en pierre du marais 2", "Sinking stone column platform.", "Plateforme de colonnes de pierre s'enfonçant dans la boue."),
			["SwampWell1"] = ("$piece_well", "Swamp Well", "Puits du marais", "Stone cistern well structure in the marshes.", "Citerne ou vieux puits maçonné au milieu du bourbier."),
			["MountainWell1"] = ("$piece_well", "Mountain Well", "Puits de montagne", "Ancient stone well frozen high in the Mountains.", "Ancien puits en pierre pris dans les glaces d'altitude."),
			["Mistlands_GuardTower1_ruined_new"] = ("$location_guardtower_ruined", "Ruined Dvergr Tower 1", "Tour Dvergr en ruine 1", "Shattered black marble tower overtaken by Seekers.", "Tour en marbre noir détruite et envahie par les Chercheurs."),
			["Mistlands_GuardTower1_ruined_new2"] = ("$location_guardtower_ruined", "Ruined Dvergr Tower 1.2", "Tour Dvergr en ruine 1.2", "Badly destroyed marble tower variant.", "Variante de tour de guet en marbre fortement endommagée."),
			["Mistlands_GuardTower3_ruined_new"] = ("$location_guardtower_ruined", "Ruined Dvergr Tower 3", "Tour Dvergr en ruine 3", "Extensively damaged large marble fortification.", "Grande forteresse de marbre éventrée."),
			["Mistlands_RoadPost1"] = ("$location_roadpost", "Mistlands Waypost", "Borne routière des brumes", "Ancient pathway marker made of black marble.", "Borne indicatrice de sentier taillée en marbre noir."),
			["Mistlands_Viaduct1"] = ("$location_viaduct", "Ancient Viaduct 1", "Viaduc antique 1", "Sprawling arched marble viaduct spanning misty crags.", "Grand viaduc à arches en marbre traversant les falaises."),
			["Mistlands_Viaduct2"] = ("$location_viaduct", "Ancient Viaduct 2", "Viaduc antique 2", "Broken marble aqueduct segment.", "Tronçon de pont ou aqueduc de marbre brisé."),
			["AshlandRuins"] = ("$location_ashlandruin", "Ashland Stone Ruins", "Ruines des Terres de cendres", "Scorched masonry piles across the cinder wastes.", "Piles de maçonnerie calcinée dans les plaines de cendres."),
			["CharredRuins1"] = ("$location_charredruin", "Charred Ruins 1", "Ruines calcinées 1", "Remains of a burned stone keep.", "Restes d'un donjon de pierre brûlé."),
			["CharredRuins2"] = ("$location_charredruin", "Charred Ruins 2", "Ruines calcinées 2", "Blackened stone wall fragments.", "Vestiges de murs de pierre noircis par le feu."),
			["CharredRuins3"] = ("$location_charredruin", "Charred Ruins 3", "Ruines calcinées 3", "Scattered burnt stone building pillars.", "Piliers calcinés d'une ancienne bâtisse éboulée."),
			["CharredRuins4"] = ("$location_charredruin", "Charred Ruins 4", "Ruines calcinées 4", "Collapsed volcanic building framework.", "Structure de bâtiment volcanique détruite."),
			["CharredTowerRuins1"] = ("$location_charredtower", "Charred Tower Ruin 1", "Tour calcinée en ruine 1", "Crumbled scorched watchtower.", "Tour de garde de pierre noire effondrée."),
			["CharredTowerRuins2"] = ("$location_charredtower", "Charred Tower Ruin 2", "Tour calcinée en ruine 2", "Eroded defensive ashland turret.", "Tourelle de défense abrasée par les cendres."),
			["CharredTowerRuins3"] = ("$location_charredtower", "Charred Tower Ruin 3", "Tour calcinée en ruine 3", "Shattered bastion tower.", "Fragment de bastion fortifié calciné."),
			["CombatRuins1"] = ("$location_combatruin", "Ashland Battleground", "Champ de bataille des cendres", "Contested battlefield ruins with active skirmishes.", "Zone de ruines disputée avec affrontements armés."),
			["FortressRuins"] = ("$location_fortressruin", "Ruined Ash Fortress", "Forteresse de cendre déchue", "Breached outer fortress ramparts.", "Remparts extérieurs percés d'une grande forteresse."),
			["MorkBorg"] = ("$location_morkborg", "Mork Borg", "Mork Borg", "Grim scorched redoubt ruin in the Ashlands.", "Redoute sombre et calcinée au cœur des Terres de cendres."),
			["DN_huto1"] = ("$location_northhut", "Frost Hut 1", "Refuge polaire 1", "Ancient snow-covered refuge in the Deep North.", "Ancien refuge de bois sous la neige du Grand Nord."),
			["NorthMemorialPlace"] = ("$location_memorial", "North Memorial", "Mémorial du Nord", "Solemn commemorative shrine in the frozen wastes.", "Monument funéraire érigé dans les étendues glacées."),
			["NorthVillage"] = ("$location_northvillage", "Abandoned North Settlement", "Village nordique abandonné", "Cluster of preserved frozen timber houses.", "Bourgade de rondins prise dans la glace éternelle."),
			["Ruin1"] = ("$location_ruin", "Generic Ruin 1", "Ruine générique 1", "Decayed stone masonry foundation.", "Fondations d'une bâtisse en pierre éboulée."),
			["Ruin2"] = ("$location_ruin", "Generic Ruin 2", "Ruine générique 2", "Collapsed stone architectural footprint.", "Vestiges partiels d'un mur de pierre ancien."),
			["Ruin3"] = ("$location_ruin", "Generic Ruin 3", "Ruine générique 3", "Old weathered rubble formation.", "Amas de gravats et blocs taillés érodés."),
			["Runestone_Ashlands"] = ("$piece_lorestone", "Ashlands Lorestone", "Pierre runique des Ashlands", "Glowing red runestone inscribed with Ashlands lore.", "Pierre runique luisante racontant les terres de cendres."),
			["Runestone_BlackForest"] = ("$piece_lorestone", "Black Forest Lorestone", "Pierre runique de Forêt Noire", "Ancient inscription about the dark woods.", "Monolithe gravé décrivant les dangers de la Forêt Noire."),
			["Runestone_Boars"] = ("$piece_lorestone", "Boar Lorestone", "Pierre runique des sangliers", "Runestone sharing wisdom on taming wild boars.", "Stèle détaillant la manière d'apprivoiser les sangliers."),
			["Runestone_DeepNorth"] = ("$piece_lorestone", "Deep North Lorestone", "Pierre runique du Grand Nord", "Frost-covered runestone detailing the far north.", "Stèle de givre décrivant les confins du Grand Nord."),
			["Runestone_Draugr"] = ("$piece_lorestone", "Draugr Lorestone", "Pierre runique des Draugrs", "Swamp runestone detailing the fallen warrior souls.", "Inscription relatant la malédiction des guerriers morts."),
			["Runestone_Greydwarfs"] = ("$piece_lorestone", "Greydwarf Lorestone", "Pierre runique des Nains-Gris", "Runic stone explaining the origin of Greydwarfs.", "Texte ancien expliquant la naissance des Nains-Gris."),
			["Runestone_Meadows"] = ("$piece_lorestone", "Meadows Lorestone", "Pierre runique des Prairies", "Introductory guide stone left by fallen warriors.", "Pierre d'orientation laissée pour guider les défunts."),
			["Runestone_Mistlands"] = ("$piece_lorestone", "Mistlands Lorestone", "Pierre runique des Brumes", "Carved runestone telling tales of ancient Jotun.", "Stèle runique narrant l'histoire des géants Jotun."),
			["Runestone_Mountains"] = ("$piece_lorestone", "Mountain Lorestone", "Pierre runique des Montagnes", "Frozen tablet warning of wolves and freezing peaks.", "Plaque gravée mettant en garde contre le froid et les loups."),
			["Runestone_Plains"] = ("$piece_lorestone", "Plains Lorestone", "Pierre runique des Plaines", "Plains stele chronicling the Fuling tribes.", "Stèle racontant les coutumes des tribus Fulings."),
			["Runestone_Swamps"] = ("$piece_lorestone", "Swamp Lorestone", "Pierre runique des Marais", "Water-damaged tablet recounting the dead swamps.", "Monolithe souillé d'eau évoquant la pourriture du marais."),
			["DrakeLorestone"] = ("$piece_lorestone", "Drake Lorestone", "Pierre runique des Drakes", "Mountain stone recounting the drakes of the peak.", "Pierre décrivant les wyvernes nichant sur les sommets."),
			["Dolmen01"] = ("$location_dolmen", "Dolmen 1", "Dolmen 1", "Table-like megalithic burial monument.", "Monument mégalithique funéraire en forme de table."),
			["Dolmen02"] = ("$location_dolmen", "Dolmen 2", "Dolmen 2", "Heavy horizontal stone slab supported by upright stones.", "Dalle de pierre brute posée sur des piliers verticaux."),
			["Dolmen03"] = ("$location_dolmen", "Dolmen 3", "Dolmen 3", "Large chambered prehistoric stone structure.", "Structure mégalithique préhistorique formant chambre."),
			["Pillar1"] = ("$location_pillar", "Plains Stone Pillar 1", "Pilier des plaines 1", "Tall monolithic natural stone needle.", "Aiguille de pierre colossale dressée dans la plaine."),
			["Pillar2"] = ("$location_pillar", "Plains Stone Pillar 2", "Pilier des plaines 2", "Massive towering stone spire in the Plains.", "Éperon rocheux vertical naturel dominant la plaine."),
			["StoneCircle"] = ("$location_stonecircle", "Stone Circle", "Cercle de pierres", "Circular ring of upright stones often hiding chests.", "Formation circulaire de menhirs dissimulant un trésor."),
			["StoneHenge1"] = ("$location_stonehenge", "Henge Formation 1", "Structure en Henge 1", "Trilithon stone arch standing in the Plains.", "Arche de trilithe en pierre érigée dans les Plaines."),
			["StoneHenge2"] = ("$location_stonehenge", "Henge Formation 2", "Structure en Henge 2", "Arranged stone formation containing a Vegvisir or chest.", "Cercle de monolithes abritant un Vegvisir ou un coffre."),
			["StoneHenge3"] = ("$location_stonehenge", "Henge Formation 3", "Structure en Henge 3", "Ringed megalithic stone altar.", "Autel circulaire bordé de pierres levées."),
			["StoneHenge4"] = ("$location_stonehenge", "Henge Formation 4", "Structure en Henge 4", "Ancient standing stones monument.", "Monument mégalithique ancien aux piliers verticaux."),
			["StoneHenge5"] = ("$location_stonehenge", "Henge Formation 5", "Structure en Henge 5", "Ruined stone circle arrangement.", "Arrangement de pierres dressées en partie effondré."),
			["StoneHenge6"] = ("$location_stonehenge", "Henge Formation 6", "Structure en Henge 6", "Tall arched stone structure.", "Hautes arches de pierres assemblées."),
			["Grave1"] = ("$location_grave", "Grave 1", "Tombe 1", "Earthen burial plot with buried jewelry and bones.", "Sépulture sous tertre cachant bijoux et ossements."),
			["MountainGrave01"] = ("$location_mountaingrave", "Mountain Grave", "Tombe de montagne", "Frozen high-altitude cairn grave with Silver necklace loot.", "Cairn funéraire des cimes renfermant un collier d'argent."),
			["ShipSetting01"] = ("$location_shipsetting", "Stone Ship Burial 1", "Bateau de pierre 1", "Boulders planted in the shape of a Viking longship.", "Pierres dressées formant le contour d'un drakkar viking."),
			["ShipSetting02"] = ("$location_shipsetting", "Stone Ship Burial 2", "Bateau de pierre 2", "Boat-shaped stone alignment hiding buried treasure.", "Enclos funéraire naviforme dissimulant un trésor enfoui."),
			["ShipSetting03"] = ("$location_shipsetting", "Stone Ship Burial 3", "Bateau de pierre 3", "Expansive stone longboat outline in the Meadows.", "Grand tracé de navire de pierre dans les Prairies."),
			["Mistlands_Giant1"] = ("$piece_giantremains", "Petrified Giant Remains 1", "Restes de géant pétrifié 1", "Huge petrified skull and ribcage yielding Soft Tissue.", "Crâne et cage thoracique de géant riches en tissus mous."),
			["Mistlands_Giant2"] = ("$piece_giantremains", "Petrified Giant Remains 2", "Restes de géant pétrifié 2", "Massive skeletal arm and weapon remnants of a Jotun.", "Bras et armes pétrifiés d'un ancien géant des brumes."),
			["Mistlands_Statue1"] = ("$piece_statue", "Ancient Statue 1", "Statue antique 1", "Carved stone sculpture from an ancient civilization.", "Statue en marbre sculptée par un peuple oublié."),
			["Mistlands_Statue2"] = ("$piece_statue", "Ancient Statue 2", "Statue antique 2", "Weathered marble idol standing in the mist.", "Effigie taillée dans la roche au milieu du brouillard."),
			["Mistlands_StatueGroup1"] = ("$piece_statue", "Statue Group", "Groupe de statues", "Cluster of carved monumental statues.", "Ensemble d'imposantes statues de pierre alignées."),
			["Mistlands_Swords1"] = ("$piece_giantsword", "Giant Sword 1", "Épée géante 1", "Colossal rusted iron blade stuck in the ground.", "Lame titanesque rouillée fichée en terre."),
			["Mistlands_Swords2"] = ("$piece_giantsword", "Giant Sword 2", "Épée géante 2", "Giant greatsword remnant harvestable for Scrap Iron.", "Vestige d'épée géante récoltable pour sa ferraille."),
			["Mistlands_Swords3"] = ("$piece_giantarmor", "Giant Armor Remains", "Armure géante brisée", "Scattered pieces of giant weapon plates and blades.", "Morceaux d'armures et plaques de métal colossales."),
			["Waymarker01"] = ("$piece_waymarker", "Waymarker 1", "Borne indicatrice 1", "Chiseled trail marker post.", "Poteau indicateur taillé pour marquer le chemin."),
			["Waymarker02"] = ("$piece_waymarker", "Waymarker 2", "Borne indicatrice 2", "Carved stone directional marker.", "Pierre directionnelle d'orientation."),
			["TarPit1"] = ("$location_tarpit", "Large Tar Pit", "Grande fosse de goudron", "Deep boiling tar lake guarded by dangerous Growths.", "Lac de goudron visqueux gardé par de redoutables Growths."),
			["TarPit1_1"] = ("$location_tarpit", "Tar Pit (Spill Variant)", "Fosse de goudron (Coulée)", "Expansive tar pool with overflow slopes.", "Bassin de goudron à déversement sur pente."),
			["TarPit2"] = ("$location_tarpit", "Medium Tar Pit", "Fosse de goudron moyenne", "Mid-sized viscous tar pocket in the Plains.", "Poche de goudron moyenne située dans les Plaines."),
			["TarPit2_1"] = ("$location_tarpit", "Medium Tar Pit 2", "Fosse de goudron moyenne 2", "Rocky tar basin containing harvestable tar clusters.", "Cuvette rocheuse remplie de monticules de goudron."),
			["TarPit3"] = ("$location_tarpit", "Small Tar Pit", "Petite fosse de goudron", "Small puddle-like tar depression.", "Flaque de goudron peu profonde dans le sol."),
			["TarPit3_1"] = ("$location_tarpit", "Small Tar Pit 2", "Petite fosse de goudron 2", "Shallow muddy tar hole.", "Trou boueux rempli de bitume liquide."),
			["DrakeNest01"] = ("$location_drakenest", "Drake Nest", "Nid de Drake", "Mountain ridge nest holding a glowing Dragon Egg.", "Nid de falaise contenant un œuf de dragon scintillant."),
			["VoltureNest"] = ("$location_volturenest", "Volture Nest", "Nid de Volture", "Ashlands cliff nest housing predatory Voltures.", "Nid rocheux des Ashlands abritant des Voltures."),
			["BigRockClearing"] = ("$location_clearing", "Boulder Clearing", "Clairière aux rochers", "Open grassy clearing dominated by large granite boulders.", "Espace dégagé parsemé d'imposants blocs de granit."),
			["FireHole"] = ("$location_firehole", "Surtling Core Geyser", "Geyser de Surtlings", "Volcanic jet of flame spawning fiery Surtlings in the Swamp.", "Geyser de flammes faisant apparaître des Surtlings dans le marais."),
			["HotSpring1"] = ("$location_hotspring", "Hot Spring 1", "Source chaude 1", "Steaming geothermal pool found in the Ashlands.", "Bassin géothermique fumant dans les Terres de cendres."),
			["HotSpring2"] = ("$location_hotspring", "Hot Spring 2", "Source chaude 2", "Bubbling thermal spring basin.", "Bassin d'eau thermale bouillonnante."),
			["HotSpring3"] = ("$location_hotspring", "Hot Spring 3", "Source chaude 3", "Mineral-rich smoking spring.", "Source d'eau minérale chaude dégageant des vapeurs."),
			["IcePond1"] = ("$location_icepond", "Glacial Ice Pond", "Étang gelé", "Frozen body of clear ice.", "Plan d'eau entièrement transformé en glace vive."),
			["InfestedTree01"] = ("$piece_infestedtree", "Guck Tree", "Arbre à guédrite", "Indestructible swamp tree coated in glowing green Guck sacks.", "Arbre indestructible couvert de poches vertes de guédrite."),
			["LeviathanLava"] = ("$enemy_leviathanlava", "Lava Leviathan", "Léviathan de lave", "Living molten crust surfacing in the Ashlands ocean.", "Créature insulaire de roche en fusion des mers du sud."),
			["Mistlands_RockSpire1"] = ("$location_rockspire", "Mistland Rock Needle", "Aiguille rocheuse des brumes", "Sharp natural jagged stone spire towering above mist.", "Éperon rocheux effilé dominant les nappes de brume."),
			["SulfurArch"] = ("$location_sulfurarch", "Sulfur Arch", "Arche de soufre", "Sulfuric rock arch structure in the Ashlands.", "Arche géologique de soufre dans les Terres de cendres."),
			["ShipWreck01"] = ("$piece_shipwreck", "Coastal Shipwreck 1", "Épave côtière 1", "Broken longship hull on the shoreline yielding Fine Wood.", "Carcasse de drakkar échouée fournissant du bois précieux."),
			["ShipWreck01_DN"] = ("$piece_shipwreck", "Frozen Shipwreck 1", "Épave gelée 1", "Glacier-locked wooden ship hull.", "Coque de navire prise dans les glaces du Grand Nord."),
			["ShipWreck02"] = ("$piece_shipwreck", "Coastal Shipwreck 2", "Épave côtière 2", "Shattered vessel ribs embedded in coastal sands.", "Ossature de bateau brisée enfouie dans le sable littoral."),
			["ShipWreck02_DN"] = ("$piece_shipwreck", "Frozen Shipwreck 2", "Épave gelée 2", "Icebound wreckage.", "Débris de navire piégés dans la banquise polaire."),
			["ShipWreck03"] = ("$piece_shipwreck", "Coastal Shipwreck 3", "Épave côtière 3", "Half-buried maritime vessel fragment.", "Fragment de navire à demi recouvert par les marées."),
			["ShipWreck04"] = ("$piece_shipwreck", "Coastal Shipwreck 4", "Épave côtière 4", "Scattered timber debris from an ancient longship.", "Débris de bordage de drakkar dispersés sur le rivage."),
			["FrozenShip01_DN"] = ("$piece_frozenship", "Glacial Shipwreck 1", "Navire polaire 1", "Large stranded ship frozen in Deep North ice pack.", "Grand voilier échoué dans la banquise du Grand Nord."),
			["FrozenShip02_DN"] = ("$piece_frozenship", "Glacial Shipwreck 2", "Navire polaire 2", "Intact frozen galleon shell locked in polar ice.", "Coque de galion préservée par le gel polaire."),
			["FrozenShip03_DN"] = ("$piece_frozenship", "Glacial Shipwreck 3", "Navire polaire 3", "Crushed ship frame preserved in polar frost.", "Squelette de navire écrasé par les glaces polaires."),
			["PlaceofMystery1"] = ("$location_mystery", "Place of Mystery 1", "Lieu de mystère 1", "Strange enigmatic stone monument.", "Monument de pierre énigmatique dégageant une aura étrange."),
			["PlaceofMystery2"] = ("$location_mystery", "Place of Mystery 2", "Lieu de mystère 2", "Unexplained mystic site with atmospheric anomalies.", "Site mystique inexpliqué aux perturbations atmosphériques."),
			["PlaceofMystery3"] = ("$location_mystery", "Place of Mystery 3", "Lieu de mystère 3", "Occult clearing with anomalous visual aura.", "Clairière occulte dotée d'effets visuels singuliers."),
			["FimbulLocation01"] = ("$location_fimbul", "Fimbulwinter Site", "Site de Fimbulvetr", "Glacial site marked by severe polar blizzard effects.", "Lieu glaciaire battu par des tempêtes polaires extrêmes."),
			["AncientUpgradeStation"] = ("$piece_ancientstation", "Ancient Upgrade Station", "Enclume d'amélioration antique", "Special relic anvil structure used to empower equipment.", "Structure d'enclume ancienne servant à renforcer l'équipement."),
			["DevBedchamber"] = ("$dev_bedchamber", "Dev Bedchamber", "Chambre Dev", "Internal test chamber with luxury bed and comfort items.", "Pièce test avec lits luxueux et objets de confort."),
			["DevDressingRoom"] = ("$dev_dressingroom", "Dev Dressing Room", "Dressing Dev", "Test chamber containing armor racks and clothing.", "Chambre d'essai avec râteliers d'armures et tenues."),
			["DevForge"] = ("$dev_forge", "Dev Forge", "Forge Dev", "Testing room equipped with upgraded smithing stations.", "Salle de forge équipée de toutes les extensions d'artisanat."),
			["DevGarden"] = ("$dev_garden", "Dev Garden", "Jardin Dev", "Farming test bed containing soil and all crop stages.", "Parcelle agricole d'essai pour toutes les plantes."),
			["DevHouse1"] = ("$dev_house", "Dev Test House 1", "Maison Dev 1", "Developer building test template 1.", "Modèle de bâtiment 1 pour les tests de développement."),
			["DevHouse2"] = ("$dev_house", "Dev Test House 2", "Maison Dev 2", "Developer building test template 2.", "Modèle de bâtiment 2 pour les tests de développement."),
			["DevHouse3"] = ("$dev_house", "Dev Test House 3", "Maison Dev 3", "Developer building test template 3.", "Modèle de bâtiment 3 pour les tests de développement."),
			["DevHouse4"] = ("$dev_house", "Dev Test House 4", "Maison Dev 4", "Developer building test template 4.", "Modèle de bâtiment 4 pour les tests de développement."),
			["DevHouse5"] = ("$dev_house", "Dev Test House 5", "Maison Dev 5", "Developer building test template 5.", "Modèle de bâtiment 5 pour les tests de développement."),
			["DevHouseStart"] = ("$dev_housestart", "Dev Starter House", "Maison Dev de départ", "Pre-built sample house used for baseline testing.", "Maison témoin servant de référence pour les tests."),
			["DevKitchen"] = ("$dev_kitchen", "Dev Kitchen", "Cuisine Dev", "Food preparation and cooking test facility.", "Atelier de test pour les postes de cuisson et la nourriture."),
			["DevMageRoom"] = ("$dev_mageroom", "Dev Mage Room", "Chambre de mage Dev", "Eitr and magic infusion workbench testing chamber.", "Chambre d'essai dédiée à l'Eitr et aux tables magiques."),
			["DevCombatRange"] = ("$dev_combatrange", "Dev Combat Range", "Champ de tir Dev", "Target practice range for bows, crossbows, and magic.", "Piste de tir pour arcs, arbalètes et sorts."),
			["DevCombatRing"] = ("$dev_combatring", "Dev Combat Ring", "Arène de combat Dev", "Circular arena sandbox for creature balance testing.", "Arène circulaire pour calibrer le combat et l'équilibrage."),
			["DevFloor1"] = ("$dev_floor", "Dev Floor Grid", "Grille de sol Dev", "Flat alignment grid measuring structural snap tolerances.", "Surface quadrillée pour tester le magnétisme des pièces."),
			["DevGround1"] = ("$dev_ground", "Dev Ground Test 1", "Terrain test Dev 1", "Flat earthen terrain plane for spawning objects.", "Zone de sol plane servant aux apparitions d'objets."),
			["DevGround2"] = ("$dev_ground", "Dev Ground Test 2", "Terrain test Dev 2", "Terrain deformation sandbox.", "Terrain d'essai pour modifier la géométrie du sol."),
			["DevSoundTest"] = ("$dev_soundtest", "Dev Sound Test", "Test audio Dev", "Acoustic room evaluating spatial audio and reverb.", "Salle acoustique mesurant la réverbération du son."),
			["DevWall1"] = ("$dev_wall", "Dev Wall 1", "Mur Dev 1", "Material integrity structural test wall.", "Mur d'essai pour vérifier l'intégrité structurelle."),
			["DevWall2"] = ("$dev_wall", "Dev Wall 2", "Mur Dev 2", "High-durability impact wall.", "Mur renforcé utilisé pour encaisser des impacts."),
			["DevWallAsh"] = ("$dev_wallash", "Dev Ashlands Wall", "Mur Dev Ashlands", "Thermal resistance test wall designed for Ashlands physics.", "Mur adapté aux effets thermiques des Terres de cendres."),
			["Hugintest"] = ("$dev_hugintest", "Hugin Test Node", "Balise de test Hugin", "Raven trigger zone testing tutorial prompts.", "Déclencheur d'apparition du corbeau pour le tutoriel."),
			["xmastree"] = ("$piece_xmas_tree", "Yule Tree", "Arbre de Yule", "Festive holid