Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of structure finder v1.0.2
BepInEx/plugins/StructureFinder/StructureFinder.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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