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 ValheimRadar v1.14.0
ValheimRadar.dll
Decompiled 4 days 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.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("ValheimRadar.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Hampus Toft")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Hampus Toft")] [assembly: AssemblyDescription("Minimap radar for Valheim: pins nearby creatures, resources and points of interest.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+889d51935b45b220caad90ee3f411f9063207a97")] [assembly: AssemblyProduct("ValheimRadar")] [assembly: AssemblyTitle("ValheimRadar")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 ValheimRadar { public static class RadarConfig { public sealed class ConfigurationManagerAttributes { public int? Order; } public sealed class CreatureDefinition { public readonly string CanonicalKey; public readonly string[] Aliases; public readonly string DisplayName; public readonly string Section; public readonly bool IsMonster; public readonly bool DefaultEnabled; public readonly bool Tameable; public CreatureDefinition(string canonicalKey, string[] aliases, string displayName, string section, bool isMonster, bool defaultEnabled = true, bool tameable = false) { CanonicalKey = canonicalKey; Aliases = aliases; DisplayName = displayName; Section = section; IsMonster = isMonster; DefaultEnabled = defaultEnabled; Tameable = tameable; } } public sealed class CreatureConfigEntry { public bool IsMonster; public ConfigEntry<bool> Enabled; public ConfigEntry<int> MinStars; public ConfigEntry<TameFilter> Tamed; } public enum TameFilter { Both, WildOnly, TamedOnly } public enum LocationGroup { BossAltar, Landmark, DungeonEntrance, Runestone, Ruin } public sealed class LocationDefinition { public readonly string CanonicalKey; public readonly string[] PrefabNames; public readonly string DisplayName; public readonly string Section; public readonly LocationGroup Group; public readonly string IconPng; public readonly string VanillaIcon; public readonly bool DefaultEnabled; public readonly bool AlwaysEnabled; public LocationDefinition(string canonicalKey, string[] prefabNames, string displayName, string section, LocationGroup group, string iconPng, string vanillaIcon = null, bool defaultEnabled = true, bool alwaysEnabled = false) { CanonicalKey = canonicalKey; PrefabNames = prefabNames; DisplayName = displayName; Section = section; Group = group; IconPng = iconPng; VanillaIcon = vanillaIcon; DefaultEnabled = defaultEnabled; AlwaysEnabled = alwaysEnabled; } } public sealed class LocationConfigEntry { public ConfigEntry<bool> Enabled; } private const string SecMeadows = "04 - Creatures (Meadows)"; private const string SecBlackForest = "05 - Creatures (Black Forest)"; private const string SecSwamp = "06 - Creatures (Swamp)"; private const string SecMountain = "07 - Creatures (Mountain)"; private const string SecPlains = "08 - Creatures (Plains)"; private const string SecMistlands = "09 - Creatures (Mistlands)"; private const string SecBosses = "03b - Bosses & Notable Creatures"; private const string SecFish = "09b - Creatures (Fish)"; private const string SecAshlands = "09c - Creatures (Ashlands)"; private const string SecDeepNorth = "09d - Creatures (Deep North)"; private const string SecBossLocations = "17 - Locations (Boss Altars)"; private const string SecDungeonLocations = "18 - Locations (Dungeon Entrances)"; private const string SecRunestoneLocations = "19 - Locations (Runestones)"; private const string SecRuinLocations = "20 - Locations (Ruins & Structures)"; private const string SecSpawnersAndLandmarks = "16 - Spawners & Landmarks"; public static readonly CreatureDefinition[] CreatureDefinitions = new CreatureDefinition[59] { new CreatureDefinition("boar", new string[3] { "boar", "boar_piggy", "boar_spiritcaller" }, "Boar", "04 - Creatures (Meadows)", isMonster: false, defaultEnabled: true, tameable: true), new CreatureDefinition("neck", new string[1] { "neck" }, "Neck", "04 - Creatures (Meadows)", isMonster: false), new CreatureDefinition("deer", new string[2] { "deer", "deer_white" }, "Deer", "04 - Creatures (Meadows)", isMonster: false), new CreatureDefinition("greyling", new string[1] { "greyling" }, "Greyling", "04 - Creatures (Meadows)", isMonster: true), new CreatureDefinition("greydwarf", new string[1] { "greydwarf" }, "Greydwarf", "05 - Creatures (Black Forest)", isMonster: true), new CreatureDefinition("greydwarf_elite", new string[1] { "greydwarf_elite" }, "Greydwarf Brute", "05 - Creatures (Black Forest)", isMonster: true), new CreatureDefinition("greydwarf_shaman", new string[1] { "greydwarf_shaman" }, "Greydwarf Shaman", "05 - Creatures (Black Forest)", isMonster: true), new CreatureDefinition("skeleton", new string[6] { "skeleton", "skeleton_meadows", "skeleton_mountains", "skeleton_swamps", "skeleton_poison", "skeleton_hildir" }, "Skeleton", "05 - Creatures (Black Forest)", isMonster: true), new CreatureDefinition("troll", new string[1] { "troll" }, "Troll", "05 - Creatures (Black Forest)", isMonster: true), new CreatureDefinition("draugr", new string[2] { "draugr", "draugr_ranged" }, "Draugr", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("draugr_elite", new string[1] { "draugr_elite" }, "Draugr Elite", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("blob", new string[1] { "blob" }, "Blob", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("blob_elite", new string[1] { "blobelite" }, "Poison Blob", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("leech", new string[1] { "leech" }, "Leech", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("wraith", new string[1] { "wraith" }, "Wraith", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("writhan", new string[1] { "writhan" }, "Writhan", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("abomination", new string[1] { "abomination" }, "Abomination", "06 - Creatures (Swamp)", isMonster: true), new CreatureDefinition("wolf", new string[3] { "wolf", "wolf_cub", "wolf_spiritcaller" }, "Wolf", "07 - Creatures (Mountain)", isMonster: true, defaultEnabled: true, tameable: true), new CreatureDefinition("bear", new string[3] { "bjorn", "bjorn_sleeping", "bjorn_spiritcaller" }, "Bear", "07 - Creatures (Mountain)", isMonster: false), new CreatureDefinition("stonegolem", new string[1] { "stonegolem" }, "Stone Golem", "07 - Creatures (Mountain)", isMonster: true), new CreatureDefinition("drake", new string[1] { "hatchling" }, "Drake", "07 - Creatures (Mountain)", isMonster: true), new CreatureDefinition("surtling", new string[1] { "surtling" }, "Surtling", "07 - Creatures (Mountain)", isMonster: true), new CreatureDefinition("lox", new string[2] { "lox", "lox_calf" }, "Lox", "08 - Creatures (Plains)", isMonster: false, defaultEnabled: true, tameable: true), new CreatureDefinition("deathsquito", new string[1] { "deathsquito" }, "Deathsquito", "08 - Creatures (Plains)", isMonster: true), new CreatureDefinition("fuling", new string[2] { "goblin", "goblinarcher" }, "Fuling", "08 - Creatures (Plains)", isMonster: true), new CreatureDefinition("fuling_berserker", new string[1] { "goblinbrute" }, "Fuling Berserker", "08 - Creatures (Plains)", isMonster: true), new CreatureDefinition("fuling_shaman", new string[1] { "goblinshaman" }, "Fuling Shaman", "08 - Creatures (Plains)", isMonster: true), new CreatureDefinition("growth", new string[1] { "growth" }, "Growth (Lox Spawn)", "08 - Creatures (Plains)", isMonster: true), new CreatureDefinition("seeker", new string[1] { "seeker" }, "Seeker", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("seeker_brood", new string[1] { "seeker_brood" }, "Seeker Brood", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("gjall", new string[1] { "gjall" }, "Gjall", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("tick", new string[1] { "tick" }, "Tick", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("dvergr", new string[1] { "dvergr" }, "Dvergr", "09 - Creatures (Mistlands)", isMonster: false), new CreatureDefinition("dvergrmage", new string[1] { "dvergrmage" }, "Dvergr Mage", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("fenring", new string[2] { "fenring", "fenring_cultist" }, "Fenring", "09 - Creatures (Mistlands)", isMonster: true), new CreatureDefinition("hen", new string[2] { "hen", "chicken" }, "Hen", "09 - Creatures (Mistlands)", isMonster: false, defaultEnabled: true, tameable: true), new CreatureDefinition("asksvin", new string[2] { "asksvin", "asksvin_hatchling" }, "Asksvin", "09c - Creatures (Ashlands)", isMonster: true, defaultEnabled: true, tameable: true), new CreatureDefinition("moose", new string[3] { "moose", "moose_calf", "moose_spiritcaller" }, "Moose", "09d - Creatures (Deep North)", isMonster: false, defaultEnabled: true, tameable: true), new CreatureDefinition("eikthyr", new string[1] { "eikthyr" }, "Eikthyr", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("elder", new string[1] { "gd_king" }, "The Elder", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("bonemass", new string[1] { "bonemass" }, "Bonemass", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("moder", new string[1] { "dragon" }, "Moder", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("yagluth", new string[1] { "goblinking" }, "Yagluth", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("queen", new string[1] { "seekerqueen" }, "The Queen", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("fader", new string[1] { "fader" }, "Fader", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("serpent", new string[1] { "serpent" }, "Sea Serpent", "03b - Bosses & Notable Creatures", isMonster: true), new CreatureDefinition("leviathan", new string[2] { "leviathan", "leviathanlava" }, "Leviathan", "03b - Bosses & Notable Creatures", isMonster: false), new CreatureDefinition("fish_perch", new string[1] { "fish1" }, "Perch", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_pike", new string[1] { "fish2" }, "Pike", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_tuna", new string[1] { "fish3" }, "Tuna", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_tetra", new string[1] { "fish4_cave" }, "Tetra", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_trollfish", new string[1] { "fish5" }, "Trollfish", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_giantherring", new string[1] { "fish6" }, "Giant Herring", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_grouper", new string[1] { "fish7" }, "Grouper", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_coralcod", new string[1] { "fish8" }, "Coral Cod", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_anglerfish", new string[1] { "fish9" }, "Anglerfish", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_northernsalmon", new string[1] { "fish10" }, "Northern Salmon", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_magmafish", new string[1] { "fish11" }, "Magmafish", "09b - Creatures (Fish)", isMonster: false), new CreatureDefinition("fish_pufferfish", new string[1] { "fish12" }, "Pufferfish", "09b - Creatures (Fish)", isMonster: false) }; public static readonly Dictionary<string, CreatureDefinition> AliasLookup = BuildAliasLookup(); public static readonly Dictionary<string, CreatureConfigEntry> Creatures = new Dictionary<string, CreatureConfigEntry>(); public static readonly LocationDefinition[] LocationDefinitions = new LocationDefinition[64] { new LocationDefinition("boss_eikthyr", new string[1] { "eikthyrnir" }, "Eikthyr Altar", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophyEikthyr"), new LocationDefinition("boss_elder", new string[1] { "gdking" }, "Elder Altar", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophyTheElder"), new LocationDefinition("boss_bonemass", new string[1] { "bonemass" }, "Bonemass Altar", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophyBonemass"), new LocationDefinition("boss_moder", new string[1] { "dragonqueen" }, "Moder Altar", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophyDragonQueen"), new LocationDefinition("boss_yagluth", new string[1] { "goblinking" }, "Yagluth Altar", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophyGoblinKing"), new LocationDefinition("boss_queen", new string[1] { "mistlands_dvergrbossentrance1" }, "Queen Entrance", "17 - Locations (Boss Altars)", LocationGroup.BossAltar, "boss_altar.png", "TrophySeekerQueen"), new LocationDefinition("landmark_starttemple", new string[1] { "starttemple" }, "Start Temple", "Always Shown", LocationGroup.Landmark, "landmark.png", null, defaultEnabled: true, alwaysEnabled: true), new LocationDefinition("landmark_trader", new string[1] { "vendor_blackforest" }, "Black Forest Trader", "Always Shown", LocationGroup.Landmark, "landmark.png", null, defaultEnabled: true, alwaysEnabled: true), new LocationDefinition("dungeon_blackforestcrypt", new string[3] { "crypt2", "crypt3", "crypt4" }, "Black Forest Dungeon", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_sunkencrypt", new string[1] { "sunkencrypt4" }, "Sunken Crypt", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_trollcave", new string[1] { "trollcave02" }, "Troll Cave", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_mountaincave", new string[1] { "mountaincave02" }, "Mountain Cave", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_mountaincave_tetra", new string[0], "Mountain Cave (Tetra Pond)", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_hildircrypt", new string[1] { "hildir_crypt" }, "Smouldering Tomb", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_hildircave", new string[1] { "hildir_cave" }, "Howling Cavern", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_hildirtower", new string[1] { "hildir_plainsfortress" }, "Sealed Tower", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_bearcave", new string[1] { "bearcave" }, "Bear Cave", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("dungeon_dvergrtown", new string[2] { "mistlands_dvergrtownentrance1", "mistlands_dvergrtownentrance2" }, "Dvergr Town", "18 - Locations (Dungeon Entrances)", LocationGroup.DungeonEntrance, "dungeon.png"), new LocationDefinition("runestone_greydwarfs", new string[1] { "runestone_greydwarfs" }, "Runestone (Greydwarfs)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_blackforest", new string[1] { "runestone_blackforest" }, "Runestone (Black Forest)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_meadows", new string[1] { "runestone_meadows" }, "Runestone (Meadows)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_boars", new string[1] { "runestone_boars" }, "Runestone (Boars)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_mistlands", new string[1] { "runestone_mistlands" }, "Runestone (Mistlands)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_mountains", new string[1] { "runestone_mountains" }, "Runestone (Mountains)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_drakelorestone", new string[1] { "drakelorestone" }, "Runestone (Drake Lorestone)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_draugr", new string[1] { "runestone_draugr" }, "Runestone (Draugr)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_swamps", new string[1] { "runestone_swamps" }, "Runestone (Swamps)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("runestone_plains", new string[1] { "runestone_plains" }, "Runestone (Plains)", "19 - Locations (Runestones)", LocationGroup.Runestone, "runestone.png"), new LocationDefinition("ruin_blackforestruins", new string[4] { "ruin1", "ruin2", "stonehouse3", "stonehouse4" }, "Black Forest Ruins", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_stonetower", new string[7] { "stonetowerruins03", "stonetowerruins07", "stonetowerruins08", "stonetowerruins09", "stonetowerruins10", "stonetowerruins04", "stonetowerruins05" }, "Stone Tower Ruins", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_greydwarfcamp", new string[1] { "greydwarf_camp1" }, "Greydwarf Camp", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_shipwreck", new string[4] { "shipwreck01", "shipwreck02", "shipwreck03", "shipwreck04" }, "Shipwreck", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_meadowsruinedhouse", new string[13] { "woodhouse1", "woodhouse2", "woodhouse3", "woodhouse4", "woodhouse5", "woodhouse6", "woodhouse7", "woodhouse8", "woodhouse9", "woodhouse10", "woodhouse11", "woodhouse12", "woodhouse13" }, "Meadows Ruined House", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_meadowsfarm", new string[1] { "woodfarm1" }, "Meadows Farm", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_meadowsvillage", new string[1] { "woodvillage1" }, "Meadows Village", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_shipburialmound", new string[1] { "shipsetting01" }, "Ship Burial Mound", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_stonecircle", new string[1] { "stonecircle" }, "Stone Circle", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "stone_ring.png"), new LocationDefinition("ruin_dolmen", new string[3] { "dolmen01", "dolmen02", "dolmen03" }, "Dolmen", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mistlandsroadpost", new string[1] { "mistlands_roadpost1" }, "Mistlands Road Post", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mistlandsstatue", new string[3] { "mistlands_statuegroup1", "mistlands_statue1", "mistlands_statue2" }, "Mistlands Statue", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png", null, defaultEnabled: false), new LocationDefinition("ruin_mistlandsharbour", new string[1] { "mistlands_harbour1" }, "Mistlands Harbour", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_dvergrguardtower", new string[6] { "mistlands_guardtower1_new", "mistlands_guardtower1_ruined_new", "mistlands_guardtower1_ruined_new2", "mistlands_guardtower2_new", "mistlands_guardtower3_new", "mistlands_guardtower3_ruined_new" }, "Dvergr Guard Tower", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mistlandslighthouse", new string[1] { "mistlands_lighthouse1_new" }, "Mistlands Lighthouse", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_dvergrexcavation", new string[3] { "mistlands_excavation1", "mistlands_excavation2", "mistlands_excavation3" }, "Dvergr Excavation", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mistlandsviaduct", new string[2] { "mistlands_viaduct1", "mistlands_viaduct2" }, "Mistlands Viaduct", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mistlandsrockspire", new string[1] { "mistlands_rockspire1" }, "Mistlands Rock Spire", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png", null, defaultEnabled: false), new LocationDefinition("ruin_giantremains", new string[2] { "mistlands_giant1", "mistlands_giant2" }, "Giant Remains", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_giantswords", new string[3] { "mistlands_swords1", "mistlands_swords2", "mistlands_swords3" }, "Giant Swords", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_abandonedcabin", new string[3] { "abandonedlogcabin02", "abandonedlogcabin03", "abandonedlogcabin04" }, "Abandoned Cabin", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mountaingrave", new string[1] { "mountaingrave01" }, "Mountain Grave", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_mountainwell", new string[1] { "mountainwell1" }, "Mountain Well", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_waymarker", new string[2] { "waymarker01", "waymarker02" }, "Waymarker", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_drakenest", new string[1] { "drakenest01" }, "Drake Nest", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_goblintower", new string[3] { "ruin3", "stonetower1", "stonetower3" }, "Goblin Tower Ruins", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_goblincamp", new string[1] { "goblincamp2" }, "Goblin Camp", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_stonehenge", new string[6] { "stonehenge1", "stonehenge2", "stonehenge3", "stonehenge4", "stonehenge5", "stonehenge6" }, "Stonehenge", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "stone_ring.png"), new LocationDefinition("ruin_tarpit", new string[3] { "tarpit1", "tarpit2", "tarpit3" }, "Tar Pit", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "tarpit.png"), new LocationDefinition("ruin_swampgrave", new string[1] { "grave1" }, "Swamp Grave", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_swampruins", new string[2] { "swampruin1", "swampruin2" }, "Swamp Ruins", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_swampfirehole", new string[1] { "firehole" }, "Swamp Fire Hole", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_infestedtree", new string[1] { "infestedtree01" }, "Infested Tree", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_swamphut", new string[5] { "swamphut1", "swamphut2", "swamphut3", "swamphut4", "swamphut5" }, "Swamp Hut", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_swampwell", new string[1] { "swampwell1" }, "Swamp Well", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png"), new LocationDefinition("ruin_meteorite", new string[1] { "meteorite" }, "Meteorite", "20 - Locations (Ruins & Structures)", LocationGroup.Ruin, "ruin.png") }; public static readonly Dictionary<string, LocationDefinition> LocationPrefabLookup = BuildLocationPrefabLookup(); public static readonly Dictionary<string, LocationDefinition> CanonicalLocationLookup = BuildCanonicalLocationLookup(); public static readonly Dictionary<string, LocationConfigEntry> Locations = new Dictionary<string, LocationConfigEntry>(); public const string MountainCaveKey = "dungeon_mountaincave"; public const string MountainCaveTetraKey = "dungeon_mountaincave_tetra"; public static ConfigEntry<float> ScanRadius; public static ConfigEntry<float> UpdateInterval; public static ConfigEntry<float> ClusterDistance; public static ConfigEntry<float> CreatureClusterDistance; public static ConfigEntry<int> ScanBatchCount; public static ConfigEntry<float> LocationScanInterval; public static ConfigEntry<float> ResourceRescanInterval; public static ConfigEntry<bool> RemoveDepletedResources; public static ConfigEntry<bool> HidePickedUntilRespawn; public static ConfigEntry<bool> EnablePinRemoval; public static ConfigEntry<bool> ClearDismissedPins; public static ConfigEntry<bool> EnablePinCrossOut; public static ConfigEntry<bool> RaisePlayerMarker; public static ConfigEntry<bool> DiagnosticLogging; public static ConfigEntry<bool> Group_Creatures; public static ConfigEntry<bool> Group_Berries; public static ConfigEntry<bool> Group_Mushrooms; public static ConfigEntry<bool> Group_FlowersAndCrops; public static ConfigEntry<bool> Group_RocksAndFlint; public static ConfigEntry<bool> Group_Ores; public static ConfigEntry<bool> Group_FunctionalStructures; public static ConfigEntry<bool> Group_SpawnersAndLandmarks; public static ConfigEntry<bool> Group_BossLocations; public static ConfigEntry<bool> Group_DungeonLocations; public static ConfigEntry<bool> Group_RunestoneLocations; public static ConfigEntry<bool> Group_RuinLocations; public static ConfigEntry<bool> EnableMonsters; public static ConfigEntry<bool> EnableAnimals; public static ConfigEntry<bool> ShowCreatureNames; public const int MaxFishQuality = 5; public static readonly ConfigEntry<bool>[] FishQualities = new ConfigEntry<bool>[5]; public static ConfigEntry<bool> TrackRaspberry; public static ConfigEntry<bool> TrackBlueberry; public static ConfigEntry<bool> TrackCloudberry; public static ConfigEntry<bool> TrackRedMushroom; public static ConfigEntry<bool> TrackYellowMushroom; public static ConfigEntry<bool> TrackBlueMushroom; public static ConfigEntry<bool> TrackDandelion; public static ConfigEntry<bool> TrackThistle; public static ConfigEntry<bool> TrackCarrotSeed; public static ConfigEntry<bool> TrackTurnipSeed; public static ConfigEntry<bool> TrackOnionSeed; public static ConfigEntry<bool> TrackBarley; public static ConfigEntry<bool> TrackFlax; public static ConfigEntry<bool> TrackMagecap; public static ConfigEntry<bool> TrackFlint; public static ConfigEntry<bool> TrackStone; public static ConfigEntry<bool> TrackWood; public static ConfigEntry<bool> TrackCopper; public static ConfigEntry<bool> TrackTin; public static ConfigEntry<bool> TrackIron; public static ConfigEntry<bool> TrackSilver; public static ConfigEntry<bool> TrackObsidian; public static ConfigEntry<bool> TrackGuck; public static ConfigEntry<bool> TrackChests; public static ConfigEntry<bool> TrackBuriedChests; public static ConfigEntry<bool> TrackBeehives; public static ConfigEntry<bool> TrackTrader; public static ConfigEntry<bool> TrackAbandonedRuins; public static ConfigEntry<bool> TrackRunestones; public static ConfigEntry<bool> TrackUnlistedDungeons; public static ConfigEntry<bool> TrackGreydwarfNest; public static ConfigEntry<bool> TrackBodyPile; public static ConfigEntry<bool> TrackBonePile; private static int _order; private static PropertyInfo _orphanedEntriesProperty; internal static bool PassesTameFilter(TameFilter filter, bool isTamed) { return filter switch { TameFilter.WildOnly => !isTamed, TameFilter.TamedOnly => isTamed, _ => true, }; } private static Dictionary<string, CreatureDefinition> BuildAliasLookup() { Dictionary<string, CreatureDefinition> dictionary = new Dictionary<string, CreatureDefinition>(); CreatureDefinition[] creatureDefinitions = CreatureDefinitions; foreach (CreatureDefinition creatureDefinition in creatureDefinitions) { string[] aliases = creatureDefinition.Aliases; foreach (string key in aliases) { dictionary[key] = creatureDefinition; } } return dictionary; } private static Dictionary<string, LocationDefinition> BuildLocationPrefabLookup() { Dictionary<string, LocationDefinition> dictionary = new Dictionary<string, LocationDefinition>(); LocationDefinition[] locationDefinitions = LocationDefinitions; foreach (LocationDefinition locationDefinition in locationDefinitions) { string[] prefabNames = locationDefinition.PrefabNames; foreach (string key in prefabNames) { dictionary[key] = locationDefinition; } } return dictionary; } private static Dictionary<string, LocationDefinition> BuildCanonicalLocationLookup() { Dictionary<string, LocationDefinition> dictionary = new Dictionary<string, LocationDefinition>(); LocationDefinition[] locationDefinitions = LocationDefinitions; foreach (LocationDefinition locationDefinition in locationDefinitions) { dictionary[locationDefinition.CanonicalKey] = locationDefinition; } return dictionary; } public static bool IsKnownLocationPrefab(string nameLower) { if (!string.IsNullOrEmpty(nameLower)) { return LocationPrefabLookup.ContainsKey(nameLower); } return false; } public static bool IsLocationGroupEnabled(LocationGroup group) { return group switch { LocationGroup.BossAltar => Group_BossLocations.Value, LocationGroup.Landmark => true, LocationGroup.DungeonEntrance => Group_DungeonLocations.Value, LocationGroup.Runestone => Group_RunestoneLocations.Value, LocationGroup.Ruin => Group_RuinLocations.Value, _ => false, }; } internal static int ClampFishQuality(int quality) { return Math.Max(1, Math.Min(5, quality)); } internal static bool IsFishQualityShown(int quality) { return FishQualities[ClampFishQuality(quality) - 1]?.Value ?? true; } private static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, string description, AcceptableValueBase acceptableValues = null) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = _order-- }; ConfigEntry<T> val = config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, new object[1] { configurationManagerAttributes })); MigrateLegacySection(config, (ConfigEntryBase)(object)val, section, key); return val; } internal static void MigrateLegacySection(ConfigFile config, ConfigEntryBase entry, string section, string key) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown string legacySectionName = GetLegacySectionName(section); if (legacySectionName != null) { Dictionary<ConfigDefinition, string> orphanedEntries = GetOrphanedEntries(config); ConfigDefinition key2 = new ConfigDefinition(legacySectionName, key); if (orphanedEntries != null && orphanedEntries.TryGetValue(key2, out var value)) { orphanedEntries.Remove(key2); entry.SetSerializedValue(value); config.Save(); } } } internal static string GetLegacySectionName(string section) { if (section == null || section.Length < 2 || section[0] != '0' || !char.IsDigit(section[1])) { return null; } return section.Substring(1); } private static Dictionary<ConfigDefinition, string> GetOrphanedEntries(ConfigFile config) { try { if (_orphanedEntriesProperty == null) { _orphanedEntriesProperty = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } return _orphanedEntriesProperty?.GetValue(config) as Dictionary<ConfigDefinition, string>; } catch (Exception) { return null; } } public static void Initialize(ConfigFile config) { _order = 0; ScanRadius = Bind(config, "01 - General", "ScanRadius", 100f, "Scan radius around player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f)); UpdateInterval = Bind(config, "01 - General", "UpdateInterval", 1f, "Scan interval in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f)); ClusterDistance = Bind(config, "01 - General", "ClusterDistance", 15f, "Max distance between resources/points of interest to group into one pin. Creatures use Creature Cluster Distance instead.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f)); CreatureClusterDistance = Bind(config, "01 - General", "Creature Cluster Distance", 15f, "Max distance between creatures (animals, monsters, fish) of the same kind to group into one pin. Lower it to see creatures individually.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f)); ScanBatchCount = Bind(config, "01 - General", "ScanBatchCount", 4, "Creatures only: splits each full-radius scan into this many spatial batches, spread across successive update ticks, so a large ScanRadius doesn't cause a lag spike on any single tick. Higher values reduce per-tick cost but make moving creatures take longer to refresh (1 = scan the whole radius every tick).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20)); LocationScanInterval = Bind(config, "01 - General", "LocationScanInterval", 5f, "How often (seconds) to poll Valheim's own zone/location system for newly-generated world Locations (dungeons, ruins, runestones, boss altars, etc.). Independent of UpdateInterval since new Locations only appear as unexplored zones generate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f)); ResourceRescanInterval = Bind(config, "01 - General", "ResourceRescanInterval", 30f, "How often (seconds) already-scanned ground in the loaded area around you is scanned again for resources and physics-detected points of interest. Rescans pick up anything missed the first time and notice resources that are gone (see Remove Depleted Resources). Only a couple of map cells are rescanned per update tick. 0 = never rescan.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f)); RemoveDepletedResources = Bind(config, "01 - General", "Remove Depleted Resources", defaultValue: true, "Remove pins for resources that don't grow back (ore deposits, obsidian, muddy scrap piles, flint, stones, branches, Guck Sacks): immediately when you hit or pick one yourself, and after rescans confirm it's gone when someone else cleared it. Berries, mushrooms, flowers and crops regrow and are never removed (see Hide Picked Until Respawn)."); HidePickedUntilRespawn = Bind(config, "01 - General", "Hide Picked Until Respawn", defaultValue: true, "When berries, mushrooms, flowers or crops near you are picked (by you or another player), hide that pin until it grows back (each pickable's own respawn time, in in-game world time) instead of leaving it on the map. Turning this off shows every currently hidden pin again."); EnablePinRemoval = Bind(config, "01 - General", "Enable Pin Removal", defaultValue: true, "Right-click a ValheimRadar resource or location pin on the large map (same as removing a normal map pin) to dismiss it. Dismissed pins stay hidden across scans and sessions for that world, and only that pin is affected - the rest of its category keeps showing. Creature pins are live and can't be dismissed."); ClearDismissedPins = Bind(config, "01 - General", "Restore Dismissed Pins", defaultValue: false, "Set to true to bring back every pin dismissed with right-click in the current world. Resets itself to false."); EnablePinCrossOut = Bind(config, "01 - General", "Enable Pin Cross-Out", defaultValue: true, "Left-click a ValheimRadar resource or location pin on the large map to cross it out (an X, like checking off a normal map pin) - e.g. a dungeon you've already looted. Left-click it again to clear the X. Crossed-out pins are remembered per world. Creature pins are live and can't be crossed out."); RaisePlayerMarker = Bind(config, "01 - General", "Raise Player Marker", defaultValue: false, "Draw your own map marker (and the ship marker) above all ValheimRadar pins. Applied when you connect to a world. Turn off to leave Valheim's map layering untouched if it conflicts with another map mod."); DiagnosticLogging = Bind(config, "01 - General", "Diagnostic Logging", defaultValue: false, "Write verbose troubleshooting lines (pin created/updated/removed, Location scan summaries, pin name state while the large map is open, unmapped Location prefabs) to the BepInEx log. Leave off unless troubleshooting."); Group_Creatures = Bind(config, "02 - Master Groups", "Enable Creatures Group", defaultValue: true, "Master toggle for all creatures, bosses, and fish."); Group_Berries = Bind(config, "02 - Master Groups", "Enable Berries Group", defaultValue: true, "Master toggle for all berry bushes."); Group_Mushrooms = Bind(config, "02 - Master Groups", "Enable Mushrooms Group", defaultValue: true, "Master toggle for all mushroom types."); Group_FlowersAndCrops = Bind(config, "02 - Master Groups", "Enable Flowers and Crops Group", defaultValue: true, "Master toggle for wild plants, seeds, and crops."); Group_RocksAndFlint = Bind(config, "02 - Master Groups", "Enable Ground Pickables Group", defaultValue: true, "Master toggle for loose rocks, flint, wood."); Group_Ores = Bind(config, "02 - Master Groups", "Enable Ores Group", defaultValue: true, "Master toggle for ore deposits (copper, tin, silver, obsidian, iron scrap piles) and harvestable Guck Sacks. Loose ore and ingots lying on the ground are never tracked."); Group_FunctionalStructures = Bind(config, "02 - Master Groups", "Enable Functional Structures", defaultValue: true, "Master toggle for chests (buried and unburied), beehives, and the Bog Witch's camp."); Group_SpawnersAndLandmarks = Bind(config, "02 - Master Groups", "Enable Spawners & Landmarks Group", defaultValue: true, "Master toggle for monster-spawner landmarks (Greydwarf Nest, Body Pile, Bone Pile)."); Group_BossLocations = Bind(config, "02 - Master Groups", "Enable Boss Altars Group", defaultValue: true, "Master toggle for boss summoning altars (Eikthyr, Elder, Bonemass, Moder, Yagluth, the Queen)."); Group_DungeonLocations = Bind(config, "02 - Master Groups", "Enable Dungeon Entrances Group", defaultValue: true, "Master toggle for dungeon/cave Location entrances (crypts, sunken crypt, troll cave, mountain caves, Hildir's Smouldering Tomb/Howling Cavern/Sealed Tower, Bear Cave, Dvergr town) plus any unlisted dungeon-plane entrance caught by the physics-scan fallback."); Group_RunestoneLocations = Bind(config, "02 - Master Groups", "Enable Runestones Group", defaultValue: true, "Master toggle for every biome's runestone Locations, plus any unlisted/modded runestone caught by the physics-scan fallback."); Group_RuinLocations = Bind(config, "02 - Master Groups", "Enable Ruins & Structures Group", defaultValue: true, "Master toggle for the full ZoneSystem-based ruins/structures roster (ruined houses, stone towers, shipwrecks, Mistlands structures, tar pits, etc.), plus any unlisted/modded ruin caught by the physics-scan fallback. The Start Temple and Black Forest Trader are always shown and have no toggle."); EnableMonsters = Bind(config, "03 - Creatures (Defaults)", "Hostile Monsters (Unlisted)", defaultValue: true, "Show hostile creatures that have no specific entry in the sections below."); EnableAnimals = Bind(config, "03 - Creatures (Defaults)", "Passive Animals (Unlisted)", defaultValue: true, "Show passive/tameable creatures that have no specific entry in the sections below."); ShowCreatureNames = Bind(config, "03 - Creatures (Defaults)", "Show Creature Names", defaultValue: false, "Show the creature's name in its pin label. When off (default), a creature pin whose icon identifies it shows only the count and star rating (a count like '2x' plus one star character per rolled star). Creatures without a specific icon always keep their name."); Creatures.Clear(); CreatureDefinition[] creatureDefinitions = CreatureDefinitions; foreach (CreatureDefinition creatureDefinition in creatureDefinitions) { CreatureConfigEntry creatureConfigEntry = new CreatureConfigEntry { IsMonster = creatureDefinition.IsMonster, Enabled = Bind(config, creatureDefinition.Section, creatureDefinition.DisplayName, creatureDefinition.DefaultEnabled, "Show " + creatureDefinition.DisplayName + ".") }; if (creatureDefinition.Tameable) { creatureConfigEntry.MinStars = Bind(config, creatureDefinition.Section, creatureDefinition.DisplayName + " - Min Stars", 0, "Minimum star level for " + creatureDefinition.DisplayName + " (0 = All, requires the toggle above to also be on).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3)); creatureConfigEntry.Tamed = Bind(config, creatureDefinition.Section, creatureDefinition.DisplayName + " - Tamed or Wild", TameFilter.Both, "Which " + creatureDefinition.DisplayName + " to show: Both, WildOnly (not tamed) or TamedOnly (tamed, including summoned ones). Requires the toggle above to also be on."); } Creatures[creatureDefinition.CanonicalKey] = creatureConfigEntry; } for (int j = 1; j <= 5; j++) { FishQualities[j - 1] = Bind(config, "09b - Creatures (Fish)", $"Fish Quality {j}", defaultValue: true, $"Show fish of quality {j} (1 = common, {5} = rarest). The quality is shown as Q{j} under the fish's pin. Requires the fish's own toggle above to also be on."); } Locations.Clear(); LocationDefinition[] locationDefinitions = LocationDefinitions; foreach (LocationDefinition locationDefinition in locationDefinitions) { if (!locationDefinition.AlwaysEnabled) { Locations[locationDefinition.CanonicalKey] = new LocationConfigEntry { Enabled = Bind(config, locationDefinition.Section, locationDefinition.DisplayName, locationDefinition.DefaultEnabled, "Show " + locationDefinition.DisplayName + ".") }; } } TrackRaspberry = Bind(config, "10 - Resources (Berries)", "Raspberries", defaultValue: true, "Show wild Raspberry bushes."); TrackBlueberry = Bind(config, "10 - Resources (Berries)", "Blueberries", defaultValue: true, "Show wild Blueberry bushes."); TrackCloudberry = Bind(config, "10 - Resources (Berries)", "Cloudberries", defaultValue: true, "Show wild Cloudberry bushes."); TrackRedMushroom = Bind(config, "11 - Resources (Mushrooms)", "Red Mushrooms", defaultValue: true, "Show standard Red Mushrooms."); TrackYellowMushroom = Bind(config, "11 - Resources (Mushrooms)", "Yellow Mushrooms", defaultValue: true, "Show Yellow Cave Mushrooms."); TrackBlueMushroom = Bind(config, "11 - Resources (Mushrooms)", "Blue Mushrooms", defaultValue: true, "Show Blue Mushrooms."); TrackDandelion = Bind(config, "12 - Resources (Plants & Crops)", "Dandelion", defaultValue: true, "Show wild Dandelions."); TrackThistle = Bind(config, "12 - Resources (Plants & Crops)", "Thistle", defaultValue: true, "Show wild Thistle."); TrackCarrotSeed = Bind(config, "12 - Resources (Plants & Crops)", "Carrot Seeds", defaultValue: true, "Show wild Carrots (never player-planted ones - those are a different, Piece-based object)."); TrackTurnipSeed = Bind(config, "12 - Resources (Plants & Crops)", "Turnip Seeds", defaultValue: true, "Show wild Turnips (never player-planted ones - those are a different, Piece-based object)."); TrackOnionSeed = Bind(config, "12 - Resources (Plants & Crops)", "Onion Seeds", defaultValue: true, "Show wild Onions (never player-planted ones - those are a different, Piece-based object)."); TrackBarley = Bind(config, "12 - Resources (Plants & Crops)", "Barley", defaultValue: true, "Show wild Barley (never player-planted ones - those are a different, Piece-based object)."); TrackFlax = Bind(config, "12 - Resources (Plants & Crops)", "Flax", defaultValue: true, "Show wild Flax (never player-planted ones - those are a different, Piece-based object)."); TrackMagecap = Bind(config, "12 - Resources (Plants & Crops)", "Magecap", defaultValue: true, "Show wild Magecap mushrooms (never player-planted ones - those are a different, Piece-based object)."); TrackFlint = Bind(config, "13 - Resources (Ground)", "Flint", defaultValue: true, "Show loose Flint."); TrackStone = Bind(config, "13 - Resources (Ground)", "Stones", defaultValue: false, "Show loose Stones."); TrackWood = Bind(config, "13 - Resources (Ground)", "Wood/Branches", defaultValue: false, "Show loose Wood/Branches."); TrackCopper = Bind(config, "14 - Resources (Ores)", "Copper", defaultValue: true, "Show Copper deposits."); TrackTin = Bind(config, "14 - Resources (Ores)", "Tin", defaultValue: true, "Show Tin deposits."); TrackIron = Bind(config, "14 - Resources (Ores)", "Iron", defaultValue: true, "Show Muddy scrap piles (iron)."); TrackSilver = Bind(config, "14 - Resources (Ores)", "Silver", defaultValue: true, "Show Silver deposits."); TrackObsidian = Bind(config, "14 - Resources (Ores)", "Obsidian", defaultValue: true, "Show Obsidian deposits."); TrackGuck = Bind(config, "14 - Resources (Ores)", "Guck Sack", defaultValue: true, "Show Guck Sacks on Swamp trees."); TrackChests = Bind(config, "15 - Structures (Functional)", "Chests (Above-Ground)", defaultValue: true, "Show natural/world-spawn treasure chests that are not buried (player-built chests are never tracked). Buried chests have their own toggle below."); TrackBuriedChests = Bind(config, "15 - Structures (Functional)", "Buried Chests", defaultValue: true, "Show buried/hidden treasure chests (e.g. the Meadows burial-mound chest, the Ashlands memorial chest)."); TrackBeehives = Bind(config, "15 - Structures (Functional)", "Beehives", defaultValue: true, "Show wild Beehives (player-built beehives are never tracked)."); TrackTrader = Bind(config, "15 - Structures (Functional)", "Bog Witch Camp", defaultValue: true, "Show the Bog Witch's camp. This is a different trader from the Black Forest Trader/Haldor, who is always shown and has no toggle."); TrackGreydwarfNest = Bind(config, "16 - Spawners & Landmarks", "Greydwarf Nest", defaultValue: true, "Show Greydwarf Nests (Black Forest monster spawner)."); TrackBodyPile = Bind(config, "16 - Spawners & Landmarks", "Body Pile", defaultValue: true, "Show Body Piles (Swamp Draugr spawner)."); TrackBonePile = Bind(config, "16 - Spawners & Landmarks", "Bone Pile", defaultValue: true, "Show Bone Piles (Swamp Skeleton spawner)."); TrackAbandonedRuins = Bind(config, "20 - Locations (Ruins & Structures)", "Abandoned Ruins (Unlisted/Modded)", defaultValue: true, "Show ruins not covered by the curated list above (Combat Ruin, Meadows Village 2)."); TrackRunestones = Bind(config, "19 - Locations (Runestones)", "Runestones (Unlisted/Modded)", defaultValue: true, "Show any runestone not covered by the curated list above."); TrackUnlistedDungeons = Bind(config, "18 - Locations (Dungeon Entrances)", "Dungeon Entrances (Unlisted)", defaultValue: true, "Show dungeon-plane entrances not covered by the curated list above (any Teleport-based entrance), detected by component signature rather than name."); } } public class ItemCluster { public string DisplayName; public Sprite Icon; public bool IsPersistent; public string CategoryKey; public string RawName; public float MaxDistance = 1f; public List<TrackedItem> Items = new List<TrackedItem>(); public bool Standalone; public Vector3 GetCentroid() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (Items == null || Items.Count == 0) { return Vector3.zero; } Vector3 val = Vector3.zero; foreach (TrackedItem item in Items) { val += item.Position; } return val / (float)Items.Count; } public string GetLabel() { if (Items.Count <= 1) { return DisplayName; } return $"{Items.Count}x {DisplayName}"; } public string GetLabel(bool hideName) { return BuildLabel(DisplayName, Items.Count, hideName); } public static string BuildLabel(string displayName, int count, bool hideName) { if (string.IsNullOrEmpty(displayName)) { displayName = string.Empty; } if (!hideName) { if (count <= 1) { return displayName; } return $"{count}x {displayName}"; } TrySplitBadgeSuffix(displayName, out var _, out var badge); if (count > 1) { if (badge.Length <= 0) { return $"{count}x"; } return $"{count}x {badge}"; } return badge; } public static string FishQualitySuffix(int quality) { return $" (Q{quality})"; } public static bool TrySplitBadgeSuffix(string displayName, out string baseName, out string badge) { if (TrySplitStarSuffix(displayName, out baseName, out badge)) { return true; } baseName = displayName ?? string.Empty; badge = string.Empty; int num = baseName.LastIndexOf(" (Q", StringComparison.Ordinal); if (num < 0 || baseName[baseName.Length - 1] != ')') { return false; } string text = baseName.Substring(num + 2, baseName.Length - num - 3); if (text.Length < 2) { return false; } for (int i = 1; i < text.Length; i++) { if (!char.IsDigit(text[i])) { return false; } } badge = text; baseName = baseName.Substring(0, num); return true; } public static bool TrySplitStarSuffix(string displayName, out string baseName, out string stars) { baseName = displayName ?? string.Empty; stars = string.Empty; if (baseName.Length < 5 || baseName[baseName.Length - 1] != ')') { return false; } int num = baseName.LastIndexOf(" (", StringComparison.Ordinal); if (num < 0) { return false; } string text = baseName.Substring(num + 2, baseName.Length - num - 3); if (text.Length == 0) { return false; } string text2 = text; for (int i = 0; i < text2.Length; i++) { if (text2[i] != '★') { return false; } } stars = text; baseName = baseName.Substring(0, num); return true; } public static bool ShouldHideCreatureName(string categoryKey, bool hasIcon, bool showCreatureNames) { if (showCreatureNames || !hasIcon || string.IsNullOrEmpty(categoryKey)) { return false; } if (!categoryKey.StartsWith("creature:", StringComparison.Ordinal)) { return false; } return !categoryKey.StartsWith("creature:_", StringComparison.Ordinal); } public string GetClusterKey() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (Items.Count == 0) { return string.Empty; } if (Standalone) { Vector3 position = Items[0].Position; return $"{DisplayName}@{Mathf.RoundToInt(position.x * 10f)}_{Mathf.RoundToInt(position.z * 10f)}"; } Vector3 centroid = GetCentroid(); float num = ((MaxDistance > 0f) ? MaxDistance : 1f); int num2 = Mathf.RoundToInt(centroid.x / num); int num3 = Mathf.RoundToInt(centroid.z / num); return $"{DisplayName}_{num2}_{num3}"; } } public class TrackedItem { public ZDOID Zdoid; public Vector3 Position; public string RawName; public string DisplayName; public Sprite Icon; public bool IsPersistent; public string CategoryKey; public int MissedScans; public float FirstMissTime; } public class TrackedLocation { public string LocationKey; public Vector3 Position; public string RawName; public string DisplayName; public string CategoryKey; } internal static class LegacyPinFiles { internal const string PointsSuffix = ".txt"; internal const string LocationsSuffix = ".locations.txt"; internal const string DismissedSuffix = ".dismissed.txt"; internal const string RespawnSuffix = ".respawn.txt"; internal const int PreVersioningFormat = 0; internal static List<PointRecord> ParsePoints(IList<string> lines, out int formatVersion) { //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) formatVersion = 0; List<PointRecord> list = new List<PointRecord>(); if (lines == null) { return list; } int num = 0; if (lines.Count > 0 && lines[0].StartsWith("#v", StringComparison.Ordinal) && int.TryParse(lines[0].Substring(2), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { formatVersion = result; num = 1; } for (int i = num; i < lines.Count; i++) { string text = lines[i]; if (string.IsNullOrEmpty(text)) { continue; } string[] array = text.Split('|'); if (array.Length == 8 && long.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && uint.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3) && TryParseFloat(array[2], out var value) && TryParseFloat(array[3], out var value2) && TryParseFloat(array[4], out var value3)) { string text2 = Unescape(array[5]); if (!string.IsNullOrEmpty(text2)) { list.Add(new PointRecord { UserId = result2, Id = result3, Position = new Vector3(value, value2, value3), DisplayName = text2, RawName = Unescape(array[6]), CategoryKey = Unescape(array[7]) }); } } } return list; } internal static List<TrackedLocation> ParseLocations(IList<string> lines) { //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) List<TrackedLocation> list = new List<TrackedLocation>(); if (lines == null) { return list; } for (int i = ((lines.Count > 0 && lines[0].StartsWith("#locv", StringComparison.Ordinal)) ? 1 : 0); i < lines.Count; i++) { string text = lines[i]; if (string.IsNullOrEmpty(text)) { continue; } string[] array = text.Split('|'); if (array.Length != 7) { continue; } string text2 = Unescape(array[0]); if (TryParseFloat(array[1], out var value) && TryParseFloat(array[2], out var value2) && TryParseFloat(array[3], out var value3)) { string text3 = Unescape(array[4]); if (!string.IsNullOrEmpty(text2) && !string.IsNullOrEmpty(text3)) { list.Add(new TrackedLocation { LocationKey = text2, Position = new Vector3(value, value2, value3), DisplayName = text3, RawName = Unescape(array[5]), CategoryKey = Unescape(array[6]) }); } } } return list; } private static bool TryParseFloat(string s, out float value) { return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value); } private static string Unescape(string value) { if (!string.IsNullOrEmpty(value)) { return Uri.UnescapeDataString(value); } return string.Empty; } } internal sealed class PointRecord { public string Key; public long UserId; public uint Id; public Vector3 Position; public string DisplayName; public string RawName; public string CategoryKey; } internal struct PointCell { public int X; public int Z; public int Count; } internal sealed class PinDatabase : IDisposable { internal const int SchemaVersion = 3; internal const string PointsFormatKey = "points_format"; private readonly SqliteConnection connection; private SqliteStatement pointsInCell; private const string PointColumns = "key, user_id, zdo_id, x, y, z, display_name, raw_name, category"; private PinDatabase(SqliteConnection connection) { this.connection = connection; } internal static PinDatabase Open(string path) { SqliteConnection sqliteConnection = SqliteConnection.Open(path); try { sqliteConnection.Execute("PRAGMA journal_mode=WAL"); sqliteConnection.Execute("PRAGMA synchronous=NORMAL"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS points (key TEXT PRIMARY KEY, user_id INTEGER NOT NULL, zdo_id INTEGER NOT NULL, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL, display_name TEXT NOT NULL, raw_name TEXT NOT NULL, category TEXT NOT NULL, cell_x INTEGER NOT NULL DEFAULT 0, cell_z INTEGER NOT NULL DEFAULT 0)"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS locations (location_key TEXT PRIMARY KEY, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL, display_name TEXT NOT NULL, raw_name TEXT NOT NULL, category TEXT NOT NULL)"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS dismissed (category TEXT NOT NULL, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL)"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS checked_pins (category TEXT NOT NULL, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL)"); sqliteConnection.Execute("CREATE TABLE IF NOT EXISTS respawn_timers (category TEXT NOT NULL, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL, picked_at REAL NOT NULL, respawn_at REAL NOT NULL)"); PinDatabase pinDatabase = new PinDatabase(sqliteConnection); pinDatabase.Migrate(); sqliteConnection.Execute("CREATE INDEX IF NOT EXISTS points_cell ON points (cell_x, cell_z)"); return pinDatabase; } catch { sqliteConnection.Dispose(); throw; } } private void Migrate() { string meta = GetMeta("schema_version"); if (meta == null) { SetMeta("schema_version", 3.ToString()); } else { if (!int.TryParse(meta, out var version) || version >= 3) { return; } InTransaction(delegate { if (version < 2) { if (!HasColumn("points", "cell_x")) { connection.Execute("ALTER TABLE points ADD COLUMN cell_x INTEGER NOT NULL DEFAULT 0"); } if (!HasColumn("points", "cell_z")) { connection.Execute("ALTER TABLE points ADD COLUMN cell_z INTEGER NOT NULL DEFAULT 0"); } connection.Execute("UPDATE points SET cell_x = " + SqlCellIndex("x") + ", cell_z = " + SqlCellIndex("z")); } SetMeta("schema_version", 3.ToString()); }); } } private static string SqlCellIndex(string column) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; string text = "((" + column + " + " + 32f.ToString("0.0", invariantCulture) + ") / " + 64f.ToString("0.0", invariantCulture) + ")"; return "(CAST(" + text + " AS INTEGER) - (" + text + " < CAST(" + text + " AS INTEGER)))"; } private bool HasColumn(string table, string column) { using (SqliteStatement sqliteStatement = connection.Prepare("PRAGMA table_info(" + table + ")")) { while (sqliteStatement.Step()) { if (sqliteStatement.GetText(1) == column) { return true; } } } return false; } public void Dispose() { pointsInCell?.Dispose(); pointsInCell = null; connection.Dispose(); } internal void InTransaction(Action action) { connection.InTransaction(action); } internal string GetMeta(string key) { using SqliteStatement sqliteStatement = connection.Prepare("SELECT value FROM meta WHERE key = ?"); sqliteStatement.Bind(1, key); return sqliteStatement.Step() ? sqliteStatement.GetText(0) : null; } internal void SetMeta(string key, string value) { using SqliteStatement sqliteStatement = connection.Prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"); sqliteStatement.Bind(1, key).Bind(2, value); sqliteStatement.StepToEnd(); } internal int GetMetaInt(string key, int fallback) { if (!int.TryParse(GetMeta(key), out var result)) { return fallback; } return result; } internal List<PointRecord> LoadPoints() { List<PointRecord> list = new List<PointRecord>(); using SqliteStatement sqliteStatement = connection.Prepare("SELECT key, user_id, zdo_id, x, y, z, display_name, raw_name, category FROM points"); while (sqliteStatement.Step()) { list.Add(ReadPoint(sqliteStatement)); } return list; } internal List<PointCell> LoadPointCells() { List<PointCell> list = new List<PointCell>(); using SqliteStatement sqliteStatement = connection.Prepare("SELECT cell_x, cell_z, COUNT(*) FROM points GROUP BY cell_x, cell_z"); while (sqliteStatement.Step()) { list.Add(new PointCell { X = (int)sqliteStatement.GetInt64(0), Z = (int)sqliteStatement.GetInt64(1), Count = (int)sqliteStatement.GetInt64(2) }); } return list; } internal List<PointRecord> LoadPointsInCell(int cellX, int cellZ) { if (pointsInCell == null) { pointsInCell = connection.Prepare("SELECT key, user_id, zdo_id, x, y, z, display_name, raw_name, category FROM points WHERE cell_x = ? AND cell_z = ?"); } List<PointRecord> list = new List<PointRecord>(); try { pointsInCell.Bind(1, cellX).Bind(2, cellZ); while (pointsInCell.Step()) { list.Add(ReadPoint(pointsInCell)); } return list; } finally { pointsInCell.Reset(); } } private static PointRecord ReadPoint(SqliteStatement s) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) return new PointRecord { Key = s.GetText(0), UserId = s.GetInt64(1), Id = (uint)s.GetInt64(2), Position = new Vector3((float)s.GetDouble(3), (float)s.GetDouble(4), (float)s.GetDouble(5)), DisplayName = s.GetText(6), RawName = s.GetText(7), CategoryKey = s.GetText(8) }; } internal void UpsertPoints(IEnumerable<PointRecord> points) { using SqliteStatement sqliteStatement = connection.Prepare("INSERT OR REPLACE INTO points (key, user_id, zdo_id, x, y, z, display_name, raw_name, category, cell_x, cell_z) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); foreach (PointRecord point in points) { sqliteStatement.Bind(1, point.Key).Bind(2, point.UserId).Bind(3, point.Id) .Bind(4, point.Position.x) .Bind(5, point.Position.y) .Bind(6, point.Position.z) .Bind(7, point.DisplayName) .Bind(8, point.RawName) .Bind(9, point.CategoryKey) .Bind(10, ScanGeometry.GetCellIndex(point.Position.x)) .Bind(11, ScanGeometry.GetCellIndex(point.Position.z)); sqliteStatement.ExecuteAndReset(); } } internal void DeletePoints(IEnumerable<string> keys) { using SqliteStatement sqliteStatement = connection.Prepare("DELETE FROM points WHERE key = ?"); foreach (string key in keys) { sqliteStatement.Bind(1, key); sqliteStatement.ExecuteAndReset(); } } internal void ReplaceAllPoints(IEnumerable<PointRecord> points) { connection.Execute("DELETE FROM points"); UpsertPoints(points); } internal List<TrackedLocation> LoadLocations() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) List<TrackedLocation> list = new List<TrackedLocation>(); using SqliteStatement sqliteStatement = connection.Prepare("SELECT location_key, x, y, z, display_name, raw_name, category FROM locations"); while (sqliteStatement.Step()) { list.Add(new TrackedLocation { LocationKey = sqliteStatement.GetText(0), Position = new Vector3((float)sqliteStatement.GetDouble(1), (float)sqliteStatement.GetDouble(2), (float)sqliteStatement.GetDouble(3)), DisplayName = sqliteStatement.GetText(4), RawName = sqliteStatement.GetText(5), CategoryKey = sqliteStatement.GetText(6) }); } return list; } internal void UpsertLocations(IEnumerable<TrackedLocation> locations) { using SqliteStatement sqliteStatement = connection.Prepare("INSERT OR REPLACE INTO locations (location_key, x, y, z, display_name, raw_name, category) VALUES (?, ?, ?, ?, ?, ?, ?)"); foreach (TrackedLocation location in locations) { sqliteStatement.Bind(1, location.LocationKey).Bind(2, location.Position.x).Bind(3, location.Position.y) .Bind(4, location.Position.z) .Bind(5, location.DisplayName) .Bind(6, location.RawName) .Bind(7, location.CategoryKey); sqliteStatement.ExecuteAndReset(); } } internal List<KeyValuePair<string, Vector3>> LoadDismissed() { return LoadPositions("dismissed"); } internal void ReplaceDismissed(IEnumerable<KeyValuePair<string, Vector3>> entries) { ReplacePositions("dismissed", entries); } internal List<KeyValuePair<string, Vector3>> LoadChecked() { return LoadPositions("checked_pins"); } internal void ReplaceChecked(IEnumerable<KeyValuePair<string, Vector3>> entries) { ReplacePositions("checked_pins", entries); } private List<KeyValuePair<string, Vector3>> LoadPositions(string table) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<string, Vector3>> list = new List<KeyValuePair<string, Vector3>>(); using SqliteStatement sqliteStatement = connection.Prepare("SELECT category, x, y, z FROM " + table); while (sqliteStatement.Step()) { list.Add(new KeyValuePair<string, Vector3>(sqliteStatement.GetText(0), new Vector3((float)sqliteStatement.GetDouble(1), (float)sqliteStatement.GetDouble(2), (float)sqliteStatement.GetDouble(3)))); } return list; } private void ReplacePositions(string table, IEnumerable<KeyValuePair<string, Vector3>> entries) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) connection.Execute("DELETE FROM " + table); using SqliteStatement sqliteStatement = connection.Prepare("INSERT INTO " + table + " (category, x, y, z) VALUES (?, ?, ?, ?)"); foreach (KeyValuePair<string, Vector3> entry in entries) { sqliteStatement.Bind(1, entry.Key).Bind(2, entry.Value.x).Bind(3, entry.Value.y) .Bind(4, entry.Value.z); sqliteStatement.ExecuteAndReset(); } } internal List<RespawnTimerStore.Entry> LoadRespawnTimers() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) List<RespawnTimerStore.Entry> list = new List<RespawnTimerStore.Entry>(); using SqliteStatement sqliteStatement = connection.Prepare("SELECT category, x, y, z, picked_at, respawn_at FROM respawn_timers"); while (sqliteStatement.Step()) { list.Add(new RespawnTimerStore.Entry { CategoryKey = sqliteStatement.GetText(0), Position = new Vector3((float)sqliteStatement.GetDouble(1), (float)sqliteStatement.GetDouble(2), (float)sqliteStatement.GetDouble(3)), PickedAt = sqliteStatement.GetDouble(4), RespawnAt = sqliteStatement.GetDouble(5) }); } return list; } internal void ReplaceRespawnTimers(IEnumerable<RespawnTimerStore.Entry> entries) { connection.Execute("DELETE FROM respawn_timers"); using SqliteStatement sqliteStatement = connection.Prepare("INSERT INTO respawn_timers (category, x, y, z, picked_at, respawn_at) VALUES (?, ?, ?, ?, ?, ?)"); foreach (RespawnTimerStore.Entry entry in entries) { sqliteStatement.Bind(1, entry.CategoryKey).Bind(2, entry.Position.x).Bind(3, entry.Position.y) .Bind(4, entry.Position.z) .Bind(5, entry.PickedAt) .Bind(6, entry.RespawnAt); sqliteStatement.ExecuteAndReset(); } } } internal sealed class SqliteConnection : IDisposable { private IntPtr db; internal static string LibraryVersion => ReadUtf8(SqliteNative.sqlite3_libversion()); private SqliteConnection(IntPtr db) { this.db = db; } internal static SqliteConnection Open(string path) { IntPtr intPtr; int num = SqliteNative.sqlite3_open_v2(Utf8(path), out intPtr, 6, IntPtr.Zero); if (num != 0) { string text = ((intPtr != IntPtr.Zero) ? ReadUtf8(SqliteNative.sqlite3_errmsg(intPtr)) : $"error {num}"); if (intPtr != IntPtr.Zero) { SqliteNative.sqlite3_close_v2(intPtr); } throw new InvalidOperationException("Couldn't open SQLite database '" + path + "': " + text); } return new SqliteConnection(intPtr); } internal void Execute(string sql) { using SqliteStatement sqliteStatement = Prepare(sql); sqliteStatement.StepToEnd(); } internal SqliteStatement Prepare(string sql) { byte[] array = Utf8(sql); if (SqliteNative.sqlite3_prepare_v2(db, array, array.Length, out var stmt, IntPtr.Zero) != 0) { throw Error("prepare failed for \"" + sql + "\""); } return new SqliteStatement(this, stmt); } internal void InTransaction(Action action) { Execute("BEGIN IMMEDIATE"); try { action(); Execute("COMMIT"); } catch { try { Execute("ROLLBACK"); } catch { } throw; } } internal InvalidOperationException Error(string context) { return new InvalidOperationException("SQLite " + context + ": " + ReadUtf8(SqliteNative.sqlite3_errmsg(db))); } public void Dispose() { if (!(db == IntPtr.Zero)) { SqliteNative.sqlite3_close_v2(db); db = IntPtr.Zero; } } internal static byte[] Utf8(string value) { byte[] array = new byte[Encoding.UTF8.GetByteCount(value) + 1]; Encoding.UTF8.GetBytes(value, 0, value.Length, array, 0); return array; } internal static string ReadUtf8(IntPtr ptr, int length = -1) { if (ptr == IntPtr.Zero) { return null; } if (length < 0) { length = 0; while (Marshal.ReadByte(ptr, length) != 0) { length++; } } byte[] array = new byte[length]; Marshal.Copy(ptr, array, 0, length); return Encoding.UTF8.GetString(array); } } internal sealed class SqliteStatement : IDisposable { private readonly SqliteConnection connection; private IntPtr stmt; internal SqliteStatement(SqliteConnection connection, IntPtr stmt) { this.connection = connection; this.stmt = stmt; } internal SqliteStatement Bind(int index, long value) { return Check(SqliteNative.sqlite3_bind_int64(stmt, index, value)); } internal SqliteStatement Bind(int index, double value) { return Check(SqliteNative.sqlite3_bind_double(stmt, index, value)); } internal SqliteStatement Bind(int index, string value) { byte[] bytes = Encoding.UTF8.GetBytes(value ?? string.Empty); return Check(SqliteNative.sqlite3_bind_text(stmt, index, bytes, bytes.Length, SqliteNative.SQLITE_TRANSIENT)); } internal bool Step() { return SqliteNative.sqlite3_step(stmt) switch { 100 => true, 101 => false, _ => throw connection.Error("step failed"), }; } internal void StepToEnd() { while (Step()) { } } internal void ExecuteAndReset() { StepToEnd(); Reset(); } internal void Reset() { SqliteNative.sqlite3_reset(stmt); SqliteNative.sqlite3_clear_bindings(stmt); } internal long GetInt64(int column) { return SqliteNative.sqlite3_column_int64(stmt, column); } internal double GetDouble(int column) { return SqliteNative.sqlite3_column_double(stmt, column); } internal string GetText(int column) { if (SqliteNative.sqlite3_column_type(stmt, column) == 5) { return string.Empty; } return SqliteConnection.ReadUtf8(SqliteNative.sqlite3_column_text(stmt, column), SqliteNative.sqlite3_column_bytes(stmt, column)) ?? string.Empty; } public void Dispose() { if (!(stmt == IntPtr.Zero)) { SqliteNative.sqlite3_finalize(stmt); stmt = IntPtr.Zero; } } private SqliteStatement Check(int rc) { if (rc != 0) { throw connection.Error("bind failed"); } return this; } } internal static class SqliteNative { private const string Lib = "e_sqlite3"; internal const int SQLITE_OK = 0; internal const int SQLITE_ROW = 100; internal const int SQLITE_DONE = 101; internal const int SQLITE_NULL = 5; internal const int SQLITE_OPEN_READWRITE = 2; internal const int SQLITE_OPEN_CREATE = 4; internal static readonly IntPtr SQLITE_TRANSIENT = new IntPtr(-1); private static bool preloadAttempted; private const int RTLD_NOW = 2; private const int RTLD_GLOBAL = 256; [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr sqlite3_libversion(); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_open_v2(byte[] filename, out IntPtr db, int flags, IntPtr vfs); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_close_v2(IntPtr db); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr sqlite3_errmsg(IntPtr db); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_prepare_v2(IntPtr db, byte[] sql, int nByte, out IntPtr stmt, IntPtr tail); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_step(IntPtr stmt); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_reset(IntPtr stmt); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_clear_bindings(IntPtr stmt); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_finalize(IntPtr stmt); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_bind_int64(IntPtr stmt, int index, long value); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_bind_double(IntPtr stmt, int index, double value); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_bind_text(IntPtr stmt, int index, byte[] value, int nByte, IntPtr destructor); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern long sqlite3_column_int64(IntPtr stmt, int column); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern double sqlite3_column_double(IntPtr stmt, int column); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr sqlite3_column_text(IntPtr stmt, int column); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_column_bytes(IntPtr stmt, int column); [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)] internal static extern int sqlite3_column_type(IntPtr stmt, int column); internal static void Preload(string directory) { if (preloadAttempted) { return; } preloadAttempted = true; if (string.IsNullOrEmpty(directory)) { return; } if (Environment.OSVersion.Platform == PlatformID.Win32NT) { string path = Path.Combine(directory, "e_sqlite3.dll"); if (File.Exists(path)) { LoadLibraryW(path); } return; } string path2 = Path.Combine(directory, "libe_sqlite3.so"); if (!File.Exists(path2)) { return; } try { dlopen2(path2, 258); } catch (DllNotFoundException) { dlopen(path2, 258); } } [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibraryW(string path); [DllImport("libdl.so.2", EntryPoint = "dlopen")] private static extern IntPtr dlopen2(string path, int flags); [DllImport("libdl")] private static extern IntPtr dlopen(string path, int flags); } internal static class DepletionPatches { private static bool loggedFailure; internal static void Apply(Harmony harmony) { Patch(harmony, typeof(Destructible), "Damage", "DestructibleDamagePrefix", isPrefix: true); Patch(harmony, typeof(MineRock), "Damage", "MineRockDamagePrefix", isPrefix: true); Patch(harmony, typeof(MineRock5), "Damage", "MineRock5DamagePrefix", isPrefix: true); Patch(harmony, typeof(Pickable), "Interact", "PickableInteractPostfix", isPrefix: false); Patch(harmony, typeof(Pickable), "SetPicked", "PickableSetPickedPostfix", isPrefix: false); } private static void Patch(Harmony harmony, Type target, string method, string patchMethod, bool isPrefix) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(target, method, (Type[])null, (Type[])null); HarmonyMethod val = new HarmonyMethod(typeof(DepletionPatches), patchMethod, (Type[])null); if (isPrefix) { harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception arg) { Debug.LogError((object)$"[ValheimRadar] Failed to patch {target.Name}.{method} (mined resources will only be removed by rescans): {arg}"); } } private static void DestructibleDamagePrefix(Destructible __instance, HitData hit) { Guard(delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (IsEffectiveLocalHit(hit, __instance.m_damages, __instance.m_minToolTier)) { Report((Component)(object)__instance); } }); } private static void MineRockDamagePrefix(MineRock __instance, HitData hit) { Guard(delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (IsEffectiveLocalHit(hit, __instance.m_damageModifiers, __instance.m_minToolTier)) { Report((Component)(object)__instance); } }); } private static void MineRock5DamagePrefix(MineRock5 __instance, HitData hit) { Guard(delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (IsEffectiveLocalHit(hit, __instance.m_damageModifiers, __instance.m_minToolTier)) { Report((Component)(object)__instance); } }); } private static void PickableInteractPostfix(Pickable __instance, Humanoid character) { Guard(delegate { if (!((Object)(object)character == (Object)null) && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && !(__instance.m_respawnTimeMinutes > 0f)) { Report((Component)(object)__instance); } }); } private static void PickableSetPickedPostfix(Pickable __instance, bool picked) { Guard(delegate { if (!(__instance.m_respawnTimeMinutes <= 0f)) { if (picked) { ReportPicked(__instance); } else { ReportRegrown(__instance); } } }); } private static bool IsEffectiveLocalHit(HitData hit, DamageModifiers modifiers, int minToolTier) { //IL_0013: 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_003e: Unknown result type (might be due to invalid IL or missing references) if (hit == null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } if (hit.m_attacker != ((Character)Player.m_localPlayer).GetZDOID()) { return false; } if (!hit.CheckToolTier(minToolTier, false)) { return false; } HitData obj = hit.Clone(); DamageModifier val = default(DamageModifier); obj.ApplyResistance(modifiers, ref val); return obj.GetTotalDamage() > 0f; } private static void Report(Component component) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (RadarConfig.RemoveDepletedResources != null && !RadarConfig.RemoveDepletedResources.Value) { return; } ZNetView componentInParent = component.GetComponentInParent<ZNetView>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid()) { return; } GameObject gameObject = ((Component)componentInParent).gameObject; string rawName = ScanGeometry.GetRawName(gameObject); if (!ObjectEvaluator.TryGetDepletableCategory(gameObject, rawName, out var categoryKey)) { if (!ScanFilters.IsDebrisName(rawName)) { return; } categoryKey = null; } PinManager.MarkDepletedAt(categoryKey, gameObject.transform.position); } private static void ReportPicked(Pickable pickable) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((RadarConfig.HidePickedUntilRespawn == null || RadarConfig.HidePickedUntilRespawn.Value) && !((Object)(object)ZNet.instance == (Object)null) && TryGetRespawningRoot(pickable, out var root, out var categoryKey)) { double timeSeconds = ZNet.instance.GetTimeSeconds(); PinManager.MarkPickedAt(categoryKey, root.transform.position, timeSeconds, RespawnTimerStore.GetRespawnAt(timeSeconds, pickable.m_respawnTimeMinutes)); } } private static void ReportRegrown(Pickable pickable) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (TryGetRespawningRoot(pickable, out var root, out var categoryKey)) { PinManager.MarkRegrownAt(categoryKey, root.transform.position); } } private static bool TryGetRespawningRoot(Pickable pickable, out GameObject root, out string categoryKey) { root = null; categoryKey = null; ZNetView componentInParent = ((Component)pickable).GetComponentInParent<ZNetView>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid()) { return false; } root = ((Component)componentInParent).gameObject; return ObjectEvaluator.TryGetRespawningCategory(root, ScanGeometry.GetRawName(root), out categoryKey); } private static void Guard(Action action) { try { action(); } catch (Exception arg) { if (!loggedFailure) { loggedFailure = true; Debug.LogError((object)$"[ValheimRadar] Failed to process a mined/picked resource: {arg}"); } } } } internal static class DepletionRules { internal const float PresenceMatchRadius = 0.5f; internal const float HitMatchRadius = 1f; internal const int MissesBeforeRemoval = 2; internal const float MinMissSpanSeconds = 10f; internal const float VerticalMargin = 5f; internal static bool IsWithinVerifiedHeight(float y, float minY, float maxY) { if (y >= minY + 5f) { return y <= maxY - 5f; } return false; } internal static bool IsPresent(string categoryKey, Vector3 position, IList<TrackedItem> found) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) foreach (TrackedItem item in found) { if (item.CategoryKey == categoryKey && DistanceXZ(item.Position, position) <= 0.5f) { return true; } } return false; } internal static void RecordObservation(TrackedItem point, bool present, float now) { if (present) { point.MissedScans = 0; return; } if (point.MissedScans == 0) { point.FirstMissTime = now; } point.MissedScans++; } internal static bool ShouldRemove(TrackedItem point, float now) { if (point.MissedScans >= 2) { return now - point.FirstMissTime >= 10f; } return false; } internal static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } } public sealed class DismissedPinStore { public const float MatchRadius = 0.5f; private const string HeaderLine = "#dismissedv1"; private readonly Dictionary<string, List<Vector3>> byCategory = new Dictionary<string, List<Vector3>>(); public int Count { get; private set; } public IEnumerable<KeyValuePair<string, Vector3>> Entries { get { foreach (KeyValuePair<string, List<Vector3>> kvp in byCategory) { foreach (Vector3 item in kvp.Value) { yield return new KeyValuePair<string, Vector3>(kvp.Key, item); } } } } public void Add(string categoryKey, Vector3 position) { //IL_000c: 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 (categoryKey == null) { categoryKey = string.Empty; } if (!Contains(categoryKey, position)) { if (!byCategory.TryGetValue(categoryKey, out var value)) { value = new List<Vector3>(); byCategory[categoryKey] = value; } value.Add(position); Count++; } } public bool Contains(string categoryKey, Vector3 position) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (Count == 0) { return false; } if (categoryKey == null) { categoryKey = string.Empty; } if (!byCategory.TryGetValue(categoryKey, out var value)) { return false; } foreach (Vector3 item in value) { if (DistanceXZ(item, position) <= 0.5f) { return true; } } return false; } public bool Remove(string categoryKey, Vector3 position) { //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) if (Count == 0) { return false; } if (categoryKey == null) { categoryKey = string.Empty; } if (!byCategory.TryGetValue(categoryKey, out var value)) { return false; } int num = value.RemoveAll((Vector3 dismissed) => DistanceXZ(dismissed, position) <= 0.5f); if (num == 0) { return false; } Count -= num; if (value.Count == 0) { byCategory.Remove(categoryKey); } return true; } public void Clear() { byCategory.Clear(); Count = 0; } public List<string> Serialize() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string> { "#dismissedv1" }; foreach (KeyValuePair<string, List<Vector3>> item in byCategory) { foreach (Vector3 item2 in item.Value) { list.Add(string.Join("|", Uri.EscapeDataString(item.Key), item2.x.ToString(CultureInfo.InvariantCulture), item2.y.ToString(CultureInfo.InvariantCulture), item2.z.ToString(CultureInfo.InvariantCulture))); } } return list; } public void Load(IEnumerable<string> lines) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) Clear(); if (lines == null) { return; } foreach (string line in lines) { if (!string.IsNullOrEmpty(line) && line[0] != '#') { string[] array = line.Split('|'); if (array.Length == 4 && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { Add(Uri.UnescapeDataString(array[0]), new Vector3(result, result2, result3)); } } } } private static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } } public static class PinDismissal { public static int IndexOfClosest(IList<Vector3> positions, Vector3 target, float radius) { //IL_000e: 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_0021: 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) int result = -1; float num = float.MaxValue; for (int i = 0; i < positions.Count; i++) { float num2 = positions[i].x - target.x; float num3 = positions[i].z - target.z; float num4 = Mathf.Sqrt(num2 * num2 + num3 * num3); if (num4 < radius && num4 < num) { result = i; num = num4; } } return result; } public static bool IsDismissible(bool isPersistent, bool isCurrentlyShown) { return isPersistent && isCurrentlyShown; } } public static class IconLoader { private static readonly Dictionary<string, Sprite> spriteCache = new Dictionary<string, Sprite>(); public static Sprite LoadPng(string filePath) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (spriteCache.TryGetValue(filePath, out var value)) { return value; } if (!File.Exists(filePath)) { spriteCache[filePath] = null; return null; } Sprite val = null; try { Texture2D val2 = AssetUtils.LoadTexture(filePath, false); if ((Object)(object)val2 != (Object)null) { val = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f), 100f); ((Object)val).name = Path.GetFileNameWithoutExtension(filePath); } } catch (Exception ex) { Debug.LogError((object)("[ValheimRadar] Failed to load custom pin PNG '" + filePath + "': " + ex.Message)); } spriteCache[filePath] = val; return val; } } public static class MinimapMarkerOrder { private const int MaxMarkerBranchTransforms = 8; public static void Apply(Minimap minimap) { if ((Object)(object)minimap == (Object)null) { return; } try { Raise(minimap.m_smallMarker, minimap.m_pinRootSmall, minimap.m_pinNameRootSmall); Raise(minimap.m_smallShipMarker, minimap.m_pinRootSmall, minimap.m_pinNameRootSmall); Raise(minimap.m_largeMarker, minimap.m_pinRootLarge, minimap.m_pinNameRootLarge); Raise(minimap.m_largeShipMarker, minimap.m_pinRootLarge, minimap.m_pinNameRootLarge); } catch (Exception ex) { Debug.LogWarning((object)("[ValheimRadar] marker-order failed, leaving vanilla ordering: " + ex.Message)); } } public static int CompareDrawOrder(IList<int> a, IList<int> b) { int num = Math.Min(a.Count, b.Count); for (int i = 0; i < num; i++) { if (a[i] != b[i]) { if (a[i] <= b[i]) { return -1; } return 1; } } return a.Count.CompareTo(b.Count); } private static void Raise(RectTransform marker, params RectTransform[] references) { if ((Object)(object)marker == (Object)null) { return; } List<Transform> list = new List<Transform>(); foreach (RectTransform val in references) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Transform)marker).root != (Object)(object)((Transform)val).root) && CompareDrawOrder(SiblingPath((Transform)(object)marker), SiblingPath((Transform)(object)val)) <= 0) { list.Add((Transform)(object)val); } } if (list.Count == 0) { RadarLog.Diag("[ValheimRadar] marker-order marker=" + PathOf((Transform)(object)marker) + " already above pins"); } else if (TryReorderBranch((Transform)(object)marker, list)) { RadarLog.Diag("[ValheimRadar] marker-order marker=" + PathOf((Transform)(object)marker) + " raised by sibling order"); } else { RadarLog.Diag("[ValheimRadar] marker-order left vanilla ordering, could not raise marker=" + PathOf((Transform)(object)marker) + " pins=" + PathOf(list[0])); } } private static bool TryReorderBranch(Transform marker, List<Transform> above) { foreach (Transform item in above) { Transform val = CommonAncestor(marker, item); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)marker || (Object)(object)val == (Object)(object)item) { return false; } Transform val2 = marker; while ((Object)(object)val2.parent != (Object)(object)val) { val2 = val2.parent; } if (((Component)val2).GetComponentsInChildren<Transform>(true).Length > 8) { return false; } val2.SetAsLastSibling(); } foreach (Transform item2 in above) { if (CompareDrawOrder(SiblingPath(marker), SiblingPath(item2)) <= 0) { return false; } } return true; } private static Transform CommonAncestor(Transform a, Transform b) { Transform val = a; while ((Object)(object)val != (Object)null) { Transform val2 = b; while ((Object)(object)val2 != (Object)null) { if ((Object)(object)val == (Object)(object)val2) { return val; } val2 = val2.parent; } val = val.parent; } return null; } private static List<int> SiblingPath(Transform t) { List<int> list = new List<int>(); while ((Object)(object)t != (Object)null) { list.Add(t.GetSiblingIndex()); t = t.parent; } list.Reverse(); return list; } private static string PathOf(Transform t) { StringBuilder stringBuilder = new StringBuilder(((Object)(object)t != (Object)null) ? ((Object)t).name : "<null>"); Transform val = (((Object)(object)t != (Object)null) ? t.parent : null); while ((Object)(object)val != (Object)null) { stringBuilder.Insert(0, ((Object)val).name + "/"); val = val.parent; } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(Minimap))] internal static class MinimapPatches { private static readonly MethodInfo ScreenToWorldPoint = AccessTools.Method(typeof(Minimap), "ScreenToWorldPoint", (Type[])null, (Type[])null); private static readonly MethodInfo GetClosestPin = AccessTools.Method(typeof(Minimap), "GetClosestPin", (Type[])null, (Type[])null); private static readonly MethodInfo PinInteractRadius = AccessTools.PropertyGetter(typeof(Minimap), "PinInteractRadius"); [HarmonyPostfix] [HarmonyPatch("RemovePin", new Type[] { typeof(Vector3), typeof(float) })] private static void RemovePinPostfix(Minimap __instance, Vector3 pos, float radius, ref bool __result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (__result) { return; } try { __result = PinManager.TryDismissPinAt(__instance, pos, radius); } catch (Exception arg) { Debug.LogError((object)$"[ValheimRadar] Failed to dismiss pin: {arg}"); } } [HarmonyPostfix] [HarmonyPatch("OnMapLeftClick")] private static void OnMapLeftClickPostfix(Minimap __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) try { Vector3 val = (Vector3)ScreenToWorldPoint.Invoke(__instance, new object[1] { ZInput.pointerPosition }); float num = (float)PinInteractRadius.Invoke(__instance, null); if (GetClosestPin.Invoke(__instance, new object[3] { val, num, true }) == null) { PinManager.TryToggleCheckedAt(__instance, val, num); } } catch (Exception arg) { Debug.LogError((object)$"[ValheimRadar] Failed to cross out pin: {arg}"); } } } internal static class MinimapPinBulk { private const int BulkThreshold = 32; private static readonly FieldRef<Minimap, List<PinData>> pinsRef = TryFieldRef<List<PinData>>("m_pins"); private static readonly FieldRef<Minimap, bool> pinUpdateRequiredRef = TryFieldRef<bool>("m_pinUpdateRequired"); private static readonly Action<Minimap, PinData> destroyPinMarker = TryDestroyPinMarker(); private static bool bulkDisabled = pinsRef == null || pinUpdateRequiredRef == null || destroyPinMarker == null; internal static void RemovePins(Minimap minimap, List<PinData> pins) { if ((Object)(object)minimap == (Object)null || pins.Count == 0) { return; } if (!bulkDisabled && pins.Count >= 32) { try { foreach (PinData pin in pins) { destroyPinMarker(minimap, pin); } HashSet<PinData> hashSet = new HashSet<PinData>(pins); pinsRef.Invoke(minimap).RemoveAll(hashSet.Contains); pinUpdateRequiredRef.Invoke(minimap) = true; return; } catch (Exception ex) { bulkDisabled = true; Debug.LogWarning((object