using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JoeyBadManners.Wayfinder.Core; using JoeyBadManners.Wayfinder.Discovery; using JoeyBadManners.Wayfinder.HUD; using JoeyBadManners.Wayfinder.Icons; using JoeyBadManners.Wayfinder.Management; using JoeyBadManners.Wayfinder.Map; using JoeyBadManners.Wayfinder.Patches; using JoeyBadManners.Wayfinder.Pins; using JoeyBadManners.Wayfinder.Tracking; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace JoeyBadManners.Wayfinder { [BepInPlugin("com.joeybadmanners.wayfinder", "Joey's Wayfinder", "1.0.0")] public sealed class WayfinderPlugin : BaseUnityPlugin { public const string PluginGuid = "com.joeybadmanners.wayfinder"; public const string PluginName = "Joey's Wayfinder"; public const string PluginVersion = "1.0.0"; private const float DiscoverySliceSpacing = 0.005f; private const double SlowDiscoveryWarningMs = 4.0; private long _loadedWorldUid = long.MinValue; private float _nextBootstrapCheck; private float _nextDiscoverySliceTime; private int _discoverySlicePhase; internal static WayfinderPlugin Instance { get; private set; } internal static Harmony Harmony { get; private set; } internal static WayfinderConfig Settings { get; private set; } internal static RuntimeIconRegistry Icons { get; private set; } internal static WayfinderPinDatabase Pins { get; private set; } internal static WayfinderPinManagementService PinManager { get; private set; } internal static WayfinderPinManagerUI PinManagerUI { get; private set; } internal static WayfinderMapController Map { get; private set; } internal static ResourceDiscovery Discovery { get; private set; } internal static AreaScanner Scanner { get; private set; } internal static StructureDiscovery Structures { get; private set; } internal static TraderDiscovery Traders { get; private set; } internal static SpawnerDiscovery Spawners { get; private set; } internal static RunestoneDiscovery Runestones { get; private set; } internal static PortalDiscovery Portals { get; private set; } internal static VehicleDiscovery Vehicles { get; private set; } internal static EnemySightingDiscovery Sightings { get; private set; } internal static OreDiagnosticScanner OreDiagnostics { get; private set; } internal static DiscoveryArtConsistency ArtConsistency { get; private set; } internal static TrackedBeaconController TrackingBeacons { get; private set; } internal static WayfinderCompassHUD CompassHUD { get; private set; } internal static GravestoneIntegration Gravestones { get; private set; } private void Awake() { //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Expected O, but got Unknown Instance = this; Settings = new WayfinderConfig(((BaseUnityPlugin)this).Config); Icons = new RuntimeIconRegistry(((BaseUnityPlugin)this).Logger, Settings); Pins = new WayfinderPinDatabase(((BaseUnityPlugin)this).Logger, Settings); PinManager = new WayfinderPinManagementService(Settings, Pins); Map = new WayfinderMapController(((BaseUnityPlugin)this).Logger, Settings, Icons, Pins, PinManager); PinManagerUI = new WayfinderPinManagerUI(((BaseUnityPlugin)this).Logger, Settings, PinManager, Icons); Discovery = new ResourceDiscovery(((BaseUnityPlugin)this).Logger, Settings, Icons, Pins); Scanner = new AreaScanner(((BaseUnityPlugin)this).Logger, Settings, Discovery); Structures = new StructureDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins, Icons); Traders = new TraderDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins); Spawners = new SpawnerDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins, Icons); Runestones = new RunestoneDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins); Portals = new PortalDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins); Vehicles = new VehicleDiscovery(((BaseUnityPlugin)this).Logger, Settings, Pins); Sightings = new EnemySightingDiscovery(((BaseUnityPlugin)this).Logger, Settings, Icons, Pins); OreDiagnostics = new OreDiagnosticScanner(((BaseUnityPlugin)this).Logger, Settings); ArtConsistency = new DiscoveryArtConsistency(((BaseUnityPlugin)this).Logger, Settings, Icons, Pins); TrackingBeacons = new TrackedBeaconController(((BaseUnityPlugin)this).Logger, Settings, PinManager); CompassHUD = new WayfinderCompassHUD(((BaseUnityPlugin)this).Logger, Settings, PinManager, Icons); Gravestones = new GravestoneIntegration(((BaseUnityPlugin)this).Logger, Settings, Icons); Harmony = new Harmony("com.joeybadmanners.wayfinder"); Harmony.PatchAll(typeof(WayfinderPlugin).Assembly); int num = WayfinderGameplayInputCaptureInstaller.Install(Harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Joey's Wayfinder 1.0.0 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Client-side Wayfinder systems initialized. No server installation is required. Runtime-safe input hooks installed: " + num + ".")); } private void Update() { if (PinManagerUI != null) { PinManagerUI.TickInput(); } if (TrackingBeacons != null) { TrackingBeacons.Tick(); } if (Vehicles != null) { Vehicles.Tick(); } if (Gravestones != null) { Gravestones.Tick(); } if (Map != null) { Map.RefreshDynamicVehiclePositions(); Map.Tick(); } TickDiscoveryWorkSlice(); if (Time.unscaledTime < _nextBootstrapCheck) { return; } _nextBootstrapCheck = Time.unscaledTime + 1f; try { BootstrapWorldState(); BootstrapIconRegistry(); Discovery.RepairKnownResourceIcons(); Discovery.ApplyLiveFilterCleanup(); if (ArtConsistency != null) { ArtConsistency.Tick(); } Portals.Tick(); OreDiagnostics.Tick(); Pins.Tick(); } catch (Exception ex) { if (Settings.DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Bootstrap check failed: " + ex)); } } } private void TickDiscoveryWorkSlice() { if (Time.unscaledTime < _nextDiscoverySliceTime) { return; } _nextDiscoverySliceTime = Time.unscaledTime + 0.005f; double previousFrameMs = (double)Time.unscaledDeltaTime * 1000.0; double adaptiveDiscoveryBudgetMs = GetAdaptiveDiscoveryBudgetMs(previousFrameMs); if (adaptiveDiscoveryBudgetMs <= 0.0) { return; } string text = string.Empty; bool flag = false; long timestamp = Stopwatch.GetTimestamp(); try { switch (_discoverySlicePhase) { case 0: text = "AreaScanner"; if (Scanner != null) { flag = Scanner.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; case 1: text = "EnemySightings"; if (Sightings != null) { flag = Sightings.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; case 2: text = "Structures"; if (Structures != null) { flag = Structures.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; case 3: text = "Spawners"; if (Spawners != null) { flag = Spawners.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; case 4: text = "Runestones"; if (Runestones != null) { flag = Runestones.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; default: text = "Traders"; if (Traders != null) { flag = Traders.TickBudgeted(adaptiveDiscoveryBudgetMs); } break; } } catch (Exception ex) { if (Settings != null && Settings.DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Discovery slice " + text + " failed: " + ex)); } } finally { _discoverySlicePhase = (_discoverySlicePhase + 1) % 6; } if (Settings == null || !Settings.DebugLogging.Value) { return; } double num = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency; if (num >= 4.0) { string text2 = string.Empty; if (text == "AreaScanner" && Scanner != null) { text2 = Scanner.GetPerfDetail(); } ((BaseUnityPlugin)this).Logger.LogWarning((object)("JW PERF: " + text + " slice took " + num.ToString("0.00") + " ms (budget=" + adaptiveDiscoveryBudgetMs.ToString("0.00") + "ms, pending=" + flag + ", prevFrame=" + previousFrameMs.ToString("0.00") + "ms)." + text2)); } } private static double GetAdaptiveDiscoveryBudgetMs(double previousFrameMs) { if (previousFrameMs >= 40.0) { return 0.0; } if (previousFrameMs >= 25.0) { return 0.75; } if (previousFrameMs >= 18.0) { return 1.0; } if (previousFrameMs >= 14.0) { return 1.4; } if (previousFrameMs >= 10.0) { return 2.0; } return 2.75; } private void BootstrapWorldState() { if ((Object)(object)ZNet.instance == (Object)null) { CloseWorldSessionIfNeeded(); return; } long num = 0L; try { num = ZNet.instance.GetWorldUID(); } catch { CloseWorldSessionIfNeeded(); return; } if (num == 0) { CloseWorldSessionIfNeeded(); } else if ((num != _loadedWorldUid || Pins.WorldUid != num) && Pins.LoadWorld(num, ZNet.instance.GetWorldName())) { _loadedWorldUid = num; LiveDiscoveryRegistry.ResetForWorld(num); PhysicalSpawnerIndex.Reset(); LiveDiscoveryRegistry.SyncFromLoadedLocations(); if (Vehicles != null) { Vehicles.ResetSession(); } if (Portals != null) { Portals.ResetSession(); } if (Traders != null) { Traders.ResetSession(); } if (ArtConsistency != null) { ArtConsistency.ResetSession(); } if (Gravestones != null) { Gravestones.ResetSession(); } Map.ForceRefresh(); } } private void CloseWorldSessionIfNeeded() { if ((_loadedWorldUid != long.MinValue || Pins.WorldUid != 0) && Pins.UnloadWorld()) { if (TrackingBeacons != null) { TrackingBeacons.Clear(); } if (Vehicles != null) { Vehicles.ClearSession(); } if (Portals != null) { Portals.ClearSession(); } if (Traders != null) { Traders.ResetSession(); } if (ArtConsistency != null) { ArtConsistency.ResetSession(); } if (Gravestones != null) { Gravestones.ClearSession(); } LiveDiscoveryRegistry.ResetForWorld(0L); PhysicalSpawnerIndex.Reset(); _loadedWorldUid = long.MinValue; Map.ForceRefresh(); } } private void BootstrapIconRegistry() { if (!((Object)(object)ObjectDB.instance == (Object)null) && ObjectDB.instance.m_items != null && ObjectDB.instance.m_items.Count != 0 && (!Icons.IsBuilt || Icons.IsDirty)) { Icons.Build(ObjectDB.instance); Discovery.RepairKnownResourceIcons(force: true); Map.ForceRefresh(); } } private void LateUpdate() { if (Map != null && (Object)(object)Minimap.instance != (Object)null) { Map.EnforceNativeShipMarkerVisibility(Minimap.instance); Map.EnforceVanillaPersistentPinUiVisibility(Minimap.instance); } } private void OnGUI() { if (Map != null) { Map.DrawLargeMapNameLabels(); } if (CompassHUD != null) { CompassHUD.Draw(); } if (PinManagerUI != null) { PinManagerUI.DrawMapButton(); PinManagerUI.Draw(); } } private void OnDestroy() { try { if (TrackingBeacons != null) { TrackingBeacons.Clear(); } if (Pins != null) { Pins.Save(); } if (Harmony != null) { Harmony.UnpatchSelf(); } } catch { } } } } namespace JoeyBadManners.Wayfinder.Core { internal sealed class WayfinderConfig { internal readonly ConfigEntry Enabled; internal readonly ConfigEntry DebugLogging; internal readonly ConfigEntry VerboseRuntimeLogging; internal readonly ConfigEntry SupersedeVanillaPinVisuals; internal readonly ConfigEntry BossPinScale; internal readonly ConfigEntry RightClickRemovesWayfinderPins; internal readonly ConfigEntry SuppressRemovedAutoPins; internal readonly ConfigEntry HiddenCategories; internal readonly ConfigEntry PinManagerToggleKey; internal readonly ConfigEntry ShowPinManagerMapButton; internal readonly ConfigEntry ReplaceVanillaPersistentPinControls; internal readonly ConfigEntry NearbyDiscoveryListRadius; internal readonly ConfigEntry OverrideVanillaBossNames; internal readonly ConfigEntry BossNameOverrides; internal readonly ConfigEntry ResourceClustering; internal readonly ConfigEntry ResourceClusterLinkDistance; internal readonly ConfigEntry ShowClusterCounts; internal readonly ConfigEntry InteractionDiscovery; internal readonly ConfigEntry AreaScanning; internal readonly ConfigEntry AreaScanRadius; internal readonly ConfigEntry MatchAreaScanToMapRevealRadius; internal readonly ConfigEntry ScanUnexploredAreas; internal readonly ConfigEntry EnemySightings; internal readonly ConfigEntry EnemySightingRadius; internal readonly ConfigEntry EnemySightingClusterDistance; internal readonly ConfigEntry ScanDungeonContents; internal readonly ConfigEntry AutoPinStone; internal readonly ConfigEntry AutoPinBranches; internal readonly ConfigEntry AutoPinFlint; internal readonly ConfigEntry AutoPinDandelions; internal readonly ConfigEntry RememberGeneratedStructures; internal readonly ConfigEntry StructureDiscoveryRadius; internal readonly ConfigEntry RememberPhysicalSpawners; internal readonly ConfigEntry AutoPinSurtlingSpawners; internal readonly ConfigEntry SpawnerDiscoveryRadius; internal readonly ConfigEntry SuppressSightingsInsideSpawnerRadius; internal readonly ConfigEntry SpawnerSightingSuppressionPadding; internal readonly ConfigEntry SpawnerSightingExclusionRadius; internal readonly ConfigEntry RememberPortals; internal readonly ConfigEntry PortalDiscoveryRadius; internal readonly ConfigEntry PortalSyncInterval; internal readonly ConfigEntry PortalNameFromTag; internal readonly ConfigEntry ShowPortalNamesOnLargeMap; internal readonly ConfigEntry RemoveDestroyedPortals; internal readonly ConfigEntry CompassEnabled; internal readonly ConfigEntry CompassShowDistance; internal readonly ConfigEntry CompassShowNames; internal readonly ConfigEntry CompassShowCardinals; internal readonly ConfigEntry CompassClampOffscreenTracked; internal readonly ConfigEntry CompassMaxDistance; internal readonly ConfigEntry CompassWidth; internal readonly ConfigEntry CompassTopOffset; internal readonly ConfigEntry CompassArcDegrees; internal readonly ConfigEntry CompassOpacity; internal readonly ConfigEntry MaxTrackedPins; internal readonly ConfigEntry WaypointEnabled; internal readonly ConfigEntry WaypointShowVerticalDifference; internal readonly ConfigEntry BeaconEnabled; internal readonly ConfigEntry BeaconOpacity; internal readonly ConfigEntry BeaconHeight; internal readonly ConfigEntry BeaconWidth; internal readonly ConfigEntry TrackBoats; internal readonly ConfigEntry TrackCarts; internal readonly ConfigEntry KeepLastKnownVehiclePosition; internal readonly ConfigEntry RemoveDestroyedVehicles; internal readonly ConfigEntry VehicleDiscoveryRadius; internal readonly ConfigEntry VehicleUpdateInterval; internal readonly ConfigEntry LastKnownVehicleOpacity; internal readonly ConfigEntry ShowLastKnownVehicleBadge; internal readonly ConfigEntry ShowVehicleNamesOnLargeMap; internal readonly ConfigEntry ExpandedExploration; internal readonly ConfigEntry WalkingExploreMultiplier; internal readonly ConfigEntry SailingExploreMultiplier; internal readonly ConfigEntry AreaScanInterval; internal readonly ConfigEntry EnemySightingInterval; internal readonly ConfigEntry StaticDiscoveryInterval; internal readonly ConfigEntry SpawnerIndexCacheSeconds; internal readonly ConfigEntry PersistenceFlushInterval; internal readonly ConfigEntry MapVisualRefreshInterval; internal WayfinderConfig(ConfigFile config) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind("00 - General", "Enabled", true, "Enable Joey's Wayfinder."); DebugLogging = config.Bind("00 - General", "DebugLogging", false, "Write additional diagnostic information to the BepInEx log."); VerboseRuntimeLogging = config.Bind("00 - General", "VerboseRuntimeLogging", false, "With DebugLogging enabled, also write high-frequency scan/sync/save/input diagnostics. Off by default to keep normal debug logs readable."); SupersedeVanillaPinVisuals = config.Bind("10 - Map", "SupersedeVanillaPinVisuals", true, "Keep vanilla pin data intact but let Wayfinder replace supported pin visuals."); BossPinScale = config.Bind("10 - Map", "BossPinScale", 1.35f, "Visual scale used for Vegvisir-discovered boss pins when Wayfinder trophy styling is active."); RightClickRemovesWayfinderPins = config.Bind("10 - Map", "RightClickRemovesWayfinderPins", true, "Allow normal map right-click removal to delete Wayfinder pins too. Managed portal pins are protected from accidental map removal; delete them deliberately from the Pins panel instead."); SuppressRemovedAutoPins = config.Bind("10 - Map", "SuppressRemovedAutoPins", true, "When you manually remove an automatically discovered Wayfinder pin, remember that choice so the same scanned objects do not immediately reappear."); HiddenCategories = config.Bind("11 - Pin Manager", "HiddenCategories", string.Empty, "Comma-separated Wayfinder categories hidden from map rendering. The in-map pin manager edits this automatically."); PinManagerToggleKey = config.Bind("11 - Pin Manager", "ToggleKey", new KeyboardShortcut((KeyCode)288, (KeyCode[])(object)new KeyCode[0]), "Fallback shortcut for opening or closing the Wayfinder Pin Manager while the large map is open."); ShowPinManagerMapButton = config.Bind("11 - Pin Manager", "ShowMapButton", true, "Show Joey's Wayfinder compact controls on the large map."); ReplaceVanillaPersistentPinControls = config.Bind("11 - Pin Manager", "ReplaceVanillaPersistentPinControls", true, "Hide Valheim's old persistent pin-type strip and old Add/Cross-off/Remove pin hints while the large map is open. Wayfinder's compact controls replace that workflow. Vanilla middle-mouse ping and Visible to other players are intentionally preserved."); NearbyDiscoveryListRadius = config.Bind("11 - Pin Manager", "NearbyDiscoveryListRadius", 100f, "In the compact Pins view, automatically discovered Resources/Sightings/Habitats farther than this are hidden from the LIST only when search is empty. Their map markers remain visible. Searching by name shows matching results at any remembered distance."); OverrideVanillaBossNames = config.Bind("11 - Pin Manager", "OverrideVanillaBossNames", true, "Let Wayfinder control the visible labels of native/Vegvisir boss pins without changing the underlying vanilla pin data."); BossNameOverrides = config.Bind("11 - Pin Manager", "BossNameOverrides", "Eikthyr=Eikthyr;TheElder=The Elder;Bonemass=Bonemass;Moder=Moder;Yagluth=Yagluth;Queen=The Queen;Fader=Fader", "Wayfinder display names for native boss pins. The in-map manager edits this automatically."); ResourceClustering = config.Bind("20 - Discovery", "ResourceClustering", true, "Merge nearby observations of the SAME resource into connected clusters."); ResourceClusterLinkDistance = config.Bind("20 - Discovery", "ResourceClusterLinkDistance", 6f, "A resource joins a cluster when it is within this many meters of ANY member of that same-resource cluster. Chained members stay one cluster."); ShowClusterCounts = config.Bind("20 - Discovery", "ShowClusterCounts", true, "Show counts such as Raspberries x5 or Copper x2."); InteractionDiscovery = config.Bind("20 - Discovery", "InteractionDiscovery", true, "Remember resources/locations after legitimate interaction or discovery."); AreaScanning = config.Bind("20 - Discovery", "AreaScanning", false, "Optionally scan the surrounding area for configured map targets. Off by default. Buried Silver is always interaction-only and is never revealed by area scanning."); AreaScanRadius = config.Bind("20 - Discovery", "AreaScanRadius", 100f, "Manual area scan radius in meters. Used only when MatchAreaScanToMapRevealRadius is disabled."); MatchAreaScanToMapRevealRadius = config.Bind("20 - Discovery", "MatchAreaScanToMapRevealRadius", true, "Match Wayfinder's area scan radius to the player's current map reveal/exploration radius. On by default so Wayfinder only remembers visible things within the same area the map is revealing."); ScanUnexploredAreas = config.Bind("20 - Discovery", "ScanUnexploredAreas", false, "Allow automatic discovery to create pins in unexplored map fog. Off by default."); EnemySightings = config.Bind("20 - Discovery", "EnemySightings", false, "Track ordinary enemy sightings/hotspots. Off by default to avoid clutter."); EnemySightingRadius = config.Bind("20 - Discovery", "EnemySightingRadius", 80f, "Maximum range in meters for optional enemy sighting discovery."); EnemySightingClusterDistance = config.Bind("20 - Discovery", "EnemySightingClusterDistance", 20f, "Same-enemy sightings within this connected distance merge into one activity hotspot."); ScanDungeonContents = config.Bind("20 - Discovery", "ScanDungeonContents", false, "Allow resource discovery/pinning inside generated dungeon interiors. Off by default because the dungeon entrance pin is usually enough."); AutoPinStone = config.Bind("21 - Resource Filters", "Stone", false, "Automatically pin loose Stone sources. Off by default to avoid map clutter."); AutoPinBranches = config.Bind("21 - Resource Filters", "Branches", false, "Automatically pin branch/basic Wood pickups. Off by default to avoid map clutter."); AutoPinFlint = config.Bind("21 - Resource Filters", "Flint", false, "Automatically pin Flint pickups. Off by default to avoid map clutter."); AutoPinDandelions = config.Bind("21 - Resource Filters", "Dandelions", false, "Automatically pin Dandelions. Off by default to avoid map clutter."); RememberGeneratedStructures = config.Bind("22 - Structures", "RememberGeneratedStructures", true, "Remember generated overworld structures/locations as Wayfinder POIs after they are actually loaded/discovered."); StructureDiscoveryRadius = config.Bind("22 - Structures", "DiscoveryRadius", 80f, "Maximum distance in meters for remembering loaded generated structures around the player."); RememberPhysicalSpawners = config.Bind("23 - Spawners", "RememberPhysicalSpawners", true, "Remember persistent physical world spawners after they are legitimately loaded/discovered, using the dedicated Wayfinder spawner icon."); AutoPinSurtlingSpawners = config.Bind("23 - Spawners", "SurtlingSpawners", true, "Automatically remember Surtling fire geyser/spawner locations using the dedicated Wayfinder spawner marker."); SpawnerDiscoveryRadius = config.Bind("23 - Spawners", "DiscoveryRadius", 80f, "Maximum distance in meters for remembering loaded physical spawners around the player."); SuppressSightingsInsideSpawnerRadius = config.Bind("23 - Spawners", "SuppressSightingsInsideSpawnerRadius", true, "Suppress ordinary enemy-sighting/hotspot pins for creatures currently inside the radius of a physical spawner that can actually spawn that creature."); SpawnerSightingSuppressionPadding = config.Bind("23 - Spawners", "SightingSuppressionPadding", 0f, "Optional extra meters added to the physical spawner radius when suppressing matching spawned-enemy sightings."); SpawnerSightingExclusionRadius = config.Bind("23 - Spawners", "SightingExclusionRadius", 25f, "Blanket no-sighting bubble around every known physical spawner. Any enemy sighting inside this many meters is suppressed so the spawner marker remains readable."); RememberPortals = config.Bind("24 - Portals", "RememberPortals", true, "Remember loaded player/world portals as dedicated Wayfinder Portal pins."); PortalDiscoveryRadius = config.Bind("24 - Portals", "DiscoveryRadius", 120f, "Maximum distance in meters for remembering and live-syncing loaded portals around the player."); PortalSyncInterval = config.Bind("24 - Portals", "SyncInterval", 1f, "Seconds between portal tag/connection-state sync passes. Portal renames normally update within this interval."); PortalNameFromTag = config.Bind("24 - Portals", "NamePinsFromTag", true, "Automatically keep discovered portal pin names synchronized to the portal's actual in-game tag. Untagged portals are named Portal unless that individual portal has a manual Wayfinder name override."); ShowPortalNamesOnLargeMap = config.Bind("24 - Portals", "ShowNamesOnLargeMap", true, "Show remembered portal names/tags beside their Wayfinder icons on the large map. Small minimap labels stay hidden to avoid clutter."); RemoveDestroyedPortals = config.Bind("24 - Portals", "RemoveDestroyedPortals", true, "Remove the remembered Wayfinder portal record when the client actually observes that portal being destroyed."); CompassEnabled = config.Bind("30 - Compass", "Enabled", true, "Enable the Wayfinder horizontal compass HUD."); CompassShowDistance = config.Bind("30 - Compass", "ShowDistance", true, "Show live meter distance beside tracked compass markers."); CompassShowNames = config.Bind("30 - Compass", "ShowNames", false, "Show tracked pin names on the compass. Off by default to keep the HUD compact."); CompassShowCardinals = config.Bind("30 - Compass", "ShowCardinals", true, "Show N/E/S/W and bearing ticks on the compass band."); CompassClampOffscreenTracked = config.Bind("30 - Compass", "ClampOffscreenTracked", true, "Keep tracked targets outside the visible compass arc pinned to the left/right edge so you still know which way to turn."); CompassMaxDistance = config.Bind("30 - Compass", "MaxDistance", 0f, "Legacy compatibility setting. Explicitly tracked Wayfinder targets are always shown at any practical world distance; this value no longer culls tracked compass markers."); CompassWidth = config.Bind("30 - Compass", "Width", 720f, "Compass width in screen pixels."); CompassTopOffset = config.Bind("30 - Compass", "TopOffset", 18f, "Distance in screen pixels from the top edge."); CompassArcDegrees = config.Bind("30 - Compass", "VisibleArcDegrees", 180f, "Horizontal bearing arc represented across the compass width."); CompassOpacity = config.Bind("30 - Compass", "Opacity", 0.72f, "Opacity of the compass background/band. Marker colors remain more vivid."); MaxTrackedPins = config.Bind("30 - Compass", "MaxTrackedPins", 5, "Maximum number of Wayfinder pins that may be explicitly tracked at once. Tracking is separate from deletion and vanilla map pings."); WaypointEnabled = config.Bind("40 - Waypoint", "Enabled", true, "Enable Wayfinder tracked-target navigation features."); WaypointShowVerticalDifference = config.Bind("40 - Waypoint", "ShowVerticalDifference", true, "Show target elevation difference for tracked targets when supported by the HUD."); BeaconEnabled = config.Bind("40 - Waypoint", "BeaconEnabled", true, "Show subtle vertical beams for explicitly tracked Wayfinder pins."); BeaconOpacity = config.Bind("40 - Waypoint", "BeaconOpacity", 1f, "Opacity of tracked-target beacon beams."); BeaconHeight = config.Bind("40 - Waypoint", "BeaconHeight", 100f, "Height in meters of each tracked-target beacon beam."); BeaconWidth = config.Bind("40 - Waypoint", "BeaconWidth", 1f, "Width in meters of each tracked-target beacon beam."); TrackBoats = config.Bind("50 - Vehicles", "TrackBoats", true, "Remember legitimately encountered loaded boats as dynamic Wayfinder vehicle markers. They can then be explicitly tracked as T1-T5 targets."); TrackCarts = config.Bind("50 - Vehicles", "TrackCarts", true, "Remember legitimately encountered loaded carts as dynamic Wayfinder vehicle markers. They can then be explicitly tracked as T1-T5 targets."); KeepLastKnownVehiclePosition = config.Bind("50 - Vehicles", "KeepLastKnownPosition", true, "Keep the most recently known map position when a remembered boat/cart leaves loaded range."); RemoveDestroyedVehicles = config.Bind("50 - Vehicles", "RemoveDestroyedVehicles", true, "Remove a remembered dynamic vehicle marker when Wayfinder observes that boat/cart being actually destroyed."); VehicleDiscoveryRadius = config.Bind("50 - Vehicles", "DiscoveryRadius", 160f, "Maximum horizontal distance in meters for first remembering a loaded boat/cart. Once a specific vehicle is remembered, Wayfinder keeps updating it anywhere that same stable vehicle remains loaded."); VehicleUpdateInterval = config.Bind("50 - Vehicles", "UpdateInterval", 0.5f, "Seconds between dynamic boat/cart position updates. Lower values are smoother but scan more often."); LastKnownVehicleOpacity = config.Bind("50 - Vehicles", "LastKnownOpacity", 0.58f, "Opacity multiplier for remembered boat/cart markers that are no longer currently observed. Also dims their tracked compass icon/beam so last-known positions are not mistaken for live positions."); ShowLastKnownVehicleBadge = config.Bind("50 - Vehicles", "ShowLastKnownBadge", true, "Show a small amber clock-ring badge on map markers that represent a boat/cart's last known position rather than a currently observed live vehicle."); ShowVehicleNamesOnLargeMap = config.Bind("50 - Vehicles", "ShowNamesOnLargeMap", true, "Show remembered boat/cart names beside their Wayfinder icons on the large map. Small minimap labels stay hidden to avoid clutter."); ExpandedExploration = config.Bind("60 - Exploration", "Enabled", false, "Enable custom map exploration radii. Off by default."); WalkingExploreMultiplier = config.Bind("60 - Exploration", "WalkingMultiplier", 1f, "Map exploration multiplier while travelling on land."); SailingExploreMultiplier = config.Bind("60 - Exploration", "SailingMultiplier", 1f, "Map exploration multiplier while sailing."); AreaScanInterval = config.Bind("90 - Performance", "AreaScanInterval", 4f, "Seconds between optional area scans. Higher values reduce scan spikes while preserving discovery behavior."); EnemySightingInterval = config.Bind("90 - Performance", "EnemySightingInterval", 4f, "Seconds between optional enemy-sighting scans. Higher values reduce CPU cost."); StaticDiscoveryInterval = config.Bind("90 - Performance", "StaticDiscoveryInterval", 5f, "Seconds between structure/spawner discovery passes."); SpawnerIndexCacheSeconds = config.Bind("90 - Performance", "SpawnerIndexCacheSeconds", 10f, "How long physical-spawner footprint data is cached before being rebuilt."); PersistenceFlushInterval = config.Bind("90 - Performance", "PersistenceFlushInterval", 4f, "Batch automatic Wayfinder disk writes for this many seconds. Logout/quit still forces an immediate save."); MapVisualRefreshInterval = config.Bind("90 - Performance", "MapVisualRefreshInterval", 0.25f, "Minimum seconds between full Wayfinder map-visual refresh passes."); } } internal static class WayfinderDiscoveryRadius { internal static float GetEffectiveAreaScanRadius(WayfinderConfig config) { if (config == null) { return 100f; } if (!config.MatchAreaScanToMapRevealRadius.Value) { return Mathf.Max(1f, config.AreaScanRadius.Value); } Minimap instance = Minimap.instance; float num = (((Object)(object)instance != (Object)null) ? instance.m_exploreRadius : config.AreaScanRadius.Value); if (config.ExpandedExploration.Value && (Object)(object)Player.m_localPlayer != (Object)null) { float num2 = (((Character)Player.m_localPlayer).IsAttachedToShip() ? config.SailingExploreMultiplier.Value : config.WalkingExploreMultiplier.Value); num *= Mathf.Max(0f, num2); } return Mathf.Max(1f, num); } } } namespace JoeyBadManners.Wayfinder.Discovery { internal sealed class AreaScanner { private sealed class ResourceComponentCacheEntry { internal Collider Collider; internal Pickable Pickable; internal MineRock MineRock; internal MineRock5 MineRock5; internal DropOnDestroyed DropOnDestroyed; internal float ExpiresAt; } private sealed class VisualOreCacheEntry { internal Collider Collider; internal GameObject Source; internal string ResourcePrefab; internal float ExpiresAt; } private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly ResourceDiscovery _resources; private float _nextScanTime; private float _nextColliderCachePrune; private readonly Collider[] _colliderBuffer = (Collider[])(object)new Collider[4096]; private readonly HashSet _visualOreObjects = new HashSet(); private readonly HashSet _scanPickables = new HashSet(); private readonly HashSet _scanMineRocks = new HashSet(); private readonly HashSet _scanMineRocks5 = new HashSet(); private readonly HashSet _scanDestroyedDrops = new HashSet(); private readonly Dictionary _resourceColliderCache = new Dictionary(); private readonly Dictionary _visualOreCache = new Dictionary(); private bool _scanInProgress; private int _scanCursor; private int _nearbyColliderCount; private Vector3 _scanCenter; private float _scanRadius; private float _scanRadiusSq; private int _scanObserved; private int _scanPickableCount; private int _scanMineRockCount; private int _scanMineRock5Count; private int _scanDestroyedDropCount; private int _scanVisualOreCount; private int _lastProcessedThisSlice; private double _lastQueryMs; internal AreaScanner(ManualLogSource log, WayfinderConfig config, ResourceDiscovery resources) { _log = log; _config = config; _resources = resources; } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || !_config.AreaScanning.Value || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (!_config.ScanDungeonContents.Value && ((Character)Player.m_localPlayer).InInterior()) { ResetActiveScan(); return false; } long startTimestamp = DiscoveryWorkBudget.Start(); if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _lastProcessedThisSlice = 0; while (_scanCursor < _nearbyColliderCount) { Collider collider = _colliderBuffer[_scanCursor++]; _lastProcessedThisSlice++; ProcessCollider(collider); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } FinishScan(); return false; } internal string GetPerfDetail() { if (_scanInProgress) { return " query=" + _lastQueryMs.ToString("0.00") + "ms processed=" + _scanCursor + "/" + _nearbyColliderCount + " yielded=true"; } return " query=" + _lastQueryMs.ToString("0.00") + "ms processed=" + _nearbyColliderCount + "/" + _nearbyColliderCount + " yielded=false"; } private void BeginScan() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.AreaScanInterval.Value); _scanCenter = ((Component)Player.m_localPlayer).transform.position; _scanRadius = WayfinderDiscoveryRadius.GetEffectiveAreaScanRadius(_config); _scanRadiusSq = _scanRadius * _scanRadius; _scanObserved = 0; _scanPickableCount = 0; _scanMineRockCount = 0; _scanMineRock5Count = 0; _scanDestroyedDropCount = 0; _scanVisualOreCount = 0; _scanPickables.Clear(); _scanMineRocks.Clear(); _scanMineRocks5.Clear(); _scanDestroyedDrops.Clear(); _visualOreObjects.Clear(); long startTimestamp = DiscoveryWorkBudget.Start(); _nearbyColliderCount = Physics.OverlapSphereNonAlloc(_scanCenter, _scanRadius, _colliderBuffer, -1, (QueryTriggerInteraction)2); _lastQueryMs = DiscoveryWorkBudget.ElapsedMilliseconds(startTimestamp); _scanCursor = 0; _scanInProgress = true; } private void ProcessCollider(Collider collider) { //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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)collider == (Object)null) { return; } ResourceComponentCacheEntry cachedResourceComponents = GetCachedResourceComponents(collider); bool flag = false; if (cachedResourceComponents != null) { Pickable pickable = cachedResourceComponents.Pickable; if ((Object)(object)pickable != (Object)null) { flag = true; int instanceID = ((Object)pickable).GetInstanceID(); Vector3 position = ((Component)pickable).transform.position; if (_scanPickables.Add(instanceID) && WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) && MayReveal(position)) { _resources.ObservePickableFromScan(pickable); _scanObserved++; _scanPickableCount++; } } MineRock mineRock = cachedResourceComponents.MineRock; if ((Object)(object)mineRock != (Object)null) { flag = true; int instanceID2 = ((Object)mineRock).GetInstanceID(); Vector3 position2 = ((Component)mineRock).transform.position; if (_scanMineRocks.Add(instanceID2) && WithinHorizontalRadius(_scanCenter, position2, _scanRadiusSq) && MayReveal(position2)) { _resources.ObserveMineRockFromScan(mineRock); _scanObserved++; _scanMineRockCount++; } } MineRock5 mineRock2 = cachedResourceComponents.MineRock5; if ((Object)(object)mineRock2 != (Object)null) { flag = true; int instanceID3 = ((Object)mineRock2).GetInstanceID(); Vector3 position3 = ((Component)mineRock2).transform.position; if (_scanMineRocks5.Add(instanceID3) && WithinHorizontalRadius(_scanCenter, position3, _scanRadiusSq) && MayReveal(position3)) { _resources.ObserveMineRock5FromScan(mineRock2); _scanObserved++; _scanMineRock5Count++; } } DropOnDestroyed dropOnDestroyed = cachedResourceComponents.DropOnDestroyed; if ((Object)(object)dropOnDestroyed != (Object)null) { flag = true; int instanceID4 = ((Object)dropOnDestroyed).GetInstanceID(); Vector3 position4 = ((Component)dropOnDestroyed).transform.position; if (_scanDestroyedDrops.Add(instanceID4) && WithinHorizontalRadius(_scanCenter, position4, _scanRadiusSq) && MayReveal(position4) && _resources.ObserveDropOnDestroyedFromScan(dropOnDestroyed)) { _scanObserved++; _scanDestroyedDropCount++; } } } if (flag || !TryGetCachedVisibleMineable(collider, out var source, out var resourcePrefab) || (Object)(object)source == (Object)null || string.IsNullOrEmpty(resourcePrefab)) { return; } string item = ((Object)source).GetInstanceID() + ":" + resourcePrefab; if (_visualOreObjects.Add(item)) { Vector3 position5 = source.transform.position; if (WithinHorizontalRadius(_scanCenter, position5, _scanRadiusSq) && MayReveal(position5) && _resources.ObserveVisibleMineableFromScan(source, resourcePrefab)) { _scanObserved++; _scanVisualOreCount++; } } } private void FinishScan() { _scanInProgress = false; if (Time.unscaledTime >= _nextColliderCachePrune) { _nextColliderCachePrune = Time.unscaledTime + 60f; PruneColliderCaches(); } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value) { _log.LogInfo((object)("Area scan checked " + _scanObserved + " nearby resource sources inside " + _scanRadius.ToString("0") + "m" + (_config.MatchAreaScanToMapRevealRadius.Value ? " [map-matched]" : " [manual]") + " (Pickable=" + _scanPickableCount + ", MineRock=" + _scanMineRockCount + ", MineRock5=" + _scanMineRock5Count + ", VisualOre=" + _scanVisualOreCount + ", DropOnDestroyed=" + _scanDestroyedDropCount + "; local collider scan).")); if (_nearbyColliderCount >= _colliderBuffer.Length) { _log.LogWarning((object)("Area scan collider buffer filled (" + _colliderBuffer.Length + "). Nearby discovery may be incomplete in this unusually dense area.")); } } } private void ResetActiveScan() { _scanInProgress = false; _scanCursor = 0; _nearbyColliderCount = 0; } private ResourceComponentCacheEntry GetCachedResourceComponents(Collider collider) { if ((Object)(object)collider == (Object)null) { return null; } int instanceID = ((Object)collider).GetInstanceID(); if (_resourceColliderCache.TryGetValue(instanceID, out var value) && value != null && object.ReferenceEquals(value.Collider, collider) && Time.unscaledTime < value.ExpiresAt) { return value; } value = new ResourceComponentCacheEntry(); value.Collider = collider; value.ExpiresAt = Time.unscaledTime + 45f; try { value.Pickable = ((Component)collider).GetComponentInParent(); if ((Object)(object)value.Pickable == (Object)null) { value.MineRock5 = ((Component)collider).GetComponentInParent(); if ((Object)(object)value.MineRock5 == (Object)null) { value.MineRock = ((Component)collider).GetComponentInParent(); if ((Object)(object)value.MineRock == (Object)null) { value.DropOnDestroyed = ((Component)collider).GetComponentInParent(); } } } } catch { } _resourceColliderCache[instanceID] = value; return value; } private void PruneColliderCaches() { List list = new List(); float unscaledTime = Time.unscaledTime; foreach (KeyValuePair item in _resourceColliderCache) { ResourceComponentCacheEntry value = item.Value; if (value == null || (Object)(object)value.Collider == (Object)null || unscaledTime >= value.ExpiresAt) { list.Add(item.Key); } } for (int i = 0; i < list.Count; i++) { _resourceColliderCache.Remove(list[i]); } list.Clear(); foreach (KeyValuePair item2 in _visualOreCache) { VisualOreCacheEntry value2 = item2.Value; if (value2 == null || (Object)(object)value2.Collider == (Object)null || unscaledTime >= value2.ExpiresAt) { list.Add(item2.Key); } } for (int j = 0; j < list.Count; j++) { _visualOreCache.Remove(list[j]); } } private bool TryGetCachedVisibleMineable(Collider collider, out GameObject source, out string resourcePrefab) { source = null; resourcePrefab = string.Empty; if ((Object)(object)collider == (Object)null) { return false; } int instanceID = ((Object)collider).GetInstanceID(); if (_visualOreCache.TryGetValue(instanceID, out var value) && value != null && object.ReferenceEquals(value.Collider, collider) && Time.unscaledTime < value.ExpiresAt) { source = value.Source; resourcePrefab = value.ResourcePrefab ?? string.Empty; if ((Object)(object)source != (Object)null) { return !string.IsNullOrEmpty(resourcePrefab); } return false; } GameObject source2; string resourcePrefab2; bool flag = TryFindVisibleMineable(collider, out source2, out resourcePrefab2); VisualOreCacheEntry visualOreCacheEntry = new VisualOreCacheEntry(); visualOreCacheEntry.Collider = collider; visualOreCacheEntry.Source = (flag ? source2 : null); visualOreCacheEntry.ResourcePrefab = (flag ? resourcePrefab2 : string.Empty); visualOreCacheEntry.ExpiresAt = Time.unscaledTime + (flag ? 60f : 30f); value = visualOreCacheEntry; _visualOreCache[instanceID] = value; source = value.Source; resourcePrefab = value.ResourcePrefab; if (flag && (Object)(object)source != (Object)null) { return !string.IsNullOrEmpty(resourcePrefab); } return false; } private static bool TryFindVisibleMineable(Collider collider, out GameObject source, out string resourcePrefab) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) source = null; resourcePrefab = string.Empty; if ((Object)(object)collider == (Object)null) { return false; } Transform val = ((Component)collider).transform; GameObject val2 = null; string text = string.Empty; int num = 0; while ((Object)(object)val != (Object)null && num < 7) { GameObject gameObject = ((Component)val).gameObject; if (!((Object)(object)gameObject == (Object)null)) { Scene scene = gameObject.scene; if (((Scene)(ref scene)).IsValid()) { if (IdentifyVisibleSurfaceMineable(gameObject, out var resourcePrefab2)) { if (IsHiddenWishboneResource(resourcePrefab2, ((Object)gameObject).name)) { return false; } source = gameObject; resourcePrefab = resourcePrefab2; return true; } if (TryIdentifyMineableFromMaterials(gameObject, out var resourcePrefab3)) { if (IsHiddenWishboneResource(resourcePrefab3, ((Object)gameObject).name)) { return false; } if ((Object)(object)val2 == (Object)null) { val2 = gameObject; text = resourcePrefab3; } } } } num++; val = val.parent; } if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(text)) { source = val2; resourcePrefab = text; return true; } return false; } private static bool IdentifyVisibleSurfaceMineable(GameObject go, out string resourcePrefab) { resourcePrefab = string.Empty; if ((Object)(object)go == (Object)null) { return false; } string text = NormalizeOreText(((Object)go).name ?? string.Empty); if (ContainsIronIdentity(text) || text.Contains("silver")) { return false; } MineRock5 component = go.GetComponent(); if ((Object)(object)component != (Object)null) { text += NormalizeOreText(SafeMineRock5Name(component)); } MineRock component2 = go.GetComponent(); if ((Object)(object)component2 != (Object)null) { text += NormalizeOreText(SafeMineRockName(component2)); } DropOnDestroyed component3 = go.GetComponent(); bool flag = (Object)(object)component != (Object)null || (Object)(object)component2 != (Object)null || (Object)(object)component3 != (Object)null; if (text.Contains("copper") && (flag || LooksLikeDepositName(text))) { resourcePrefab = "CopperOre"; return true; } if (text.Contains("tin") && (flag || LooksLikeDepositName(text))) { resourcePrefab = "TinOre"; return true; } if (text.Contains("obsidian") && (flag || LooksLikeDepositName(text))) { resourcePrefab = "Obsidian"; return true; } if (text.Contains("flametal") && (flag || LooksLikeDepositName(text) || text.Contains("meteor"))) { resourcePrefab = "FlametalOreNew"; return true; } if (text.Contains("blackmarble") && flag) { resourcePrefab = "BlackMarble"; return true; } if (text.Contains("softtissue") && flag) { resourcePrefab = "SoftTissue"; return true; } if (text.Contains("crystal") && flag) { resourcePrefab = "Crystal"; return true; } return false; } private static bool TryIdentifyMineableFromMaterials(GameObject go, out string resourcePrefab) { resourcePrefab = string.Empty; if ((Object)(object)go == (Object)null) { return false; } Renderer[] componentsInChildren; try { componentsInChildren = go.GetComponentsInChildren(true); } catch { return false; } foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || val.sharedMaterials == null) { continue; } Material[] sharedMaterials = val.sharedMaterials; foreach (Material val2 in sharedMaterials) { if ((Object)(object)val2 == (Object)null) { continue; } string text = NormalizeOreText(((Object)val2).name ?? string.Empty); if (!text.Contains("silver") && !ContainsIronIdentity(text)) { bool flag = text.Contains("rock") || text.Contains("ore") || text.Contains("deposit") || text.Contains("vein") || text.Contains("meteor"); if (text.Contains("copper") && flag) { resourcePrefab = "CopperOre"; return true; } if (text.Contains("tin") && flag) { resourcePrefab = "TinOre"; return true; } if (text.Contains("obsidian") && flag) { resourcePrefab = "Obsidian"; return true; } if (text.Contains("flametal") && flag) { resourcePrefab = "FlametalOreNew"; return true; } if (text.Contains("blackmarble") && flag) { resourcePrefab = "BlackMarble"; return true; } if (text.Contains("softtissue") && (flag || text.Contains("tissue"))) { resourcePrefab = "SoftTissue"; return true; } if (text.Contains("crystal") && flag) { resourcePrefab = "Crystal"; return true; } } } } return false; } private static bool LooksLikeDepositName(string normalizedName) { if (!normalizedName.Contains("minerock") && !normalizedName.Contains("deposit") && !normalizedName.Contains("ore") && !normalizedName.Contains("vein") && !normalizedName.Contains("meteor")) { return normalizedName.Contains("rock"); } return true; } private static bool IsHiddenWishboneResource(string resourcePrefab, string objectName) { string text = NormalizeOreText((resourcePrefab ?? string.Empty) + " " + (objectName ?? string.Empty)); if (!text.Contains("silver")) { return ContainsIronIdentity(text); } return true; } private static bool ContainsIronIdentity(string normalized) { if (string.IsNullOrEmpty(normalized)) { return false; } if (!normalized.Contains("ironscrap") && !normalized.Contains("scrapiron") && !normalized.Contains("muddyscrap") && !normalized.Contains("muddypile") && !normalized.Contains("muddy")) { return normalized.Contains("buriediron"); } return true; } private static string SafeMineRock5Name(MineRock5 rock) { if ((Object)(object)rock == (Object)null) { return string.Empty; } try { return rock.m_name ?? string.Empty; } catch { return string.Empty; } } private static string SafeMineRockName(MineRock rock) { if ((Object)(object)rock == (Object)null) { return string.Empty; } try { return rock.m_name ?? string.Empty; } catch { return string.Empty; } } private static string NormalizeOreText(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } private static bool IsLiveSceneObject(Component component) { //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) if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null) { return false; } try { Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).IsValid(); } catch { return false; } } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } } internal sealed class DiscoveryArtConsistency { private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly RuntimeIconRegistry _icons; private readonly WayfinderPinDatabase _database; private long _processedWorldUid = long.MinValue; internal DiscoveryArtConsistency(ManualLogSource log, WayfinderConfig config, RuntimeIconRegistry icons, WayfinderPinDatabase database) { _log = log; _config = config; _icons = icons; _database = database; } internal void ResetSession() { _processedWorldUid = long.MinValue; } internal void Tick() { if (!_config.Enabled.Value || _database.WorldUid == 0 || _processedWorldUid == _database.WorldUid || _icons == null || !_icons.IsBuilt) { return; } _processedWorldUid = _database.WorldUid; IReadOnlyList records = _database.Records; if (records == null || records.Count == 0) { return; } List list = new List(); List list2 = new List(); int num = 0; int num2 = 0; int num3 = 0; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.source == WayfinderPinSource.Manual || wayfinderPinRecord.source == WayfinderPinSource.Imported || wayfinderPinRecord.source == WayfinderPinSource.Vanilla) { continue; } string name = (wayfinderPinRecord.subtype ?? string.Empty) + " " + (wayfinderPinRecord.displayName ?? string.Empty); if (wayfinderPinRecord.category != WayfinderPinCategory.Boss && WayfinderIconPolicy.IsBossGuidanceRunestoneName(name)) { if (!string.IsNullOrEmpty(wayfinderPinRecord.id)) { list2.Add(wayfinderPinRecord.id); } continue; } bool flag = false; if (wayfinderPinRecord.category == WayfinderPinCategory.Sighting && (wayfinderPinRecord.iconOverride || string.IsNullOrEmpty(wayfinderPinRecord.iconKey) || string.Equals(wayfinderPinRecord.iconKey, "wayfinder:sighting", StringComparison.OrdinalIgnoreCase))) { WayfinderIconEntry wayfinderIconEntry = _icons.FindBestTrophy(wayfinderPinRecord.subtype); if (wayfinderIconEntry == null) { wayfinderIconEntry = _icons.FindBestTrophy(wayfinderPinRecord.displayName); } if (wayfinderIconEntry != null && !string.IsNullOrEmpty(wayfinderIconEntry.Key) && !string.Equals(wayfinderPinRecord.iconKey, wayfinderIconEntry.Key, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = wayfinderIconEntry.Key; flag = true; num2++; } } if (WayfinderIconPolicy.IsRunestoneName(name) && !string.Equals(wayfinderPinRecord.iconKey, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = "wayfinder:runestone"; flag = true; num++; } string defaultIconKey = WayfinderIconPolicy.GetDefaultIconKey(wayfinderPinRecord); if (!string.IsNullOrEmpty(defaultIconKey) && !string.Equals(wayfinderPinRecord.iconKey, defaultIconKey, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = defaultIconKey; flag = true; num3++; } if (flag) { list.Add(wayfinderPinRecord); } } for (int j = 0; j < list2.Count; j++) { _database.Remove(list2[j], suppressAutoRediscovery: false); } for (int k = 0; k < list.Count; k++) { _database.AddOrUpdate(list[k]); } if (_config.DebugLogging.Value) { _log.LogInfo((object)("Art/discovery consistency pass: removed " + list2.Count + " obsolete boss-guidance marker(s), normalized " + num + " runestone icon record(s), restored " + num2 + " enemy sighting trophy icon(s), repaired " + num3 + " other automatic semantic icon key(s). Manual/imported/vanilla records were untouched.")); } } } internal static class DiscoveryWorkBudget { internal static long Start() { return Stopwatch.GetTimestamp(); } internal static bool Expired(long startTimestamp, double budgetMs) { if (budgetMs <= 0.0) { return true; } return ElapsedMilliseconds(startTimestamp) >= budgetMs; } internal static double ElapsedMilliseconds(long startTimestamp) { return (double)(Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / (double)Stopwatch.Frequency; } } internal sealed class EnemySightingDiscovery { private const float DungeonInteriorY = 3000f; private static readonly MethodInfo IsPlayerMethod = AccessTools.Method(typeof(Character), "IsPlayer", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo IsTamedMethod = AccessTools.Method(typeof(Character), "IsTamed", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo IsBossMethod = AccessTools.Method(typeof(Character), "IsBoss", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo GetHoverNameMethod = AccessTools.Method(typeof(Character), "GetHoverName", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo IsEnemyMethod = FindIsEnemyMethod(); private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly RuntimeIconRegistry _icons; private readonly WayfinderPinDatabase _database; private float _nextScanTime; private long _seenWorldUid = long.MinValue; private readonly HashSet _seenLiveInstances = new HashSet(); private float _lastSpawnerCleanupTime; private bool _scanInProgress; private List _scanCharacters = new List(); private int _scanCursor; private Player _scanPlayer; private Vector3 _scanCenter; private float _scanRadiusSq; private List _scanSpawnerFootprints; private int _scanRemembered; private bool _cleanupAfterScan; internal EnemySightingDiscovery(ManualLogSource log, WayfinderConfig config, RuntimeIconRegistry icons, WayfinderPinDatabase database) { _log = log; _config = config; _icons = icons; _database = database; } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || !_config.EnemySightings.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (_seenWorldUid != _database.WorldUid) { _seenWorldUid = _database.WorldUid; _seenLiveInstances.Clear(); _lastSpawnerCleanupTime = 0f; ResetActiveScan(); } long startTimestamp = DiscoveryWorkBudget.Start(); if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } int num = 0; while (_scanCursor < _scanCharacters.Count) { Character character = _scanCharacters[_scanCursor++]; num++; ProcessCharacter(character); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } if (_cleanupAfterScan) { if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } RunSpawnerCleanup(); _cleanupAfterScan = false; } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && _scanRemembered > 0) { _log.LogInfo((object)("Enemy sightings remembered/confirmed: " + _scanRemembered)); } _scanInProgress = false; _scanCharacters.Clear(); return false; } private void BeginScan() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.EnemySightingInterval.Value); _scanPlayer = Player.m_localPlayer; _scanCenter = ((Component)_scanPlayer).transform.position; float num = Mathf.Max(5f, _config.EnemySightingRadius.Value); _scanRadiusSq = num * num; _scanRemembered = 0; _scanCursor = 0; _scanSpawnerFootprints = null; if (_config.SuppressSightingsInsideSpawnerRadius.Value) { _scanSpawnerFootprints = PhysicalSpawnerIndex.GetCached(_config.SpawnerSightingSuppressionPadding.Value, _config.SpawnerIndexCacheSeconds.Value); } _cleanupAfterScan = _config.SuppressSightingsInsideSpawnerRadius.Value && Time.unscaledTime - _lastSpawnerCleanupTime >= Mathf.Max(2f, _config.SpawnerIndexCacheSeconds.Value); List allCharacters = Character.GetAllCharacters(); _scanCharacters = ((allCharacters != null) ? new List(allCharacters) : new List()); _scanInProgress = true; } private void ProcessCharacter(Character character) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || (Object)(object)((Component)character).gameObject == (Object)null || object.ReferenceEquals(character, _scanPlayer)) { return; } Vector3 position = ((Component)character).transform.position; if ((position.y > 3000f && !_config.ScanDungeonContents.Value) || !WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) || !MayReveal(position)) { return; } int instanceID = ((Object)character).GetInstanceID(); if (_seenLiveInstances.Contains(instanceID) || InvokeBool(character, IsPlayerMethod, fallback: false) || InvokeBool(character, IsTamedMethod, fallback: false) || InvokeBool(character, IsBossMethod, fallback: false) || !IsHostileOrMonster(character, _scanPlayer)) { return; } string prefabName = GetPrefabName(((Component)character).gameObject); if (string.IsNullOrEmpty(prefabName)) { return; } if (_config.SuppressSightingsInsideSpawnerRadius.Value) { float exclusionRadius = Mathf.Max(0f, _config.SpawnerSightingExclusionRadius.Value); if (PhysicalSpawnerIndex.WithinAny(_scanSpawnerFootprints, position, exclusionRadius) || PhysicalSpawnerIndex.Covers(_scanSpawnerFootprints, position, prefabName)) { return; } } string displayName = GetDisplayName(character, prefabName); string iconKey = string.Empty; if (_icons != null && _icons.IsBuilt) { WayfinderIconEntry wayfinderIconEntry = _icons.FindBestTrophy(prefabName); if (wayfinderIconEntry == null) { wayfinderIconEntry = _icons.FindBestTrophy(displayName); } if (wayfinderIconEntry != null) { iconKey = wayfinderIconEntry.Key; } } WayfinderPinRecord wayfinderPinRecord = _database.AddSightingObservation(prefabName, displayName, position, iconKey, "sighting:" + prefabName + ":" + instanceID, Mathf.Max(2f, _config.EnemySightingClusterDistance.Value)); if (wayfinderPinRecord != null) { _seenLiveInstances.Add(instanceID); _scanRemembered++; } } private void RunSpawnerCleanup() { _lastSpawnerCleanupTime = Time.unscaledTime; float exclusionRadius = Mathf.Max(0f, _config.SpawnerSightingExclusionRadius.Value); int num = _database.RemoveSightingMembers((string subtype, Vector3 position) => PhysicalSpawnerIndex.WithinAny(_scanSpawnerFootprints, position, exclusionRadius) || PhysicalSpawnerIndex.Covers(_scanSpawnerFootprints, position, subtype)); if (_config.DebugLogging.Value && num > 0) { _log.LogInfo((object)("Spawner-aware sightings: removed " + num + " redundant sighting member(s) inside matching physical spawner radii.")); } } private void ResetActiveScan() { _scanInProgress = false; _scanCharacters.Clear(); _scanCursor = 0; _scanPlayer = null; _scanSpawnerFootprints = null; _cleanupAfterScan = false; } private static MethodInfo FindIsEnemyMethod() { try { MethodInfo[] methods = typeof(BaseAI).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (string.Equals(methodInfo.Name, "IsEnemy", StringComparison.Ordinal)) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (methodInfo.IsStatic && parameters.Length == 2 && typeof(Character).IsAssignableFrom(parameters[0].ParameterType) && typeof(Character).IsAssignableFrom(parameters[1].ParameterType)) { return methodInfo; } } } } catch { } return null; } private static bool IsHostileOrMonster(Character character, Player player) { if ((Object)(object)character == (Object)null || (Object)(object)player == (Object)null || IsEnemyMethod == null) { return false; } try { object obj = IsEnemyMethod.Invoke(null, new object[2] { player, character }); return obj is bool && (bool)obj; } catch { return false; } } private static bool InvokeBool(object instance, MethodInfo method, bool fallback) { if (instance == null || method == null) { return fallback; } try { object obj = method.Invoke(instance, null); return (obj is bool) ? ((bool)obj) : fallback; } catch { return fallback; } } private static string GetDisplayName(Character character, string fallback) { string text = fallback; if ((Object)(object)character != (Object)null && GetHoverNameMethod != null) { try { object obj = GetHoverNameMethod.Invoke(character, null); string text2 = obj as string; if (!string.IsNullOrEmpty(text2)) { text = text2; } } catch { } } try { if (Localization.instance != null) { text = Localization.instance.Localize(text); } } catch { } if (!string.IsNullOrEmpty(text)) { return text; } return fallback; } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return true; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return !(obj is bool) || (bool)obj; } catch { return true; } } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } } internal sealed class GravestoneIntegration { private sealed class GraveState { internal string Key; internal Vector3 Position; internal PinData Pin; } private static readonly FieldInfo PinsField = AccessTools.Field(typeof(Minimap), "m_pins"); private static readonly Type ZNetViewType = AccessTools.TypeByName("ZNetView"); private static readonly MethodInfo GetZdoIdMethod = FindOptionalInstanceMethod(ZNetViewType, "GetZDOID"); private static readonly MethodInfo GetZdoMethod = FindOptionalInstanceMethod(ZNetViewType, "GetZDO"); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly RuntimeIconRegistry _icons; private readonly Dictionary _graves = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _graveKeyByPin = new Dictionary(); private float _nextBindTime; internal GravestoneIntegration(ManualLogSource log, WayfinderConfig config, RuntimeIconRegistry icons) { _log = log; _config = config; _icons = icons; } internal void ResetSession() { _graves.Clear(); _graveKeyByPin.Clear(); _nextBindTime = 0f; } internal void ClearSession() { ResetSession(); } internal void RegisterLoadedGrave(TombStone tombstone) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tombstone == (Object)null) { return; } Component val = null; try { if (ZNetViewType != null) { val = ((Component)tombstone).GetComponent(ZNetViewType); } } catch { } if ((Object)(object)val == (Object)null) { return; } string stableGraveKey = GetStableGraveKey(val); if (!string.IsNullOrEmpty(stableGraveKey)) { if (!_graves.TryGetValue(stableGraveKey, out var value) || value == null) { GraveState graveState = new GraveState(); graveState.Key = stableGraveKey; value = graveState; _graves[stableGraveKey] = value; } try { value.Position = ((Component)tombstone).transform.position; } catch { } TryBindStateToNativeDeathPin(value, Minimap.instance); } } internal void BeforePermanentNetworkDestroy(GameObject gameObject) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gameObject == (Object)null) { return; } TombStone val = null; try { val = gameObject.GetComponent(); } catch { } if ((Object)(object)val == (Object)null) { return; } Component val2 = null; try { if (ZNetViewType != null) { val2 = gameObject.GetComponent(ZNetViewType); } } catch { } if ((Object)(object)val2 == (Object)null) { return; } string stableGraveKey = GetStableGraveKey(val2); Vector3 position = Vector3.zero; try { position = ((Component)val).transform.position; } catch { } GraveState value = null; if (!string.IsNullOrEmpty(stableGraveKey)) { _graves.TryGetValue(stableGraveKey, out value); } if (value == null) { GraveState graveState = new GraveState(); graveState.Key = stableGraveKey ?? string.Empty; graveState.Position = position; value = graveState; } else { value.Position = position; } Minimap instance = Minimap.instance; if (value.Pin == null) { TryBindStateToNativeDeathPin(value, instance); } PinData pin = value.Pin; if (pin != null && (Object)(object)instance != (Object)null) { try { instance.RemovePin(pin); if (_config != null && _config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value) { _log.LogInfo((object)("Gravestone permanently disappeared; removed its associated native death marker (" + (string.IsNullOrEmpty(stableGraveKey) ? "unkeyed" : stableGraveKey) + ").")); } } catch (Exception ex) { if (_config != null && _config.DebugLogging.Value) { _log.LogWarning((object)("Could not remove cleaned-up gravestone marker: " + ex.Message)); } } } if (pin != null) { _graveKeyByPin.Remove(pin); } if (!string.IsNullOrEmpty(stableGraveKey)) { _graves.Remove(stableGraveKey); } } internal void Tick() { if (_config == null || !_config.Enabled.Value || Time.unscaledTime < _nextBindTime) { return; } _nextBindTime = Time.unscaledTime + 0.25f; Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || _graves.Count == 0) { return; } List pins = GetPins(instance); if (pins == null) { return; } List list = null; foreach (KeyValuePair grafe in _graves) { GraveState value = grafe.Value; if (value != null && value.Pin != null && !pins.Contains(value.Pin)) { _graveKeyByPin.Remove(value.Pin); value.Pin = null; if (list == null) { list = new List(); } list.Add(grafe.Key); } } foreach (KeyValuePair grafe2 in _graves) { GraveState value2 = grafe2.Value; if (value2 != null && value2.Pin == null) { TryBindStateToNativeDeathPin(value2, instance); } } } internal bool TryGetGravestoneSprite(out Sprite sprite) { sprite = null; if (_icons != null && _icons.TryGet("wayfinder:gravestone", out sprite)) { return (Object)(object)sprite != (Object)null; } return false; } internal static bool IsNativeDeathPin(PinData pin) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (pin == null) { return false; } string text = string.Empty; try { text = ((object)pin.m_type).ToString(); } catch { } if (!string.IsNullOrEmpty(text) && text.IndexOf("death", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } string a = pin.m_name ?? string.Empty; if (!string.Equals(a, "$msg_mapmarker_death", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "$map_death", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "Death", StringComparison.OrdinalIgnoreCase); } return true; } private void TryBindStateToNativeDeathPin(GraveState state, Minimap map) { if (state == null || state.Pin != null || (Object)(object)map == (Object)null) { return; } List pins = GetPins(map); if (pins == null) { return; } PinData val = null; float num = 144f; for (int i = 0; i < pins.Count; i++) { PinData val2 = pins[i]; if (IsNativeDeathPin(val2) && !_graveKeyByPin.ContainsKey(val2)) { float num2 = val2.m_pos.x - state.Position.x; float num3 = val2.m_pos.z - state.Position.z; float num4 = num2 * num2 + num3 * num3; if (num4 <= num) { num = num4; val = val2; } } } if (val != null) { state.Pin = val; _graveKeyByPin[val] = state.Key ?? string.Empty; } } private static List GetPins(Minimap map) { if ((Object)(object)map == (Object)null || PinsField == null) { return null; } try { return PinsField.GetValue(map) as List; } catch { return null; } } private static string GetStableGraveKey(Component view) { if ((Object)(object)view == (Object)null) { return string.Empty; } object obj = null; if (GetZdoIdMethod != null) { try { obj = GetZdoIdMethod.Invoke(view, null); } catch { obj = null; } } if (obj == null && GetZdoMethod != null) { try { object obj3 = GetZdoMethod.Invoke(view, null); if (obj3 != null) { FieldInfo field = obj3.GetType().GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { obj = field.GetValue(obj3); } } } catch { obj = null; } } string text = StableIdToString(obj); if (!string.IsNullOrEmpty(text)) { switch (text) { case "0": case "0:0": case "0_0": break; default: return "grave:" + text; } } return string.Empty; } private static MethodInfo FindOptionalInstanceMethod(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } try { return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); } catch { return null; } } private static string StableIdToString(object id) { if (id == null) { return string.Empty; } try { Type type = id.GetType(); BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; object obj = ReadSilentMember(type, id, flags, "m_userID", "userID", "UserID"); object obj2 = ReadSilentMember(type, id, flags, "m_id", "id", "ID"); if (obj != null && obj2 != null) { string text = Convert.ToString(obj); string text2 = Convert.ToString(obj2); if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2)) { return text + ":" + text2; } } } catch { } try { return id.ToString(); } catch { return string.Empty; } } private static object ReadSilentMember(Type type, object instance, BindingFlags flags, params string[] names) { if (type == null || instance == null || names == null) { return null; } foreach (string text in names) { if (string.IsNullOrEmpty(text)) { continue; } try { FieldInfo field = type.GetField(text, flags); if (field != null) { return field.GetValue(instance); } } catch { } try { PropertyInfo property = type.GetProperty(text, flags); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(instance, null); } } catch { } } return null; } } internal static class LiveDiscoveryRegistry { private static readonly FieldInfo AllLocationsField = AccessTools.Field(typeof(Location), "s_allLocations"); private static readonly Dictionary Locations = new Dictionary(); private static readonly Dictionary SpawnAreas = new Dictionary(); private static readonly Dictionary Runestones = new Dictionary(); private static readonly Dictionary Vegvisirs = new Dictionary(); private static readonly Dictionary Traders = new Dictionary(); private static long _worldUid = long.MinValue; private static int _revision; internal static int Revision => _revision; internal static void ResetForWorld(long worldUid) { if (_worldUid != worldUid) { _worldUid = worldUid; Locations.Clear(); SpawnAreas.Clear(); Runestones.Clear(); Vegvisirs.Clear(); Traders.Clear(); _revision++; } } internal static void RegisterLocation(Location location) { if (!AddLive(Locations, location)) { return; } try { RegisterMany(((Component)location).GetComponentsInChildren(true), RegisterSpawnArea); RegisterMany(((Component)location).GetComponentsInChildren(true), RegisterRunestone); RegisterMany(((Component)location).GetComponentsInChildren(true), RegisterVegvisir); RegisterMany(((Component)location).GetComponentsInChildren(true), RegisterTrader); } catch { } } internal static void UnregisterLocation(Location location) { RemoveLive(Locations, location); } internal static void RegisterSpawnArea(SpawnArea area) { AddLive(SpawnAreas, area); } internal static void RegisterRunestone(RuneStone stone) { AddLive(Runestones, stone); } internal static void RegisterVegvisir(Vegvisir vegvisir) { AddLive(Vegvisirs, vegvisir); } internal static void RegisterTrader(Trader trader) { AddLive(Traders, trader); } internal static void SyncFromLoadedLocations() { if (AllLocationsField == null) { PruneDead(); return; } try { if (AllLocationsField.GetValue(null) is IEnumerable enumerable) { foreach (object item in enumerable) { Location val = (Location)((item is Location) ? item : null); if ((Object)(object)val != (Object)null) { RegisterLocation(val); } } } } catch { } PruneDead(); } internal static Location[] SnapshotLocations() { SyncFromLoadedLocations(); return Snapshot(Locations); } internal static SpawnArea[] SnapshotSpawnAreas() { SyncFromLoadedLocations(); return Snapshot(SpawnAreas); } internal static RuneStone[] SnapshotRunestones() { SyncFromLoadedLocations(); return Snapshot(Runestones); } internal static Vegvisir[] SnapshotVegvisirs() { SyncFromLoadedLocations(); return Snapshot(Vegvisirs); } internal static Trader[] SnapshotTraders() { SyncFromLoadedLocations(); return Snapshot(Traders); } private static bool AddLive(Dictionary registry, T value) where T : Object { if (registry == null || (Object)(object)value == (Object)null) { return false; } int instanceID; try { instanceID = ((Object)value/*cast due to .constrained prefix*/).GetInstanceID(); } catch { return false; } if (registry.TryGetValue(instanceID, out var value2) && (Object)(object)value2 != (Object)null) { return false; } registry[instanceID] = value; _revision++; return true; } private static void RemoveLive(Dictionary registry, T value) where T : Object { if (registry == null || object.ReferenceEquals(value, null)) { return; } try { if (registry.Remove(((Object)value/*cast due to .constrained prefix*/).GetInstanceID())) { _revision++; } } catch { } } private static void RegisterMany(T[] values, Action register) { if (values != null && register != null) { for (int i = 0; i < values.Length; i++) { register(values[i]); } } } private static T[] Snapshot(Dictionary registry) where T : Object { if (registry == null || registry.Count == 0) { return new T[0]; } List list = new List(registry.Count); List list2 = null; foreach (KeyValuePair item in registry) { T value = item.Value; if ((Object)(object)value == (Object)null) { if (list2 == null) { list2 = new List(); } list2.Add(item.Key); } else { list.Add(value); } } if (list2 != null) { for (int i = 0; i < list2.Count; i++) { registry.Remove(list2[i]); } _revision++; } return list.ToArray(); } private static void PruneDead() { PruneDead(Locations); PruneDead(SpawnAreas); PruneDead(Runestones); PruneDead(Vegvisirs); PruneDead(Traders); } private static void PruneDead(Dictionary registry) where T : Object { if (registry == null || registry.Count == 0) { return; } List list = null; foreach (KeyValuePair item in registry) { if (!((Object)(object)item.Value != (Object)null)) { if (list == null) { list = new List(); } list.Add(item.Key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { registry.Remove(list[i]); } _revision++; } } } internal sealed class OreDiagnosticScanner { private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly HashSet _loggedCandidates = new HashSet(); private float _nextScanTime; internal OreDiagnosticScanner(ManualLogSource log, WayfinderConfig config) { _log = log; _config = config; } internal void Tick() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!_config.DebugLogging.Value || !_config.VerboseRuntimeLogging.Value || (Object)(object)Player.m_localPlayer == (Object)null || Time.unscaledTime < _nextScanTime) { return; } _nextScanTime = Time.unscaledTime + 4f; Vector3 position = ((Component)Player.m_localPlayer).transform.position; Collider[] array = Physics.OverlapSphere(position, 22f, -1, (QueryTriggerInteraction)2); HashSet hashSet = new HashSet(); int num = 0; foreach (Collider val in array) { if ((Object)(object)val == (Object)null) { continue; } Transform val2 = ((Component)val).transform; int num2 = 0; while ((Object)(object)val2 != (Object)null && num2 < 5) { GameObject gameObject = ((Component)val2).gameObject; if (!((Object)(object)gameObject == (Object)null)) { Scene scene = gameObject.scene; if (((Scene)(ref scene)).IsValid()) { int instanceID = ((Object)gameObject).GetInstanceID(); if (hashSet.Add(instanceID) && !_loggedCandidates.Contains(instanceID) && LooksOreLike(gameObject, out var reason)) { _loggedCandidates.Add(instanceID); LogCandidate(gameObject, "nearby:" + reason); num++; if (num >= 12) { break; } } } } num2++; val2 = val2.parent; } if (num >= 12) { break; } } if (num > 0) { _log.LogInfo((object)("ORE-DIAG logged " + num + " new nearby ore/deposit candidate(s).")); } } internal void LogHit(GameObject gameObject, HitData hit, string hook) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!_config.DebugLogging.Value || !_config.VerboseRuntimeLogging.Value || (Object)(object)gameObject == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } try { Vector3 position = gameObject.transform.position; Vector3 position2 = ((Component)Player.m_localPlayer).transform.position; float num = position.x - position2.x; float num2 = position.z - position2.z; if (num * num + num2 * num2 > 1225f) { return; } } catch { } LogCandidate(gameObject, "HIT:" + (hook ?? "unknown")); } private void LogCandidate(GameObject go, string reason) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return; } try { StringBuilder stringBuilder = new StringBuilder(1024); StringBuilder stringBuilder2 = stringBuilder.Append("ORE-DIAG ").Append(reason).Append(" | prefab='") .Append(GetPrefabName(go)) .Append("'") .Append(" object='") .Append(((Object)go).name ?? string.Empty) .Append("'") .Append(" hierarchy='") .Append(GetHierarchy(go.transform)) .Append("'") .Append(" pos="); float x = go.transform.position.x; StringBuilder stringBuilder3 = stringBuilder2.Append(x.ToString("0.0")).Append(","); float y = go.transform.position.y; StringBuilder stringBuilder4 = stringBuilder3.Append(y.ToString("0.0")).Append(","); float z = go.transform.position.z; stringBuilder4.Append(z.ToString("0.0")).Append(" activeSelf=").Append(go.activeSelf) .Append(" activeHierarchy=") .Append(go.activeInHierarchy) .Append(" components=[") .Append(GetComponentNames(go)) .Append("]"); string value = TryGetHoverName(go); if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(" hover='").Append(value).Append("'"); } string visualHints = GetVisualHints(go); if (!string.IsNullOrEmpty(visualHints)) { stringBuilder.Append(" visuals=[").Append(visualHints).Append("]"); } string interestingComponentFields = GetInterestingComponentFields(go); if (!string.IsNullOrEmpty(interestingComponentFields)) { stringBuilder.Append(" fields=[").Append(interestingComponentFields).Append("]"); } _log.LogInfo((object)stringBuilder.ToString()); } catch (Exception ex) { _log.LogWarning((object)("ORE-DIAG failed to describe candidate: " + ex.Message)); } } private static bool LooksOreLike(GameObject go, out string reason) { reason = string.Empty; if ((Object)(object)go == (Object)null) { return false; } string normalized = Normalize((((Object)go).name ?? string.Empty) + " " + GetHierarchy(go.transform)); if (ContainsOreToken(normalized)) { reason = "name"; return true; } Component[] components = go.GetComponents(); foreach (Component val in components) { if (!((Object)(object)val == (Object)null)) { string text = Normalize(((object)val).GetType().Name); if (text.Contains("minerock")) { reason = "component:" + ((object)val).GetType().Name; return true; } } } Renderer[] componentsInChildren = go.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null) { continue; } Material[] sharedMaterials = val2.sharedMaterials; if (sharedMaterials == null) { continue; } foreach (Material val3 in sharedMaterials) { if (!((Object)(object)val3 == (Object)null)) { string normalized2 = Normalize(((Object)val3).name ?? string.Empty); if (ContainsOreToken(normalized2)) { reason = "material:" + ((Object)val3).name; return true; } } } } MeshFilter[] componentsInChildren2 = go.GetComponentsInChildren(true); foreach (MeshFilter val4 in componentsInChildren2) { if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.sharedMesh == (Object)null)) { string normalized3 = Normalize(((Object)val4.sharedMesh).name ?? string.Empty); if (ContainsOreToken(normalized3)) { reason = "mesh:" + ((Object)val4.sharedMesh).name; return true; } } } return false; } private static bool ContainsOreToken(string normalized) { if (string.IsNullOrEmpty(normalized)) { return false; } if (!normalized.Contains("copper") && !normalized.Contains("tin") && !normalized.Contains("silver") && !normalized.Contains("obsidian") && !normalized.Contains("flametal") && !normalized.Contains("deposit") && !normalized.Contains("ore")) { return normalized.Contains("minerock"); } return true; } private static string GetComponentNames(GameObject go) { Component[] components = go.GetComponents(); StringBuilder stringBuilder = new StringBuilder(); foreach (Component val in components) { if (!((Object)(object)val == (Object)null)) { if (stringBuilder.Length > 0) { stringBuilder.Append(","); } stringBuilder.Append(((object)val).GetType().Name); } } return stringBuilder.ToString(); } private static string GetInterestingComponentFields(GameObject go) { Component[] components = go.GetComponents(); StringBuilder stringBuilder = new StringBuilder(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach (Component val in components) { if ((Object)(object)val == (Object)null) { continue; } string name = ((object)val).GetType().Name; string text = Normalize(name); if (!text.Contains("mine") && !text.Contains("drop") && !text.Contains("destruct") && !text.Contains("pickable") && !text.Contains("wear") && !text.Contains("health")) { continue; } FieldInfo[] fields; try { fields = ((object)val).GetType().GetFields(bindingAttr); } catch { continue; } int num = 0; for (int j = 0; j < fields.Length; j++) { if (num >= 8) { break; } FieldInfo fieldInfo = fields[j]; string text2 = Normalize(fieldInfo.Name); if (!text2.Contains("name") && !text2.Contains("drop") && !text2.Contains("item") && !text2.Contains("prefab") && !text2.Contains("health") && !text2.Contains("destroy")) { continue; } object value = null; try { value = fieldInfo.GetValue(val); } catch { } string value2 = RenderValue(value); if (!string.IsNullOrEmpty(value2)) { if (stringBuilder.Length > 0) { stringBuilder.Append("; "); } stringBuilder.Append(name).Append(".").Append(fieldInfo.Name) .Append("=") .Append(value2); num++; } } } return stringBuilder.ToString(); } private static string RenderValue(object value) { if (value == null) { return string.Empty; } if (value is string result) { return result; } GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { return "GameObject(" + GetPrefabName(val) + ")"; } Object val2 = (Object)((value is Object) ? value : null); if (val2 != (Object)null) { return ((object)val2).GetType().Name + "(" + (val2.name ?? string.Empty) + ")"; } Type type = value.GetType(); string name = type.Name; if (name.IndexOf("DropTable", StringComparison.OrdinalIgnoreCase) >= 0) { string text = TryDescribeDropContainer(value); if (!string.IsNullOrEmpty(text)) { return name + "(" + text + ")"; } return name; } if (value is IEnumerable enumerable && !(value is string)) { string text2 = TryDescribeEnumerable(enumerable); if (!string.IsNullOrEmpty(text2)) { return name + "(" + text2 + ")"; } } if (type.IsPrimitive || value is decimal) { return Convert.ToString(value, CultureInfo.InvariantCulture); } return name; } private static string TryDescribeDropContainer(object container) { if (container == null) { return string.Empty; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields; try { fields = container.GetType().GetFields(bindingAttr); } catch { return string.Empty; } foreach (FieldInfo fieldInfo in fields) { string text = Normalize(fieldInfo.Name); if (!text.Contains("drop") && !text.Contains("item")) { continue; } object obj2 = null; try { obj2 = fieldInfo.GetValue(container); } catch { } if (obj2 is IEnumerable enumerable && !(obj2 is string)) { string text2 = TryDescribeEnumerable(enumerable); if (!string.IsNullOrEmpty(text2)) { return text2; } } } return string.Empty; } private static string TryDescribeEnumerable(IEnumerable enumerable) { if (enumerable == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); int num = 0; foreach (object item in enumerable) { if (item == null || num >= 8) { continue; } GameObject val = (GameObject)((item is GameObject) ? item : null); if ((Object)(object)val != (Object)null) { if (stringBuilder.Length > 0) { stringBuilder.Append(","); } stringBuilder.Append(GetPrefabName(val)); num++; continue; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields; try { fields = item.GetType().GetFields(bindingAttr); } catch { continue; } for (int i = 0; i < fields.Length; i++) { if (!typeof(GameObject).IsAssignableFrom(fields[i].FieldType)) { continue; } string text = Normalize(fields[i].Name); if (!text.Contains("item") && !text.Contains("prefab") && !text.Contains("drop")) { continue; } GameObject val2 = null; try { object? value = fields[i].GetValue(item); val2 = (GameObject)((value is GameObject) ? value : null); } catch { } if (!((Object)(object)val2 == (Object)null)) { if (stringBuilder.Length > 0) { stringBuilder.Append(","); } stringBuilder.Append(GetPrefabName(val2)); num++; break; } } } return stringBuilder.ToString(); } private static string TryGetHoverName(GameObject go) { Component[] componentsInParent = go.GetComponentsInParent(true); foreach (Component val in componentsInParent) { if ((Object)(object)val == (Object)null) { continue; } MethodInfo method = ((object)val).GetType().GetMethod("GetHoverName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null || method.ReturnType != typeof(string)) { continue; } try { string text = method.Invoke(val, null) as string; if (string.IsNullOrEmpty(text)) { continue; } try { if (Localization.instance != null) { text = Localization.instance.Localize(text); } } catch { } return text; } catch { } } return string.Empty; } private static string GetVisualHints(GameObject go) { StringBuilder stringBuilder = new StringBuilder(); Renderer[] componentsInChildren = go.GetComponentsInChildren(true); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || val.sharedMaterials == null) { continue; } for (int j = 0; j < val.sharedMaterials.Length; j++) { Material val2 = val.sharedMaterials[j]; if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((Object)val2).name)) { hashSet.Add(((Object)val2).name); } } } int num = 0; foreach (string item in hashSet) { if (num < 8) { if (stringBuilder.Length > 0) { stringBuilder.Append(","); } stringBuilder.Append("mat:").Append(item); num++; continue; } break; } MeshFilter[] componentsInChildren2 = go.GetComponentsInChildren(true); for (int k = 0; k < componentsInChildren2.Length; k++) { if (num >= 12) { break; } MeshFilter val3 = componentsInChildren2[k]; if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.sharedMesh == (Object)null) && !string.IsNullOrEmpty(((Object)val3.sharedMesh).name)) { if (stringBuilder.Length > 0) { stringBuilder.Append(","); } stringBuilder.Append("mesh:").Append(((Object)val3.sharedMesh).name); num++; } } return stringBuilder.ToString(); } private static string GetHierarchy(Transform transform) { if ((Object)(object)transform == (Object)null) { return string.Empty; } List list = new List(); Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { list.Add(((Object)val).name ?? string.Empty); val = val.parent; num++; } list.Reverse(); return string.Join("/", list.ToArray()); } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class PhysicalSpawnerFootprint { internal Vector3 Position; internal float Radius; internal readonly HashSet CreaturePrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); } internal static class PhysicalSpawnerIndex { private static List _cached = new List(); private static float _cacheExpiresAt; private static float _cachedPadding = float.MinValue; internal static void Reset() { _cached = new List(); _cacheExpiresAt = 0f; _cachedPadding = float.MinValue; } internal static List GetCached(float padding, float cacheSeconds) { if (_cached != null && Time.unscaledTime < _cacheExpiresAt && Mathf.Abs(_cachedPadding - padding) < 0.01f) { return _cached; } _cachedPadding = padding; _cached = Build(padding); _cacheExpiresAt = Time.unscaledTime + Mathf.Max(1f, cacheSeconds); return _cached; } internal static List Build(float padding) { //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 list = new List(); SpawnArea[] array = LiveDiscoveryRegistry.SnapshotSpawnAreas(); foreach (SpawnArea val in array) { if (!IsLiveSceneObject((Component)(object)val)) { continue; } List list2 = ExtractSpawnPrefabs(val); if (list2.Count == 0) { continue; } PhysicalSpawnerFootprint physicalSpawnerFootprint = new PhysicalSpawnerFootprint(); physicalSpawnerFootprint.Position = ((Component)val).transform.position; physicalSpawnerFootprint.Radius = Mathf.Max(1f, ReadSpawnRadius(val) + Mathf.Max(0f, padding)); for (int j = 0; j < list2.Count; j++) { string text = Normalize(GetPrefabName(list2[j])); if (!string.IsNullOrEmpty(text)) { physicalSpawnerFootprint.CreaturePrefabs.Add(text); } } if (physicalSpawnerFootprint.CreaturePrefabs.Count > 0) { list.Add(physicalSpawnerFootprint); } } return list; } internal static bool Covers(List footprints, Vector3 position, string creaturePrefab) { if (footprints == null || footprints.Count == 0 || string.IsNullOrEmpty(creaturePrefab)) { return false; } string text = Normalize(creaturePrefab); if (string.IsNullOrEmpty(text)) { return false; } for (int i = 0; i < footprints.Count; i++) { PhysicalSpawnerFootprint physicalSpawnerFootprint = footprints[i]; if (physicalSpawnerFootprint == null) { continue; } float num = physicalSpawnerFootprint.Position.x - position.x; float num2 = physicalSpawnerFootprint.Position.z - position.z; float radius = physicalSpawnerFootprint.Radius; if (num * num + num2 * num2 > radius * radius) { continue; } foreach (string creaturePrefab2 in physicalSpawnerFootprint.CreaturePrefabs) { if (creaturePrefab2 == text) { return true; } if (creaturePrefab2.Length >= 4 && (text.StartsWith(creaturePrefab2, StringComparison.OrdinalIgnoreCase) || creaturePrefab2.StartsWith(text, StringComparison.OrdinalIgnoreCase))) { return true; } } } return false; } internal static bool WithinAny(List footprints, Vector3 position, float exclusionRadius) { if (footprints == null || footprints.Count == 0 || exclusionRadius <= 0f) { return false; } float num = Mathf.Max(1f, exclusionRadius); float num2 = num * num; for (int i = 0; i < footprints.Count; i++) { PhysicalSpawnerFootprint physicalSpawnerFootprint = footprints[i]; if (physicalSpawnerFootprint != null) { float num3 = physicalSpawnerFootprint.Position.x - position.x; float num4 = physicalSpawnerFootprint.Position.z - position.z; if (num3 * num3 + num4 * num4 <= num2) { return true; } } } return false; } private static bool IsLiveSceneObject(Component component) { //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) if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null) { return false; } try { Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).IsValid(); } catch { return false; } } private static float ReadSpawnRadius(SpawnArea area) { try { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields = typeof(SpawnArea).GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType != typeof(float)) { continue; } string text = Normalize(fieldInfo.Name); if (text.Contains("spawnradius") || text == "mradius") { float num = (float)fieldInfo.GetValue(area); if (num > 0f && num < 250f) { return num; } } } } catch { } return 20f; } private static List ExtractSpawnPrefabs(SpawnArea area) { List result = new List(); if ((Object)(object)area == (Object)null) { return result; } try { FieldInfo[] fields = typeof(SpawnArea).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { object value = null; try { value = fields[i].GetValue(area); } catch { } AddSpawnPrefabsFromValue(value, result); } } catch { } return result; } private static void AddSpawnPrefabsFromValue(object value, List result) { if (value == null || result == null || value is string) { return; } GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { AddUnique(result, val); } else { if (!(value is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } GameObject val2 = (GameObject)((item is GameObject) ? item : null); if ((Object)(object)val2 != (Object)null) { AddUnique(result, val2); continue; } Type type = item.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields = type.GetFields(bindingAttr); for (int i = 0; i < fields.Length; i++) { if (!typeof(GameObject).IsAssignableFrom(fields[i].FieldType)) { continue; } string text = Normalize(fields[i].Name); if (!text.Contains("prefab") && !text.Contains("creature") && !text.Contains("spawn")) { continue; } try { object? value2 = fields[i].GetValue(item); GameObject val3 = (GameObject)((value2 is GameObject) ? value2 : null); if ((Object)(object)val3 != (Object)null) { AddUnique(result, val3); } } catch { } } } } } private static void AddUnique(List list, GameObject value) { if ((Object)(object)value == (Object)null) { return; } for (int i = 0; i < list.Count; i++) { if (object.ReferenceEquals(list[i], value)) { return; } } list.Add(value); } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class PortalDiscovery { private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private float _nextScanTime; internal PortalDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database) { _log = log; _config = config; _database = database; } internal void ResetSession() { _nextScanTime = 0f; _database.BeginPortalObservationPass(); } internal void ClearSession() { _nextScanTime = 0f; _database.BeginPortalObservationPass(); } internal void Tick() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!_config.Enabled.Value || !_config.RememberPortals.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null || Time.unscaledTime < _nextScanTime) { return; } _nextScanTime = Time.unscaledTime + Mathf.Max(0.25f, _config.PortalSyncInterval.Value); _database.BeginPortalObservationPass(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(5f, _config.PortalDiscoveryRadius.Value); float radiusSq = num * num; TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); int num2 = 0; foreach (TeleportWorld val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { Vector3 position2 = ((Component)val).transform.position; if (WithinHorizontalRadius(position, position2, radiusSq) && MayReveal(position2) && SyncPortal(val)) { num2++; } } } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && num2 > 0) { _log.LogInfo((object)("Portal sync confirmed " + num2 + " loaded portal(s).")); } } internal void ForceSyncPortal(TeleportWorld portal) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)portal == (Object)null) && !((Object)(object)((Component)portal).gameObject == (Object)null) && _config.Enabled.Value && _config.RememberPortals.Value && _database.WorldUid != 0) { Vector3 position = ((Component)portal).transform.position; if (MayReveal(position)) { SyncPortal(portal); } } } private bool SyncPortal(TeleportWorld portal) { //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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)portal == (Object)null || (Object)(object)((Component)portal).gameObject == (Object)null) { return false; } Vector3 position = ((Component)portal).transform.position; string prefabName = GetPrefabName(((Component)portal).gameObject); string text = Normalize(prefabName); if (text.Contains("dungeon")) { return false; } string portalTag = ReadPortalTag(portal); bool connected; bool connectionKnown = TryReadConnectionState(portal, out connected); string subtype = (string.IsNullOrEmpty(prefabName) ? "Portal" : prefabName); string memberKey = BuildPortalMemberKey(prefabName, position); WayfinderPinRecord wayfinderPinRecord = _database.UpsertPortalObservation(subtype, portalTag, position, memberKey, connectionKnown, connected, _config.PortalNameFromTag.Value); return wayfinderPinRecord != null; } internal void NotifyDestroyed(GameObject source) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || !_config.RemoveDestroyedPortals.Value || _database.WorldUid == 0) { return; } TeleportWorld val = FindPortal(source); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { Vector3 position = ((Component)val).transform.position; string prefabName = GetPrefabName(((Component)val).gameObject); string memberKey = BuildPortalMemberKey(prefabName, position); if (_database.RemovePointByMemberKey(WayfinderPinCategory.Portal, memberKey) && _config.DebugLogging.Value) { _log.LogInfo((object)"Removed destroyed Wayfinder portal marker."); } } } private static TeleportWorld FindPortal(GameObject source) { if ((Object)(object)source == (Object)null) { return null; } Transform val = source.transform; while ((Object)(object)val != (Object)null) { try { TeleportWorld component = ((Component)val).gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { return component; } } catch { } val = val.parent; } try { TeleportWorld[] componentsInChildren = source.GetComponentsInChildren(true); if (componentsInChildren != null && componentsInChildren.Length > 0) { return componentsInChildren[0]; } } catch { } return null; } private static string BuildPortalMemberKey(string prefabName, Vector3 position) { return "portal:" + (prefabName ?? string.Empty) + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static string ReadPortalTag(TeleportWorld portal) { if ((Object)(object)portal == (Object)null) { return string.Empty; } try { string text = portal.GetText(); if (!string.IsNullOrEmpty(text)) { return text.Trim(); } } catch { } Type type = ((object)portal).GetType(); BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string text2 = ReadStringMember(type, portal, bindingFlags, "m_targetTag", "targetTag", "TargetTag", "m_tag", "m_portalTag", "portalTag", "Tag", "tag"); if (!string.IsNullOrEmpty(text2)) { return text2.Trim(); } string text3 = ReadTagFromZdo(type, portal, bindingFlags); if (!string.IsNullOrEmpty(text3)) { return text3.Trim(); } try { FieldInfo[] fields = type.GetFields(bindingFlags); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo == null || fieldInfo.FieldType != typeof(string)) { continue; } string text4 = fieldInfo.Name ?? string.Empty; if (text4.IndexOf("tag", StringComparison.OrdinalIgnoreCase) >= 0) { string text5 = fieldInfo.GetValue(portal) as string; if (!string.IsNullOrEmpty(text5)) { return text5.Trim(); } } } } catch { } return string.Empty; } private static string ReadTagFromZdo(Type portalType, TeleportWorld portal, BindingFlags flags) { object obj = ReadObjectMember(portalType, portal, flags, "m_nview", "m_netView", "nview", "NetView"); if (obj == null) { return string.Empty; } object obj2 = null; try { MethodInfo method = obj.GetType().GetMethod("GetZDO", flags, null, Type.EmptyTypes, null); if (method != null) { obj2 = method.Invoke(obj, null); } } catch { } if (obj2 == null) { return string.Empty; } Type type = obj2.GetType(); try { MethodInfo method2 = type.GetMethod("GetString", flags, null, new Type[2] { typeof(string), typeof(string) }, null); if (method2 != null) { object obj4 = method2.Invoke(obj2, new object[2] { "tag", string.Empty }); string text = obj4 as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } try { Assembly assembly = portalType.Assembly; Type type2 = assembly.GetType("ZDOVars"); if (type2 != null) { int? num = ReadStaticIntMember(type2, "s_tag", "s_portalTag"); if (num.HasValue) { MethodInfo method3 = type.GetMethod("GetString", flags, null, new Type[2] { typeof(int), typeof(string) }, null); if (method3 != null) { object obj6 = method3.Invoke(obj2, new object[2] { num.Value, string.Empty }); string text2 = obj6 as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } } } catch { } return string.Empty; } private static bool TryReadConnectionState(TeleportWorld portal, out bool connected) { connected = false; if ((Object)(object)portal == (Object)null) { return false; } Type type = ((object)portal).GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string[] array = new string[4] { "TargetFound", "IsConnected", "HasTarget", "HasConnectedPortal" }; for (int i = 0; i < array.Length; i++) { try { MethodInfo method = type.GetMethod(array[i], bindingAttr, null, Type.EmptyTypes, null); if (!(method == null) && !(method.ReturnType != typeof(bool))) { object obj = method.Invoke(portal, null); if (obj is bool) { connected = (bool)obj; return true; } } } catch { } } string[] array2 = new string[6] { "m_targetFound", "m_target_found", "m_connected", "m_isConnected", "connected", "IsConnected" }; for (int j = 0; j < array2.Length; j++) { try { FieldInfo field = type.GetField(array2[j], bindingAttr); if (field != null && field.FieldType == typeof(bool)) { connected = (bool)field.GetValue(portal); return true; } } catch { } try { PropertyInfo property = type.GetProperty(array2[j], bindingAttr); if (property != null && property.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0) { connected = (bool)property.GetValue(portal, null); return true; } } catch { } } return false; } private static string ReadStringMember(Type type, object instance, BindingFlags flags, params string[] names) { if (type == null || instance == null || names == null) { return string.Empty; } foreach (string name in names) { try { FieldInfo field = type.GetField(name, flags); if (field != null && field.FieldType == typeof(string)) { string text = field.GetValue(instance) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } try { PropertyInfo property = type.GetProperty(name, flags); if (property != null && property.PropertyType == typeof(string) && property.GetIndexParameters().Length == 0) { string text2 = property.GetValue(instance, null) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } catch { } } return string.Empty; } private static object ReadObjectMember(Type type, object instance, BindingFlags flags, params string[] names) { if (type == null || instance == null || names == null) { return null; } for (int i = 0; i < names.Length; i++) { try { FieldInfo field = type.GetField(names[i], flags); if (field != null) { object value = field.GetValue(instance); if (value != null) { return value; } } } catch { } try { PropertyInfo property = type.GetProperty(names[i], flags); if (property != null && property.GetIndexParameters().Length == 0) { object value2 = property.GetValue(instance, null); if (value2 != null) { return value2; } } } catch { } } return null; } private static int? ReadStaticIntMember(Type type, params string[] names) { if (type == null || names == null) { return null; } BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; for (int i = 0; i < names.Length; i++) { try { FieldInfo field = type.GetField(names[i], bindingAttr); if (field != null && field.FieldType == typeof(int)) { return (int)field.GetValue(null); } } catch { } try { PropertyInfo property = type.GetProperty(names[i], bindingAttr); if (property != null && property.PropertyType == typeof(int) && property.GetIndexParameters().Length == 0) { return (int)property.GetValue(null, null); } } catch { } } return null; } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class ResourceDiscovery { private const float DungeonInteriorY = 3000f; private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly RuntimeIconRegistry _icons; private readonly WayfinderPinDatabase _database; private long _lastCleanupWorldUid = long.MinValue; private string _lastCleanupSignature = string.Empty; private long _lastIconRepairWorldUid = long.MinValue; private int _lastIconRepairRecordCount = -1; internal ResourceDiscovery(ManualLogSource log, WayfinderConfig config, RuntimeIconRegistry icons, WayfinderPinDatabase database) { _log = log; _config = config; _icons = icons; _database = database; } internal void ObservePickable(Pickable pickable) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverInteraction() && !ShouldSuppressDungeonContents() && !((Object)(object)pickable == (Object)null) && !((Object)(object)pickable.m_itemPrefab == (Object)null)) { ObserveItemSource(pickable.m_itemPrefab, ((Component)pickable).transform.position, "pickable:" + StableObjectKey(((Component)pickable).gameObject), WayfinderPinSource.InteractionDiscovery); } } internal void ObservePickableFromScan(Pickable pickable) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverAreaScan() && !ShouldSuppressDungeonContents() && !((Object)(object)pickable == (Object)null) && !((Object)(object)pickable.m_itemPrefab == (Object)null)) { ObserveItemSource(pickable.m_itemPrefab, ((Component)pickable).transform.position, "pickable:" + StableObjectKey(((Component)pickable).gameObject), WayfinderPinSource.AreaScan); } } internal void ObserveMineRock(MineRock rock, HitData hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverInteraction() && !ShouldSuppressDungeonContents() && !((Object)(object)rock == (Object)null) && WasHitByLocalPlayer(hit)) { GameObject val = ResolveMineableResourceItem(((Component)rock).gameObject, rock.m_name, rock.m_dropItems); if ((Object)(object)val != (Object)null) { ObserveItemSource(val, ((Component)rock).transform.position, "minerock:" + StableObjectKey(((Component)rock).gameObject), WayfinderPinSource.InteractionDiscovery); } } } internal void ObserveMineRockFromScan(MineRock rock) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverAreaScan() && !ShouldSuppressDungeonContents() && !((Object)(object)rock == (Object)null) && !IsWishboneOnlyMineable(((Component)rock).gameObject, rock.m_name)) { GameObject val = ResolveMineableResourceItem(((Component)rock).gameObject, rock.m_name, rock.m_dropItems); if ((Object)(object)val != (Object)null) { ObserveItemSource(val, ((Component)rock).transform.position, "minerock:" + StableObjectKey(((Component)rock).gameObject), WayfinderPinSource.AreaScan); } } } internal void ObserveMineRock5(MineRock5 rock, HitData hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverInteraction() && !ShouldSuppressDungeonContents() && !((Object)(object)rock == (Object)null) && WasHitByLocalPlayer(hit)) { GameObject val = ResolveMineableResourceItem(((Component)rock).gameObject, rock.m_name, rock.m_dropItems); if ((Object)(object)val != (Object)null) { ObserveItemSource(val, ((Component)rock).transform.position, "minerock5:" + StableObjectKey(((Component)rock).gameObject), WayfinderPinSource.InteractionDiscovery); } } } internal void ObserveMineRock5FromScan(MineRock5 rock) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (CanDiscoverAreaScan() && !ShouldSuppressDungeonContents() && !((Object)(object)rock == (Object)null) && !IsWishboneOnlyMineable(((Component)rock).gameObject, rock.m_name)) { GameObject val = ResolveMineableResourceItem(((Component)rock).gameObject, rock.m_name, rock.m_dropItems); if ((Object)(object)val != (Object)null) { ObserveItemSource(val, ((Component)rock).transform.position, "minerock5:" + StableObjectKey(((Component)rock).gameObject), WayfinderPinSource.AreaScan); } } } internal bool ObserveVisibleMineableFromScan(GameObject sourceObject, string resourcePrefab) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!CanDiscoverAreaScan() || (Object)(object)sourceObject == (Object)null || string.IsNullOrEmpty(resourcePrefab)) { return false; } Vector3 position = sourceObject.transform.position; if (ShouldSuppressResourcePosition(position)) { return false; } if (IsWishboneOnlyResourcePrefab(resourcePrefab)) { return false; } GameObject val = FindRepresentativeResourceItem(resourcePrefab); if ((Object)(object)val == (Object)null) { return false; } ObserveItemSource(val, position, "visualore:" + resourcePrefab + ":" + StableObjectKey(sourceObject), WayfinderPinSource.AreaScan); return true; } internal bool ObserveDropOnDestroyedFromScan(DropOnDestroyed dropper) { //IL_0019: 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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (!CanDiscoverAreaScan() || (Object)(object)dropper == (Object)null) { return false; } Vector3 position = ((Component)dropper).transform.position; if (ShouldSuppressResourcePosition(position)) { return false; } DropTable dropOnDestroyedTable = GetDropOnDestroyedTable(dropper); if (!IsKnownMineableDropNode(((Component)dropper).gameObject, dropOnDestroyedTable)) { return false; } if (IsWishboneOnlyMineable(((Component)dropper).gameObject, ((Object)((Component)dropper).gameObject).name)) { return false; } GameObject val = ResolveMineableResourceItem(((Component)dropper).gameObject, GetHoverTextOrName(((Component)dropper).gameObject), dropOnDestroyedTable); if ((Object)(object)val == (Object)null) { return false; } ObserveItemSource(val, position, "destroyeddrop:" + StableObjectKey(((Component)dropper).gameObject), WayfinderPinSource.AreaScan); return true; } internal void ObserveDestructibleDrop(GameObject hitObject, HitData hit) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!CanDiscoverInteraction() || (Object)(object)hitObject == (Object)null || !WasHitByLocalPlayer(hit)) { return; } DropOnDestroyed val = hitObject.GetComponent(); if ((Object)(object)val == (Object)null) { val = hitObject.GetComponentInParent(); } if ((Object)(object)val == (Object)null) { val = hitObject.GetComponentInChildren(); } if ((Object)(object)val == (Object)null) { return; } Vector3 position = ((Component)val).transform.position; if (ShouldSuppressResourcePosition(position)) { return; } DropTable dropOnDestroyedTable = GetDropOnDestroyedTable(val); if (IsKnownMineableDropNode(((Component)val).gameObject, dropOnDestroyedTable)) { GameObject val2 = ResolveMineableResourceItem(((Component)val).gameObject, GetHoverTextOrName(((Component)val).gameObject), dropOnDestroyedTable); if (!((Object)(object)val2 == (Object)null)) { ObserveItemSource(val2, position, "destroyeddrop:" + StableObjectKey(((Component)val).gameObject), WayfinderPinSource.InteractionDiscovery); } } } internal void ApplyLiveFilterCleanup() { if (_database.WorldUid == 0) { return; } string text = _config.AutoPinStone.Value + "|" + _config.AutoPinBranches.Value + "|" + _config.AutoPinFlint.Value + "|" + _config.AutoPinDandelions.Value + "|" + _config.ScanDungeonContents.Value; if (_lastCleanupWorldUid != _database.WorldUid || !string.Equals(_lastCleanupSignature, text, StringComparison.Ordinal)) { _lastCleanupWorldUid = _database.WorldUid; _lastCleanupSignature = text; if (!_config.AutoPinStone.Value) { _database.RemoveAutoResourceSubtype("Stone"); } if (!_config.AutoPinBranches.Value) { _database.RemoveAutoResourceSubtype("Wood"); _database.RemoveAutoResourceSubtype("Branch"); } if (!_config.AutoPinFlint.Value) { _database.RemoveAutoResourceSubtype("Flint"); } if (!_config.AutoPinDandelions.Value) { _database.RemoveAutoResourceSubtype("Dandelion"); } _database.RemoveResourceMembers((string subtype, Vector3 position) => IsLowValueResource(subtype) && IsNearGeneratedSite(position, 12f)); _database.RemoveAutoResourceSubtypeBySource("Silver", WayfinderPinSource.AreaScan); _database.RemoveAutoResourceSubtypeBySource("SilverOre", WayfinderPinSource.AreaScan); _database.RemoveAutoResourceSubtypeBySource("IronScrap", WayfinderPinSource.AreaScan); _database.RemoveAutoResourceSubtypeBySource("ScrapIron", WayfinderPinSource.AreaScan); _database.RemoveAutoResourceSubtypeAboveY("IronScrap", 3000f); _database.RemoveAutoResourceSubtypeAboveY("ScrapIron", 3000f); if (!_config.ScanDungeonContents.Value) { _database.RemoveAutoDungeonResources(3000f); } } } private bool CanDiscoverInteraction() { if (_config.Enabled.Value && _config.InteractionDiscovery.Value) { return _database.WorldUid != 0; } return false; } private bool CanDiscoverAreaScan() { if (_config.Enabled.Value && _config.AreaScanning.Value) { return _database.WorldUid != 0; } return false; } private bool ShouldSuppressDungeonContents() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanDungeonContents.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { return ((Character)localPlayer).InInterior(); } catch { return (Object)(object)((Component)localPlayer).transform != (Object)null && ((Component)localPlayer).transform.position.y > 3000f; } } private void ObserveItemSource(GameObject itemPrefab, Vector3 position, string memberKey, WayfinderPinSource source) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemPrefab == (Object)null || ShouldSuppressResourcePosition(position)) { return; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return; } string text = CleanPrefabName(((Object)itemPrefab).name ?? string.Empty); if (string.IsNullOrEmpty(text) || !ShouldAutoPinResource(text) || ShouldSuppressOreObservation(text, position, source)) { return; } string name = component.m_itemData.m_shared.m_name; string text2 = Localize(name, text); string text3 = CanonicalResourceKey(text); if (IsLowValueResource(text3) && IsNearGeneratedSite(position, 12f)) { return; } string text4 = ResolveResourceIconKey(text); if (string.IsNullOrEmpty(text4)) { text4 = "item:" + text; if (!_icons.TryGet(text4, out var _)) { _icons.MarkDirty(); } } WayfinderPinRecord wayfinderPinRecord = _database.AddResourceObservation(text3, text2, position, text4, source, memberKey); if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && wayfinderPinRecord != null) { _log.LogInfo((object)("Remembered resource: " + text2 + " (cluster count " + wayfinderPinRecord.count + ", icon " + text4 + ", pos " + position.x.ToString("0.0") + "," + position.z.ToString("0.0") + ", member " + memberKey + ")")); } } internal void RepairKnownResourceIcons(bool force = false) { if (!_icons.IsBuilt || _database.WorldUid == 0) { return; } int num = ((_database.Records != null) ? _database.Records.Count : 0); if (!force && _lastIconRepairWorldUid == _database.WorldUid && _lastIconRepairRecordCount == num) { return; } _lastIconRepairWorldUid = _database.WorldUid; _lastIconRepairRecordCount = num; List list = new List(); IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != WayfinderPinCategory.Resource) { continue; } string text = ResolveResourceIconKey(wayfinderPinRecord.subtype); if (!string.IsNullOrEmpty(text)) { if (!string.Equals(wayfinderPinRecord.iconKey, text, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = text; list.Add(wayfinderPinRecord); } string text2 = CanonicalResourceKey(wayfinderPinRecord.subtype); if (!string.IsNullOrEmpty(text2) && !string.Equals(wayfinderPinRecord.subtype, text2, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.subtype = text2; list.Add(wayfinderPinRecord); } } } HashSet hashSet = new HashSet(StringComparer.Ordinal); for (int j = 0; j < list.Count; j++) { WayfinderPinRecord wayfinderPinRecord2 = list[j]; if (wayfinderPinRecord2 != null && !string.IsNullOrEmpty(wayfinderPinRecord2.id) && hashSet.Add(wayfinderPinRecord2.id)) { _database.AddOrUpdate(wayfinderPinRecord2); } } if (_config.DebugLogging.Value && hashSet.Count > 0) { _log.LogInfo((object)("Repaired " + hashSet.Count + " persisted resource icon records.")); } } private bool IsNearGeneratedSite(Vector3 position, float radius) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList records = _database.Records; if (records == null || radius <= 0f) { return false; } float num = radius * radius; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && WayfinderIconPolicy.IsGeneratedLocationCategory(wayfinderPinRecord.category)) { float num2 = wayfinderPinRecord.Position.x - position.x; float num3 = wayfinderPinRecord.Position.z - position.z; if (num2 * num2 + num3 * num3 <= num) { return true; } } } return false; } private static bool IsLowValueResource(string subtype) { string text = Normalize(subtype); switch (text) { default: return text == "dandelion"; case "stone": case "wood": case "branch": case "flint": return true; } } private string ResolveResourceIconKey(string resourceName) { if (!_icons.IsBuilt || string.IsNullOrEmpty(resourceName)) { return string.Empty; } string text = Normalize(resourceName); if (text.Contains("copper")) { string text2 = ExactIconKey("CopperOre"); if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("CopperOre", "$item_copperore", "Copper"); } return text2; } if (text.Contains("tin")) { string text2 = ExactIconKey("TinOre"); if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("TinOre", "$item_tinore", "Tin"); } return text2; } if (text.Contains("obsidian")) { string text2 = ExactIconKey("Obsidian"); if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("Obsidian"); } return text2; } if (text.Contains("silver")) { string text2 = ExactIconKey("SilverOre"); if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("SilverOre", "$item_silverore", "Silver"); } return text2; } if (text.Contains("ironscrap") || text.Contains("scrapiron") || text.Contains("muddy")) { string text2 = ExactIconKey("IronScrap"); if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("IronScrap", "ScrapIron"); } return text2; } if (text.Contains("flametal")) { string text2 = ExactIconKey("FlametalOreNew"); if (string.IsNullOrEmpty(text2)) { text2 = ExactIconKey("FlametalOre"); } if (string.IsNullOrEmpty(text2)) { return _icons.FindBestItemKey("FlametalOreNew", "FlametalOre", "FlametalNew", "Flametal"); } return text2; } if (text.Contains("blackmarble")) { return ExactOrBest("BlackMarble"); } if (text.Contains("softtissue")) { return ExactOrBest("SoftTissue"); } if (text.Contains("crystal")) { return ExactOrBest("Crystal"); } if (text.Contains("guck")) { return ExactOrBest("Guck"); } return _icons.FindBestItemKey(resourceName); } private string ExactOrBest(string prefabName) { string text = ExactIconKey(prefabName); if (string.IsNullOrEmpty(text)) { return _icons.FindBestItemKey(prefabName); } return text; } private string ExactIconKey(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return string.Empty; } string text = "item:" + prefabName; if (!_icons.TryGet(text, out var sprite) || !((Object)(object)sprite != (Object)null)) { return string.Empty; } return text; } private static string CanonicalResourceKey(string resourceName) { if (string.IsNullOrEmpty(resourceName)) { return resourceName; } string text = Normalize(resourceName); if (text.Contains("copper")) { return "Copper"; } if (text.Contains("tin")) { return "Tin"; } if (text.Contains("obsidian")) { return "Obsidian"; } if (text.Contains("silver")) { return "Silver"; } if (text.Contains("ironscrap") || text.Contains("scrapiron") || text.Contains("muddy")) { return "IronScrap"; } if (text.Contains("flametal")) { return "Flametal"; } if (text.Contains("blackmarble")) { return "BlackMarble"; } if (text.Contains("softtissue")) { return "SoftTissue"; } if (text.Contains("crystal")) { return "Crystal"; } if (text.Contains("guck")) { return "Guck"; } return resourceName; } private bool ShouldAutoPinResource(string prefabName) { if (string.Equals(prefabName, "Stone", StringComparison.OrdinalIgnoreCase)) { return _config.AutoPinStone.Value; } if (string.Equals(prefabName, "Wood", StringComparison.OrdinalIgnoreCase) || string.Equals(prefabName, "Branch", StringComparison.OrdinalIgnoreCase) || prefabName.IndexOf("branch", StringComparison.OrdinalIgnoreCase) >= 0) { return _config.AutoPinBranches.Value; } if (string.Equals(prefabName, "Flint", StringComparison.OrdinalIgnoreCase)) { return _config.AutoPinFlint.Value; } if (string.Equals(prefabName, "Dandelion", StringComparison.OrdinalIgnoreCase)) { return _config.AutoPinDandelions.Value; } return true; } private bool ShouldSuppressResourcePosition(Vector3 position) { if (!_config.ScanDungeonContents.Value) { return position.y > 3000f; } return false; } private static DropTable GetDropOnDestroyedTable(DropOnDestroyed dropper) { if ((Object)(object)dropper == (Object)null) { return null; } try { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo field = typeof(DropOnDestroyed).GetField("m_dropWhenDestroyed", bindingAttr); if (field != null) { object value = field.GetValue(dropper); DropTable val = (DropTable)((value is DropTable) ? value : null); if (val != null) { return val; } } FieldInfo[] fields = typeof(DropOnDestroyed).GetFields(bindingAttr); for (int i = 0; i < fields.Length; i++) { if (typeof(DropTable).IsAssignableFrom(fields[i].FieldType)) { object? value2 = fields[i].GetValue(dropper); DropTable val2 = (DropTable)((value2 is DropTable) ? value2 : null); if (val2 != null) { return val2; } } } } catch { } return null; } private static bool IsKnownMineableDropNode(GameObject worldObject, DropTable table) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) string text = Normalize(((Object)(object)worldObject == (Object)null) ? string.Empty : CleanPrefabName(((Object)worldObject).name ?? string.Empty)); if (text.Contains("tin") || text.Contains("copper") || text.Contains("silver") || text.Contains("obsidian") || text.Contains("flametal") || text.Contains("meteorite") || text.Contains("muddy") || text.Contains("scrapiron") || text.Contains("ironscrap") || text.Contains("blackmarble") || text.Contains("softtissue") || text.Contains("crystal") || text.Contains("guck")) { return true; } if (table == null || table.m_drops == null) { return false; } for (int i = 0; i < table.m_drops.Count; i++) { GameObject item = table.m_drops[i].m_item; if (!((Object)(object)item == (Object)null)) { string text2 = Normalize(CleanPrefabName(((Object)item).name ?? string.Empty)); if (text2.Contains("tinore") || text2.Contains("copperore") || text2.Contains("silverore") || text2.Contains("obsidian") || text2.Contains("flametal") || text2.Contains("ironscrap") || text2.Contains("scrapiron") || text2.Contains("blackmarble") || text2.Contains("softtissue") || text2.Contains("crystal") || text2.Contains("guck")) { return true; } } } return false; } private static string GetHoverTextOrName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { HoverText component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { MethodInfo method = typeof(HoverText).GetMethod("GetHoverText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.GetParameters().Length == 0) { object obj = method.Invoke(component, null); string text = obj as string; if (!string.IsNullOrEmpty(text)) { return text; } } } } catch { } return ((Object)gameObject).name ?? string.Empty; } private static GameObject FindRepresentativeResourceItem(string resourcePrefab) { string text = Normalize(resourcePrefab); if (text.Contains("copper")) { return FindObjectDbItem("CopperOre", "Copper"); } if (text.Contains("tin")) { return FindObjectDbItem("TinOre", "Tin"); } if (text.Contains("obsidian")) { return FindObjectDbItem("Obsidian"); } if (text.Contains("flametal")) { return FindObjectDbItem("FlametalOreNew", "FlametalOre", "FlametalNew", "Flametal"); } if (text.Contains("blackmarble")) { return FindObjectDbItem("BlackMarble"); } if (text.Contains("softtissue")) { return FindObjectDbItem("SoftTissue"); } if (text.Contains("crystal")) { return FindObjectDbItem("Crystal"); } if (text.Contains("guck")) { return FindObjectDbItem("Guck"); } if (text.Contains("silver")) { return FindObjectDbItem("SilverOre", "Silver"); } if (IsIronIdentity(text)) { return FindObjectDbItem("IronScrap", "ScrapIron"); } return FindObjectDbItem(resourcePrefab); } private static bool IsWishboneOnlyMineable(GameObject worldObject, string configuredName) { string text = (((Object)(object)worldObject == (Object)null) ? string.Empty : CleanPrefabName(((Object)worldObject).name ?? string.Empty)); string text2 = Normalize(text + " " + (configuredName ?? string.Empty)); if (!text2.Contains("silver")) { return IsIronIdentity(text2); } return true; } private static bool IsWishboneOnlyResourcePrefab(string prefabName) { string text = Normalize(prefabName); if (!(text == "silver") && !(text == "silverore")) { return IsIronIdentity(text); } return true; } private static bool IsIronIdentity(string normalized) { if (string.IsNullOrEmpty(normalized)) { return false; } if (!normalized.Contains("ironscrap") && !normalized.Contains("scrapiron") && !normalized.Contains("muddyscrap") && !normalized.Contains("muddypile") && !normalized.Contains("muddy")) { return normalized.Contains("buriediron"); } return true; } private bool ShouldSuppressOreObservation(string prefabName, Vector3 position, WayfinderPinSource source) { string text = Normalize(prefabName); if (source == WayfinderPinSource.AreaScan && (text == "silver" || text == "silverore" || IsIronIdentity(text))) { return true; } if (IsIronIdentity(text) && position.y > 3000f) { return true; } return false; } private static GameObject ResolveMineableResourceItem(GameObject worldObject, string configuredName, DropTable table) { string text = (((Object)(object)worldObject == (Object)null) ? string.Empty : CleanPrefabName(((Object)worldObject).name ?? string.Empty)); string text2 = Normalize(text + " " + (configuredName ?? string.Empty)); GameObject val = null; if (text2.Contains("copper")) { val = FindObjectDbItem("CopperOre", "Copper"); } else if (text2.Contains("tin")) { val = FindObjectDbItem("TinOre", "Tin"); } else if (text2.Contains("silver")) { val = FindObjectDbItem("SilverOre", "Silver"); } else if (text2.Contains("obsidian")) { val = FindObjectDbItem("Obsidian"); } else if (text2.Contains("flametal")) { val = FindObjectDbItem("FlametalOreNew", "FlametalOre", "FlametalNew", "Flametal"); } else if (text2.Contains("muddy") || text2.Contains("scrapiron") || text2.Contains("ironscrap")) { val = FindObjectDbItem("IronScrap", "ScrapIron"); } else if (text2.Contains("blackmarble")) { val = FindObjectDbItem("BlackMarble"); } else if (text2.Contains("softtissue")) { val = FindObjectDbItem("SoftTissue"); } else if (text2.Contains("crystal")) { val = FindObjectDbItem("Crystal"); } else if (text2.Contains("guck")) { val = FindObjectDbItem("Guck"); } if (!((Object)(object)val != (Object)null)) { return FindPrimaryDrop(table); } return val; } private static GameObject FindObjectDbItem(params string[] prefabNames) { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || prefabNames == null) { return null; } foreach (string text in prefabNames) { if (string.IsNullOrEmpty(text)) { continue; } try { GameObject itemPrefab = instance.GetItemPrefab(text); if ((Object)(object)itemPrefab != (Object)null) { return itemPrefab; } } catch { } if (instance.m_items == null) { continue; } for (int j = 0; j < instance.m_items.Count; j++) { GameObject val = instance.m_items[j]; if (!((Object)(object)val == (Object)null)) { string a = CleanPrefabName(((Object)val).name ?? string.Empty); if (string.Equals(a, text, StringComparison.OrdinalIgnoreCase)) { return val; } } } } return null; } private static bool IsSilverMineable(GameObject worldObject, string configuredName) { string text = (((Object)(object)worldObject == (Object)null) ? string.Empty : CleanPrefabName(((Object)worldObject).name ?? string.Empty)); string text2 = Normalize(text + " " + (configuredName ?? string.Empty)); return text2.Contains("silver"); } private static bool IsSilverResourcePrefab(string prefabName) { string text = Normalize(prefabName); if (!(text == "silver")) { return text == "silverore"; } return true; } private static GameObject FindPrimaryDrop(DropTable table) { //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) if (table == null || table.m_drops == null || table.m_drops.Count == 0) { return null; } GameObject val = null; for (int i = 0; i < table.m_drops.Count; i++) { DropData val2 = table.m_drops[i]; if (!((Object)(object)val2.m_item == (Object)null)) { if ((Object)(object)val == (Object)null) { val = val2.m_item; } string prefabName = CleanPrefabName(((Object)val2.m_item).name ?? string.Empty); if (!IsGenericRockByproduct(prefabName)) { return val2.m_item; } } } return val; } private static bool IsGenericRockByproduct(string prefabName) { return prefabName.Equals("Stone", StringComparison.OrdinalIgnoreCase); } private static bool WasHitByLocalPlayer(HitData hit) { if (hit == null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } Character attacker = hit.GetAttacker(); return object.ReferenceEquals(attacker, Player.m_localPlayer); } private static string Localize(string rawName, string fallback) { if (string.IsNullOrEmpty(rawName)) { return fallback; } try { if (Localization.instance != null) { return Localization.instance.Localize(rawName); } } catch { } return rawName; } private static string StableObjectKey(GameObject gameObject) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gameObject == (Object)null) { return "missing"; } Vector3 position = gameObject.transform.position; return CleanPrefabName(((Object)gameObject).name ?? "resource") + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static string CleanPrefabName(string value) { if (!string.IsNullOrEmpty(value)) { return value.Replace("(Clone)", string.Empty).Trim(); } return string.Empty; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class RunestoneDiscovery { private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private float _nextScanTime; private long _cleanedWorldUid = long.MinValue; private long _cacheWorldUid = long.MinValue; private RuneStone[] _cachedRunestones = (RuneStone[])(object)new RuneStone[0]; private Vegvisir[] _cachedVegvisirs = (Vegvisir[])(object)new Vegvisir[0]; private Vector3 _runestoneCacheCenter; private Vector3 _vegvisirCacheCenter; private float _nextRunestoneCacheRefresh; private float _nextVegvisirCacheRefresh; private bool _runestoneCacheInitialized; private bool _vegvisirCacheInitialized; private bool _refreshVegvisirNext; private bool _scanInProgress; private int _scanPhase; private int _scanCursor; private Vector3 _scanCenter; private float _scanRadiusSq; private int _scanRemembered; private long _scanWorldUid = long.MinValue; internal RunestoneDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database) { _log = log; _config = config; _database = database; } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || !_config.RememberGeneratedStructures.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (_scanWorldUid != _database.WorldUid) { _scanWorldUid = _database.WorldUid; ResetActiveScan(); } long startTimestamp = DiscoveryWorkBudget.Start(); if (_cleanedWorldUid != _database.WorldUid) { CleanupRememberedRunestones(); _cleanedWorldUid = _database.WorldUid; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } int num = 0; while (_scanPhase < 2) { if (_scanPhase == 0) { RuneStone[] array = _cachedRunestones; if (array == null) { array = (RuneStone[])(object)new RuneStone[0]; } while (_scanCursor < array.Length) { RuneStone stone = array[_scanCursor++]; num++; ProcessRunestone(stone); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 1; _scanCursor = 0; continue; } Vegvisir[] array2 = _cachedVegvisirs; if (array2 == null) { array2 = (Vegvisir[])(object)new Vegvisir[0]; } while (_scanCursor < array2.Length) { Vegvisir vegvisir = array2[_scanCursor++]; num++; ProcessVegvisir(vegvisir); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 2; } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && _scanRemembered > 0) { _log.LogInfo((object)("Runestone/Vegvisir discovery remembered/confirmed " + _scanRemembered + " stone(s).")); } _scanInProgress = false; _scanCursor = 0; _scanPhase = 0; return false; } private void BeginScan() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.StaticDiscoveryInterval.Value); _scanCenter = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(5f, _config.StructureDiscoveryRadius.Value); _scanRadiusSq = num * num; _scanRemembered = 0; _scanCursor = 0; _scanPhase = 0; RefreshOneSceneCacheIfNeeded(_scanCenter); _scanInProgress = true; } private void ProcessRunestone(RuneStone stone) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)stone == (Object)null || (Object)(object)((Component)stone).gameObject == (Object)null || (Object)(object)((Component)stone).GetComponent() != (Object)null || (Object)(object)((Component)stone).GetComponentInParent() != (Object)null) { return; } Vector3 position = ((Component)stone).transform.position; if (WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) && MayReveal(position)) { string prefabName = GetPrefabName(((Component)stone).gameObject); if (!IsIgnoredStartStone(((Component)stone).transform, prefabName) && RememberRunestone(prefabName, "Runestone", position) != null) { _scanRemembered++; } } } private void ProcessVegvisir(Vegvisir vegvisir) { //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vegvisir == (Object)null || (Object)(object)((Component)vegvisir).gameObject == (Object)null) { return; } Vector3 position = ((Component)vegvisir).transform.position; if (WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) && MayReveal(position)) { string prefabName = GetPrefabName(((Component)vegvisir).gameObject); if (!IsIgnoredStartStone(((Component)vegvisir).transform, prefabName) && RememberRunestone(prefabName, "Vegvisir", position) != null) { _scanRemembered++; } } } private void ResetActiveScan() { _scanInProgress = false; _scanPhase = 0; _scanCursor = 0; } private void RefreshOneSceneCacheIfNeeded(Vector3 center) { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) if (_cacheWorldUid != _database.WorldUid) { _cacheWorldUid = _database.WorldUid; _cachedRunestones = (RuneStone[])(object)new RuneStone[0]; _cachedVegvisirs = (Vegvisir[])(object)new Vegvisir[0]; _runestoneCacheInitialized = false; _vegvisirCacheInitialized = false; _refreshVegvisirNext = false; _nextRunestoneCacheRefresh = 0f; _nextVegvisirCacheRefresh = 0f; } float num = Mathf.Max(12f, _config.StaticDiscoveryInterval.Value * 3f); float num2 = center.x - _runestoneCacheCenter.x; float num3 = center.z - _runestoneCacheCenter.z; bool flag = num2 * num2 + num3 * num3 >= 4096f; bool flag2 = !_runestoneCacheInitialized || _cachedRunestones == null || Time.unscaledTime >= _nextRunestoneCacheRefresh || flag; num2 = center.x - _vegvisirCacheCenter.x; num3 = center.z - _vegvisirCacheCenter.z; bool flag3 = num2 * num2 + num3 * num3 >= 4096f; bool flag4 = !_vegvisirCacheInitialized || _cachedVegvisirs == null || Time.unscaledTime >= _nextVegvisirCacheRefresh || flag3; if (flag2 && flag4) { if (_refreshVegvisirNext) { _cachedVegvisirs = LiveDiscoveryRegistry.SnapshotVegvisirs(); _vegvisirCacheInitialized = true; _vegvisirCacheCenter = center; _nextVegvisirCacheRefresh = Time.unscaledTime + num; } else { _cachedRunestones = LiveDiscoveryRegistry.SnapshotRunestones(); _runestoneCacheInitialized = true; _runestoneCacheCenter = center; _nextRunestoneCacheRefresh = Time.unscaledTime + num; } _refreshVegvisirNext = !_refreshVegvisirNext; } else if (flag2) { _cachedRunestones = LiveDiscoveryRegistry.SnapshotRunestones(); _runestoneCacheInitialized = true; _runestoneCacheCenter = center; _nextRunestoneCacheRefresh = Time.unscaledTime + num; } else if (flag4) { _cachedVegvisirs = LiveDiscoveryRegistry.SnapshotVegvisirs(); _vegvisirCacheInitialized = true; _vegvisirCacheCenter = center; _nextVegvisirCacheRefresh = Time.unscaledTime + num; } } private void CleanupRememberedRunestones() { IReadOnlyList records = _database.Records; if (records == null || records.Count == 0) { return; } List list = new List(); List list2 = new List(); for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.PointOfInterest && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported && wayfinderPinRecord.source != WayfinderPinSource.Vanilla) { string name = (wayfinderPinRecord.subtype ?? string.Empty) + " " + (wayfinderPinRecord.displayName ?? string.Empty); if (WayfinderIconPolicy.IsBossGuidanceRunestoneName(name)) { list.Add(wayfinderPinRecord.id); } else if ((string.Equals(wayfinderPinRecord.iconKey, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase) || WayfinderIconPolicy.IsRunestoneName(name)) && !string.Equals(wayfinderPinRecord.iconKey, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = "wayfinder:runestone"; list2.Add(wayfinderPinRecord); } } } for (int j = 0; j < list.Count; j++) { _database.Remove(list[j], suppressAutoRediscovery: false); } for (int k = 0; k < list2.Count; k++) { _database.AddOrUpdate(list2[k]); } if (_config.DebugLogging.Value && (list.Count > 0 || list2.Count > 0)) { _log.LogInfo((object)("Runestone cleanup removed " + list.Count + " boss-guidance marker(s) and repaired " + list2.Count + " runestone icon record(s).")); } } private WayfinderPinRecord RememberRunestone(string prefabName, string subtype, Vector3 position) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) string memberKey = "runestone:" + prefabName + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); return _database.AddPointObservation((!string.IsNullOrEmpty(prefabName)) ? prefabName : (string.Equals(subtype, "Vegvisir", StringComparison.OrdinalIgnoreCase) ? "Vegvisir" : "Runestone"), subtype, position, "wayfinder:runestone", WayfinderPinCategory.PointOfInterest, WayfinderPinSource.InteractionDiscovery, memberKey); } private static bool IsIgnoredStartStone(Transform transform, string prefabName) { if ((Object)(object)transform == (Object)null) { return true; } if (WayfinderIconPolicy.IsBossGuidanceRunestoneName(prefabName)) { return true; } Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 16) { if (WayfinderIconPolicy.IsBossGuidanceRunestoneName(((Object)(object)((Component)val).gameObject == (Object)null) ? string.Empty : ((Object)((Component)val).gameObject).name)) { return true; } val = val.parent; num++; } return false; } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } } internal sealed class SpawnerDiscovery { private const float DungeonInteriorY = 3000f; private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private readonly RuntimeIconRegistry _icons; private float _nextScanTime; private SpawnArea[] _cachedSpawnAreas = (SpawnArea[])(object)new SpawnArea[0]; private Vector3 _spawnAreaCacheCenter; private float _nextSpawnAreaCacheRefresh; private bool _spawnAreaCacheInitialized; private long _spawnAreaCacheWorldUid = long.MinValue; private bool _scanInProgress; private SpawnArea[] _scanAreas = (SpawnArea[])(object)new SpawnArea[0]; private int _scanCursor; private Vector3 _scanCenter; private float _scanRadiusSq; private int _scanRemembered; private long _scanWorldUid = long.MinValue; private static readonly FieldInfo[] SpawnAreaFields = typeof(SpawnArea).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal SpawnerDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database, RuntimeIconRegistry icons) { _log = log; _config = config; _database = database; _icons = icons; } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || !_config.RememberPhysicalSpawners.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (_scanWorldUid != _database.WorldUid) { _scanWorldUid = _database.WorldUid; ResetActiveScan(); } long startTimestamp = DiscoveryWorkBudget.Start(); if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } int num = 0; while (_scanCursor < _scanAreas.Length) { SpawnArea area = _scanAreas[_scanCursor++]; num++; ProcessSpawnArea(area); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && _scanRemembered > 0) { _log.LogInfo((object)("Physical spawner discovery remembered/confirmed " + _scanRemembered + " spawner locations.")); } _scanInProgress = false; _scanAreas = (SpawnArea[])(object)new SpawnArea[0]; _scanCursor = 0; return false; } private void BeginScan() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.StaticDiscoveryInterval.Value); _scanCenter = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(5f, _config.SpawnerDiscoveryRadius.Value); _scanRadiusSq = num * num; _scanAreas = GetCachedSpawnAreas(_scanCenter); if (_scanAreas == null) { _scanAreas = (SpawnArea[])(object)new SpawnArea[0]; } _scanCursor = 0; _scanRemembered = 0; _scanInProgress = true; } private void ProcessSpawnArea(SpawnArea area) { //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_0033: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)area == (Object)null || (Object)(object)((Component)area).gameObject == (Object)null) { return; } Vector3 position = ((Component)area).transform.position; if (position.y > 3000f || !WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) || !MayReveal(position)) { return; } List list = ExtractSpawnPrefabs(area); if (list.Count != 0 && TryGetSpawnerIdentity(((Component)area).gameObject, list, out var creatureLookup, out var subtype, out var displayName) && (!string.Equals(creatureLookup, "Surtling", StringComparison.OrdinalIgnoreCase) || _config.AutoPinSurtlingSpawners.Value)) { WayfinderPinRecord wayfinderPinRecord = _database.AddPointObservation(subtype, displayName, position, "wayfinder:spawner", WayfinderPinCategory.Spawner, WayfinderPinSource.InteractionDiscovery, BuildSpawnerKey(subtype, position)); if (wayfinderPinRecord != null) { _scanRemembered++; } } } private void ResetActiveScan() { _scanInProgress = false; _scanAreas = (SpawnArea[])(object)new SpawnArea[0]; _scanCursor = 0; } private SpawnArea[] GetCachedSpawnAreas(Vector3 center) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) float num = center.x - _spawnAreaCacheCenter.x; float num2 = center.z - _spawnAreaCacheCenter.z; bool flag = num * num + num2 * num2 >= 4096f; if (!_spawnAreaCacheInitialized || _spawnAreaCacheWorldUid != _database.WorldUid || _cachedSpawnAreas == null || Time.unscaledTime >= _nextSpawnAreaCacheRefresh || flag) { _cachedSpawnAreas = LiveDiscoveryRegistry.SnapshotSpawnAreas(); _spawnAreaCacheInitialized = true; _spawnAreaCacheWorldUid = _database.WorldUid; _spawnAreaCacheCenter = center; float num3 = Mathf.Max(12f, _config.StaticDiscoveryInterval.Value * 3f); _nextSpawnAreaCacheRefresh = Time.unscaledTime + num3; } return _cachedSpawnAreas; } private static List ExtractSpawnPrefabs(SpawnArea area) { List result = new List(); if ((Object)(object)area == (Object)null) { return result; } try { for (int i = 0; i < SpawnAreaFields.Length; i++) { object value = null; try { value = SpawnAreaFields[i].GetValue(area); } catch { } AddSpawnPrefabsFromValue(value, result); } } catch { } return result; } private static void AddSpawnPrefabsFromValue(object value, List result) { if (value == null || result == null || value is string) { return; } GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { AddUnique(result, val); } else { if (!(value is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } GameObject val2 = (GameObject)((item is GameObject) ? item : null); if ((Object)(object)val2 != (Object)null) { AddUnique(result, val2); continue; } Type type = item.GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { if (!typeof(GameObject).IsAssignableFrom(fields[i].FieldType)) { continue; } string text = Normalize(fields[i].Name); if (!text.Contains("prefab") && !text.Contains("creature") && !text.Contains("spawn")) { continue; } try { object? value2 = fields[i].GetValue(item); GameObject val3 = (GameObject)((value2 is GameObject) ? value2 : null); if ((Object)(object)val3 != (Object)null) { AddUnique(result, val3); } } catch { } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int j = 0; j < properties.Length; j++) { if (!properties[j].CanRead || !typeof(GameObject).IsAssignableFrom(properties[j].PropertyType)) { continue; } string text2 = Normalize(properties[j].Name); if (!text2.Contains("prefab") && !text2.Contains("creature") && !text2.Contains("spawn")) { continue; } try { object? value3 = properties[j].GetValue(item, null); GameObject val4 = (GameObject)((value3 is GameObject) ? value3 : null); if ((Object)(object)val4 != (Object)null) { AddUnique(result, val4); } } catch { } } } } } private static void AddUnique(List list, GameObject value) { if ((Object)(object)value == (Object)null) { return; } for (int i = 0; i < list.Count; i++) { if (object.ReferenceEquals(list[i], value)) { return; } } list.Add(value); } private static bool TryGetSpawnerIdentity(GameObject worldObject, List spawnPrefabs, out string creatureLookup, out string subtype, out string displayName) { creatureLookup = string.Empty; subtype = string.Empty; displayName = string.Empty; if (spawnPrefabs == null || spawnPrefabs.Count == 0) { return false; } string prefabName = GetPrefabName(worldObject); string text = Normalize(prefabName); string text2 = string.Empty; for (int i = 0; i < spawnPrefabs.Count; i++) { GameObject val = spawnPrefabs[i]; if ((Object)(object)val == (Object)null) { continue; } string prefabName2 = GetPrefabName(val); if (!string.IsNullOrEmpty(prefabName2)) { if (string.IsNullOrEmpty(text2)) { text2 = HumanizeCreatureName(prefabName2); } string text3 = Normalize(prefabName2); if (text3.Contains("surtling")) { creatureLookup = "Surtling"; subtype = "SurtlingSpawner"; displayName = "Surtling Spawner"; return true; } if (text3.Contains("greydwarf")) { creatureLookup = "Greydwarf"; subtype = "GreydwarfNest"; displayName = "Greydwarf Nest"; return true; } if (text3.Contains("skeleton") || text3.Contains("rancidremains")) { creatureLookup = "Skeleton"; subtype = "SkeletonSpawner"; displayName = (text.Contains("bonepile") ? "Evil Bone Pile" : "Skeleton Spawner"); return true; } if (text3.Contains("draugr")) { creatureLookup = "Draugr"; subtype = "DraugrSpawner"; displayName = (text.Contains("bodypile") ? "Body Pile" : "Draugr Spawner"); return true; } if (text3.Contains("charred") || text3.Contains("twitcher")) { creatureLookup = "Charred"; subtype = "CharredSpawner"; displayName = HumanizeSpawnerWorldName(prefabName, "Charred Spawner"); return true; } } } if (string.IsNullOrEmpty(text2)) { return false; } creatureLookup = text2; subtype = Normalize(text2) + "Spawner"; displayName = HumanizeSpawnerWorldName(prefabName, text2 + " Spawner"); return true; } private static string HumanizeSpawnerWorldName(string worldName, string fallback) { string text = Normalize(worldName); if (text.Contains("monumentoftorment")) { return "Monument of Torment"; } if (text.Contains("effigyofmalice")) { return "Effigy of Malice"; } if (text.Contains("greydwarfnest")) { return "Greydwarf Nest"; } if (text.Contains("bonepile")) { return "Evil Bone Pile"; } if (text.Contains("bodypile")) { return "Body Pile"; } return fallback; } private static string HumanizeCreatureName(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return "Creature"; } string text = prefabName.Replace("(Clone)", string.Empty).Replace('_', ' ').Trim(); if (text.IndexOf("Greydwarf", StringComparison.OrdinalIgnoreCase) >= 0) { return "Greydwarf"; } if (text.IndexOf("Surtling", StringComparison.OrdinalIgnoreCase) >= 0) { return "Surtling"; } if (text.IndexOf("Skeleton", StringComparison.OrdinalIgnoreCase) >= 0) { return "Skeleton"; } if (text.IndexOf("Draugr", StringComparison.OrdinalIgnoreCase) >= 0) { return "Draugr"; } return text; } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private static string BuildSpawnerKey(string type, Vector3 position) { return "spawner:" + type + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class StructureDiscovery { private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private static readonly FieldInfo PinsField = AccessTools.Field(typeof(Minimap), "m_pins"); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private readonly RuntimeIconRegistry _icons; private float _nextScanTime; private long _cleanupWorldUid = long.MinValue; private Location[] _cachedLocations = (Location[])(object)new Location[0]; private Vector3 _locationCacheCenter; private float _nextLocationCacheRefresh; private bool _locationCacheInitialized; private bool _scanInProgress; private Location[] _scanLocations = (Location[])(object)new Location[0]; private int _scanCursor; private Vector3 _scanCenter; private float _scanRadiusSq; private int _scanRemembered; private int _scanRevisionBefore; private bool _scanFinishPending; private long _scanWorldUid = long.MinValue; internal StructureDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database, RuntimeIconRegistry icons) { _log = log; _config = config; _database = database; _icons = icons; } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || !_config.RememberGeneratedStructures.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (_scanWorldUid != _database.WorldUid) { _scanWorldUid = _database.WorldUid; ResetActiveScan(); } long startTimestamp = DiscoveryWorkBudget.Start(); if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } int num = 0; while (!_scanFinishPending && _scanCursor < _scanLocations.Length) { Location location = _scanLocations[_scanCursor++]; num++; ProcessLocation(location); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanFinishPending = true; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } FinishScan(); return false; } private void BeginScan() { //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.StaticDiscoveryInterval.Value); if (_cleanupWorldUid != _database.WorldUid) { _cleanupWorldUid = _database.WorldUid; _locationCacheInitialized = false; _database.RemoveAutoPointSubtypesContaining("greydwarfnest", "spawner", "bonepile", "bodypile", "monumentoftorment", "elite monument of torment", "effigyofmalice", "firehole", "surtling"); int num = NormalizeExistingGeneratedLocationMetadata(); if (_config.DebugLogging.Value && num > 0) { _log.LogInfo((object)("Normalized " + num + " existing generated-location category/name/art record(s).")); } int num2 = _database.CollapseNearbyAutoPoints(delegate(WayfinderPinRecord record) { if (record == null || record.category == WayfinderPinCategory.Portal || !WayfinderIconPolicy.IsGeneratedLocationCategory(record.category)) { return (string)null; } string defaultIconKey = WayfinderIconPolicy.GetDefaultIconKey(record); return (!string.IsNullOrEmpty(defaultIconKey)) ? ("generated:" + defaultIconKey) : null; }, 18f); if (_config.DebugLogging.Value && num2 > 0) { _log.LogInfo((object)("Collapsed " + num2 + " overlapping generated-location marker(s) into one site marker.")); } } _scanCenter = ((Component)Player.m_localPlayer).transform.position; float num3 = Mathf.Max(5f, _config.StructureDiscoveryRadius.Value); _scanRadiusSq = num3 * num3; _scanRevisionBefore = _database.Revision; _scanLocations = GetCachedLocations(_scanCenter); if (_scanLocations == null) { _scanLocations = (Location[])(object)new Location[0]; } _scanCursor = 0; _scanRemembered = 0; _scanFinishPending = false; _scanInProgress = true; } private void ProcessLocation(Location location) { //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)location == (Object)null || (Object)(object)((Component)location).gameObject == (Object)null) { return; } Vector3 position = ((Component)location).transform.position; if (!WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) || !MayReveal(position)) { return; } string locationPrefabName = GetLocationPrefabName(location); if (string.IsNullOrEmpty(locationPrefabName) || ShouldLeaveToVanillaPins(locationPrefabName) || LooksLikePhysicalSpawner(locationPrefabName)) { return; } string memberKey = BuildLocationKey(locationPrefabName, position); WayfinderPinCategory category = WayfinderIconPolicy.ClassifyGeneratedLocation(locationPrefabName); string displayName = WayfinderIconPolicy.GetGeneratedLocationDisplayName(locationPrefabName); string iconKey = WayfinderIconPolicy.GetGeneratedLocationIconKey(locationPrefabName, category); if (TryGetBossInfo(locationPrefabName, out var displayName2, out var trophyLookup)) { category = WayfinderPinCategory.Boss; displayName = displayName2; if (HasVanillaBossPinNear(position, 30f)) { return; } if (_icons != null && _icons.IsBuilt) { WayfinderIconEntry wayfinderIconEntry = _icons.FindBestBossTrophy(trophyLookup); if (wayfinderIconEntry != null) { iconKey = wayfinderIconEntry.Key; } } } WayfinderPinRecord wayfinderPinRecord = _database.AddPointObservation(locationPrefabName, displayName, position, iconKey, category, WayfinderPinSource.InteractionDiscovery, memberKey); if (wayfinderPinRecord != null) { _scanRemembered++; } } private void FinishScan() { int num = 0; if (_database.Revision != _scanRevisionBefore) { num = _database.CollapseNearbyAutoPoints(delegate(WayfinderPinRecord record) { if (record == null || record.category == WayfinderPinCategory.Portal || !WayfinderIconPolicy.IsGeneratedLocationCategory(record.category)) { return (string)null; } string defaultIconKey = WayfinderIconPolicy.GetDefaultIconKey(record); return (!string.IsNullOrEmpty(defaultIconKey)) ? ("generated:" + defaultIconKey) : null; }, 18f); } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && (_scanRemembered > 0 || num > 0)) { _log.LogInfo((object)("Structure discovery remembered/confirmed " + _scanRemembered + " generated POIs and collapsed " + num + " overlapping site marker(s).")); } _scanInProgress = false; _scanFinishPending = false; _scanLocations = (Location[])(object)new Location[0]; _scanCursor = 0; } private void ResetActiveScan() { _scanInProgress = false; _scanFinishPending = false; _scanLocations = (Location[])(object)new Location[0]; _scanCursor = 0; } private Location[] GetCachedLocations(Vector3 center) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) float num = center.x - _locationCacheCenter.x; float num2 = center.z - _locationCacheCenter.z; bool flag = num * num + num2 * num2 >= 4096f; if (!_locationCacheInitialized || _cachedLocations == null || Time.unscaledTime >= _nextLocationCacheRefresh || flag) { _cachedLocations = LiveDiscoveryRegistry.SnapshotLocations(); _locationCacheInitialized = true; _locationCacheCenter = center; float num3 = Mathf.Max(12f, _config.StaticDiscoveryInterval.Value * 3f); _nextLocationCacheRefresh = Time.unscaledTime + num3; } return _cachedLocations; } private static string GetLocationPrefabName(Location location) { if ((Object)(object)location == (Object)null || (Object)(object)((Component)location).gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(((Component)location).gameObject); if (!string.IsNullOrEmpty(prefabName)) { return CleanPrefabName(prefabName); } } catch { } return CleanPrefabName(((Object)((Component)location).gameObject).name ?? string.Empty); } private static string CleanPrefabName(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("(Clone)", string.Empty).Trim(); } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private int NormalizeExistingGeneratedLocationMetadata() { //IL_011d: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList records = _database.Records; if (records == null) { return 0; } List list = new List(); List list2 = new List(); for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.source == WayfinderPinSource.Manual || wayfinderPinRecord.source == WayfinderPinSource.Imported || wayfinderPinRecord.source == WayfinderPinSource.Vanilla || !IsGeneratedLocationRecord(wayfinderPinRecord)) { continue; } if (IsIgnoredGeneratedLocationNoise(wayfinderPinRecord.subtype)) { if (!string.IsNullOrEmpty(wayfinderPinRecord.id)) { list2.Add(wayfinderPinRecord.id); } } else { if (wayfinderPinRecord.category == WayfinderPinCategory.Boss || wayfinderPinRecord.category == WayfinderPinCategory.Trader || wayfinderPinRecord.category == WayfinderPinCategory.Portal || wayfinderPinRecord.category == WayfinderPinCategory.Spawner || WayfinderIconPolicy.IsRunestoneName(wayfinderPinRecord.subtype) || string.Equals(wayfinderPinRecord.iconKey, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase) || TraderIdentity.IsTraderLocationPrefab(wayfinderPinRecord.subtype)) { continue; } WayfinderPinCategory wayfinderPinCategory; string text; string text2; if (TryGetBossInfo(wayfinderPinRecord.subtype, out var displayName, out var trophyLookup)) { if (HasVanillaBossPinNear(wayfinderPinRecord.Position, 30f)) { if (!string.IsNullOrEmpty(wayfinderPinRecord.id)) { list2.Add(wayfinderPinRecord.id); } continue; } wayfinderPinCategory = WayfinderPinCategory.Boss; text = displayName; text2 = wayfinderPinRecord.iconKey; if (_icons != null && _icons.IsBuilt) { WayfinderIconEntry wayfinderIconEntry = _icons.FindBestBossTrophy(trophyLookup); if (wayfinderIconEntry != null && !string.IsNullOrEmpty(wayfinderIconEntry.Key)) { text2 = wayfinderIconEntry.Key; } } if (string.IsNullOrEmpty(text2)) { text2 = "wayfinder:creature"; } } else { wayfinderPinCategory = WayfinderIconPolicy.ClassifyGeneratedLocation(wayfinderPinRecord.subtype); text = WayfinderIconPolicy.GetGeneratedLocationDisplayName(wayfinderPinRecord.subtype); text2 = WayfinderIconPolicy.GetGeneratedLocationIconKey(wayfinderPinRecord.subtype, wayfinderPinCategory); } bool flag = false; if (wayfinderPinRecord.category != wayfinderPinCategory) { wayfinderPinRecord.category = wayfinderPinCategory; flag = true; } if (!string.IsNullOrEmpty(text) && !string.Equals(wayfinderPinRecord.displayName, text, StringComparison.Ordinal)) { wayfinderPinRecord.displayName = text; flag = true; } if (!string.IsNullOrEmpty(text2) && !string.Equals(wayfinderPinRecord.iconKey, text2, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = text2; flag = true; } if (flag) { list.Add(wayfinderPinRecord); } } } for (int j = 0; j < list2.Count; j++) { _database.Remove(list2[j], suppressAutoRediscovery: false); } for (int k = 0; k < list.Count; k++) { _database.AddOrUpdate(list[k]); } return list.Count + list2.Count; } private static bool IsGeneratedLocationRecord(WayfinderPinRecord record) { if (record == null || record.members == null) { return false; } for (int i = 0; i < record.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = record.members[i]; if (wayfinderClusterMember != null && !string.IsNullOrEmpty(wayfinderClusterMember.key) && wayfinderClusterMember.key.StartsWith("location:", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static bool ShouldLeaveToVanillaPins(string prefabName) { string text = Normalize(prefabName); if (text.Length == 0) { return true; } if (IsIgnoredGeneratedLocationNoise(prefabName)) { return true; } if (WayfinderIconPolicy.IsVegvisirName(prefabName)) { return true; } if (text.Contains("starttemple") || text.Contains("sacrificial") || WayfinderIconPolicy.IsBossGuidanceRunestoneName(prefabName)) { return true; } if (TraderIdentity.IsTraderLocationPrefab(prefabName) || text.Contains("merchant") || text.Contains("haldor") || text.Contains("hildir") || text.Contains("bogwitch")) { return true; } return false; } private static bool IsIgnoredGeneratedLocationNoise(string prefabName) { string text = Normalize(prefabName); if (text.Length == 0) { return false; } if (!text.Contains("infestedtree")) { return text.Contains("greydwarfcamp"); } return true; } private static bool LooksLikePhysicalSpawner(string prefabName) { string text = Normalize(prefabName); if (text.Length == 0) { return false; } if (!text.Contains("greydwarfnest") && !text.Contains("spawner") && !text.Contains("bonepile") && !text.Contains("bodypile") && !text.Contains("monumentoftorment") && !text.Contains("effigyofmalice") && !text.Contains("firehole")) { return text.Contains("surtling"); } return true; } private static bool TryGetBossInfo(string prefabName, out string displayName, out string trophyLookup) { displayName = string.Empty; trophyLookup = string.Empty; string text = Normalize(prefabName); if (text.Contains("eikthyrnir") || text.Contains("eikthyr")) { displayName = "Eikthyr"; trophyLookup = "Eikthyr"; return true; } if (text.Contains("gdking") || text.Contains("theelder") || text == "elder") { displayName = "The Elder"; trophyLookup = "Elder"; return true; } if (text.Contains("bonemass")) { displayName = "Bonemass"; trophyLookup = "Bonemass"; return true; } if (text.Contains("dragonqueen") || text.Contains("moder")) { displayName = "Moder"; trophyLookup = "Moder"; return true; } if (text.Contains("goblinking") || text.Contains("yagluth")) { displayName = "Yagluth"; trophyLookup = "Yagluth"; return true; } if (text.Contains("seekerqueen") || text.Contains("queenboss") || text.Contains("mistlandsdvergrbossentrance") || text.Contains("dvergrbossentrance")) { displayName = "The Queen"; trophyLookup = "Queen"; return true; } if (text.Contains("fader") || text.Contains("faderboss")) { displayName = "Fader"; trophyLookup = "Fader"; return true; } return false; } private static bool HasVanillaBossPinNear(Vector3 position, float radius) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || PinsField == null) { return false; } try { if (!(PinsField.GetValue(instance) is List list)) { return false; } float num = radius * radius; for (int i = 0; i < list.Count; i++) { PinData val = list[i]; if (val != null && (int)val.m_type == 9) { float num2 = val.m_pos.x - position.x; float num3 = val.m_pos.z - position.z; if (num2 * num2 + num3 * num3 <= num) { return true; } } } } catch { } return false; } private static WayfinderPinCategory ClassifyLocation(string prefabName) { string text = Normalize(prefabName); if (text.Contains("crypt") || text.Contains("burial") || text.Contains("cave") || text.Contains("mine") || text.Contains("tomb") || text.Contains("dungeon") || text.Contains("citadel") || text.Contains("sealedtower") || text.Contains("howlingcavern") || text.Contains("putridhole")) { return WayfinderPinCategory.Dungeon; } return WayfinderPinCategory.PointOfInterest; } private static string HumanizePrefabName(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return "Point of Interest"; } string text = prefabName.Replace("(Clone)", string.Empty).Replace('_', ' ').Trim(); StringBuilder stringBuilder = new StringBuilder(text.Length + 8); char c = '\0'; for (int i = 0; i < text.Length; i++) { char c2 = text[i]; if (i > 0 && c2 != ' ' && c != ' ' && ((char.IsUpper(c2) && char.IsLower(c)) || (char.IsDigit(c2) && !char.IsDigit(c)))) { stringBuilder.Append(' '); } stringBuilder.Append(c2); c = c2; } return stringBuilder.ToString().Trim(); } private static string BuildLocationKey(string prefabName, Vector3 position) { return "location:" + prefabName + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class TraderDiscovery { private sealed class TraderCampObservation { internal string TraderType; internal string DisplayName; internal string PossibleDisplayName; internal string PrefabName; internal Vector3 Position; } private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); private static readonly FieldInfo PinsField = AccessTools.Field(typeof(Minimap), "m_pins"); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private float _nextScanTime; private long _cacheWorldUid = long.MinValue; private Location[] _cachedLocations = (Location[])(object)new Location[0]; private Trader[] _cachedTraders = (Trader[])(object)new Trader[0]; private Vector3 _locationCacheCenter; private Vector3 _traderCacheCenter; private float _nextLocationCacheRefresh; private float _nextTraderCacheRefresh; private bool _locationCacheInitialized; private bool _traderCacheInitialized; private bool _refreshTraderNext; private bool _scanInProgress; private int _scanPhase; private int _scanCursor; private Vector3 _scanCenter; private float _scanRadiusSq; private List _scanCamps = new List(); private List _scanPins = new List(); private int _scanConfirmed; private int _scanCandidates; private int _scanPurged; private long _scanWorldUid = long.MinValue; internal TraderDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database) { _log = log; _config = config; _database = database; } internal void ResetSession() { _nextScanTime = 0f; _cacheWorldUid = long.MinValue; _cachedLocations = (Location[])(object)new Location[0]; _cachedTraders = (Trader[])(object)new Trader[0]; _locationCacheInitialized = false; _traderCacheInitialized = false; _refreshTraderNext = false; _nextLocationCacheRefresh = 0f; _nextTraderCacheRefresh = 0f; ResetActiveScan(); } internal void Tick() { TickBudgeted(1000.0); } internal bool TickBudgeted(double budgetMs) { if (!_config.Enabled.Value || _database.WorldUid == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { ResetActiveScan(); return false; } if (_scanWorldUid != _database.WorldUid) { _scanWorldUid = _database.WorldUid; ResetActiveScan(); } long startTimestamp = DiscoveryWorkBudget.Start(); if (!_scanInProgress) { if (Time.unscaledTime < _nextScanTime) { return false; } if (AllKnownTraderTypesConfirmed()) { _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.StaticDiscoveryInterval.Value); return false; } BeginScan(); if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } int num = 0; while (_scanPhase < 4) { if (_scanPhase == 0) { Location[] array = _cachedLocations; if (array == null) { array = (Location[])(object)new Location[0]; } while (_scanCursor < array.Length) { ProcessCampLocation(array[_scanCursor++]); num++; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 1; _scanCursor = 0; continue; } if (_scanPhase == 1) { while (_scanCursor < _scanPins.Count) { ProcessNativePin(_scanPins[_scanCursor++]); num++; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 2; _scanCursor = 0; continue; } if (_scanPhase == 2) { Trader[] array2 = _cachedTraders; if (array2 == null) { array2 = (Trader[])(object)new Trader[0]; } while (_scanCursor < array2.Length) { ProcessLoadedTrader(array2[_scanCursor++]); num++; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 3; _scanCursor = 0; continue; } while (_scanCursor < _scanCamps.Count) { ProcessCandidateCamp(_scanCamps[_scanCursor++]); num++; if (DiscoveryWorkBudget.Expired(startTimestamp, budgetMs)) { return true; } } _scanPhase = 4; } if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && (_scanConfirmed > 0 || _scanCandidates > 0 || _scanPurged > 0)) { _log.LogInfo((object)("Trader discovery confirmed " + _scanConfirmed + " trader(s), remembered/confirmed " + _scanCandidates + " encountered candidate camp(s), and purged " + _scanPurged + " stale same-type candidate marker(s).")); } ResetActiveScan(); return false; } private void BeginScan() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) _nextScanTime = Time.unscaledTime + Mathf.Max(1f, _config.StaticDiscoveryInterval.Value); _scanCenter = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(5f, _config.StructureDiscoveryRadius.Value); _scanRadiusSq = num * num; _scanCamps.Clear(); _scanPins.Clear(); _scanConfirmed = 0; _scanCandidates = 0; _scanPurged = 0; _scanCursor = 0; _scanPhase = 0; RefreshOneSceneCacheIfNeeded(_scanCenter); Minimap instance = Minimap.instance; if ((Object)(object)instance != (Object)null && PinsField != null) { try { if (PinsField.GetValue(instance) is List collection) { _scanPins.AddRange(collection); } } catch { } } _scanInProgress = true; } private void ProcessCampLocation(Location location) { //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)location == (Object)null || (Object)(object)((Component)location).gameObject == (Object)null) { return; } Vector3 position = ((Component)location).transform.position; if (WithinHorizontalRadius(_scanCenter, position, _scanRadiusSq) && MayReveal(position)) { string prefabName = TraderIdentity.GetPrefabName(((Component)location).gameObject); if (TraderIdentity.TryFromLocationPrefab(prefabName, out var traderType, out var displayName, out var possibleDisplayName)) { TraderCampObservation traderCampObservation = new TraderCampObservation(); traderCampObservation.TraderType = traderType; traderCampObservation.DisplayName = displayName; traderCampObservation.PossibleDisplayName = possibleDisplayName; traderCampObservation.PrefabName = prefabName; traderCampObservation.Position = position; _scanCamps.Add(traderCampObservation); } } } private void ProcessNativePin(PinData pin) { //IL_0043: 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) if (!TraderIdentity.TryFromNativePin(pin, out var traderType, out var displayName)) { return; } if (string.Equals(traderType, "generic", StringComparison.OrdinalIgnoreCase)) { string text = InferTraderTypeNear(pin.m_pos, 96f); if (string.IsNullOrEmpty(text)) { return; } traderType = text; displayName = TraderIdentity.GetDisplayName(traderType); } if (ConfirmTrader(traderType, displayName, pin.m_pos, ref _scanPurged)) { _scanConfirmed++; } } private void ProcessLoadedTrader(Trader trader) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)trader == (Object)null) && !((Object)(object)((Component)trader).gameObject == (Object)null) && TraderIdentity.TryFromTraderObject(trader, out var traderType, out var displayName)) { Vector3 position = ((Component)trader).transform.position; TraderCampObservation traderCampObservation = FindNearestCamp(_scanCamps, traderType, position, 96f); if (traderCampObservation != null) { position = traderCampObservation.Position; } if (MayReveal(position) && ConfirmTrader(traderType, displayName, position, ref _scanPurged)) { _scanConfirmed++; } } } private void ProcessCandidateCamp(TraderCampObservation camp) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (camp != null && !string.IsNullOrEmpty(camp.TraderType) && !HasConfirmedTrader(camp.TraderType)) { WayfinderPinRecord wayfinderPinRecord = _database.AddPointObservation(TraderIdentity.CandidateSubtype(camp.TraderType), camp.PossibleDisplayName, camp.Position, "wayfinder:trader", WayfinderPinCategory.Trader, WayfinderPinSource.InteractionDiscovery, BuildCandidateKey(camp.TraderType, camp.PrefabName, camp.Position)); if (wayfinderPinRecord != null) { _scanCandidates++; } } } private void ResetActiveScan() { _scanInProgress = false; _scanPhase = 0; _scanCursor = 0; _scanCamps.Clear(); _scanPins.Clear(); } private void RefreshOneSceneCacheIfNeeded(Vector3 center) { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) if (_cacheWorldUid != _database.WorldUid) { _cacheWorldUid = _database.WorldUid; _cachedLocations = (Location[])(object)new Location[0]; _cachedTraders = (Trader[])(object)new Trader[0]; _locationCacheInitialized = false; _traderCacheInitialized = false; _refreshTraderNext = false; _nextLocationCacheRefresh = 0f; _nextTraderCacheRefresh = 0f; } float num = Mathf.Max(15f, _config.StaticDiscoveryInterval.Value * 4f); float num2 = center.x - _locationCacheCenter.x; float num3 = center.z - _locationCacheCenter.z; bool flag = num2 * num2 + num3 * num3 >= 6400f; bool flag2 = !_locationCacheInitialized || _cachedLocations == null || Time.unscaledTime >= _nextLocationCacheRefresh || flag; num2 = center.x - _traderCacheCenter.x; num3 = center.z - _traderCacheCenter.z; bool flag3 = num2 * num2 + num3 * num3 >= 6400f; bool flag4 = !_traderCacheInitialized || _cachedTraders == null || Time.unscaledTime >= _nextTraderCacheRefresh || flag3; if (flag2 && flag4) { if (_refreshTraderNext) { _cachedTraders = LiveDiscoveryRegistry.SnapshotTraders(); _traderCacheInitialized = true; _traderCacheCenter = center; _nextTraderCacheRefresh = Time.unscaledTime + num; } else { _cachedLocations = LiveDiscoveryRegistry.SnapshotLocations(); _locationCacheInitialized = true; _locationCacheCenter = center; _nextLocationCacheRefresh = Time.unscaledTime + num; } _refreshTraderNext = !_refreshTraderNext; } else if (flag2) { _cachedLocations = LiveDiscoveryRegistry.SnapshotLocations(); _locationCacheInitialized = true; _locationCacheCenter = center; _nextLocationCacheRefresh = Time.unscaledTime + num; } else if (flag4) { _cachedTraders = LiveDiscoveryRegistry.SnapshotTraders(); _traderCacheInitialized = true; _traderCacheCenter = center; _nextTraderCacheRefresh = Time.unscaledTime + num; } } private bool AllKnownTraderTypesConfirmed() { if (HasConfirmedTrader("haldor") && HasConfirmedTrader("hildir")) { return HasConfirmedTrader("bogwitch"); } return false; } private List FindEncounteredTraderCamps() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(5f, _config.StructureDiscoveryRadius.Value); float radiusSq = num * num; Location[] cachedLocations = _cachedLocations; foreach (Location val in cachedLocations) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } Vector3 position2 = ((Component)val).transform.position; if (WithinHorizontalRadius(position, position2, radiusSq) && MayReveal(position2)) { string prefabName = TraderIdentity.GetPrefabName(((Component)val).gameObject); if (TraderIdentity.TryFromLocationPrefab(prefabName, out var traderType, out var displayName, out var possibleDisplayName)) { TraderCampObservation traderCampObservation = new TraderCampObservation(); traderCampObservation.TraderType = traderType; traderCampObservation.DisplayName = displayName; traderCampObservation.PossibleDisplayName = possibleDisplayName; traderCampObservation.PrefabName = prefabName; traderCampObservation.Position = position2; list.Add(traderCampObservation); } } } return list; } private int ConfirmFromVanillaPins(ref int purged) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || PinsField == null) { return 0; } List list = null; try { list = PinsField.GetValue(instance) as List; } catch { } if (list == null) { return 0; } int num = 0; for (int i = 0; i < list.Count; i++) { PinData val = list[i]; if (!TraderIdentity.TryFromNativePin(val, out var traderType, out var displayName)) { continue; } if (string.Equals(traderType, "generic", StringComparison.OrdinalIgnoreCase)) { string text = InferTraderTypeNear(val.m_pos, 96f); if (string.IsNullOrEmpty(text)) { continue; } traderType = text; displayName = TraderIdentity.GetDisplayName(traderType); } if (ConfirmTrader(traderType, displayName, val.m_pos, ref purged)) { num++; } } return num; } private int ConfirmFromLoadedTraders(List camps, ref int purged) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_006e: Unknown result type (might be due to invalid IL or missing references) Trader[] cachedTraders = _cachedTraders; int num = 0; foreach (Trader val in cachedTraders) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && TraderIdentity.TryFromTraderObject(val, out var traderType, out var displayName)) { Vector3 position = ((Component)val).transform.position; TraderCampObservation traderCampObservation = FindNearestCamp(camps, traderType, position, 96f); if (traderCampObservation != null) { position = traderCampObservation.Position; } if (MayReveal(position) && ConfirmTrader(traderType, displayName, position, ref purged)) { num++; } } } return num; } private bool ConfirmTrader(string traderType, string displayName, Vector3 position, ref int purged) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(traderType)) { return false; } if (HasConfirmedTrader(traderType)) { purged += PurgeCandidateMarkers(traderType); return false; } WayfinderPinRecord wayfinderPinRecord = _database.AddPointObservation(TraderIdentity.ConfirmedSubtype(traderType), string.IsNullOrEmpty(displayName) ? TraderIdentity.GetDisplayName(traderType) : displayName, position, "wayfinder:trader", WayfinderPinCategory.Trader, WayfinderPinSource.InteractionDiscovery, BuildConfirmedKey(traderType, position)); if (wayfinderPinRecord == null) { return false; } purged += PurgeCandidateMarkers(traderType); return true; } private bool HasConfirmedTrader(string traderType) { IReadOnlyList records = _database.Records; string b = TraderIdentity.ConfirmedSubtype(traderType); for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Trader && string.Equals(wayfinderPinRecord.subtype, b, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private int PurgeCandidateMarkers(string traderType) { List list = new List(); IReadOnlyList records = _database.Records; string b = TraderIdentity.CandidateSubtype(traderType); for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Trader && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported && wayfinderPinRecord.source != WayfinderPinSource.Vanilla && string.Equals(wayfinderPinRecord.subtype, b, StringComparison.OrdinalIgnoreCase)) { list.Add(wayfinderPinRecord.id); } } int num = 0; for (int j = 0; j < list.Count; j++) { if (_database.Remove(list[j], suppressAutoRediscovery: false)) { num++; } } return num; } private string InferTraderTypeNear(Vector3 position, float radius) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Location[] cachedLocations = _cachedLocations; float radiusSq = radius * radius; foreach (Location val in cachedLocations) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && WithinHorizontalRadius(position, ((Component)val).transform.position, radiusSq) && TraderIdentity.TryFromLocationPrefab(TraderIdentity.GetPrefabName(((Component)val).gameObject), out var traderType, out var _, out var _)) { return traderType; } } return string.Empty; } private static TraderCampObservation FindNearestCamp(List camps, string traderType, Vector3 position, float radius) { if (camps == null) { return null; } float num = radius * radius; TraderCampObservation result = null; for (int i = 0; i < camps.Count; i++) { TraderCampObservation traderCampObservation = camps[i]; if (traderCampObservation != null && string.Equals(traderCampObservation.TraderType, traderType, StringComparison.OrdinalIgnoreCase)) { float num2 = traderCampObservation.Position.x - position.x; float num3 = traderCampObservation.Position.z - position.z; float num4 = num2 * num2 + num3 * num3; if (num4 <= num) { num = num4; result = traderCampObservation; } } } return result; } private bool MayReveal(Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_config.ScanUnexploredAreas.Value) { return true; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || IsExploredMethod == null) { return false; } try { object obj = IsExploredMethod.Invoke(instance, new object[1] { position }); return obj is bool && (bool)obj; } catch { return false; } } private static string BuildCandidateKey(string traderType, string prefabName, Vector3 position) { return "tradercandidate:" + traderType + ":" + prefabName + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static string BuildConfirmedKey(string traderType, Vector3 position) { return "traderconfirmed:" + traderType + "@" + Mathf.RoundToInt(position.x * 10f) + ":" + Mathf.RoundToInt(position.y * 10f) + ":" + Mathf.RoundToInt(position.z * 10f); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } } internal static class TraderIdentity { internal const string Haldor = "haldor"; internal const string Hildir = "hildir"; internal const string BogWitch = "bogwitch"; internal static bool TryFromLocationPrefab(string prefabName, out string traderType, out string displayName, out string possibleDisplayName) { traderType = string.Empty; displayName = string.Empty; possibleDisplayName = string.Empty; switch (Normalize(prefabName)) { case "vendorblackforest": traderType = "haldor"; displayName = "Haldor"; possibleDisplayName = "Possible Haldor Camp"; return true; case "hildircamp": traderType = "hildir"; displayName = "Hildir"; possibleDisplayName = "Possible Hildir Camp"; return true; case "bogwitchcamp": traderType = "bogwitch"; displayName = "Bog Witch"; possibleDisplayName = "Possible Bog Witch Hut"; return true; default: return false; } } internal static bool IsTraderLocationPrefab(string prefabName) { string traderType; string displayName; string possibleDisplayName; return TryFromLocationPrefab(prefabName, out traderType, out displayName, out possibleDisplayName); } internal static bool TryFromTraderObject(Trader trader, out string traderType, out string displayName) { traderType = string.Empty; displayName = string.Empty; if ((Object)(object)trader == (Object)null || (Object)(object)((Component)trader).gameObject == (Object)null) { return false; } string prefabName = GetPrefabName(((Component)trader).gameObject); string text = Normalize(prefabName + " " + ((Object)((Component)trader).gameObject).name); if (text.Contains("haldor")) { traderType = "haldor"; displayName = "Haldor"; return true; } if (text.Contains("hildir") && !ContainsHildirQuestSuffix(text)) { traderType = "hildir"; displayName = "Hildir"; return true; } if (text.Contains("bogwitch")) { traderType = "bogwitch"; displayName = "Bog Witch"; return true; } Location val = null; try { val = ((Component)trader).GetComponentInParent(); } catch { } if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && TryFromLocationPrefab(GetPrefabName(((Component)val).gameObject), out traderType, out displayName, out var _)) { return true; } return false; } internal static bool TryFromNativePin(PinData pin, out string traderType, out string displayName) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) traderType = string.Empty; displayName = string.Empty; if (pin == null) { return false; } string text = pin.m_name ?? string.Empty; string value = text; try { if (Localization.instance != null && !string.IsNullOrEmpty(text)) { value = Localization.instance.Localize(text); } } catch { } string value2 = string.Empty; try { value2 = ((object)pin.m_type).ToString(); } catch { } string text2 = Normalize(value2); string text3 = Normalize(text); string text4 = Normalize(value); string text5 = text2 + " " + text3 + " " + text4; if (text3.Contains("npchaldor") || text4 == "haldor" || text2 == "haldor") { traderType = "haldor"; displayName = "Haldor"; return true; } if (text3.Contains("npchildir") || text4 == "hildir" || text2 == "hildir") { traderType = "hildir"; displayName = "Hildir"; return true; } if (text3.Contains("npcbogwitch") || text4 == "bogwitch" || text2 == "bogwitch") { traderType = "bogwitch"; displayName = "Bog Witch"; return true; } if (text2 == "trader" || text3 == "trader" || text4 == "trader") { traderType = "generic"; displayName = "Trader"; return true; } if (text5.Contains("haldor")) { traderType = "haldor"; displayName = "Haldor"; return true; } if (text5.Contains("bogwitch")) { traderType = "bogwitch"; displayName = "Bog Witch"; return true; } return false; } internal static string ConfirmedSubtype(string traderType) { return "trader:" + (traderType ?? string.Empty); } internal static string CandidateSubtype(string traderType) { return ConfirmedSubtype(traderType) + ":candidate"; } internal static string GetDisplayName(string traderType) { if (string.Equals(traderType, "haldor", StringComparison.OrdinalIgnoreCase)) { return "Haldor"; } if (string.Equals(traderType, "hildir", StringComparison.OrdinalIgnoreCase)) { return "Hildir"; } if (string.Equals(traderType, "bogwitch", StringComparison.OrdinalIgnoreCase)) { return "Bog Witch"; } return "Trader"; } internal static string GetPossibleDisplayName(string traderType) { if (string.Equals(traderType, "haldor", StringComparison.OrdinalIgnoreCase)) { return "Possible Haldor Camp"; } if (string.Equals(traderType, "hildir", StringComparison.OrdinalIgnoreCase)) { return "Possible Hildir Camp"; } if (string.Equals(traderType, "bogwitch", StringComparison.OrdinalIgnoreCase)) { return "Possible Bog Witch Hut"; } return "Possible Trader Camp"; } internal static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } internal static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } private static bool ContainsHildirQuestSuffix(string normalized) { if (!normalized.Contains("hildir1") && !normalized.Contains("hildir2") && !normalized.Contains("hildir3") && !normalized.Contains("hildircave") && !normalized.Contains("hildircrypt")) { return normalized.Contains("hildirplainsfortress"); } return true; } } internal sealed class VehicleDiscovery { private sealed class LiveVehicleObservation { internal string Kind; internal string StableKey; internal string Subtype; internal Vector3 Position; } private static readonly MethodInfo FindObjectsByTypeTypeMethod = typeof(Object).GetMethod("FindObjectsByType", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Type), typeof(FindObjectsSortMode) }, null); private static readonly MethodInfo FindObjectsOfTypeTypeMethod = typeof(Object).GetMethod("FindObjectsOfType", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private readonly Type _shipType; private readonly Type _cartType; private readonly Type _znetViewType; private readonly MethodInfo _getZdoIdMethod; private readonly MethodInfo _getZdoMethod; private readonly MethodInfo _isValidMethod; private float _nextScanTime; private bool _warnedMissingShipType; private bool _warnedMissingCartType; internal VehicleDiscovery(ManualLogSource log, WayfinderConfig config, WayfinderPinDatabase database) { _log = log; _config = config; _database = database; _shipType = AccessTools.TypeByName("Ship"); _cartType = AccessTools.TypeByName("Vagon"); _znetViewType = AccessTools.TypeByName("ZNetView"); _getZdoIdMethod = FindOptionalInstanceMethod(_znetViewType, "GetZDOID"); _getZdoMethod = FindOptionalInstanceMethod(_znetViewType, "GetZDO"); _isValidMethod = FindOptionalInstanceMethod(_znetViewType, "IsValid"); } internal void ResetSession() { _nextScanTime = 0f; int num = _database.CollapseDuplicateDynamicMemberKeys(WayfinderPinCategory.Vehicle); if (num > 0 && _config.DebugLogging.Value) { _log.LogInfo((object)("Collapsed " + num + " duplicate dynamic vehicle record(s) sharing the same stable ZDO identity.")); } } internal void ClearSession() { _nextScanTime = 0f; } internal void Tick() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) if (_config.Enabled.Value && _database.WorldUid != 0 && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && (_config.TrackBoats.Value || _config.TrackCarts.Value) && !(Time.unscaledTime < _nextScanTime)) { _nextScanTime = Time.unscaledTime + Mathf.Max(0.1f, _config.VehicleUpdateInterval.Value); Vector3 position = ((Component)Player.m_localPlayer).transform.position; float num = Mathf.Max(10f, _config.VehicleDiscoveryRadius.Value); float radiusSq = num * num; HashSet hashSet = new HashSet(StringComparer.Ordinal); List observations = new List(); if (_config.TrackBoats.Value) { ScanType(_shipType, "boat", position, radiusSq, hashSet, observations); } if (_config.TrackCarts.Value) { ScanType(_cartType, "cart", position, radiusSq, hashSet, observations); } CleanupStaleAliases(observations, hashSet); float staleGraceSeconds = Mathf.Max(1.25f, Mathf.Max(0.1f, _config.VehicleUpdateInterval.Value) * 3f); _database.FinalizeDynamicObservationPass(WayfinderPinCategory.Vehicle, hashSet, staleGraceSeconds); if (!_config.KeepLastKnownVehiclePosition.Value) { _database.RemoveUnseenDynamicPoints(WayfinderPinCategory.Vehicle, hashSet); } } } private void ScanType(Type type, string kind, Vector3 playerPosition, float radiusSq, HashSet seenKeys, List observations) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) if (type == null) { if (kind == "boat" && !_warnedMissingShipType) { _warnedMissingShipType = true; if (_config.DebugLogging.Value) { _log.LogWarning((object)"Vehicle tracker could not resolve Valheim Ship type."); } } else if (kind == "cart" && !_warnedMissingCartType) { _warnedMissingCartType = true; if (_config.DebugLogging.Value) { _log.LogWarning((object)"Vehicle tracker could not resolve Valheim Vagon type."); } } return; } Object[] array = FindSceneObjectsOfType(type); if (array == null) { return; } foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null || !val.gameObject.activeInHierarchy) { continue; } Vector3 position = val.transform.position; bool flag = WithinHorizontalRadius(playerPosition, position, radiusSq); string text = BuildStableVehicleKey(val, kind); if (string.IsNullOrEmpty(text)) { if (flag && _config.DebugLogging.Value) { _log.LogWarning((object)("Skipping loaded " + kind + " because a stable ZDO identity was not available: " + GetPrefabName(val.gameObject))); } continue; } bool flag2 = _database.HasDynamicPointByMemberKey(WayfinderPinCategory.Vehicle, text); if (flag || flag2) { seenKeys.Add(text); string prefabName = GetPrefabName(val.gameObject); string displayName = GetDisplayName(kind, prefabName); string subtype = "vehicle:" + kind + ":" + (string.IsNullOrEmpty(prefabName) ? displayName : prefabName); string iconKey = ((kind == "cart") ? "wayfinder:cart" : "wayfinder:boat"); _database.UpsertDynamicPoint(subtype, displayName, position, iconKey, WayfinderPinCategory.Vehicle, text, 0.08f); observations?.Add(new LiveVehicleObservation { Kind = kind, StableKey = text, Subtype = subtype, Position = position }); } } } private void CleanupStaleAliases(List observations, HashSet liveMemberKeys) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (observations == null || observations.Count == 0) { return; } int num = 0; for (int i = 0; i < observations.Count; i++) { LiveVehicleObservation liveVehicleObservation = observations[i]; if (liveVehicleObservation != null && !string.IsNullOrEmpty(liveVehicleObservation.StableKey)) { num += _database.RemoveStaleDynamicAliasesNear(WayfinderPinCategory.Vehicle, liveVehicleObservation.Position, 1.35f, "vehicle:" + liveVehicleObservation.Kind + ":", liveVehicleObservation.StableKey, liveMemberKeys); } } if (num > 0 && _config.DebugLogging.Value) { _log.LogInfo((object)("Removed " + num + " stale dynamic vehicle alias record(s) near live vehicles.")); } } internal void NotifyDestroyed(GameObject source) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || !_config.RemoveDestroyedVehicles.Value || _database.WorldUid == 0) { return; } Component val = FindVehicleInParents(source, _shipType); string text = "boat"; if ((Object)(object)val == (Object)null) { val = FindVehicleInParents(source, _cartType); text = "cart"; } if (!((Object)(object)val == (Object)null)) { string text2 = BuildStableVehicleKey(val, text); bool flag = false; if (!string.IsNullOrEmpty(text2)) { flag = _database.RemoveDynamicPointByMemberKey(WayfinderPinCategory.Vehicle, text2); } if (!flag) { flag = _database.RemoveNearestDynamicPoint(WayfinderPinCategory.Vehicle, val.transform.position, 2.25f, "vehicle:" + text + ":"); } if (flag && _config.DebugLogging.Value) { _log.LogInfo((object)("Removed destroyed Wayfinder " + text + " marker.")); } } } private string BuildStableVehicleKey(Component component, string kind) { if ((Object)(object)component == (Object)null) { return null; } Component val = FindZNetView(component); if ((Object)(object)val == (Object)null) { return null; } if (_isValidMethod != null) { try { object obj = _isValidMethod.Invoke(val, null); if (obj is bool && !(bool)obj) { return null; } } catch { } } object obj3 = null; if (_getZdoIdMethod != null) { try { obj3 = _getZdoIdMethod.Invoke(val, null); } catch { obj3 = null; } } if (obj3 == null && _getZdoMethod != null) { try { object obj5 = _getZdoMethod.Invoke(val, null); if (obj5 != null) { FieldInfo field = obj5.GetType().GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { obj3 = field.GetValue(obj5); } } } catch { obj3 = null; } } string text = StableIdToString(obj3); if (!string.IsNullOrEmpty(text)) { switch (text) { case "0": case "0:0": case "0_0": break; default: return "vehicle:" + kind + ":" + text; } } return null; } private static MethodInfo FindOptionalInstanceMethod(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } try { return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); } catch { return null; } } private static string StableIdToString(object id) { if (id == null) { return string.Empty; } try { Type type = id.GetType(); BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; object obj = ReadSilentMember(type, id, flags, "m_userID", "userID", "UserID"); object obj2 = ReadSilentMember(type, id, flags, "m_id", "id", "ID"); if (obj != null && obj2 != null) { string text = Convert.ToString(obj); string text2 = Convert.ToString(obj2); if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2)) { return text + ":" + text2; } } } catch { } try { return id.ToString(); } catch { return string.Empty; } } private static object ReadSilentMember(Type type, object instance, BindingFlags flags, params string[] names) { if (type == null || instance == null || names == null) { return null; } foreach (string text in names) { if (string.IsNullOrEmpty(text)) { continue; } try { FieldInfo field = type.GetField(text, flags); if (field != null) { return field.GetValue(instance); } } catch { } try { PropertyInfo property = type.GetProperty(text, flags); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(instance, null); } } catch { } } return null; } private Component FindZNetView(Component component) { if ((Object)(object)component == (Object)null || _znetViewType == null) { return null; } try { Component component2 = component.gameObject.GetComponent(_znetViewType); if ((Object)(object)component2 != (Object)null) { return component2; } } catch { } Transform val = component.transform; while ((Object)(object)val != (Object)null) { try { Component component3 = ((Component)val).gameObject.GetComponent(_znetViewType); if ((Object)(object)component3 != (Object)null) { return component3; } } catch { } val = val.parent; } try { Component[] componentsInChildren = component.gameObject.GetComponentsInChildren(_znetViewType, true); if (componentsInChildren != null && componentsInChildren.Length > 0) { return componentsInChildren[0]; } } catch { } return null; } private static Component FindVehicleInParents(GameObject source, Type vehicleType) { if ((Object)(object)source == (Object)null || vehicleType == null) { return null; } Transform val = source.transform; while ((Object)(object)val != (Object)null) { try { Component component = ((Component)val).gameObject.GetComponent(vehicleType); if ((Object)(object)component != (Object)null) { return component; } } catch { } val = val.parent; } return null; } private static Object[] FindSceneObjectsOfType(Type type) { if (type == null) { return (Object[])(object)new Object[0]; } try { if (FindObjectsByTypeTypeMethod != null) { object obj = FindObjectsByTypeTypeMethod.Invoke(null, new object[2] { type, (object)(FindObjectsSortMode)0 }); if (obj is Object[] result) { return result; } } } catch { } try { if (FindObjectsOfTypeTypeMethod != null) { object obj3 = FindObjectsOfTypeTypeMethod.Invoke(null, new object[1] { type }); if (obj3 is Object[] result2) { return result2; } } } catch { } return (Object[])(object)new Object[0]; } private static string GetDisplayName(string kind, string prefabName) { return WayfinderVehicleNaming.GetAutomaticDisplayName(kind, prefabName); } private static string GetPrefabName(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } try { string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrEmpty(prefabName)) { return prefabName.Replace("(Clone)", string.Empty).Trim(); } } catch { } return (((Object)gameObject).name ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); } private static bool WithinHorizontalRadius(Vector3 a, Vector3 b, float radiusSq) { float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2 <= radiusSq; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } } namespace JoeyBadManners.Wayfinder.HUD { internal sealed class WayfinderCompassHUD { private static readonly FieldInfo LargeRootField = AccessTools.Field(typeof(Minimap), "m_largeRoot"); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinManagementService _manager; private readonly RuntimeIconRegistry _icons; private Texture2D _frameTexture; private Texture2D _trackAccentTexture; private Texture2D _cardNTexture; private Texture2D _cardETexture; private Texture2D _cardSTexture; private Texture2D _cardWTexture; private GUIStyle _distanceStyle; private GUIStyle _nameStyle; internal WayfinderCompassHUD(ManualLogSource log, WayfinderConfig config, WayfinderPinManagementService manager, RuntimeIconRegistry icons) { _log = log; _config = config; _manager = manager; _icons = icons; } internal void Draw() { //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) if (!_config.Enabled.Value || !_config.CompassEnabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || IsLargeMapOpen()) { return; } List trackedPins = _manager.GetTrackedPins(); bool value = _config.CompassShowCardinals.Value; if (trackedPins.Count == 0 && !value) { return; } EnsureStyles(); EnsureArt(); float num = Mathf.Max(360f, _config.CompassWidth.Value); float num2 = Mathf.Min(num, Mathf.Max(360f, (float)Screen.width - 32f)); float top = Mathf.Max(0f, _config.CompassTopOffset.Value); float num3 = ((float)Screen.width - num2) * 0.5f; float num4 = num2 * 0.07527926f; num4 = Mathf.Clamp(num4, 54f, 82f); float totalHeight = num4 + (_config.CompassShowNames.Value ? 30f : 16f); float centerX = num3 + num2 * 0.5f; float num5 = Mathf.Clamp(_config.CompassArcDegrees.Value, 60f, 360f); float halfArc = num5 * 0.5f; Vector3 viewForward = GetViewForward(); float heading = BearingFromDirection(viewForward); int depth = GUI.depth; Color color = GUI.color; GUI.depth = -900; try { DrawFrame(num3, top, num2, num4); if (value) { DrawBearingTicks(num3, top, num2, num4, heading, halfArc); } DrawCenterGuide(centerX, top, num4); DrawTrackedMarkers(trackedPins, num3, top, num2, num4, totalHeight, heading, halfArc); } catch (Exception ex) { if (_config.DebugLogging.Value) { _log.LogWarning((object)("Compass draw failed: " + ex.Message)); } } finally { GUI.color = color; GUI.depth = depth; } } private void DrawFrame(float left, float top, float width, float frameHeight) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(_config.CompassOpacity.Value); if ((Object)(object)_frameTexture != (Object)null) { GUI.color = new Color(1f, 1f, 1f, Mathf.Clamp01(0.78f + num * 0.22f)); GUI.DrawTexture(new Rect(left, top, width, frameHeight), (Texture)(object)_frameTexture, (ScaleMode)0, true); } } private void DrawBearingTicks(float left, float top, float width, float frameHeight, float heading, float halfArc) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) float num = top + frameHeight * 0.33f; for (int i = 0; i < 360; i += 15) { float num2 = Mathf.DeltaAngle(heading, (float)i); if (Mathf.Abs(num2) > halfArc) { continue; } float num3 = Mathf.Clamp(width * 0.085f, 34f, 72f); float num4 = width - num3 * 2f; float num5 = left + num3 + (num2 + halfArc) / (halfArc * 2f) * num4; bool flag = i % 90 == 0; bool flag2 = i % 45 == 0; float num6 = (flag ? 8f : (flag2 ? 5f : 3f)); GUI.color = (flag ? new Color(0.79f, 0.67f, 0.43f, 0.82f) : new Color(0.64f, 0.62f, 0.57f, flag2 ? 0.5f : 0.27f)); GUI.DrawTexture(new Rect(Mathf.Round(num5), num - num6 * 0.5f, flag ? 2f : 1f, num6), (Texture)(object)Texture2D.whiteTexture); if (flag) { Texture2D cardinalTexture = GetCardinalTexture(i); if (!((Object)(object)cardinalTexture == (Object)null)) { float num7 = Mathf.Clamp(frameHeight * 0.44f, 26f, 36f); float num8 = Mathf.Round(num5 - num7 * 0.5f); float num9 = Mathf.Max(0f, Mathf.Round(top - num7 * 0.22f)); GUI.color = Color.white; GUI.DrawTexture(new Rect(num8, num9, num7, num7), (Texture)(object)cardinalTexture, (ScaleMode)2, true); } } } } private Texture2D GetCardinalTexture(int bearing) { return (Texture2D)(Mathf.RoundToInt(Mathf.Repeat((float)bearing, 360f)) switch { 0 => _cardNTexture, 90 => _cardETexture, 180 => _cardSTexture, 270 => _cardWTexture, _ => null, }); } private void DrawCenterGuide(float centerX, float top, float frameHeight) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) GUI.color = new Color(0.32f, 0.78f, 0.92f, 0.76f); GUI.DrawTexture(new Rect(centerX - 1f, top + frameHeight * 0.27f, 2f, frameHeight * 0.18f), (Texture)(object)Texture2D.whiteTexture); } private void DrawTrackedMarkers(List tracked, float left, float top, float width, float frameHeight, float totalHeight, float heading, float halfArc) { //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) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Unknown result type (might be due to invalid IL or missing references) if (tracked == null || tracked.Count == 0) { return; } Vector3 playerPosition = ((Component)Player.m_localPlayer).transform.position; bool value = _config.CompassShowDistance.Value; bool value2 = _config.CompassShowNames.Value; bool value3 = _config.CompassClampOffscreenTracked.Value; bool value4 = _config.WaypointShowVerticalDifference.Value; tracked.Sort(delegate(WayfinderPinRecord a, WayfinderPinRecord b) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) if (object.ReferenceEquals(a, b)) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } Vector3 val3 = a.Position - playerPosition; Vector3 val4 = b.Position - playerPosition; float num24 = Mathf.Sqrt(val3.x * val3.x + val3.z * val3.z); float num25 = Mathf.Sqrt(val4.x * val4.x + val4.z * val4.z); return (Mathf.Abs(num24 - num25) > 0.5f) ? num25.CompareTo(num24) : b.trackSlot.CompareTo(a.trackSlot); }); Vector3 viewForward = default(Vector3); for (int num = 0; num < tracked.Count; num++) { WayfinderPinRecord wayfinderPinRecord = tracked[num]; if (wayfinderPinRecord == null || !wayfinderPinRecord.tracked) { continue; } Vector3 val = wayfinderPinRecord.Position - playerPosition; float num2 = Mathf.Sqrt(val.x * val.x + val.z * val.z); ((Vector3)(ref viewForward))..ctor(val.x, 0f, val.z); if (((Vector3)(ref viewForward)).sqrMagnitude < 0.0001f) { viewForward = GetViewForward(); } float num3 = BearingFromDirection(viewForward); float num4 = Mathf.DeltaAngle(heading, num3); bool flag = Mathf.Abs(num4) > halfArc; if (flag && !value3) { continue; } float num5 = ((!flag) ? Mathf.Clamp(num4 / halfArc, -1f, 1f) : ((num4 < 0f) ? (-1f) : 1f)); float num6 = Mathf.Clamp(width * 0.095f, 38f, 78f); float num7 = width * 0.5f - num6; float num8 = left + width * 0.5f + num5 * num7; if (flag && value3) { float offscreenStackInsetPixels = GetOffscreenStackInsetPixels(tracked, wayfinderPinRecord, playerPosition, heading, halfArc, num5); num8 += ((num5 < 0f) ? offscreenStackInsetPixels : (0f - offscreenStackInsetPixels)); } float num9 = Mathf.Clamp01(Mathf.Abs(num5)); float num10 = (flag ? 0.7f : Mathf.Lerp(1f, 0.82f, num9)); bool flag2 = WayfinderDynamicState.IsLastKnownVehicle(wayfinderPinRecord); if (flag2) { num10 *= Mathf.Clamp01(_config.LastKnownVehicleOpacity.Value); } Color color = TrackedColorPalette.GetColor(wayfinderPinRecord.trackSlot, num10); Sprite val2 = ResolveRecordIcon(wayfinderPinRecord); float num11 = (flag ? Mathf.Clamp(frameHeight * 0.4f, 24f, 32f) : Mathf.Clamp(frameHeight * 0.46f, 27f, 36f)); float num12 = num8; float num13 = num12 - num11 * 0.5f; float num14 = top + frameHeight * 0.08f; if ((Object)(object)val2 != (Object)null) { DrawSprite(new Rect(num13, num14, num11, num11), val2, new Color(1f, 1f, 1f, num10)); } if ((Object)(object)_trackAccentTexture != (Object)null) { float num15 = (flag ? 15f : 19f); float num16 = num15 * 0.6875f; float num17 = num12 - num15 * 0.5f; float num18 = num14 + num11 - 1f; GUI.color = color; GUI.DrawTexture(new Rect(num17, num18, num15, num16), (Texture)(object)_trackAccentTexture, (ScaleMode)2, true); } bool flag3 = HasPreferredTrackedMarkerOverlap(tracked, wayfinderPinRecord, playerPosition, left, width, heading, halfArc, value3, num12, num2); string text = string.Empty; if (value && !flag3) { text = (flag2 ? "~" : string.Empty) + Mathf.RoundToInt(num2) + "m"; } if (!flag3 && value4 && Mathf.Abs(val.y) >= 8f) { int num19 = Mathf.RoundToInt(val.y); text += ((num19 >= 0) ? (" +" + num19) : (" " + num19)); } if (!string.IsNullOrEmpty(text)) { float num20 = 104f; float num21 = Mathf.Clamp(num12 - num20 * 0.5f, left + 4f, left + width - num20 - 4f); string text2 = "T" + (wayfinderPinRecord.trackSlot + 1) + " " + text; if (flag) { text2 = ((num5 < 0f) ? ("‹ " + text2) : (text2 + " ›")); } DrawShadowLabel(new Rect(num21, top + frameHeight * 0.76f, num20, 18f), text2, _distanceStyle, color); } if (value2 && !flag3) { string text3 = GetRecordName(wayfinderPinRecord); if (flag2) { text3 += " (last known)"; } if (text3.Length > 32) { text3 = text3.Substring(0, 31) + "..."; } float num22 = 166f; float num23 = Mathf.Clamp(num8 - num22 * 0.5f, left + 4f, left + width - num22 - 4f); DrawShadowLabel(new Rect(num23, top + frameHeight + 2f, num22, 16f), text3, _nameStyle, new Color(0.88f, 0.84f, 0.75f, num10)); } } } private bool HasPreferredTrackedMarkerOverlap(List tracked, WayfinderPinRecord current, Vector3 playerPosition, float left, float width, float heading, float halfArc, bool clampOffscreen, float currentMarkerX, float currentDistance) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (tracked == null || current == null || tracked.Count <= 1) { return false; } float num = 40f; for (int i = 0; i < tracked.Count; i++) { WayfinderPinRecord wayfinderPinRecord = tracked[i]; if (wayfinderPinRecord != null && !object.ReferenceEquals(wayfinderPinRecord, current) && wayfinderPinRecord.tracked) { Vector3 val = wayfinderPinRecord.Position - playerPosition; float candidateDistance = Mathf.Sqrt(val.x * val.x + val.z * val.z); if (HasTrackingPriority(wayfinderPinRecord, candidateDistance, current, currentDistance) && TryGetTrackedMarkerX(tracked, wayfinderPinRecord, playerPosition, left, width, heading, halfArc, clampOffscreen, out var markerX) && Mathf.Abs(markerX - currentMarkerX) <= num) { return true; } } } return false; } private float GetOffscreenStackInsetPixels(List tracked, WayfinderPinRecord current, Vector3 playerPosition, float heading, float halfArc, float currentNormalized) { //IL_0016: 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_001c: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (tracked == null || current == null || tracked.Count <= 1) { return 0f; } Vector3 val = current.Position - playerPosition; float currentDistance = Mathf.Sqrt(val.x * val.x + val.z * val.z); int num = 0; Vector3 viewForward = default(Vector3); for (int i = 0; i < tracked.Count; i++) { WayfinderPinRecord wayfinderPinRecord = tracked[i]; if (wayfinderPinRecord == null || object.ReferenceEquals(wayfinderPinRecord, current) || !wayfinderPinRecord.tracked) { continue; } Vector3 val2 = wayfinderPinRecord.Position - playerPosition; float candidateDistance = Mathf.Sqrt(val2.x * val2.x + val2.z * val2.z); if (!HasTrackingPriority(wayfinderPinRecord, candidateDistance, current, currentDistance)) { continue; } ((Vector3)(ref viewForward))..ctor(val2.x, 0f, val2.z); if (((Vector3)(ref viewForward)).sqrMagnitude < 0.0001f) { viewForward = GetViewForward(); } float num2 = BearingFromDirection(viewForward); float num3 = Mathf.DeltaAngle(heading, num2); if (!(Mathf.Abs(num3) <= halfArc)) { float num4 = ((num3 < 0f) ? (-1f) : 1f); if (num4 < 0f == currentNormalized < 0f) { num++; } } } return Mathf.Min(28f, (float)num * 7f); } private bool TryGetTrackedMarkerX(List tracked, WayfinderPinRecord record, Vector3 playerPosition, float left, float width, float heading, float halfArc, bool clampOffscreen, out float markerX) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) markerX = 0f; if (record == null) { return false; } Vector3 val = record.Position - playerPosition; Vector3 viewForward = default(Vector3); ((Vector3)(ref viewForward))..ctor(val.x, 0f, val.z); if (((Vector3)(ref viewForward)).sqrMagnitude < 0.0001f) { viewForward = GetViewForward(); } float num = BearingFromDirection(viewForward); float num2 = Mathf.DeltaAngle(heading, num); bool flag = Mathf.Abs(num2) > halfArc; if (flag && !clampOffscreen) { return false; } float num3 = ((!flag) ? Mathf.Clamp(num2 / halfArc, -1f, 1f) : ((num2 < 0f) ? (-1f) : 1f)); float num4 = Mathf.Clamp(width * 0.095f, 38f, 78f); float num5 = width * 0.5f - num4; markerX = left + width * 0.5f + num3 * num5; if (flag && clampOffscreen) { float offscreenStackInsetPixels = GetOffscreenStackInsetPixels(tracked, record, playerPosition, heading, halfArc, num3); markerX += ((num3 < 0f) ? offscreenStackInsetPixels : (0f - offscreenStackInsetPixels)); } return true; } private static bool HasTrackingPriority(WayfinderPinRecord candidate, float candidateDistance, WayfinderPinRecord current, float currentDistance) { if (candidate == null || current == null) { return false; } if (candidateDistance < currentDistance - 0.5f) { return true; } if (Mathf.Abs(candidateDistance - currentDistance) <= 0.5f) { return candidate.trackSlot < current.trackSlot; } return false; } private Sprite ResolveRecordIcon(WayfinderPinRecord record) { if (record == null || _icons == null) { return null; } string effectiveIconKey = WayfinderIconPolicy.GetEffectiveIconKey(record); if (!string.IsNullOrEmpty(effectiveIconKey) && _icons.TryGet(effectiveIconKey, out var sprite)) { return sprite; } if (!string.IsNullOrEmpty(record.iconKey) && _icons.TryGet(record.iconKey, out sprite)) { return sprite; } string key; switch (record.category) { case WayfinderPinCategory.Resource: key = "wayfinder:resource"; break; case WayfinderPinCategory.Creature: key = "wayfinder:creature"; break; case WayfinderPinCategory.Boss: key = "wayfinder:creature"; break; case WayfinderPinCategory.Dungeon: key = "wayfinder:dungeon"; break; case WayfinderPinCategory.Spawner: key = "wayfinder:spawner"; break; case WayfinderPinCategory.Habitat: key = "wayfinder:habitat"; break; case WayfinderPinCategory.Sighting: key = "wayfinder:sighting"; break; case WayfinderPinCategory.Vehicle: key = "wayfinder:vehicle"; break; case WayfinderPinCategory.Trader: key = "wayfinder:trader"; break; case WayfinderPinCategory.Portal: key = "wayfinder:portal"; break; case WayfinderPinCategory.Outpost: key = "wayfinder:outpost"; break; case WayfinderPinCategory.Farm: key = "wayfinder:farm"; break; case WayfinderPinCategory.Dock: key = "wayfinder:dock"; break; case WayfinderPinCategory.Road: key = "wayfinder:road"; break; case WayfinderPinCategory.Bridge: key = "wayfinder:bridge"; break; case WayfinderPinCategory.Camp: key = "wayfinder:camp"; break; case WayfinderPinCategory.Storage: key = "wayfinder:storage"; break; case WayfinderPinCategory.Danger: key = "wayfinder:danger"; break; case WayfinderPinCategory.PointOfInterest: case WayfinderPinCategory.Base: key = "wayfinder:structure"; break; default: key = "wayfinder:custom"; break; } if (!_icons.TryGet(key, out sprite)) { return null; } return sprite; } private static void DrawSprite(Rect rect, Sprite sprite, Color color) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sprite == (Object)null) && !((Object)(object)sprite.texture == (Object)null)) { Color color2 = GUI.color; GUI.color = color; try { Rect textureRect = sprite.textureRect; Texture2D texture = sprite.texture; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); GUI.DrawTextureWithTexCoords(rect, (Texture)(object)texture, val, true); } catch { GUI.DrawTexture(rect, (Texture)(object)sprite.texture, (ScaleMode)2, true); } GUI.color = color2; } } private void EnsureStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if (_distanceStyle == null) { _distanceStyle = new GUIStyle(GUI.skin.label); _distanceStyle.alignment = (TextAnchor)4; _distanceStyle.fontSize = 11; _distanceStyle.fontStyle = (FontStyle)1; _distanceStyle.clipping = (TextClipping)1; _nameStyle = new GUIStyle(GUI.skin.label); _nameStyle.alignment = (TextAnchor)4; _nameStyle.fontSize = 10; _nameStyle.fontStyle = (FontStyle)0; _nameStyle.clipping = (TextClipping)1; } } private void EnsureArt() { if ((Object)(object)_frameTexture == (Object)null) { _frameTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_frame_v3.rgba", 2059, 155, "Wayfinder HQ Compass Frame V3"); } if ((Object)(object)_trackAccentTexture == (Object)null) { _trackAccentTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_track_accent.rgba", 32, 22, "Wayfinder HQ Track Accent"); } if ((Object)(object)_cardNTexture == (Object)null) { _cardNTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_card_N.rgba", 112, 112, "Wayfinder HQ Cardinal N"); } if ((Object)(object)_cardETexture == (Object)null) { _cardETexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_card_E.rgba", 112, 112, "Wayfinder HQ Cardinal E"); } if ((Object)(object)_cardSTexture == (Object)null) { _cardSTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_card_S.rgba", 112, 112, "Wayfinder HQ Cardinal S"); } if ((Object)(object)_cardWTexture == (Object)null) { _cardWTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_hq_card_W.rgba", 112, 112, "Wayfinder HQ Cardinal W"); } } private Texture2D LoadEmbeddedRgba(string resourceName, int width, int height, string textureName) { //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(resourceName); if (stream == null) { if (_config.DebugLogging.Value) { _log.LogWarning((object)("Missing compass art resource: " + resourceName)); } return null; } int num = width * height * 4; byte[] array = new byte[num]; int i; int num2; for (i = 0; i < num; i += num2) { num2 = stream.Read(array, i, num - i); if (num2 <= 0) { break; } } if (i != num) { if (_config.DebugLogging.Value) { _log.LogWarning((object)("Compass art byte count mismatch for " + resourceName + ": " + i + "/" + num)); } return null; } Color32[] array2 = (Color32[])(object)new Color32[width * height]; for (int j = 0; j < height; j++) { int num3 = j * width * 4; int num4 = (height - 1 - j) * width; for (int k = 0; k < width; k++) { int num5 = num3 + k * 4; ref Color32 reference = ref array2[num4 + k]; reference = new Color32(array[num5], array[num5 + 1], array[num5 + 2], array[num5 + 3]); } } Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); ((Object)val).name = textureName; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; val.SetPixels32(array2); val.Apply(false, false); return val; } catch (Exception ex) { if (_config.DebugLogging.Value) { _log.LogWarning((object)("Failed loading compass art " + resourceName + ": " + ex.Message)); } return null; } } private static void DrawShadowLabel(Rect rect, string text, GUIStyle style, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = new Color(0f, 0f, 0f, Mathf.Clamp01(color.a * 0.96f)); Rect val = rect; ((Rect)(ref val)).x = ((Rect)(ref val)).x + 1f; ((Rect)(ref val)).y = ((Rect)(ref val)).y + 1f; GUI.Label(val, text, style); GUI.color = color; GUI.Label(rect, text, style); GUI.color = color2; } private static Vector3 GetViewForward() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) try { Camera main = Camera.main; if ((Object)(object)main != (Object)null) { Vector3 forward = ((Component)main).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f) { return ((Vector3)(ref forward)).normalized; } } } catch { } if ((Object)(object)Player.m_localPlayer != (Object)null) { Vector3 forward2 = ((Component)Player.m_localPlayer).transform.forward; forward2.y = 0f; if (((Vector3)(ref forward2)).sqrMagnitude > 0.0001f) { return ((Vector3)(ref forward2)).normalized; } } return Vector3.forward; } private static float BearingFromDirection(Vector3 direction) { direction.y = 0f; if (((Vector3)(ref direction)).sqrMagnitude < 0.0001f) { return 0f; } return Mathf.Repeat(Mathf.Atan2(direction.x, direction.z) * 57.29578f, 360f); } private static string GetRecordName(WayfinderPinRecord record) { if (record == null) { return "Tracked Pin"; } if (!string.IsNullOrEmpty(record.displayName)) { return record.displayName; } if (!string.IsNullOrEmpty(record.subtype)) { return record.subtype; } return "Tracked Pin"; } private static bool IsLargeMapOpen() { Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return false; } try { if (LargeRootField != null) { object? value = LargeRootField.GetValue(instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { return val.activeInHierarchy; } } } catch { } return false; } } } namespace JoeyBadManners.Wayfinder.Icons { internal sealed class WayfinderIconEntry { internal string Key; internal string PrefabName; internal string RawName; internal Sprite Sprite; internal bool IsTrophy; internal string ItemTypeName; } internal sealed class RuntimeIconRegistry { private sealed class ScoredIcon { internal WayfinderIconEntry Entry; internal int Score; } private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly Dictionary _byKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _byPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly List _all = new List(); private readonly List _trophies = new List(); internal bool IsBuilt { get; private set; } internal bool IsDirty { get; private set; } internal IReadOnlyList All => _all; internal IReadOnlyList Trophies => _trophies; internal RuntimeIconRegistry(ManualLogSource log, WayfinderConfig config) { _log = log; _config = config; IsDirty = true; } internal void MarkDirty() { IsDirty = true; } internal void Build(ObjectDB objectDb) { //IL_0155: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)objectDb == (Object)null || objectDb.m_items == null) { return; } _byKey.Clear(); _byPrefab.Clear(); _all.Clear(); _trophies.Clear(); for (int i = 0; i < objectDb.m_items.Count; i++) { GameObject val = objectDb.m_items[i]; if ((Object)(object)val == (Object)null) { continue; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { continue; } Sprite[] icons = component.m_itemData.m_shared.m_icons; if (icons != null && icons.Length != 0 && !((Object)(object)icons[0] == (Object)null)) { string text = ((Object)val).name ?? string.Empty; string text2 = component.m_itemData.m_shared.m_name ?? text; bool flag = text.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase) || text2.IndexOf("trophy", StringComparison.OrdinalIgnoreCase) >= 0; WayfinderIconEntry wayfinderIconEntry = new WayfinderIconEntry(); wayfinderIconEntry.Key = "item:" + text; wayfinderIconEntry.PrefabName = text; wayfinderIconEntry.RawName = text2; wayfinderIconEntry.Sprite = icons[0]; wayfinderIconEntry.IsTrophy = flag; wayfinderIconEntry.ItemTypeName = ((object)component.m_itemData.m_shared.m_itemType).ToString(); WayfinderIconEntry wayfinderIconEntry2 = wayfinderIconEntry; _all.Add(wayfinderIconEntry2); _byKey[wayfinderIconEntry2.Key] = wayfinderIconEntry2; _byPrefab[text] = wayfinderIconEntry2; if (flag) { _trophies.Add(wayfinderIconEntry2); } } } RegisterEmbeddedRgbaIcon("wayfinder:dungeon", "JoeyBadManners.Wayfinder.Assets.wayfinder_dungeon.rgba", "Wayfinder Dungeon", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:poi", "JoeyBadManners.Wayfinder.Assets.wayfinder_poi.rgba", "Wayfinder POI", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:spawner", "JoeyBadManners.Wayfinder.Assets.wayfinder_spawner.rgba", "Wayfinder Spawner", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:structure", "JoeyBadManners.Wayfinder.Assets.wayfinder_structure.rgba", "Wayfinder Structure", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:runestone", "JoeyBadManners.Wayfinder.Assets.wayfinder_runestone.rgba", "Wayfinder Runestone", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:outpost", "JoeyBadManners.Wayfinder.Assets.wayfinder_outpost.rgba", "Wayfinder Outpost", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:farm", "JoeyBadManners.Wayfinder.Assets.wayfinder_farm.rgba", "Wayfinder Farm", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:dock", "JoeyBadManners.Wayfinder.Assets.wayfinder_dock.rgba", "Wayfinder Dock", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:road", "JoeyBadManners.Wayfinder.Assets.wayfinder_road.rgba", "Wayfinder Road", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:bridge", "JoeyBadManners.Wayfinder.Assets.wayfinder_bridge.rgba", "Wayfinder Bridge", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:camp", "JoeyBadManners.Wayfinder.Assets.wayfinder_camp.rgba", "Wayfinder Camp", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:storage", "JoeyBadManners.Wayfinder.Assets.wayfinder_storage.rgba", "Wayfinder Storage", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:portal", "JoeyBadManners.Wayfinder.Assets.wayfinder_portal.rgba", "Wayfinder Portal", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:vehicle", "JoeyBadManners.Wayfinder.Assets.wayfinder_vehicle.rgba", "Wayfinder Vehicle", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:boat", "JoeyBadManners.Wayfinder.Assets.wayfinder_boat.rgba", "Wayfinder Boat", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:cart", "JoeyBadManners.Wayfinder.Assets.wayfinder_cart.rgba", "Wayfinder Cart", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:trader", "JoeyBadManners.Wayfinder.Assets.wayfinder_trader.rgba", "Wayfinder Trader", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:habitat", "JoeyBadManners.Wayfinder.Assets.wayfinder_habitat.rgba", "Wayfinder Habitat", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:sighting", "JoeyBadManners.Wayfinder.Assets.wayfinder_sighting.rgba", "Wayfinder Sighting", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:creature", "JoeyBadManners.Wayfinder.Assets.wayfinder_creature.rgba", "Wayfinder Creature", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:danger", "JoeyBadManners.Wayfinder.Assets.wayfinder_danger.rgba", "Wayfinder Danger", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:gravestone", "JoeyBadManners.Wayfinder.Assets.wayfinder_gravestone.rgba", "Wayfinder Gravestone", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:custom", "JoeyBadManners.Wayfinder.Assets.wayfinder_custom.rgba", "Wayfinder Custom", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:resource", "JoeyBadManners.Wayfinder.Assets.wayfinder_resource.rgba", "Wayfinder Resource", 96, 96); RegisterEmbeddedRgbaIcon("wayfinder:active_waypoint", "JoeyBadManners.Wayfinder.Assets.wayfinder_active_waypoint.rgba", "Wayfinder Active Waypoint", 96, 96); IsBuilt = true; IsDirty = false; if (_config != null && _config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value) { _log.LogInfo((object)("Wayfinder runtime icon registry built: " + _all.Count + " icons total, " + _trophies.Count + " trophy candidates (including embedded Wayfinder UI icons).")); } } internal bool TryGet(string key, out Sprite sprite) { sprite = null; if (string.IsNullOrEmpty(key)) { return false; } if (_byKey.TryGetValue(key, out var value) && (Object)(object)value.Sprite != (Object)null) { sprite = value.Sprite; return true; } return false; } internal bool TryGetItem(string prefabName, out Sprite sprite) { sprite = null; if (!string.IsNullOrEmpty(prefabName) && _byPrefab.TryGetValue(prefabName, out var value) && (Object)(object)value.Sprite != (Object)null) { sprite = value.Sprite; return true; } return false; } private void RegisterEmbeddedRgbaIcon(string key, string resourceName, string displayName, int width, int height) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(resourceName); if (stream == null) { _log.LogWarning((object)("Embedded Wayfinder icon was not found: " + resourceName)); return; } int num = checked(width * height * 4); byte[] array = new byte[num]; int i; int num2; for (i = 0; i < array.Length; i += num2) { num2 = stream.Read(array, i, array.Length - i); if (num2 <= 0) { break; } } if (i != num) { _log.LogWarning((object)("Embedded Wayfinder icon had unexpected RGBA byte length: " + resourceName)); return; } Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); ((Object)val).name = displayName + " Texture"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; Color32[] array2 = (Color32[])(object)new Color32[width * height]; for (int j = 0; j < height; j++) { int num3 = j * width * 4; int num4 = (height - 1 - j) * width; for (int k = 0; k < width; k++) { int num5 = num3 + k * 4; ref Color32 reference = ref array2[num4 + k]; reference = new Color32(array[num5], array[num5 + 1], array[num5 + 2], array[num5 + 3]); } } val.SetPixels32(array2); val.Apply(false, false); Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)val2).name = displayName; WayfinderIconEntry wayfinderIconEntry = new WayfinderIconEntry(); wayfinderIconEntry.Key = key; wayfinderIconEntry.PrefabName = key; wayfinderIconEntry.RawName = displayName; wayfinderIconEntry.Sprite = val2; wayfinderIconEntry.IsTrophy = false; wayfinderIconEntry.ItemTypeName = "Wayfinder"; WayfinderIconEntry wayfinderIconEntry2 = wayfinderIconEntry; _byKey[key] = wayfinderIconEntry2; _all.Add(wayfinderIconEntry2); } catch (Exception ex) { _log.LogWarning((object)("Could not load embedded Wayfinder icon " + resourceName + ": " + ex.Message)); } } internal WayfinderIconEntry FindBestItem(params string[] candidates) { if (candidates == null || candidates.Length == 0) { return null; } foreach (string text in candidates) { if (!string.IsNullOrEmpty(text) && _byPrefab.TryGetValue(text, out var value) && value != null && (Object)(object)value.Sprite != (Object)null) { return value; } } WayfinderIconEntry result = null; int num = int.MinValue; for (int j = 0; j < _all.Count; j++) { WayfinderIconEntry wayfinderIconEntry = _all[j]; if (wayfinderIconEntry == null || (Object)(object)wayfinderIconEntry.Sprite == (Object)null || wayfinderIconEntry.IsTrophy) { continue; } string text2 = Normalize(wayfinderIconEntry.PrefabName); string text3 = Normalize(wayfinderIconEntry.RawName); string text4 = string.Empty; try { if (Localization.instance != null && !string.IsNullOrEmpty(wayfinderIconEntry.RawName)) { text4 = Normalize(Localization.instance.Localize(wayfinderIconEntry.RawName)); } } catch { } for (int k = 0; k < candidates.Length; k++) { string text5 = Normalize(candidates[k]); if (!string.IsNullOrEmpty(text5)) { int num2 = 0; if (text2 == text5) { num2 += 2000; } if (text4 == text5) { num2 += 1900; } if (text3 == text5) { num2 += 1800; } if (text2.EndsWith(text5, StringComparison.Ordinal)) { num2 += 700 + text5.Length; } if (text4.EndsWith(text5, StringComparison.Ordinal)) { num2 += 650 + text5.Length; } if (text3.EndsWith(text5, StringComparison.Ordinal)) { num2 += 600 + text5.Length; } if (text2.Contains(text5)) { num2 += 300 + text5.Length; } if (text4.Contains(text5)) { num2 += 280 + text5.Length; } if (text3.Contains(text5)) { num2 += 260 + text5.Length; } if (num2 > num) { num = num2; result = wayfinderIconEntry; } } } } if (num <= 0) { return null; } return result; } internal string FindBestItemKey(params string[] candidates) { WayfinderIconEntry wayfinderIconEntry = FindBestItem(candidates); if (wayfinderIconEntry != null) { return wayfinderIconEntry.Key; } return string.Empty; } internal WayfinderIconEntry FindBestTrophy(string creaturePrefabName) { if (!string.IsNullOrEmpty(creaturePrefabName)) { string text = Normalize(creaturePrefabName); if (text == "troll" || text.Contains("summonedtroll") || text.Contains("trollclone")) { WayfinderIconEntry wayfinderIconEntry = FindByPrefab("TrophyFrostTroll"); if (wayfinderIconEntry != null) { return wayfinderIconEntry; } } if (text.Contains("greyling")) { WayfinderIconEntry wayfinderIconEntry2 = FindByPrefab("TrophyGreydwarf"); if (wayfinderIconEntry2 != null) { return wayfinderIconEntry2; } } if (text.Contains("stonegolem") || text == "golem") { WayfinderIconEntry wayfinderIconEntry3 = FindByPrefab("TrophySGolem"); if (wayfinderIconEntry3 != null) { return wayfinderIconEntry3; } } if (text.Contains("greydwarfbrute")) { WayfinderIconEntry wayfinderIconEntry4 = FindByPrefab("TrophyGreydwarfBrute"); if (wayfinderIconEntry4 != null) { return wayfinderIconEntry4; } } } return FindBestTrophyInternal(creaturePrefabName, compareRawName: true); } internal WayfinderIconEntry FindBestBossTrophy(string bossPinName) { if (string.IsNullOrEmpty(bossPinName)) { return null; } string text = bossPinName; try { if (Localization.instance != null) { text = Localization.instance.Localize(bossPinName); } } catch { } string text2 = Normalize(text + " " + bossPinName); WayfinderIconEntry wayfinderIconEntry = null; if (text2.Contains("eikthyr")) { wayfinderIconEntry = FindByPrefab("TrophyEikthyr"); } else if (text2.Contains("elder") || text2.Contains("gdking")) { wayfinderIconEntry = FindByPrefab("TrophyTheElder"); } else if (text2.Contains("bonemass")) { wayfinderIconEntry = FindByPrefab("TrophyBonemass"); } else if (text2.Contains("moder") || text2.Contains("dragonqueen")) { wayfinderIconEntry = FindByPrefab("TrophyDragonQueen"); } else if (text2.Contains("yagluth") || text2.Contains("goblinking")) { wayfinderIconEntry = FindByPrefab("TrophyGoblinKing"); } else if (text2.Contains("queen") || text2.Contains("seekerqueen")) { wayfinderIconEntry = FindByPrefab("TrophySeekerQueen"); } else if (text2.Contains("fader")) { wayfinderIconEntry = FindByPrefab("TrophyFader"); } if (wayfinderIconEntry != null && (Object)(object)wayfinderIconEntry.Sprite != (Object)null) { return wayfinderIconEntry; } return FindBestTrophyInternal(text, compareRawName: true); } internal List Search(string query, int maxResults) { return Search(query, WayfinderPinCategory.Custom, maxResults); } internal List Search(string query, WayfinderPinCategory category, int maxResults) { List list = new List(); if (_all.Count == 0) { return new List(); } string text = Normalize(query ?? string.Empty); int num = ((maxResults <= 0) ? 40 : maxResults); for (int i = 0; i < _all.Count; i++) { WayfinderIconEntry wayfinderIconEntry = _all[i]; if (wayfinderIconEntry == null || (Object)(object)wayfinderIconEntry.Sprite == (Object)null || !IsCompatibleWithCategory(wayfinderIconEntry, category)) { continue; } string text2 = string.Empty; try { if (Localization.instance != null && !string.IsNullOrEmpty(wayfinderIconEntry.RawName)) { text2 = Localization.instance.Localize(wayfinderIconEntry.RawName); } } catch { } string text3 = Normalize((wayfinderIconEntry.Key ?? string.Empty) + " " + (wayfinderIconEntry.PrefabName ?? string.Empty) + " " + (wayfinderIconEntry.RawName ?? string.Empty) + " " + text2 + " " + (wayfinderIconEntry.IsTrophy ? " trophy creature enemy boss" : " item resource icon")); if (!string.IsNullOrEmpty(text) && !text3.Contains(text)) { continue; } int num2 = CategoryPriority(wayfinderIconEntry, category); if (!string.IsNullOrEmpty(text)) { string text4 = Normalize(wayfinderIconEntry.PrefabName); string text5 = Normalize(wayfinderIconEntry.RawName); string text6 = Normalize(text2); if (text4 == text) { num2 += 5000; } if (text6 == text) { num2 += 4500; } if (text5 == text) { num2 += 4000; } if (text4.StartsWith(text, StringComparison.Ordinal)) { num2 += 1200; } if (text6.StartsWith(text, StringComparison.Ordinal)) { num2 += 1100; } if (text5.StartsWith(text, StringComparison.Ordinal)) { num2 += 1000; } } list.Add(new ScoredIcon { Entry = wayfinderIconEntry, Score = num2 }); } list.Sort(delegate(ScoredIcon a, ScoredIcon b) { int num4 = b.Score.CompareTo(a.Score); return (num4 != 0) ? num4 : string.Compare((a.Entry == null) ? string.Empty : a.Entry.PrefabName, (b.Entry == null) ? string.Empty : b.Entry.PrefabName, StringComparison.OrdinalIgnoreCase); }); List list2 = new List(); for (int num3 = 0; num3 < list.Count; num3++) { if (list2.Count >= num) { break; } list2.Add(list[num3].Entry); } return list2; } internal bool IsCompatibleWithCategory(WayfinderIconEntry entry, WayfinderPinCategory category) { if (entry == null) { return false; } bool flag = !string.IsNullOrEmpty(entry.Key) && entry.Key.StartsWith("wayfinder:", StringComparison.OrdinalIgnoreCase); switch (category) { case WayfinderPinCategory.Boss: return IsBossTrophy(entry); case WayfinderPinCategory.Creature: if (!string.Equals(entry.Key, "wayfinder:creature", StringComparison.OrdinalIgnoreCase)) { if (entry.IsTrophy) { return !IsBossTrophy(entry); } return false; } return true; case WayfinderPinCategory.Sighting: if (!string.Equals(entry.Key, "wayfinder:sighting", StringComparison.OrdinalIgnoreCase)) { if (entry.IsTrophy) { return !IsBossTrophy(entry); } return false; } return true; case WayfinderPinCategory.Habitat: if (!string.Equals(entry.Key, "wayfinder:habitat", StringComparison.OrdinalIgnoreCase)) { if (entry.IsTrophy) { return !IsBossTrophy(entry); } return false; } return true; case WayfinderPinCategory.Spawner: if (!string.Equals(entry.Key, "wayfinder:spawner", StringComparison.OrdinalIgnoreCase)) { if (entry.IsTrophy) { return !IsBossTrophy(entry); } return false; } return true; case WayfinderPinCategory.Resource: if (!string.Equals(entry.Key, "wayfinder:resource", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return IsResourceLikeItem(entry); } return false; } return true; case WayfinderPinCategory.Dungeon: return string.Equals(entry.Key, "wayfinder:dungeon", StringComparison.OrdinalIgnoreCase); case WayfinderPinCategory.PointOfInterest: if (!string.Equals(entry.Key, "wayfinder:structure", StringComparison.OrdinalIgnoreCase)) { return string.Equals(entry.Key, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase); } return true; case WayfinderPinCategory.Vehicle: if (!string.Equals(entry.Key, "wayfinder:vehicle", StringComparison.OrdinalIgnoreCase) && !string.Equals(entry.Key, "wayfinder:boat", StringComparison.OrdinalIgnoreCase) && !string.Equals(entry.Key, "wayfinder:cart", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "boat", "ship", "raft", "karve", "longship", "drakkar", "cart"); } return false; } return true; case WayfinderPinCategory.Trader: if (!string.Equals(entry.Key, "wayfinder:trader", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "coin", "amber", "ruby", "silvernecklace", "gold"); } return false; } return true; case WayfinderPinCategory.Portal: if (!string.Equals(entry.Key, "wayfinder:portal", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "portal", "surtlingcore", "greydwarfeye"); } return false; } return true; case WayfinderPinCategory.Base: if (!string.Equals(entry.Key, "wayfinder:structure", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "hammer", "wood", "stone", "workbench", "bed", "fire", "chest", "banner"); } return false; } return true; case WayfinderPinCategory.Outpost: if (!string.Equals(entry.Key, "wayfinder:outpost", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "tower", "fort", "keep", "banner", "torch", "stone", "wood"); } return false; } return true; case WayfinderPinCategory.Farm: if (!string.Equals(entry.Key, "wayfinder:farm", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "seed", "carrot", "turnip", "onion", "barley", "flax", "cultivator", "hoe", "mushroom", "berry", "raspberry", "blueberry"); } return false; } return true; case WayfinderPinCategory.Dock: if (!string.Equals(entry.Key, "wayfinder:dock", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "anchor", "boat", "ship", "raft", "karve", "longship", "drakkar", "wood", "iron"); } return false; } return true; case WayfinderPinCategory.Road: if (!string.Equals(entry.Key, "wayfinder:road", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "hoe", "stone", "wood", "pickaxe"); } return false; } return true; case WayfinderPinCategory.Bridge: if (!string.Equals(entry.Key, "wayfinder:bridge", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "wood", "stone", "iron"); } return false; } return true; case WayfinderPinCategory.Camp: if (!string.Equals(entry.Key, "wayfinder:camp", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "fire", "torch", "bed", "wood", "tent", "banner"); } return false; } return true; case WayfinderPinCategory.Storage: if (!string.Equals(entry.Key, "wayfinder:storage", StringComparison.OrdinalIgnoreCase)) { if (!entry.IsTrophy && !flag) { return EntryMatchesAny(entry, "chest", "crate", "barrel", "box", "coin"); } return false; } return true; case WayfinderPinCategory.Danger: if (!string.Equals(entry.Key, "wayfinder:danger", StringComparison.OrdinalIgnoreCase)) { if (entry.IsTrophy) { return !IsBossTrophy(entry); } return false; } return true; default: return true; } } internal static bool IsBossTrophy(WayfinderIconEntry entry) { if (entry == null || !entry.IsTrophy) { return false; } string text = Normalize(entry.PrefabName); if (!(text == Normalize("TrophyEikthyr")) && !(text == Normalize("TrophyTheElder")) && !(text == Normalize("TrophyBonemass")) && !(text == Normalize("TrophyDragonQueen")) && !(text == Normalize("TrophyGoblinKing")) && !(text == Normalize("TrophySeekerQueen"))) { return text == Normalize("TrophyFader"); } return true; } private static bool IsResourceLikeItem(WayfinderIconEntry entry) { if (entry == null) { return false; } string text = Normalize(entry.ItemTypeName); if (string.IsNullOrEmpty(text)) { return true; } if (text.Contains("weapon") || text.Contains("armor") || text.Contains("helmet") || text.Contains("chest") || text.Contains("legs") || text.Contains("hands") || text.Contains("shoulder") || text.Contains("shield") || text.Contains("bow") || text.Contains("tool") || text.Contains("torch") || text.Contains("ammo") || text.Contains("utility")) { return false; } return true; } private static bool EntryMatchesAny(WayfinderIconEntry entry, params string[] needles) { if (entry == null || needles == null || needles.Length == 0) { return false; } string text = string.Empty; try { if (Localization.instance != null && !string.IsNullOrEmpty(entry.RawName)) { text = Localization.instance.Localize(entry.RawName); } } catch { } string text2 = Normalize((entry.Key ?? string.Empty) + " " + (entry.PrefabName ?? string.Empty) + " " + (entry.RawName ?? string.Empty) + " " + text); for (int i = 0; i < needles.Length; i++) { string value = Normalize(needles[i]); if (!string.IsNullOrEmpty(value) && text2.Contains(value)) { return true; } } return false; } private static int CategoryPriority(WayfinderIconEntry entry, WayfinderPinCategory category) { if (entry == null) { return 0; } string text = string.Empty; switch (category) { case WayfinderPinCategory.Resource: text = "wayfinder:resource"; break; case WayfinderPinCategory.Creature: text = "wayfinder:creature"; break; case WayfinderPinCategory.Dungeon: text = "wayfinder:dungeon"; break; case WayfinderPinCategory.PointOfInterest: text = "wayfinder:structure"; break; case WayfinderPinCategory.Spawner: text = "wayfinder:spawner"; break; case WayfinderPinCategory.Habitat: text = "wayfinder:habitat"; break; case WayfinderPinCategory.Sighting: text = "wayfinder:sighting"; break; case WayfinderPinCategory.Vehicle: text = "wayfinder:vehicle"; break; case WayfinderPinCategory.Trader: text = "wayfinder:trader"; break; case WayfinderPinCategory.Portal: text = "wayfinder:portal"; break; case WayfinderPinCategory.Base: text = "wayfinder:structure"; break; case WayfinderPinCategory.Outpost: text = "wayfinder:outpost"; break; case WayfinderPinCategory.Farm: text = "wayfinder:farm"; break; case WayfinderPinCategory.Dock: text = "wayfinder:dock"; break; case WayfinderPinCategory.Road: text = "wayfinder:road"; break; case WayfinderPinCategory.Bridge: text = "wayfinder:bridge"; break; case WayfinderPinCategory.Camp: text = "wayfinder:camp"; break; case WayfinderPinCategory.Storage: text = "wayfinder:storage"; break; case WayfinderPinCategory.Danger: text = "wayfinder:danger"; break; } if (!string.IsNullOrEmpty(text) && string.Equals(entry.Key, text, StringComparison.OrdinalIgnoreCase)) { return 15000; } if (category == WayfinderPinCategory.PointOfInterest && string.Equals(entry.Key, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase)) { return 14000; } if (category == WayfinderPinCategory.Boss && IsBossTrophy(entry)) { return 12000; } if (entry.IsTrophy && (category == WayfinderPinCategory.Creature || category == WayfinderPinCategory.Sighting || category == WayfinderPinCategory.Habitat || category == WayfinderPinCategory.Spawner || category == WayfinderPinCategory.Danger)) { return 5000; } return 100; } private WayfinderIconEntry FindBestTrophyInternal(string creatureName, bool compareRawName) { if (string.IsNullOrEmpty(creatureName)) { return null; } string text = Normalize(creatureName); WayfinderIconEntry result = null; int num = int.MinValue; for (int i = 0; i < _trophies.Count; i++) { WayfinderIconEntry wayfinderIconEntry = _trophies[i]; string text2 = Normalize(wayfinderIconEntry.PrefabName.Replace("Trophy", string.Empty)); string text3 = (compareRawName ? Normalize(wayfinderIconEntry.RawName) : string.Empty); string text4 = string.Empty; if (compareRawName) { try { if (Localization.instance != null && !string.IsNullOrEmpty(wayfinderIconEntry.RawName)) { text4 = Normalize(Localization.instance.Localize(wayfinderIconEntry.RawName)); } } catch { } } if (text2.Length != 0) { int num2 = 0; if (text2 == text) { num2 += 1000; } if (text.Contains(text2)) { num2 += 200 + text2.Length; } if (text2.Contains(text)) { num2 += 100 + text.Length; } if (compareRawName && text3.Contains(text)) { num2 += 300 + text.Length; } if (compareRawName && text4.Contains(text)) { num2 += 500 + text.Length; } if (compareRawName && text.Contains(text4) && text4.Length > 3) { num2 += 350 + text4.Length; } if (num2 > num) { num = num2; result = wayfinderIconEntry; } } } if (num <= 0) { return null; } return result; } private WayfinderIconEntry FindByPrefab(string prefabName) { if (!_byPrefab.TryGetValue(prefabName, out var value)) { return null; } return value; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal static class WayfinderIconPolicy { internal static WayfinderPinCategory ClassifyGeneratedLocation(string prefabName) { string text = Normalize(prefabName); if (ContainsAny(text, "hildircave", "hildircrypt", "hildirplainsfortress", "mistlandsdvergrtownentrance")) { return WayfinderPinCategory.Dungeon; } if (text.Contains("tarpit")) { return WayfinderPinCategory.Danger; } if (IsDungeon(text)) { return WayfinderPinCategory.Dungeon; } if (ContainsAny(text, "drakenest", "volturenest")) { return WayfinderPinCategory.Habitat; } if (ContainsAny(text, "watchtower", "tower", "outpost", "fort", "keep", "lighthouse")) { return WayfinderPinCategory.Outpost; } if (ContainsAny(text, "farm", "field", "barley", "flax")) { return WayfinderPinCategory.Farm; } if (ContainsAny(text, "dock", "harbor", "harbour", "pier", "shipyard")) { return WayfinderPinCategory.Dock; } if (text.Contains("bridge") || text.Contains("viaduct")) { return WayfinderPinCategory.Bridge; } if (ContainsAny(text, "road", "path", "causeway")) { return WayfinderPinCategory.Road; } if (ContainsAny(text, "camp", "campsite", "tent", "village", "settlement")) { return WayfinderPinCategory.Camp; } if (ContainsAny(text, "storage", "warehouse", "storehouse")) { return WayfinderPinCategory.Storage; } if (text.Contains("portal")) { return WayfinderPinCategory.Portal; } return WayfinderPinCategory.PointOfInterest; } internal static string GetGeneratedLocationIconKey(string prefabName, WayfinderPinCategory category) { string n = Normalize(prefabName); if (IsRunestone(n)) { return "wayfinder:runestone"; } return category switch { WayfinderPinCategory.Dungeon => "wayfinder:dungeon", WayfinderPinCategory.Habitat => "wayfinder:habitat", WayfinderPinCategory.Outpost => "wayfinder:outpost", WayfinderPinCategory.Farm => "wayfinder:farm", WayfinderPinCategory.Dock => "wayfinder:dock", WayfinderPinCategory.Road => "wayfinder:road", WayfinderPinCategory.Bridge => "wayfinder:bridge", WayfinderPinCategory.Camp => "wayfinder:camp", WayfinderPinCategory.Storage => "wayfinder:storage", WayfinderPinCategory.Portal => "wayfinder:portal", WayfinderPinCategory.Danger => "wayfinder:danger", _ => "wayfinder:structure", }; } internal static string GetGeneratedLocationDisplayName(string prefabName) { string text = Normalize(prefabName); if (text.StartsWith("crypt")) { return "Burial Chamber"; } if (text.StartsWith("trollcave")) { return "Troll Cave"; } if (text.StartsWith("mountaincave")) { return "Frost Cave"; } if (text.StartsWith("sunkencrypt")) { return "Sunken Crypt"; } if (text.StartsWith("hildircave")) { return "Howling Caverns"; } if (text.StartsWith("hildircrypt")) { return "Smouldering Tombs"; } if (text.StartsWith("hildirplainsfortress")) { return "Sealed Tower"; } if (text.StartsWith("mistlandsdvergrtownentrance")) { return "Infested Mine"; } if (text.StartsWith("mistlandsdvergrbossentrance")) { return "Infested Citadel"; } if (text.StartsWith("goblincamp")) { return "Fuling Village"; } if (text.StartsWith("greydwarfcamp")) { return "Greydwarf Camp"; } if (text.StartsWith("drakenest")) { return "Drake Nest"; } if (text.StartsWith("volturenest")) { return "Volture Nest"; } if (text.StartsWith("morgenhole")) { return "Morgen Cave"; } if (text.StartsWith("mistlandsguardtower") && text.Contains("ruined")) { return "Ruined Dvergr Guard Tower"; } if (text.StartsWith("mistlandsguardtower")) { return "Dvergr Guard Tower"; } if (text.StartsWith("mistlandslighthouse")) { return "Dvergr Lighthouse"; } if (text.StartsWith("mistlandsexcavation")) { return "Dvergr Excavation"; } if (text.StartsWith("mistlandsstatuegroup") || text.StartsWith("mistlandsstatue")) { return "Mistlands Statue"; } if (text.StartsWith("stonetowerruins")) { return "Ruined Stone Tower"; } if (text.StartsWith("stonetower")) { return "Stone Tower"; } if (text.StartsWith("stonehouse")) { return "Stone House"; } if (text.StartsWith("woodhouse")) { return "Abandoned House"; } if (text.StartsWith("abandonedlogcabin")) { return "Abandoned Cabin"; } if (text.StartsWith("charredtowerruins")) { return "Ruined Charred Tower"; } if (text.StartsWith("charredruins")) { return "Charred Ruins"; } if (text.StartsWith("ashlandruins")) { return "Ashlands Ruins"; } if (text.StartsWith("fortressruins")) { return "Fortress Ruins"; } if (text.StartsWith("shipwreck")) { return "Shipwreck"; } if (text.StartsWith("shipsetting")) { return "Viking Graveyard"; } if (text.StartsWith("tarpit")) { return "Tar Pit"; } if (text.StartsWith("dolmen")) { return "Dolmen"; } if (text.StartsWith("stonecircle")) { return "Ancient Stone Circle"; } if (text.StartsWith("mountaingrave")) { return "Mountain Grave"; } if (text.StartsWith("swampwell") || text.StartsWith("mountainwell")) { return "Inverted Tower"; } if (text.StartsWith("bigrockclearing")) { return "Big Rock Clearing"; } if (text.StartsWith("mistlandsharbour")) { return "Dvergr Harbour"; } if (text.StartsWith("mistlandsviaduct")) { return "Dvergr Viaduct"; } if (text.StartsWith("mistlandsroadpost")) { return "Dvergr Road Post"; } return HumanizePrefabName(prefabName); } internal static string GetEffectiveIconKey(WayfinderPinRecord record) { if (record == null) { return string.Empty; } if (record.iconOverride) { return record.iconOverrideKey ?? string.Empty; } return GetDefaultIconKey(record); } internal static string GetDefaultIconKey(WayfinderPinRecord record) { if (record == null) { return string.Empty; } if (record.source == WayfinderPinSource.Manual || record.source == WayfinderPinSource.Imported) { return record.iconKey ?? string.Empty; } string text = record.subtype ?? string.Empty; string text2 = record.displayName ?? string.Empty; string text3 = Normalize(text + " " + text2); if (record.category == WayfinderPinCategory.Habitat || ContainsAny(text3, "drakenest", "volturenest")) { return "wayfinder:habitat"; } if (IsSpawnerLike(text3)) { return "wayfinder:spawner"; } if (string.Equals(record.iconKey, "wayfinder:runestone", StringComparison.OrdinalIgnoreCase)) { return "wayfinder:runestone"; } if (IsRunestone(text3)) { return "wayfinder:runestone"; } if (text3.Contains("portal")) { return "wayfinder:portal"; } if (record.category == WayfinderPinCategory.Dungeon || IsDungeon(text3)) { return "wayfinder:dungeon"; } if (IsGeneratedLocationCategory(record.category)) { return GetGeneratedLocationIconKey(text + " " + text2, record.category); } if (record.category != WayfinderPinCategory.Resource) { WayfinderPinCategory category = ClassifyGeneratedLocation(text + " " + text2); if (IsGeneratedLocationCategory(category) && LooksLikeGeneratedSite(text3)) { return GetGeneratedLocationIconKey(text + " " + text2, category); } } if (record.category == WayfinderPinCategory.Resource) { string text4 = ResolveAutoResourceIconKey(text3, record.iconKey); if (!string.IsNullOrEmpty(text4)) { return text4; } } if ((record.category == WayfinderPinCategory.Unknown || record.category == WayfinderPinCategory.Custom) && LooksLikeResource(text3)) { string text5 = ResolveAutoResourceIconKey(text3, record.iconKey); if (!string.IsNullOrEmpty(text5)) { return text5; } } if (record.category == WayfinderPinCategory.Vehicle) { if (ContainsAny(text3, "cart", "vagon", "wagon")) { return "wayfinder:cart"; } if (ContainsAny(text3, "boat", "ship", "raft", "karve", "longship", "vikingship", "drakkar", "ashland")) { return "wayfinder:boat"; } return "wayfinder:vehicle"; } switch (record.category) { case WayfinderPinCategory.Spawner: return "wayfinder:spawner"; case WayfinderPinCategory.Habitat: return "wayfinder:habitat"; case WayfinderPinCategory.Sighting: if (!string.IsNullOrEmpty(record.iconKey)) { return record.iconKey; } return "wayfinder:sighting"; case WayfinderPinCategory.Trader: return "wayfinder:trader"; case WayfinderPinCategory.Danger: return "wayfinder:danger"; case WayfinderPinCategory.Base: return "wayfinder:structure"; case WayfinderPinCategory.Custom: if (!string.IsNullOrEmpty(record.iconKey)) { return record.iconKey; } return "wayfinder:custom"; default: return record.iconKey ?? string.Empty; } } internal static float GetVisualScale(WayfinderPinRecord record, string effectiveIconKey) { float num = ((record == null) ? 1f : Mathf.Max(0.25f, record.scale)); if (string.IsNullOrEmpty(effectiveIconKey) || !effectiveIconKey.StartsWith("wayfinder:", StringComparison.OrdinalIgnoreCase)) { return num; } switch (effectiveIconKey) { case "wayfinder:dungeon": return Mathf.Max(num, 1.2f); case "wayfinder:spawner": return Mathf.Max(num, 1.18f); case "wayfinder:runestone": return Mathf.Max(num, 1.15f); case "wayfinder:boat": case "wayfinder:cart": return Mathf.Max(num, 1.14f); case "wayfinder:structure": case "wayfinder:outpost": case "wayfinder:farm": case "wayfinder:dock": case "wayfinder:road": case "wayfinder:bridge": case "wayfinder:camp": case "wayfinder:storage": case "wayfinder:portal": return Mathf.Max(num, 1.16f); default: return num; } } internal static bool IsGeneratedLocationCategory(WayfinderPinCategory category) { if (category != WayfinderPinCategory.Dungeon && category != WayfinderPinCategory.PointOfInterest && category != WayfinderPinCategory.Outpost && category != WayfinderPinCategory.Farm && category != WayfinderPinCategory.Dock && category != WayfinderPinCategory.Road && category != WayfinderPinCategory.Bridge && category != WayfinderPinCategory.Camp && category != WayfinderPinCategory.Storage) { return category == WayfinderPinCategory.Portal; } return true; } internal static bool IsRunestoneName(string name) { return IsRunestone(Normalize(name)); } internal static bool IsVegvisirName(string name) { string text = Normalize(name); if (!string.IsNullOrEmpty(text)) { return text.Contains("vegvisir"); } return false; } internal static bool IsBossGuidanceRunestoneName(string name) { string text = Normalize(name); if (string.IsNullOrEmpty(text)) { return false; } if (ContainsAny(text, "starttemple", "sacrificial", "bossstone", "bossrune", "bossrunestone")) { return true; } if (text.Contains("vegvisir")) { return text.Contains("eikthyr"); } return false; } private static bool IsRunestone(string n) { return ContainsAny(n, "runestone", "lorestone", "rune", "vegvisir"); } private static bool IsDungeon(string n) { return ContainsAny(n, "crypt", "burial", "chamber", "cave", "mine", "tomb", "dungeon", "citadel", "sealedtower", "howlingcavern", "smoulderingtomb", "putridhole", "morgenhole"); } private static bool LooksLikeGeneratedSite(string n) { return ContainsAny(n, "hut", "house", "tower", "watchtower", "lighthouse", "outpost", "camp", "village", "settlement", "cabin", "farm", "fort", "keep", "ruin", "hall", "bridge", "viaduct", "dock", "harbor", "harbour", "pier", "shipwreck", "building", "structure", "road", "path", "causeway", "portal", "warehouse", "storehouse", "excavation", "grave", "well", "dolmen", "statue"); } private static string HumanizePrefabName(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return "Point of Interest"; } string text = prefabName.Replace("(Clone)", string.Empty).Replace('_', ' ').Trim(); StringBuilder stringBuilder = new StringBuilder(text.Length + 8); char c = '\0'; for (int i = 0; i < text.Length; i++) { char c2 = text[i]; if (i > 0 && c2 != ' ' && c != ' ' && ((char.IsUpper(c2) && char.IsLower(c)) || (char.IsDigit(c2) && !char.IsDigit(c)))) { stringBuilder.Append(' '); } stringBuilder.Append(c2); c = c2; } return stringBuilder.ToString().Trim(); } private static bool LooksLikeResource(string n) { return ContainsAny(n, "copper", "tin", "obsidian", "silver", "ironscrap", "scrapiron", "muddy", "flametal", "blackmarble", "softtissue", "crystal", "guck", "branch", "wood", "flint", "stone", "dandelion", "raspberries", "blueberries", "thistle", "mushroom"); } private static string ResolveAutoResourceIconKey(string n, string existingKey) { if (string.IsNullOrEmpty(n)) { if (!string.IsNullOrEmpty(existingKey) && existingKey.StartsWith("item:", StringComparison.OrdinalIgnoreCase)) { return existingKey; } return "wayfinder:resource"; } if (n.Contains("copper")) { return "item:CopperOre"; } if (n.Contains("tin")) { return "item:TinOre"; } if (n.Contains("obsidian")) { return "item:Obsidian"; } if (n.Contains("silver")) { return "item:SilverOre"; } if (n.Contains("ironscrap") || n.Contains("scrapiron") || n.Contains("muddy")) { return "item:IronScrap"; } if (n.Contains("flametal")) { return "item:FlametalOreNew"; } if (n.Contains("blackmarble")) { return "item:BlackMarble"; } if (n.Contains("softtissue")) { return "item:SoftTissue"; } if (n.Contains("crystal")) { return "item:Crystal"; } if (n.Contains("guck")) { return "item:Guck"; } if (n.Contains("wood") || n.Contains("branch")) { return "item:Wood"; } if (n.Contains("flint")) { return "item:Flint"; } if (n.Contains("stone")) { return "item:Stone"; } if (n.Contains("dandelion")) { return "item:Dandelion"; } if (n.Contains("raspberr")) { return "item:Raspberry"; } if (n.Contains("blueberr")) { return "item:Blueberries"; } if (n.Contains("thistle")) { return "item:Thistle"; } if (n.Contains("mushroom")) { return "item:Mushroom"; } if (!string.IsNullOrEmpty(existingKey) && existingKey.StartsWith("item:", StringComparison.OrdinalIgnoreCase)) { return existingKey; } return "wayfinder:resource"; } private static bool IsSpawnerLike(string n) { return ContainsAny(n, "spawner", "greydwarfnest", "bonepile", "bodypile", "monumentoftorment", "effigyofmalice", "firehole", "surtling", "nest"); } private static bool ContainsAny(string value, params string[] needles) { if (string.IsNullOrEmpty(value) || needles == null) { return false; } for (int i = 0; i < needles.Length; i++) { if (!string.IsNullOrEmpty(needles[i]) && value.Contains(needles[i])) { return true; } } return false; } internal static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } } namespace JoeyBadManners.Wayfinder.Management { internal static class WayfinderBossNaming { private static readonly string[] _keys = new string[7] { "Eikthyr", "TheElder", "Bonemass", "Moder", "Yagluth", "Queen", "Fader" }; internal static string[] KnownKeys => _keys; internal static string IdentifyBossKey(string sourceName) { if (string.IsNullOrEmpty(sourceName)) { return string.Empty; } string text = sourceName; try { if (Localization.instance != null) { text = Localization.instance.Localize(sourceName); } } catch { } string text2 = Normalize(text + " " + sourceName); if (text2.Contains("eikthyr")) { return "Eikthyr"; } if (text2.Contains("elder") || text2.Contains("gdking")) { return "TheElder"; } if (text2.Contains("bonemass")) { return "Bonemass"; } if (text2.Contains("moder") || text2.Contains("dragonqueen")) { return "Moder"; } if (text2.Contains("yagluth") || text2.Contains("goblinking")) { return "Yagluth"; } if (text2.Contains("seekerqueen") || text2 == "queen" || text2.Contains("thequeen")) { return "Queen"; } if (text2.Contains("fader")) { return "Fader"; } return string.Empty; } internal static string DefaultDisplayName(string key) { switch (key) { case "Eikthyr": return "Eikthyr"; case "TheElder": return "The Elder"; case "Bonemass": return "Bonemass"; case "Moder": return "Moder"; case "Yagluth": return "Yagluth"; case "Queen": return "The Queen"; case "Fader": return "Fader"; default: if (!string.IsNullOrEmpty(key)) { return key; } return "Boss"; } } internal static string GetDisplayName(WayfinderConfig config, string key) { if (string.IsNullOrEmpty(key)) { return string.Empty; } Dictionary dictionary = Parse(config); if (dictionary.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value)) { return value; } return DefaultDisplayName(key); } internal static void SetDisplayName(WayfinderConfig config, string key, string value) { if (config == null || string.IsNullOrEmpty(key)) { return; } Dictionary dictionary = Parse(config); string text = (string.IsNullOrEmpty(value) ? string.Empty : value.Trim()); dictionary[key] = (string.IsNullOrEmpty(text) ? DefaultDisplayName(key) : text); List list = new List(); for (int i = 0; i < _keys.Length; i++) { string text2 = _keys[i]; if (!dictionary.TryGetValue(text2, out var value2) || string.IsNullOrEmpty(value2)) { value2 = DefaultDisplayName(text2); } value2 = value2.Replace(";", " ").Replace("=", " "); list.Add(text2 + "=" + value2); } config.BossNameOverrides.Value = string.Join(";", list.ToArray()); } private static Dictionary Parse(WayfinderConfig config) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (config == null) { return dictionary; } string text = config.BossNameOverrides.Value ?? string.Empty; string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { int num = array[i].IndexOf('='); if (num > 0) { string text2 = array[i].Substring(0, num).Trim(); string value = array[i].Substring(num + 1).Trim(); if (!string.IsNullOrEmpty(text2)) { dictionary[text2] = value; } } } return dictionary; } private static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = new char[value.Length]; int length = 0; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { array[length++] = c; } } return new string(array, 0, length); } } internal sealed class WayfinderPinManagementService { private readonly WayfinderConfig _config; private readonly WayfinderPinDatabase _database; private readonly HashSet _hidden = new HashSet(); private string _lastHiddenValue; internal IReadOnlyList Records => _database.Records; internal int MaxTrackedPins => Mathf.Clamp(_config.MaxTrackedPins.Value, 1, 5); internal int TrackedCount { get { EnsureTrackedSlots(); int num = 0; IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.tracked) { num++; } } return num; } } internal WayfinderPinManagementService(WayfinderConfig config, WayfinderPinDatabase database) { _config = config; _database = database; } internal bool IsCategoryVisible(WayfinderPinCategory category) { RefreshHiddenCache(); return !_hidden.Contains(category); } internal void SetCategoryVisible(WayfinderPinCategory category, bool visible) { RefreshHiddenCache(); if (visible) { _hidden.Remove(category); } else { _hidden.Add(category); } PersistHiddenCategories(); } internal List Search(string query, bool favoritesOnly, int maxResults) { return Search(query, favoritesOnly, null, maxResults); } internal List Search(string query, bool favoritesOnly, WayfinderPinCategory? categoryOnly, int maxResults) { List list = new List(); string value = (string.IsNullOrEmpty(query) ? string.Empty : query.Trim().ToLowerInvariant()); IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord == null || (favoritesOnly && !wayfinderPinRecord.favorite) || (categoryOnly.HasValue && wayfinderPinRecord.category != categoryOnly.Value)) { continue; } if (!string.IsNullOrEmpty(value)) { string text = (wayfinderPinRecord.displayName ?? string.Empty).ToLowerInvariant(); string text2 = (wayfinderPinRecord.subtype ?? string.Empty).ToLowerInvariant(); string text3 = wayfinderPinRecord.category.ToString().ToLowerInvariant(); string text4 = (wayfinderPinRecord.portalTag ?? string.Empty).ToLowerInvariant(); if (!text.Contains(value) && !text2.Contains(value) && !text3.Contains(value) && !text4.Contains(value)) { continue; } } list.Add(wayfinderPinRecord); } list.Sort(CompareRecords); if (maxResults > 0 && list.Count > maxResults) { list.RemoveRange(maxResults, list.Count - maxResults); } return list; } internal int CountOtherPortalsWithTag(WayfinderPinRecord portal) { if (portal == null || portal.category != WayfinderPinCategory.Portal || string.IsNullOrEmpty(portal.portalTag)) { return 0; } int num = 0; IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && !object.ReferenceEquals(wayfinderPinRecord, portal) && wayfinderPinRecord.category == WayfinderPinCategory.Portal && string.Equals(wayfinderPinRecord.portalTag ?? string.Empty, portal.portalTag, StringComparison.OrdinalIgnoreCase)) { num++; } } return num; } internal WayfinderPinRecord FindNearestPortalTagPartner(WayfinderPinRecord portal) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (portal == null || portal.category != WayfinderPinCategory.Portal || string.IsNullOrEmpty(portal.portalTag)) { return null; } WayfinderPinRecord result = null; float num = float.MaxValue; IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && !object.ReferenceEquals(wayfinderPinRecord, portal) && wayfinderPinRecord.category == WayfinderPinCategory.Portal && string.Equals(wayfinderPinRecord.portalTag ?? string.Empty, portal.portalTag, StringComparison.OrdinalIgnoreCase)) { float num2 = wayfinderPinRecord.Position.x - portal.Position.x; float num3 = wayfinderPinRecord.Position.z - portal.Position.z; float num4 = num2 * num2 + num3 * num3; if (num4 < num) { num = num4; result = wayfinderPinRecord; } } } return result; } internal WayfinderPinRecord Find(string id) { if (string.IsNullOrEmpty(id)) { return null; } IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.id == id) { return wayfinderPinRecord; } } return null; } internal bool Rename(string id, string newName) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } string text = (string.IsNullOrEmpty(newName) ? string.Empty : newName.Trim()); if (wayfinderPinRecord.category == WayfinderPinCategory.Portal) { wayfinderPinRecord.portalNameOverride = true; wayfinderPinRecord.displayName = (string.IsNullOrEmpty(text) ? "Portal" : text); } else if (wayfinderPinRecord.category == WayfinderPinCategory.Vehicle && wayfinderPinRecord.source == WayfinderPinSource.Dynamic) { wayfinderPinRecord.vehicleNameOverride = true; wayfinderPinRecord.displayName = (string.IsNullOrEmpty(text) ? WayfinderVehicleNaming.GetAutomaticDisplayName(wayfinderPinRecord) : text); } else { wayfinderPinRecord.displayName = (string.IsNullOrEmpty(text) ? wayfinderPinRecord.subtype : text); } _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool UsePortalTagName(string id) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null || wayfinderPinRecord.category != WayfinderPinCategory.Portal) { return false; } wayfinderPinRecord.portalNameOverride = false; wayfinderPinRecord.displayName = (string.IsNullOrEmpty(wayfinderPinRecord.portalTag) ? "Portal" : wayfinderPinRecord.portalTag); _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool UseVehicleTypeName(string id) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null || wayfinderPinRecord.category != WayfinderPinCategory.Vehicle || wayfinderPinRecord.source != WayfinderPinSource.Dynamic) { return false; } wayfinderPinRecord.vehicleNameOverride = false; wayfinderPinRecord.displayName = WayfinderVehicleNaming.GetAutomaticDisplayName(wayfinderPinRecord); _database.AddOrUpdate(wayfinderPinRecord); return true; } internal List GetTrackedPins() { EnsureTrackedSlots(); List list = new List(); IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.tracked) { list.Add(wayfinderPinRecord); } } list.Sort(delegate(WayfinderPinRecord a, WayfinderPinRecord b) { int num = a?.trackSlot ?? 999; int value = b?.trackSlot ?? 999; int num2 = num.CompareTo(value); return (num2 == 0) ? CompareRecords(a, b) : num2; }); return list; } internal bool SetTracked(string id, bool tracked) { EnsureTrackedSlots(); WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } if (!tracked) { wayfinderPinRecord.tracked = false; wayfinderPinRecord.trackSlot = -1; _database.MarkMetadataChanged(); return true; } if (wayfinderPinRecord.tracked) { return true; } if (TrackedCount >= MaxTrackedPins) { return false; } int num = FindFirstFreeTrackSlot(); if (num < 0) { return false; } wayfinderPinRecord.tracked = true; wayfinderPinRecord.trackSlot = num; _database.MarkMetadataChanged(); return true; } private int FindFirstFreeTrackSlot() { bool[] array = new bool[5]; IReadOnlyList records = _database.Records; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.tracked && wayfinderPinRecord.trackSlot >= 0 && wayfinderPinRecord.trackSlot < array.Length) { array[wayfinderPinRecord.trackSlot] = true; } } int num = Mathf.Min(MaxTrackedPins, array.Length); for (int j = 0; j < num; j++) { if (!array[j]) { return j; } } return -1; } private void EnsureTrackedSlots() { IReadOnlyList records = _database.Records; if (records == null) { return; } List list = new List(); bool flag = false; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord == null) { continue; } if (!wayfinderPinRecord.tracked) { if (wayfinderPinRecord.trackSlot != -1) { wayfinderPinRecord.trackSlot = -1; flag = true; } } else { list.Add(wayfinderPinRecord); } } list.Sort(delegate(WayfinderPinRecord a, WayfinderPinRecord b) { bool flag2 = a != null && a.trackSlot >= 0 && a.trackSlot < MaxTrackedPins; bool flag3 = b != null && b.trackSlot >= 0 && b.trackSlot < MaxTrackedPins; if (flag2 != flag3) { if (!flag2) { return 1; } return -1; } return (flag2 && flag3 && a.trackSlot != b.trackSlot) ? a.trackSlot.CompareTo(b.trackSlot) : CompareRecords(a, b); }); bool[] array = new bool[5]; for (int num = 0; num < list.Count; num++) { WayfinderPinRecord wayfinderPinRecord2 = list[num]; if (wayfinderPinRecord2 == null) { continue; } int num2 = wayfinderPinRecord2.trackSlot; if (num2 < 0 || num2 >= MaxTrackedPins || num2 >= array.Length || array[num2]) { num2 = -1; int num3 = Mathf.Min(MaxTrackedPins, array.Length); for (int num4 = 0; num4 < num3; num4++) { if (!array[num4]) { num2 = num4; break; } } if (num2 < 0) { wayfinderPinRecord2.tracked = false; wayfinderPinRecord2.trackSlot = -1; flag = true; continue; } if (wayfinderPinRecord2.trackSlot != num2) { wayfinderPinRecord2.trackSlot = num2; flag = true; } } array[num2] = true; } if (flag) { _database.MarkMetadataChanged(); } } internal bool SetFavorite(string id, bool favorite) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.favorite = favorite; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool SetScale(string id, float scale) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.scale = Mathf.Clamp(scale, 0.5f, 2.5f); _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool SetCategory(string id, WayfinderPinCategory category) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.category = category; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool SetColor(string id, Color color) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.Color = color; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool ResetColorToDefault(string id) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.Color = Color.white; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool SetIcon(string id, string iconKey) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } string iconOverrideKey = iconKey ?? string.Empty; wayfinderPinRecord.iconOverride = true; wayfinderPinRecord.iconOverrideKey = iconOverrideKey; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal bool ResetIconToDefault(string id) { WayfinderPinRecord wayfinderPinRecord = Find(id); if (wayfinderPinRecord == null) { return false; } wayfinderPinRecord.iconOverride = false; wayfinderPinRecord.iconOverrideKey = string.Empty; _database.AddOrUpdate(wayfinderPinRecord); return true; } internal WayfinderPinRecord CreateManualPin(string displayName, WayfinderPinCategory category, Vector3 position, string iconKey) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrEmpty(displayName) ? "Wayfinder Pin" : displayName.Trim()); WayfinderPinRecord wayfinderPinRecord = new WayfinderPinRecord(); wayfinderPinRecord.id = Guid.NewGuid().ToString("N"); wayfinderPinRecord.displayName = (string.IsNullOrEmpty(text) ? "Wayfinder Pin" : text); wayfinderPinRecord.subtype = "manual:" + category.ToString().ToLowerInvariant(); wayfinderPinRecord.iconKey = iconKey ?? string.Empty; wayfinderPinRecord.category = category; wayfinderPinRecord.source = WayfinderPinSource.Manual; wayfinderPinRecord.Position = position; wayfinderPinRecord.count = 1; wayfinderPinRecord.isCluster = false; wayfinderPinRecord.scale = 1f; wayfinderPinRecord.favorite = false; wayfinderPinRecord.tracked = false; wayfinderPinRecord.trackSlot = -1; WayfinderPinRecord wayfinderPinRecord2 = wayfinderPinRecord; wayfinderPinRecord2.members.Add(new WayfinderClusterMember("manual:" + wayfinderPinRecord2.id, position)); _database.AddOrUpdate(wayfinderPinRecord2); return wayfinderPinRecord2; } internal bool Delete(string id) { return _database.Remove(id); } private void RefreshHiddenCache() { string text = _config.HiddenCategories.Value ?? string.Empty; if (string.Equals(text, _lastHiddenValue, StringComparison.Ordinal)) { return; } _lastHiddenValue = text; _hidden.Clear(); string[] array = text.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { if (Enum.TryParse(array[i].Trim(), ignoreCase: true, out var result)) { _hidden.Add(result); } } } private void PersistHiddenCategories() { List list = new List(); foreach (WayfinderPinCategory item in _hidden) { list.Add(item.ToString()); } list.Sort(StringComparer.OrdinalIgnoreCase); _config.HiddenCategories.Value = string.Join(",", list.ToArray()); _lastHiddenValue = _config.HiddenCategories.Value ?? string.Empty; } private static int CompareRecords(WayfinderPinRecord a, WayfinderPinRecord b) { if (object.ReferenceEquals(a, b)) { return 0; } if (a == null) { return 1; } if (b == null) { return -1; } if (a.favorite != b.favorite) { if (!a.favorite) { return 1; } return -1; } string strA = (string.IsNullOrEmpty(a.displayName) ? a.subtype : a.displayName); string strB = (string.IsNullOrEmpty(b.displayName) ? b.subtype : b.displayName); int num = string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase); if (num != 0) { return num; } return string.Compare(a.id, b.id, StringComparison.Ordinal); } } internal sealed class WayfinderPinManagerUI { private enum ManagerView { Full, Create, Pins, Visibility, Tracking } private enum ControlGlyph { Create, Pins, Visibility, Tracking } private const float MapPlacementDragThresholdPixels = 8f; private static readonly FieldInfo LargeRootField = AccessTools.Field(typeof(Minimap), "m_largeRoot"); private static readonly MethodInfo[] MinimapMethods = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinManagementService _manager; private readonly RuntimeIconRegistry _icons; private Rect _windowRect = new Rect(80f, 70f, 820f, 720f); private Vector2 _pinScroll; private Vector2 _contentScroll; private string _search = string.Empty; private bool _favoritesOnly; private bool _portalsOnly; private string _selectedId = string.Empty; private WayfinderPinCategory _selectedCategory = WayfinderPinCategory.Unknown; private Vector3 _selectedPosition; private string _renameBuffer = string.Empty; private float _scaleBuffer = 1f; private string _newPinName = "New Wayfinder Pin"; private WayfinderPinCategory _newPinCategory = WayfinderPinCategory.Custom; private string _deleteArmedId = string.Empty; private string _typeSearch = string.Empty; private string _iconSearch = string.Empty; private string _selectedIconKey = string.Empty; private string _selectedIconLabel = "Category default"; private Vector2 _typeScroll; private Vector2 _iconScroll; private string _editIconSearch = string.Empty; private Vector2 _editIconScroll; private string _cachedEditIconQuery; private int _cachedEditIconRegistryCount = -1; private WayfinderPinCategory _cachedEditIconCategory = (WayfinderPinCategory)(-1); private List _cachedEditIconResults = new List(); private string _cachedIconQuery; private int _cachedIconRegistryCount = -1; private WayfinderPinCategory _cachedIconCategory = (WayfinderPinCategory)(-1); private int _bossNameIndex; private string _bossNameBuffer = string.Empty; private string _trackingStatus = string.Empty; private List _cachedIconResults = new List(); private float _openedFromMapGraceUntil; private float _ignoreUiMouseUntil; private bool _placeOnMapArmed; private bool _textInputFocused; private bool _textFieldHitThisMouseDown; private float _nextMapKeyDebugLogTime; private bool _mapPrimaryPointerDown; private Vector2 _mapPrimaryDownScreenPosition; private Rect _toolbarHitRect; private bool _toolbarHitRectValid; private Texture2D _jwButtonTexture; private Texture2D _createControlTexture; private Texture2D _pinsControlTexture; private Texture2D _visibilityControlTexture; private Texture2D _trackingControlTexture; private Texture2D _panelTexture; private Texture2D _buttonTexture; private Texture2D _buttonHoverTexture; private Texture2D _fieldTexture; private Texture2D _sectionTexture; private GUIStyle _windowStyle; private GUIStyle _buttonStyle; private GUIStyle _textFieldStyle; private GUIStyle _labelStyle; private GUIStyle _toggleStyle; private GUIStyle _sectionStyle; private GUIStyle _mapButtonStyle; private GUIStyle _titleStyle; private GUIStyle _subtitleStyle; private GUIStyle _toolbarButtonStyle; private string _toolbarTooltip = string.Empty; private float _toolbarTooltipY; private ManagerView _view; internal bool Visible { get; private set; } internal WayfinderPinManagerUI(ManualLogSource log, WayfinderConfig config, WayfinderPinManagementService manager, RuntimeIconRegistry icons) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) _log = log; _config = config; _manager = manager; _icons = icons; } internal void TickInput() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null) { Visible = false; _textInputFocused = false; _placeOnMapArmed = false; ResetMapPrimaryGesture(); _toolbarHitRectValid = false; return; } if (!IsLargeMapOpen()) { if (!Visible || !(Time.unscaledTime < _openedFromMapGraceUntil)) { Visible = false; _textInputFocused = false; _placeOnMapArmed = false; ResetMapPrimaryGesture(); _toolbarHitRectValid = false; } return; } try { KeyboardShortcut value = _config.PinManagerToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (Visible && _view == ManagerView.Full) { Visible = false; CancelMapPlacement(); } else { OpenView(ManagerView.Full); } } } catch { } } internal void DrawMapButton() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) if (!_config.ShowPinManagerMapButton.Value || !IsLargeMapOpen()) { _toolbarHitRectValid = false; return; } EnsureTheme(); float num = 44f; Rect rect = default(Rect); bool flag = WayfinderPlugin.Map != null && WayfinderPlugin.Map.TryGetPersistentPinPanelGuiRect(out rect); if (flag) { num = Mathf.Clamp(((Rect)(ref rect)).width - 18f, 36f, 46f); } float num2 = num * 5f + 16f; float num3; float num4; if (flag) { num3 = ((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num) * 0.5f; num4 = ((Rect)(ref rect)).y + Mathf.Max(4f, (((Rect)(ref rect)).height - num2) * 0.5f); } else { num3 = Mathf.Max(12f, (float)Screen.width - num - 128f); num4 = Mathf.Max(70f, (float)Screen.height - num2 - 108f); } _toolbarHitRect = (Rect)(flag ? rect : new Rect(num3 - 4f, num4 - 4f, num + 8f, num2 + 8f)); _toolbarHitRectValid = true; _toolbarTooltip = string.Empty; _toolbarTooltipY = num4; int depth = GUI.depth; Color color = GUI.color; GUI.depth = -700; GUI.color = Color.white; Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num3, num4, num, num); if (DrawToolbarIconButton(rect2, _jwButtonTexture, ManagerView.Full, "Wayfinder")) { ToggleView(ManagerView.Full); } ((Rect)(ref rect2)).y = ((Rect)(ref rect2)).y + (num + 4f); if (DrawToolbarIconButton(rect2, _createControlTexture, ManagerView.Create, "Add Pin")) { ToggleView(ManagerView.Create); } ((Rect)(ref rect2)).y = ((Rect)(ref rect2)).y + (num + 4f); if (DrawToolbarIconButton(rect2, _pinsControlTexture, ManagerView.Pins, "Pins")) { ToggleView(ManagerView.Pins); } ((Rect)(ref rect2)).y = ((Rect)(ref rect2)).y + (num + 4f); if (DrawToolbarIconButton(rect2, _visibilityControlTexture, ManagerView.Visibility, "Visibility")) { ToggleView(ManagerView.Visibility); } ((Rect)(ref rect2)).y = ((Rect)(ref rect2)).y + (num + 4f); if (DrawToolbarIconButton(rect2, _trackingControlTexture, ManagerView.Tracking, "Track / Travel")) { ToggleView(ManagerView.Tracking); } if (!string.IsNullOrEmpty(_toolbarTooltip)) { Rect val = default(Rect); ((Rect)(ref val))..ctor(num3 - 128f - 8f, _toolbarTooltipY + (num - 28f) * 0.5f, 128f, 28f); GUI.Box(val, _toolbarTooltip, _sectionStyle); } GUI.color = color; GUI.depth = depth; } private bool DrawToolbarIconButton(Rect rect, Texture2D icon, ManagerView view, string tooltip) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) bool result = GUI.Button(rect, GUIContent.none, _toolbarButtonStyle); if ((Object)(object)icon != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 7f, ((Rect)(ref rect)).y + 7f, ((Rect)(ref rect)).width - 14f, ((Rect)(ref rect)).height - 14f), (Texture)(object)icon, (ScaleMode)2, true); } if (Visible && _view == view) { DrawBronzeRect(rect, 2f); } Event current = Event.current; if (current != null && ((Rect)(ref rect)).Contains(current.mousePosition)) { _toolbarTooltip = tooltip ?? string.Empty; _toolbarTooltipY = ((Rect)(ref rect)).y; } return result; } private void ToggleView(ManagerView view) { if (Visible && _view == view) { Visible = false; CancelMapPlacement(); } else { OpenView(view); } } private void OpenView(ManagerView view) { if (view != ManagerView.Create) { CancelMapPlacement(); } _view = view; Visible = true; _openedFromMapGraceUntil = Time.unscaledTime + 0.25f; _ignoreUiMouseUntil = Time.unscaledTime + 0.12f; } internal void Draw() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (Visible && IsLargeMapOpen()) { EnsureTheme(); Vector2 desiredWindowSize = GetDesiredWindowSize(); float num = Mathf.Min(desiredWindowSize.x, Mathf.Max(520f, (float)Screen.width - 80f)); float num2 = Mathf.Min(desiredWindowSize.y, Mathf.Max(380f, (float)Screen.height - 80f)); ((Rect)(ref _windowRect)).width = num; ((Rect)(ref _windowRect)).height = num2; ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - num)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - num2)); _windowRect = GUI.Window(913701, _windowRect, new WindowFunction(DrawWindow), string.Empty, _windowStyle); } } private void DrawWindow(int windowId) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null && (int)current.type == 0) { _textFieldHitThisMouseDown = false; } if (current != null && current.isMouse && Time.unscaledTime < _ignoreUiMouseUntil) { current.Use(); } GUIStyle button = GUI.skin.button; GUIStyle label = GUI.skin.label; GUIStyle textField = GUI.skin.textField; GUIStyle toggle = GUI.skin.toggle; GUIStyle box = GUI.skin.box; GUI.skin.button = _buttonStyle; GUI.skin.label = _labelStyle; GUI.skin.textField = _textFieldStyle; GUI.skin.toggle = _toggleStyle; GUI.skin.box = _sectionStyle; try { DrawPanelBorder(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height)); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[0]); DrawManagerHeader(); DrawHorizontalRule(); if (_view == ManagerView.Pins || _view == ManagerView.Tracking || _view == ManagerView.Full) { DrawSearchBar(); if (_view == ManagerView.Pins) { GUILayout.Label("Nearby discoveries stay easy to browse. Search a name to find remembered matches anywhere.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } else if (_view == ManagerView.Tracking) { GUILayout.Label("Tracked pins plus your remembered portals and vehicles.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.Space(5f); } float num = Mathf.Max(240f, ((Rect)(ref _windowRect)).height - ((_view == ManagerView.Full) ? 132f : 98f)); _contentScroll = GUILayout.BeginScrollView(_contentScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); if (_view == ManagerView.Full) { DrawVisibilitySection(); GUILayout.Space(6f); DrawBossSection(); GUILayout.Space(6f); DrawCreateSection(); GUILayout.Space(8f); DrawPinsEditorSection(); } else if (_view == ManagerView.Create) { DrawCreateSection(); GUILayout.Space(8f); GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("QUICK HELP", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Choose a type/icon, arm Place on Map, then click once outside this panel.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("The full JW manager is still available from the medallion button.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.EndVertical(); } else if (_view == ManagerView.Visibility) { DrawVisibilitySection(); } else { DrawPinsEditorSection(); } GUILayout.Space(12f); GUILayout.EndScrollView(); GUILayout.EndVertical(); RefreshTextInputFocusFromGui(); } finally { GUI.skin.button = button; GUI.skin.label = label; GUI.skin.textField = textField; GUI.skin.toggle = toggle; GUI.skin.box = box; } GUI.DragWindow(new Rect(0f, 0f, 10000f, 42f)); } private void DrawManagerHeader() { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }); if ((Object)(object)_jwButtonTexture != (Object)null) { GUILayout.Label((Texture)(object)_jwButtonTexture, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(27f), GUILayout.Height(27f) }); } GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("JOEY'S WAYFINDER", _titleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(GetViewTitle(), _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); if (_view != ManagerView.Full && GUILayout.Button("FULL", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(27f) })) { CancelMapPlacement(); _view = ManagerView.Full; } if (GUILayout.Button("X", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(34f), GUILayout.Height(27f) })) { Visible = false; CancelMapPlacement(); } GUILayout.EndHorizontal(); } private void DrawSearchBar() { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label((_view == ManagerView.Tracking) ? "Tracked" : "Search", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }); _search = DrawNamedTextField("pin_search", _search ?? string.Empty, GUILayout.ExpandWidth(true)); if (_view != ManagerView.Tracking) { _favoritesOnly = GUILayout.Toggle(_favoritesOnly, "Favorites", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) }); _portalsOnly = GUILayout.Toggle(_portalsOnly, "Portals", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(78f) }); } GUILayout.EndHorizontal(); } private void DrawVisibilitySection() { GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("VISIBILITY", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); DrawCategoryVisibility(); GUILayout.EndVertical(); } private void DrawBossSection() { GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("BOSS LABELS", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); DrawBossNamingControls(); GUILayout.EndVertical(); } private void DrawCreateSection() { GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("CREATE PIN", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); DrawPlacementPalette(); GUILayout.EndVertical(); } private void DrawPinsEditorSection() { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((_view == ManagerView.Full) ? 370f : 330f) }); DrawPinList(); GUILayout.EndVertical(); GUILayout.Space(7f); GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (_view == ManagerView.Full) { DrawEditor(); } else { DrawCompactEditor(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private string GetViewTitle() { return _view switch { ManagerView.Create => "ADD PIN", ManagerView.Pins => "PINS", ManagerView.Visibility => "VISIBILITY", ManagerView.Tracking => "TRACKED", _ => "PIN MANAGER", }; } private Vector2 GetDesiredWindowSize() { //IL_002b: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) return (Vector2)(_view switch { ManagerView.Create => new Vector2(680f, 540f), ManagerView.Visibility => new Vector2(760f, 440f), ManagerView.Pins => new Vector2(820f, 620f), ManagerView.Tracking => new Vector2(760f, 560f), _ => new Vector2(900f, 760f), }); } private void DrawCategoryVisibility() { Array values = Enum.GetValues(typeof(WayfinderPinCategory)); int num = 4; for (int i = 0; i < values.Length; i += num) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); for (int j = 0; j < num && i + j < values.Length; j++) { WayfinderPinCategory category = (WayfinderPinCategory)values.GetValue(i + j); bool flag = _manager.IsCategoryVisible(category); string text = (flag ? "[ON] " : "[OFF] ") + FriendlyCategoryName(category); if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) })) { _manager.SetCategoryVisible(category, !flag); ForceMapRefresh(); } } GUILayout.EndHorizontal(); } } private void DrawPinList() { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(350f) }); List list = _manager.Search(_search, _favoritesOnly, _portalsOnly ? new WayfinderPinCategory?(WayfinderPinCategory.Portal) : ((WayfinderPinCategory?)null), 0); bool flag = !string.IsNullOrEmpty((_search ?? string.Empty).Trim()); Vector3 playerPosition = (((Object)(object)Player.m_localPlayer == (Object)null) ? Vector3.zero : ((Component)Player.m_localPlayer).transform.position); bool flag2 = (Object)(object)Player.m_localPlayer != (Object)null; if (_view == ManagerView.Tracking) { list.RemoveAll((WayfinderPinRecord record) => record == null || (!record.tracked && record.category != WayfinderPinCategory.Vehicle && record.category != WayfinderPinCategory.Portal)); } else if (_view == ManagerView.Pins && !flag && flag2) { float num = Mathf.Max(25f, _config.NearbyDiscoveryListRadius.Value); float radiusSq = num * num; list.RemoveAll(delegate(WayfinderPinRecord record) { //IL_000e: 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) if (record == null || !IsDenseDiscoveryListCategory(record)) { return false; } float num4 = record.Position.x - playerPosition.x; float num5 = record.Position.z - playerPosition.z; return num4 * num4 + num5 * num5 > radiusSq; }); } if (flag2) { list.Sort(delegate(WayfinderPinRecord a, WayfinderPinRecord b) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (_view == ManagerView.Tracking) { bool flag3 = a?.tracked ?? false; bool flag4 = b?.tracked ?? false; if (flag3 != flag4) { if (!flag3) { return 1; } return -1; } } float num4 = HorizontalDistanceSq(a, playerPosition); float value = HorizontalDistanceSq(b, playerPosition); int num5 = num4.CompareTo(value); return (num5 != 0) ? num5 : string.Compare((a == null) ? string.Empty : a.displayName, (b == null) ? string.Empty : b.displayName, StringComparison.OrdinalIgnoreCase); }); } int num2 = Mathf.Min(120, list.Count); GUILayout.Label(((_view == ManagerView.Tracking) ? "Travel / Track (" : "Pins (") + num2 + ((list.Count > num2) ? "+" : "") + ")", (GUILayoutOption[])(object)new GUILayoutOption[0]); if (_view == ManagerView.Pins && !flag) { GUILayout.Label("Resources, sightings and habitats: nearby only in this list.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } _pinScroll = GUILayout.BeginScrollView(_pinScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(260f) }); for (int num3 = 0; num3 < list.Count && num3 < 120; num3++) { WayfinderPinRecord wayfinderPinRecord = list[num3]; if (wayfinderPinRecord != null) { string text = (string.IsNullOrEmpty(wayfinderPinRecord.displayName) ? wayfinderPinRecord.subtype : wayfinderPinRecord.displayName); string text2 = ((wayfinderPinRecord.tracked && wayfinderPinRecord.trackSlot >= 0) ? ("T" + (wayfinderPinRecord.trackSlot + 1) + " • ") : string.Empty); string text3 = string.Empty; if (WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord)) { text3 = (wayfinderPinRecord.dynamicLive ? "LIVE • " : "LAST • "); } else if (wayfinderPinRecord.category == WayfinderPinCategory.Portal && wayfinderPinRecord.portalLive && wayfinderPinRecord.portalConnectionKnown) { text3 = (wayfinderPinRecord.portalConnected ? "LINK • " : "OPEN • "); } string text4 = (flag2 ? (" • " + FormatRelativeLocation(playerPosition, wayfinderPinRecord.Position)) : string.Empty); string text5 = text2 + text3 + (wayfinderPinRecord.favorite ? "★ " : string.Empty) + text + " [" + FriendlyCategoryName(wayfinderPinRecord.category) + "]" + text4; if (wayfinderPinRecord.count > 1) { text5 = text5 + " x" + wayfinderPinRecord.count; } if (!_manager.IsCategoryVisible(wayfinderPinRecord.category)) { text5 += " (map hidden)"; } if (GUILayout.Button(text5, (GUILayoutOption[])(object)new GUILayoutOption[0])) { Select(wayfinderPinRecord); } } } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private static bool IsDenseDiscoveryListCategory(WayfinderPinRecord record) { if (record == null || record.source == WayfinderPinSource.Manual || record.source == WayfinderPinSource.Imported) { return false; } if (record.category != WayfinderPinCategory.Resource && record.category != WayfinderPinCategory.Sighting) { return record.category == WayfinderPinCategory.Habitat; } return true; } private static float HorizontalDistanceSq(WayfinderPinRecord record, Vector3 position) { //IL_000a: 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) if (record == null) { return float.MaxValue; } float num = record.Position.x - position.x; float num2 = record.Position.z - position.z; return num * num + num2 * num2; } private static string FormatRelativeLocation(Vector3 from, Vector3 to) { float num = to.x - from.x; float num2 = to.z - from.z; float num3 = Mathf.Sqrt(num * num + num2 * num2); string text = CardinalDirection(num, num2); return num3.ToString("0") + "m " + text; } private static string CardinalDirection(float dx, float dz) { if (Mathf.Abs(dx) < 0.001f && Mathf.Abs(dz) < 0.001f) { return "HERE"; } float num = Mathf.Atan2(dx, dz) * 57.29578f; if (num < 0f) { num += 360f; } string[] array = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; int num2 = Mathf.RoundToInt(num / 45f) % 8; return array[num2]; } private void DrawCompactEditor() { //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_050b: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Unknown result type (might be due to invalid IL or missing references) //IL_0579: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); WayfinderPinRecord wayfinderPinRecord = _manager.Find(_selectedId); if (wayfinderPinRecord == null && !string.IsNullOrEmpty(_selectedId)) { wayfinderPinRecord = RecoverSelectedRecord(); if (wayfinderPinRecord != null) { Select(wayfinderPinRecord); } } if (wayfinderPinRecord == null) { GUILayout.Label((_view == ManagerView.Tracking) ? "Select a tracked pin, portal, or vehicle." : "Select a pin.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Space(8f); GUILayout.Label("The JW button opens the full advanced manager.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.EndVertical(); return; } string text = (string.IsNullOrEmpty(wayfinderPinRecord.displayName) ? wayfinderPinRecord.subtype : wayfinderPinRecord.displayName); GUILayout.Label(text, _titleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(FriendlyCategoryName(wayfinderPinRecord.category), _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); if (WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord)) { GUILayout.Label(wayfinderPinRecord.dynamicLive ? "Vehicle • Live" : ("Vehicle • Last known " + WayfinderDynamicState.FormatLastSeen(wayfinderPinRecord)), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Label source: " + (wayfinderPinRecord.vehicleNameOverride ? "Custom Wayfinder label" : "Vehicle type"), (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (wayfinderPinRecord.category == WayfinderPinCategory.Portal) { GUILayout.Label("Portal tag: " + WayfinderPortalState.GetTagLabel(wayfinderPinRecord), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Connection: " + WayfinderPortalState.GetConnectionLabel(wayfinderPinRecord), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(wayfinderPinRecord.portalNameOverride ? "Wayfinder label override; Valheim portal tag is unchanged." : "Wayfinder label follows the Valheim portal tag.", (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.Space(6f); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Label", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); _renameBuffer = DrawNamedTextField("rename_compact", _renameBuffer ?? string.Empty, GUILayout.ExpandWidth(true)); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(62f) })) { _manager.Rename(wayfinderPinRecord.id, _renameBuffer); ForceMapRefresh(); } GUILayout.EndHorizontal(); if (wayfinderPinRecord.category == WayfinderPinCategory.Portal && wayfinderPinRecord.portalNameOverride) { if (GUILayout.Button("Use Portal Tag as Label", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.UsePortalTagName(wayfinderPinRecord.id); WayfinderPinRecord wayfinderPinRecord2 = _manager.Find(wayfinderPinRecord.id); _renameBuffer = ((wayfinderPinRecord2 == null) ? string.Empty : wayfinderPinRecord2.displayName); ForceMapRefresh(); } } else if (WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord) && wayfinderPinRecord.vehicleNameOverride && GUILayout.Button("Use Vehicle Type as Label", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.UseVehicleTypeName(wayfinderPinRecord.id); WayfinderPinRecord wayfinderPinRecord3 = _manager.Find(wayfinderPinRecord.id); _renameBuffer = ((wayfinderPinRecord3 == null) ? string.Empty : wayfinderPinRecord3.displayName); ForceMapRefresh(); } GUILayout.Space(5f); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button(wayfinderPinRecord.favorite ? "★ Favorite" : "☆ Favorite", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.SetFavorite(wayfinderPinRecord.id, !wayfinderPinRecord.favorite); ForceMapRefresh(); } if (GUILayout.Button(wayfinderPinRecord.tracked ? "Stop Tracking" : "Track", (GUILayoutOption[])(object)new GUILayoutOption[0])) { bool tracked = wayfinderPinRecord.tracked; bool flag = _manager.SetTracked(wayfinderPinRecord.id, !tracked); _trackingStatus = ((!flag) ? "All tracking slots are in use." : (tracked ? "Tracking stopped." : ("Tracking as T" + (wayfinderPinRecord.trackSlot + 1) + "."))); if (WayfinderPlugin.Map != null) { WayfinderPlugin.Map.RefreshTrackedOverlays(); } } GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(_trackingStatus)) { GUILayout.Label(_trackingStatus, _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (!HasInGameIcon(wayfinderPinRecord)) { GUILayout.Space(5f); GUILayout.Label("Color", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("Default", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ResetColorToDefault(wayfinderPinRecord.id); } if (GUILayout.Button("Red", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.95f, 0.28f, 0.25f, 1f)); } if (GUILayout.Button("Gold", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.95f, 0.72f, 0.22f, 1f)); } if (GUILayout.Button("Green", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.35f, 0.78f, 0.38f, 1f)); } if (GUILayout.Button("Blue", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.3f, 0.62f, 0.95f, 1f)); } if (GUILayout.Button("Purple", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.68f, 0.42f, 0.92f, 1f)); } GUILayout.EndHorizontal(); } GUILayout.Space(8f); if (GUILayout.Button("Advanced…", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _view = ManagerView.Full; } GUILayout.EndVertical(); } private void DrawEditor() { //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_08a8: Unknown result type (might be due to invalid IL or missing references) //IL_08df: Unknown result type (might be due to invalid IL or missing references) //IL_0916: Unknown result type (might be due to invalid IL or missing references) //IL_094d: Unknown result type (might be due to invalid IL or missing references) //IL_0984: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); WayfinderPinRecord wayfinderPinRecord = _manager.Find(_selectedId); if (wayfinderPinRecord == null && !string.IsNullOrEmpty(_selectedId)) { wayfinderPinRecord = RecoverSelectedRecord(); if (wayfinderPinRecord != null) { Select(wayfinderPinRecord); } } if (wayfinderPinRecord == null) { GUILayout.Label("Select a Wayfinder pin to edit it.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Space(8f); GUILayout.Label("Choose a pin from the list or double-click a Wayfinder marker on the map.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.EndVertical(); return; } GUILayout.Label("Edit selected pin", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(string.Concat("Category: ", wayfinderPinRecord.category, " Source: ", wayfinderPinRecord.source), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord)) { string text = (wayfinderPinRecord.dynamicLive ? "LIVE" : "LAST KNOWN"); GUILayout.Label("Vehicle status: " + text + " | Last seen: " + WayfinderDynamicState.FormatLastSeen(wayfinderPinRecord), (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (wayfinderPinRecord.category == WayfinderPinCategory.Portal) { GUILayout.Label("Portal tag: " + WayfinderPortalState.GetTagLabel(wayfinderPinRecord), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Connection: " + WayfinderPortalState.GetConnectionLabel(wayfinderPinRecord), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Wayfinder label source: " + (wayfinderPinRecord.portalNameOverride ? "Custom Wayfinder label" : "Portal tag sync"), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Changing this Wayfinder label does not change the Valheim portal tag or connection.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Label", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); _renameBuffer = DrawNamedTextField("rename_full_portal", _renameBuffer ?? string.Empty, GUILayout.ExpandWidth(true)); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(62f) })) { _manager.Rename(wayfinderPinRecord.id, _renameBuffer); ForceMapRefresh(); } GUILayout.EndHorizontal(); if (wayfinderPinRecord.portalNameOverride && GUILayout.Button("Use Portal Tag as Label", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.UsePortalTagName(wayfinderPinRecord.id); WayfinderPinRecord wayfinderPinRecord2 = _manager.Find(wayfinderPinRecord.id); _renameBuffer = ((wayfinderPinRecord2 == null) ? string.Empty : wayfinderPinRecord2.displayName); ForceMapRefresh(); } int num = _manager.CountOtherPortalsWithTag(wayfinderPinRecord); if (!string.IsNullOrEmpty(wayfinderPinRecord.portalTag)) { GUILayout.Label("Other remembered portals with this tag: " + num, (GUILayoutOption[])(object)new GUILayoutOption[0]); WayfinderPinRecord wayfinderPinRecord3 = _manager.FindNearestPortalTagPartner(wayfinderPinRecord); if (wayfinderPinRecord3 != null) { float num2 = wayfinderPinRecord3.Position.x - wayfinderPinRecord.Position.x; float num3 = wayfinderPinRecord3.Position.z - wayfinderPinRecord.Position.z; float num4 = Mathf.Sqrt(num2 * num2 + num3 * num3); string text2 = (string.IsNullOrEmpty(wayfinderPinRecord3.displayName) ? "Portal" : wayfinderPinRecord3.displayName); GUILayout.Label("Nearest same-tag portal: " + text2 + " (" + num4.ToString("0") + "m)", (GUILayoutOption[])(object)new GUILayoutOption[0]); } } } if (WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord)) { GUILayout.Label("Wayfinder label source: " + (wayfinderPinRecord.vehicleNameOverride ? "Custom label tied to this vehicle" : "Automatic vehicle type"), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Vehicle identity is tied to its stable world object, so movement does not replace a custom label.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Label", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); _renameBuffer = DrawNamedTextField("rename_full_vehicle", _renameBuffer ?? string.Empty, GUILayout.ExpandWidth(true)); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(62f) })) { _manager.Rename(wayfinderPinRecord.id, _renameBuffer); ForceMapRefresh(); } GUILayout.EndHorizontal(); if (wayfinderPinRecord.vehicleNameOverride && GUILayout.Button("Use Vehicle Type as Label", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.UseVehicleTypeName(wayfinderPinRecord.id); WayfinderPinRecord wayfinderPinRecord4 = _manager.Find(wayfinderPinRecord.id); _renameBuffer = ((wayfinderPinRecord4 == null) ? string.Empty : wayfinderPinRecord4.displayName); ForceMapRefresh(); } } if (wayfinderPinRecord.category != WayfinderPinCategory.Portal && !WayfinderDynamicState.IsDynamicVehicle(wayfinderPinRecord)) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); _renameBuffer = DrawNamedTextField("rename_full", _renameBuffer ?? string.Empty, GUILayout.ExpandWidth(true)); if (GUILayout.Button("Rename", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { _manager.Rename(wayfinderPinRecord.id, _renameBuffer); ForceMapRefresh(); } GUILayout.EndHorizontal(); } if (GUILayout.Button(wayfinderPinRecord.favorite ? "Remove Favorite" : "Favorite", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.SetFavorite(wayfinderPinRecord.id, !wayfinderPinRecord.favorite); ForceMapRefresh(); } GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Tracked: " + _manager.TrackedCount + "/" + _manager.MaxTrackedPins, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); if (GUILayout.Button(wayfinderPinRecord.tracked ? "Stop Tracking" : "Track Pin", (GUILayoutOption[])(object)new GUILayoutOption[0])) { bool tracked = wayfinderPinRecord.tracked; bool flag = _manager.SetTracked(wayfinderPinRecord.id, !tracked); _trackingStatus = ((!flag) ? ("Tracking limit reached (" + _manager.MaxTrackedPins + ").") : (tracked ? "Tracking stopped." : ("Tracking slot " + (wayfinderPinRecord.trackSlot + 1) + " — " + TrackedColorPalette.GetName(wayfinderPinRecord.trackSlot) + "."))); if (WayfinderPlugin.Map != null) { WayfinderPlugin.Map.RefreshTrackedOverlays(); } } GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(_trackingStatus)) { GUILayout.Label(_trackingStatus, (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("< Category", (GUILayoutOption[])(object)new GUILayoutOption[0])) { WayfinderPinCategory category = PreviousCategory(wayfinderPinRecord.category); _manager.SetCategory(wayfinderPinRecord.id, category); ForceMapRefresh(); } if (GUILayout.Button("Category >", (GUILayoutOption[])(object)new GUILayoutOption[0])) { WayfinderPinCategory category2 = NextCategory(wayfinderPinRecord.category); _manager.SetCategory(wayfinderPinRecord.id, category2); ForceMapRefresh(); } GUILayout.EndHorizontal(); GUILayout.Space(7f); DrawExistingPinIconPicker(wayfinderPinRecord); if (!HasInGameIcon(wayfinderPinRecord)) { GUILayout.Label("Custom/fallback pin styling", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Scale: " + _scaleBuffer.ToString("0.00"), (GUILayoutOption[])(object)new GUILayoutOption[0]); _scaleBuffer = GUILayout.HorizontalSlider(_scaleBuffer, 0.5f, 2.5f, (GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("Apply Scale", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _manager.SetScale(wayfinderPinRecord.id, _scaleBuffer); ForceMapRefresh(); } GUILayout.Label("Color presets", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("Default", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ResetColorToDefault(wayfinderPinRecord.id); } if (GUILayout.Button("Red", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.95f, 0.28f, 0.25f, 1f)); } if (GUILayout.Button("Gold", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.95f, 0.72f, 0.22f, 1f)); } if (GUILayout.Button("Green", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.35f, 0.78f, 0.38f, 1f)); } if (GUILayout.Button("Blue", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.3f, 0.62f, 0.95f, 1f)); } if (GUILayout.Button("Purple", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyColor(wayfinderPinRecord.id, new Color(0.68f, 0.42f, 0.92f, 1f)); } GUILayout.EndHorizontal(); GUILayout.Label("Default = original Wayfinder pin art / no tint.", (GUILayoutOption[])(object)new GUILayoutOption[0]); } else { GUILayout.Label("Using an in-game icon: size/color are kept at the icon's normal presentation.", (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.Space(8f); if (_deleteArmedId == wayfinderPinRecord.id) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Delete this pin?", (GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("YES", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _manager.Delete(wayfinderPinRecord.id); _selectedId = string.Empty; _deleteArmedId = string.Empty; ForceMapRefresh(); } if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _deleteArmedId = string.Empty; } GUILayout.EndHorizontal(); } else if (GUILayout.Button("Delete Pin", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _deleteArmedId = wayfinderPinRecord.id; } GUILayout.EndVertical(); } private void DrawExistingPinIconPicker(WayfinderPinRecord selected) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) if (selected != null) { GUILayout.BeginVertical(_sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("ICON", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); _editIconSearch = DrawNamedTextField("edit_icon_search", _editIconSearch ?? string.Empty); RefreshEditIconSearch(selected.category); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(GetExistingPinIconSelectionLabel(selected), _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.FlexibleSpace(); GUI.enabled = selected.iconOverride; if (GUILayout.Button("Default", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }) && _manager.ResetIconToDefault(selected.id)) { ForceMapRefresh(); } GUI.enabled = true; GUILayout.EndHorizontal(); _editIconScroll = GUILayout.BeginScrollView(_editIconScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(155f) }); DrawEditIconPickerGrid(selected); GUILayout.EndScrollView(); if (!string.IsNullOrEmpty(GUI.tooltip)) { GUILayout.Label(GUI.tooltip, _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } else { GUILayout.Label(GetExistingPinIconSelectionLabel(selected), _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.EndVertical(); } } private void RefreshEditIconSearch(WayfinderPinCategory category) { if (_icons == null || !_icons.IsBuilt) { _cachedEditIconResults.Clear(); return; } int num = ((_icons.All != null) ? _icons.All.Count : 0); string text = _editIconSearch ?? string.Empty; if (!string.Equals(text, _cachedEditIconQuery, StringComparison.Ordinal) || num != _cachedEditIconRegistryCount || _cachedEditIconCategory != category) { _cachedEditIconQuery = text; _cachedEditIconRegistryCount = num; _cachedEditIconCategory = category; _cachedEditIconResults = _icons.Search(text, category, 48); } } private void DrawEditIconPickerGrid(WayfinderPinRecord selected) { int num = -1; string displayedIconKey = GetDisplayedIconKey(selected); bool flag = !selected.iconOverride; Sprite sprite = ResolveExistingPinDefaultPreviewSprite(selected); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (DrawIconButton(sprite, flag, "Default") && _manager.ResetIconToDefault(selected.id)) { ForceMapRefresh(); } num++; for (int i = 0; i < _cachedEditIconResults.Count; i++) { WayfinderIconEntry wayfinderIconEntry = _cachedEditIconResults[i]; if (wayfinderIconEntry != null) { if (num > 0 && num % 8 == 0) { GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); } string iconDisplayName = GetIconDisplayName(wayfinderIconEntry); bool selected2 = !flag && string.Equals(displayedIconKey, wayfinderIconEntry.Key, StringComparison.OrdinalIgnoreCase); if (DrawIconButton(wayfinderIconEntry.Sprite, selected2, iconDisplayName) && _manager.SetIcon(selected.id, wayfinderIconEntry.Key)) { ForceMapRefresh(); } num++; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private Sprite ResolveExistingPinDefaultPreviewSprite(WayfinderPinRecord record) { if (record == null || _icons == null) { return null; } string defaultIconKey = WayfinderIconPolicy.GetDefaultIconKey(record); if (!string.IsNullOrEmpty(defaultIconKey) && _icons.TryGet(defaultIconKey, out var sprite)) { return sprite; } return ResolveCategoryDefaultPreviewSprite(record.category); } private string GetExistingPinIconSelectionLabel(WayfinderPinRecord record) { if (record == null) { return "Selected: Default"; } bool flag = !record.iconOverride; string displayedIconKey = GetDisplayedIconKey(record); WayfinderIconEntry wayfinderIconEntry = FindIconEntry(displayedIconKey); string text = ((wayfinderIconEntry != null) ? GetIconDisplayName(wayfinderIconEntry) : (string.IsNullOrEmpty(displayedIconKey) ? "Default" : displayedIconKey)); if (!flag) { return "Selected: " + text; } return "Default: " + text; } private static string GetDisplayedIconKey(WayfinderPinRecord record) { if (record != null) { return WayfinderIconPolicy.GetEffectiveIconKey(record); } return string.Empty; } private void DrawPlacementPalette() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Choose what you want to mark.", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(215f) }); GUILayout.Label("Type", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); _typeSearch = DrawNamedTextField("type_search", _typeSearch ?? string.Empty); _typeScroll = GUILayout.BeginScrollView(_typeScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(145f) }); Array values = Enum.GetValues(typeof(WayfinderPinCategory)); string value = (_typeSearch ?? string.Empty).Trim().ToLowerInvariant(); for (int i = 0; i < values.Length; i++) { WayfinderPinCategory wayfinderPinCategory = (WayfinderPinCategory)values.GetValue(i); string text = FriendlyCategoryName(wayfinderPinCategory); if (string.IsNullOrEmpty(value) || text.ToLowerInvariant().IndexOf(value, StringComparison.Ordinal) >= 0 || wayfinderPinCategory.ToString().ToLowerInvariant().IndexOf(value, StringComparison.Ordinal) >= 0) { string text2 = ((wayfinderPinCategory == _newPinCategory) ? "• " : string.Empty); if (GUILayout.Button(text2 + text, (GUILayoutOption[])(object)new GUILayoutOption[0])) { SelectNewPinCategory(wayfinderPinCategory); } } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Icon", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); _iconSearch = DrawNamedTextField("icon_search", _iconSearch ?? string.Empty); RefreshIconSearch(); _iconScroll = GUILayout.BeginScrollView(_iconScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(145f) }); DrawIconPickerGrid(); GUILayout.EndScrollView(); if (!string.IsNullOrEmpty(GUI.tooltip)) { GUILayout.Label(GUI.tooltip, _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } else { GUILayout.Label("Selected: " + _selectedIconLabel, _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(5f); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); _newPinName = DrawNamedTextField("new_pin_name", _newPinName ?? string.Empty, GUILayout.ExpandWidth(true)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("Create Here", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { CancelMapPlacement(); CreateSelectedPinAtPlayer(); } if (GUILayout.Button(_placeOnMapArmed ? "Cancel Map Placement" : "Place on Map", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(155f) })) { _placeOnMapArmed = !_placeOnMapArmed; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (_placeOnMapArmed) { GUILayout.Label("Click any spot on the large map to place it there.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } else { GUILayout.Label("Arm Place on Map to choose a location. Double-click an existing Wayfinder pin to select it.", _subtitleStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]); } } private void DrawIconPickerGrid() { int num = -1; Sprite sprite = ResolveCategoryDefaultPreviewSprite(_newPinCategory); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (DrawIconButton(sprite, string.IsNullOrEmpty(_selectedIconKey), "Category default")) { _selectedIconKey = string.Empty; _selectedIconLabel = "Category default"; } num++; for (int i = 0; i < _cachedIconResults.Count; i++) { WayfinderIconEntry wayfinderIconEntry = _cachedIconResults[i]; if (wayfinderIconEntry == null) { continue; } if (num > 0 && num % 8 == 0) { GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); } string iconDisplayName = GetIconDisplayName(wayfinderIconEntry); bool selected = string.Equals(_selectedIconKey, wayfinderIconEntry.Key, StringComparison.OrdinalIgnoreCase); if (DrawIconButton(wayfinderIconEntry.Sprite, selected, iconDisplayName)) { _selectedIconKey = wayfinderIconEntry.Key ?? string.Empty; _selectedIconLabel = iconDisplayName; if (wayfinderIconEntry.IsTrophy && _newPinCategory == WayfinderPinCategory.Custom) { SelectNewPinCategory(WayfinderPinCategory.Creature); } if (string.IsNullOrEmpty(_newPinName) || string.Equals(_newPinName, "New Wayfinder Pin", StringComparison.Ordinal)) { _newPinName = CleanIconName(wayfinderIconEntry); } } num++; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private Sprite ResolveCategoryDefaultPreviewSprite(WayfinderPinCategory category) { if (_icons == null) { return null; } string key; switch (category) { case WayfinderPinCategory.Resource: key = "wayfinder:resource"; break; case WayfinderPinCategory.Creature: key = "wayfinder:creature"; break; case WayfinderPinCategory.Dungeon: key = "wayfinder:dungeon"; break; case WayfinderPinCategory.PointOfInterest: case WayfinderPinCategory.Base: key = "wayfinder:structure"; break; case WayfinderPinCategory.Spawner: key = "wayfinder:spawner"; break; case WayfinderPinCategory.Habitat: key = "wayfinder:habitat"; break; case WayfinderPinCategory.Sighting: key = "wayfinder:sighting"; break; case WayfinderPinCategory.Vehicle: key = "wayfinder:vehicle"; break; case WayfinderPinCategory.Trader: key = "wayfinder:trader"; break; case WayfinderPinCategory.Portal: key = "wayfinder:portal"; break; case WayfinderPinCategory.Outpost: key = "wayfinder:outpost"; break; case WayfinderPinCategory.Farm: key = "wayfinder:farm"; break; case WayfinderPinCategory.Dock: key = "wayfinder:dock"; break; case WayfinderPinCategory.Road: key = "wayfinder:road"; break; case WayfinderPinCategory.Bridge: key = "wayfinder:bridge"; break; case WayfinderPinCategory.Camp: key = "wayfinder:camp"; break; case WayfinderPinCategory.Storage: key = "wayfinder:storage"; break; case WayfinderPinCategory.Danger: key = "wayfinder:danger"; break; default: key = "wayfinder:custom"; break; } if (_icons.TryGet(key, out var sprite)) { return sprite; } return null; } private bool DrawIconButton(Sprite sprite, bool selected, string tooltip) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(42f, 42f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(42f), GUILayout.Height(42f) }); bool result = GUI.Button(rect, new GUIContent(string.Empty, tooltip), _buttonStyle); if ((Object)(object)sprite != (Object)null && (Object)(object)sprite.texture != (Object)null) { Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 10f, ((Rect)(ref rect)).height - 10f); try { Rect textureRect = sprite.textureRect; Texture texture = (Texture)(object)sprite.texture; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref textureRect)).x / (float)texture.width, ((Rect)(ref textureRect)).y / (float)texture.height, ((Rect)(ref textureRect)).width / (float)texture.width, ((Rect)(ref textureRect)).height / (float)texture.height); GUI.DrawTextureWithTexCoords(val, texture, val2, true); } catch { GUI.DrawTexture(val, (Texture)(object)sprite.texture, (ScaleMode)2, true); } } if (selected) { DrawBronzeRect(rect, 2f); } return result; } private void RefreshIconSearch() { if (_icons == null || !_icons.IsBuilt) { _cachedIconResults.Clear(); return; } int num = ((_icons.All != null) ? _icons.All.Count : 0); string text = _iconSearch ?? string.Empty; if (!string.Equals(text, _cachedIconQuery, StringComparison.Ordinal) || num != _cachedIconRegistryCount || _cachedIconCategory != _newPinCategory) { _cachedIconQuery = text; _cachedIconRegistryCount = num; _cachedIconCategory = _newPinCategory; _cachedIconResults = _icons.Search(text, _newPinCategory, 48); } } private void CreateSelectedPinAtPlayer() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer == (Object)null)) { WayfinderPinRecord wayfinderPinRecord = _manager.CreateManualPin(_newPinName, _newPinCategory, ((Component)Player.m_localPlayer).transform.position, _selectedIconKey); if (wayfinderPinRecord != null) { Select(wayfinderPinRecord); ForceMapRefresh(); } } } internal bool BeforeVanillaMapLeftDown() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (!IsLargeMapOpen()) { ResetMapPrimaryGesture(); return true; } if (IsPointerOverWayfinderUi()) { ResetMapPrimaryGesture(); return false; } _mapPrimaryPointerDown = true; _mapPrimaryDownScreenPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y); return true; } internal bool BeforeVanillaMapLeftClick() { //IL_0039: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (!IsLargeMapOpen()) { ResetMapPrimaryGesture(); return true; } if (IsPointerOverWayfinderUi()) { ResetMapPrimaryGesture(); return false; } if (!_placeOnMapArmed || !Visible) { ResetMapPrimaryGesture(); return true; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Input.mousePosition.x, Input.mousePosition.y); if (_mapPrimaryPointerDown && Vector2.Distance(_mapPrimaryDownScreenPosition, val) > 8f) { ResetMapPrimaryGesture(); return true; } ResetMapPrimaryGesture(); if (!TryScreenPointToWorld(Input.mousePosition, out var worldPosition)) { return false; } WayfinderPinRecord wayfinderPinRecord = _manager.CreateManualPin(_newPinName, _newPinCategory, worldPosition, _selectedIconKey); if (wayfinderPinRecord != null) { CancelMapPlacement(); Select(wayfinderPinRecord); ForceMapRefresh(); } return false; } internal bool BeforeVanillaMapDoubleClick() { //IL_0013: 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) if (!IsLargeMapOpen()) { return true; } if (IsPointerOverWayfinderUi()) { return false; } if (!TryScreenPointToWorld(Input.mousePosition, out var worldPosition)) { return false; } WayfinderPinRecord wayfinderPinRecord = FindNearestWayfinderRecord(worldPosition, 22f); if (wayfinderPinRecord != null) { CancelMapPlacement(); _view = ManagerView.Pins; Visible = true; _openedFromMapGraceUntil = Time.unscaledTime + 0.45f; _ignoreUiMouseUntil = Time.unscaledTime + 0.18f; Select(wayfinderPinRecord); if (wayfinderPinRecord.tracked && wayfinderPinRecord.trackSlot >= 0) { _trackingStatus = "Tracking slot " + (wayfinderPinRecord.trackSlot + 1) + " — " + TrackedColorPalette.GetName(wayfinderPinRecord.trackSlot) + "."; } else { _trackingStatus = "Selected existing pin. Press Track Pin to track it."; } return false; } return false; } private WayfinderPinRecord FindNearestWayfinderRecord(Vector3 worldPosition, float radius) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList records = _manager.Records; if (records == null || radius <= 0f) { return null; } WayfinderPinRecord result = null; float num = radius * radius; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null) { float num2 = wayfinderPinRecord.Position.x - worldPosition.x; float num3 = wayfinderPinRecord.Position.z - worldPosition.z; float num4 = num2 * num2 + num3 * num3; if (!(num4 > num)) { num = num4; result = wayfinderPinRecord; } } } return result; } private string DrawNamedTextField(string id, string value, params GUILayoutOption[] options) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) string text = "JW_TEXT_" + (id ?? "field"); GUI.SetNextControlName(text); string result = GUILayout.TextField(value ?? string.Empty, options); Event current = Event.current; Rect lastRect = GUILayoutUtility.GetLastRect(); if (current != null && (int)current.type == 0 && current.button == 0 && ((Rect)(ref lastRect)).Contains(current.mousePosition)) { _textFieldHitThisMouseDown = true; _textInputFocused = true; } string nameOfFocusedControl = GUI.GetNameOfFocusedControl(); if (string.Equals(nameOfFocusedControl, text, StringComparison.Ordinal)) { _textInputFocused = true; } return result; } private void RefreshTextInputFocusFromGui() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) string nameOfFocusedControl = GUI.GetNameOfFocusedControl(); if (Visible && !string.IsNullOrEmpty(nameOfFocusedControl) && nameOfFocusedControl.StartsWith("JW_TEXT_", StringComparison.Ordinal)) { _textInputFocused = true; } else if (GUIUtility.keyboardControl == 0) { _textInputFocused = false; } Event current = Event.current; if (current != null && (int)current.type == 0 && current.button == 0 && !_textFieldHitThisMouseDown) { _textInputFocused = false; } } internal bool ShouldSuppressGameplayInputForTextInput() { if (Visible && IsLargeMapOpen()) { return _textInputFocused; } return false; } internal bool ShouldSuppressMapActionForTextInput() { return ShouldSuppressGameplayInputForTextInput(); } internal void LogSuppressedGameplayInput(string inputName) { if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value && !(Time.unscaledTime < _nextMapKeyDebugLogTime)) { _nextMapKeyDebugLogTime = Time.unscaledTime + 0.25f; _log.LogInfo((object)("JW INPUT: text field captured gameplay input: " + (string.IsNullOrEmpty(inputName) ? "unknown" : inputName) + ".")); } } internal bool ShouldBlockMapKeyboardInput() { if (ShouldSuppressGameplayInputForTextInput()) { return Input.anyKeyDown; } return false; } internal bool ShouldBlockMapPointerInput() { if (IsLargeMapOpen()) { return IsPointerOverWayfinderUi(); } return false; } internal bool ShouldBlockMapZoomInput() { return ShouldBlockMapPointerInput(); } private bool IsPointerOverWayfinderUi() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) Vector3 mousePosition = Input.mousePosition; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(mousePosition.x, (float)Screen.height - mousePosition.y); if (Visible && ((Rect)(ref _windowRect)).Contains(val)) { return true; } if (_toolbarHitRectValid) { return ((Rect)(ref _toolbarHitRect)).Contains(val); } return false; } private void CancelMapPlacement() { _placeOnMapArmed = false; ResetMapPrimaryGesture(); } private void ResetMapPrimaryGesture() { //IL_0008: 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) _mapPrimaryPointerDown = false; _mapPrimaryDownScreenPosition = Vector2.zero; } private static bool TryScreenPointToWorld(Vector3 screenPoint, out Vector3 worldPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) worldPosition = Vector3.zero; Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return false; } for (int i = 0; i < MinimapMethods.Length; i++) { MethodInfo methodInfo = MinimapMethods[i]; if (methodInfo == null || !string.Equals(methodInfo.Name, "ScreenToWorldPoint", StringComparison.Ordinal)) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); try { object obj = null; if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Vector3)) { obj = methodInfo.Invoke(instance, new object[1] { screenPoint }); goto IL_00e7; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Vector2)) { obj = methodInfo.Invoke(instance, new object[1] { (object)new Vector2(screenPoint.x, screenPoint.y) }); goto IL_00e7; } goto end_IL_0054; IL_00e7: if (obj is Vector3) { worldPosition = (Vector3)obj; return true; } end_IL_0054:; } catch { } } return false; } private void SelectNewPinCategory(WayfinderPinCategory category) { _newPinCategory = category; _cachedIconQuery = null; _cachedIconCategory = (WayfinderPinCategory)(-1); if (!string.IsNullOrEmpty(_selectedIconKey) && _icons != null) { WayfinderIconEntry wayfinderIconEntry = FindIconEntry(_selectedIconKey); if (wayfinderIconEntry != null && !_icons.IsCompatibleWithCategory(wayfinderIconEntry, category)) { _selectedIconKey = string.Empty; _selectedIconLabel = "Category default"; } } } private WayfinderIconEntry FindIconEntry(string key) { if (_icons == null || _icons.All == null || string.IsNullOrEmpty(key)) { return null; } for (int i = 0; i < _icons.All.Count; i++) { WayfinderIconEntry wayfinderIconEntry = _icons.All[i]; if (wayfinderIconEntry != null && string.Equals(wayfinderIconEntry.Key, key, StringComparison.OrdinalIgnoreCase)) { return wayfinderIconEntry; } } return null; } private void DrawBossNamingControls() { if (!_config.OverrideVanillaBossNames.Value) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Native boss labels: vanilla", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); if (GUILayout.Button("Use Wayfinder Names", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { _config.OverrideVanillaBossNames.Value = true; _bossNameBuffer = string.Empty; ForceMapRefresh(); } GUILayout.EndHorizontal(); return; } string[] knownKeys = WayfinderBossNaming.KnownKeys; if (knownKeys != null && knownKeys.Length != 0) { _bossNameIndex = Mathf.Clamp(_bossNameIndex, 0, knownKeys.Length - 1); string key = knownKeys[_bossNameIndex]; if (string.IsNullOrEmpty(_bossNameBuffer)) { _bossNameBuffer = WayfinderBossNaming.GetDisplayName(_config, key); } GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Boss label", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); if (GUILayout.Button("<", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _bossNameIndex = (_bossNameIndex - 1 + knownKeys.Length) % knownKeys.Length; _bossNameBuffer = WayfinderBossNaming.GetDisplayName(_config, knownKeys[_bossNameIndex]); } GUILayout.Label(WayfinderBossNaming.DefaultDisplayName(knownKeys[_bossNameIndex]), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) }); if (GUILayout.Button(">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _bossNameIndex = (_bossNameIndex + 1) % knownKeys.Length; _bossNameBuffer = WayfinderBossNaming.GetDisplayName(_config, knownKeys[_bossNameIndex]); } _bossNameBuffer = DrawNamedTextField("boss_name", _bossNameBuffer ?? string.Empty, GUILayout.Width(180f)); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { WayfinderBossNaming.SetDisplayName(_config, knownKeys[_bossNameIndex], _bossNameBuffer); ForceMapRefresh(); } if (GUILayout.Button("Vanilla Names", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { _config.OverrideVanillaBossNames.Value = false; ForceMapRefresh(); } GUILayout.EndHorizontal(); } } private static bool HasInGameIcon(WayfinderPinRecord record) { string displayedIconKey = GetDisplayedIconKey(record); if (!string.IsNullOrEmpty(displayedIconKey)) { return displayedIconKey.StartsWith("item:", StringComparison.OrdinalIgnoreCase); } return false; } private void EnsureTheme() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Expected O, but got Unknown //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0338: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Expected O, but got Unknown //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Expected O, but got Unknown //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Expected O, but got Unknown //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04d8: Expected O, but got Unknown //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Expected O, but got Unknown //IL_0509: Unknown result type (might be due to invalid IL or missing references) //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0540: Unknown result type (might be due to invalid IL or missing references) //IL_0551: Unknown result type (might be due to invalid IL or missing references) //IL_0562: Unknown result type (might be due to invalid IL or missing references) //IL_0573: Unknown result type (might be due to invalid IL or missing references) //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_0595: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Expected O, but got Unknown //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Unknown result type (might be due to invalid IL or missing references) //IL_05da: Expected O, but got Unknown //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_05ee: Expected O, but got Unknown //IL_05f5: Unknown result type (might be due to invalid IL or missing references) //IL_05ff: Expected O, but got Unknown //IL_0605: Unknown result type (might be due to invalid IL or missing references) //IL_060f: Expected O, but got Unknown //IL_062f: Unknown result type (might be due to invalid IL or missing references) //IL_0639: Expected O, but got Unknown //IL_0644: Unknown result type (might be due to invalid IL or missing references) //IL_066a: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Expected O, but got Unknown //IL_067f: Unknown result type (might be due to invalid IL or missing references) if (_windowStyle == null) { _panelTexture = MakeSolidTexture(Color32.op_Implicit(new Color32((byte)20, (byte)22, (byte)24, (byte)248)), "JW Panel"); _buttonTexture = MakeSolidTexture(Color32.op_Implicit(new Color32((byte)43, (byte)44, (byte)43, byte.MaxValue)), "JW Button"); _buttonHoverTexture = MakeSolidTexture(Color32.op_Implicit(new Color32((byte)72, (byte)61, (byte)45, byte.MaxValue)), "JW Button Hover"); _fieldTexture = MakeSolidTexture(Color32.op_Implicit(new Color32((byte)28, (byte)30, (byte)31, byte.MaxValue)), "JW Field"); _sectionTexture = MakeSolidTexture(Color32.op_Implicit(new Color32((byte)30, (byte)32, (byte)33, (byte)238)), "JW Section"); _jwButtonTexture = LoadEmbeddedRgba("JoeyBadManners.Wayfinder.Assets.wayfinder_compass_jw_small.rgba", 26, 26, "JW Pin Manager Button", flipVertical: true); _createControlTexture = MakeControlGlyphTexture(ControlGlyph.Create, "JW Add Pin"); _pinsControlTexture = MakeControlGlyphTexture(ControlGlyph.Pins, "JW Pins"); _visibilityControlTexture = MakeControlGlyphTexture(ControlGlyph.Visibility, "JW Visibility"); _trackingControlTexture = MakeControlGlyphTexture(ControlGlyph.Tracking, "JW Track"); Color textColor = Color32.op_Implicit(new Color32((byte)188, (byte)151, (byte)99, byte.MaxValue)); Color textColor2 = Color32.op_Implicit(new Color32((byte)224, (byte)216, (byte)195, byte.MaxValue)); Color textColor3 = Color32.op_Implicit(new Color32((byte)151, (byte)148, (byte)138, byte.MaxValue)); _windowStyle = new GUIStyle(GUI.skin.window); _windowStyle.normal.background = _panelTexture; _windowStyle.hover.background = _panelTexture; _windowStyle.active.background = _panelTexture; _windowStyle.focused.background = _panelTexture; _windowStyle.onNormal.background = _panelTexture; _windowStyle.onHover.background = _panelTexture; _windowStyle.onActive.background = _panelTexture; _windowStyle.onFocused.background = _panelTexture; _windowStyle.padding = new RectOffset(12, 12, 10, 12); _buttonStyle = new GUIStyle(GUI.skin.button); _buttonStyle.normal.background = _buttonTexture; _buttonStyle.hover.background = _buttonTexture; _buttonStyle.active.background = _buttonTexture; _buttonStyle.focused.background = _buttonTexture; _buttonStyle.onNormal.background = _buttonTexture; _buttonStyle.onHover.background = _buttonTexture; _buttonStyle.onActive.background = _buttonTexture; _buttonStyle.onFocused.background = _buttonTexture; _buttonStyle.normal.textColor = textColor2; _buttonStyle.hover.textColor = textColor2; _buttonStyle.active.textColor = textColor2; _buttonStyle.focused.textColor = textColor2; _buttonStyle.onNormal.textColor = textColor2; _buttonStyle.onHover.textColor = textColor2; _buttonStyle.onActive.textColor = textColor2; _buttonStyle.onFocused.textColor = textColor2; _buttonStyle.fontStyle = (FontStyle)1; _buttonStyle.padding = new RectOffset(8, 8, 5, 5); _textFieldStyle = new GUIStyle(GUI.skin.textField); _textFieldStyle.normal.background = _fieldTexture; _textFieldStyle.focused.background = _fieldTexture; _textFieldStyle.hover.background = _fieldTexture; _textFieldStyle.active.background = _fieldTexture; _textFieldStyle.onNormal.background = _fieldTexture; _textFieldStyle.onHover.background = _fieldTexture; _textFieldStyle.onActive.background = _fieldTexture; _textFieldStyle.onFocused.background = _fieldTexture; _textFieldStyle.normal.textColor = textColor2; _textFieldStyle.focused.textColor = Color.white; _textFieldStyle.padding = new RectOffset(7, 7, 5, 5); _labelStyle = new GUIStyle(GUI.skin.label); _labelStyle.normal.textColor = textColor2; _toggleStyle = new GUIStyle(GUI.skin.toggle); _toggleStyle.normal.textColor = textColor2; _toggleStyle.onNormal.textColor = textColor; _toggleStyle.hover.textColor = Color.white; _toggleStyle.active.textColor = textColor2; _toggleStyle.focused.textColor = textColor2; _toggleStyle.onHover.textColor = textColor; _toggleStyle.onActive.textColor = textColor; _toggleStyle.onFocused.textColor = textColor; _sectionStyle = new GUIStyle(GUI.skin.box); _sectionStyle.normal.background = _sectionTexture; _sectionStyle.normal.textColor = textColor2; _sectionStyle.padding = new RectOffset(8, 8, 7, 8); _sectionStyle.margin = new RectOffset(2, 2, 2, 2); _mapButtonStyle = new GUIStyle(_buttonStyle); _toolbarButtonStyle = new GUIStyle(GUIStyle.none); _toolbarButtonStyle.alignment = (TextAnchor)4; _toolbarButtonStyle.fontSize = 10; _titleStyle = new GUIStyle(_labelStyle); _titleStyle.normal.textColor = textColor; _titleStyle.fontSize = 15; _titleStyle.fontStyle = (FontStyle)1; _subtitleStyle = new GUIStyle(_labelStyle); _subtitleStyle.normal.textColor = textColor3; _subtitleStyle.fontSize = 10; _subtitleStyle.fontStyle = (FontStyle)1; } } private static Texture2D MakeControlGlyphTexture(ControlGlyph glyph, string name) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false); ((Object)val).name = name; Color32 val2 = default(Color32); ((Color32)(ref val2))..ctor((byte)0, (byte)0, (byte)0, (byte)0); Color32 color = default(Color32); ((Color32)(ref color))..ctor((byte)205, (byte)166, (byte)106, byte.MaxValue); Color32[] array = (Color32[])(object)new Color32[1024]; for (int i = 0; i < array.Length; i++) { array[i] = val2; } val.SetPixels32(array); switch (glyph) { case ControlGlyph.Create: DrawGlyphDiamond(val, 16, 16, 11, color, 1); DrawGlyphLine(val, 16, 9, 16, 23, color, 2); DrawGlyphLine(val, 9, 16, 23, 16, color, 2); break; case ControlGlyph.Pins: DrawGlyphPin(val, 9, 20, color); DrawGlyphPin(val, 16, 13, color); DrawGlyphPin(val, 23, 20, color); break; case ControlGlyph.Visibility: DrawGlyphEllipse(val, 16, 16, 12, 7, color, 1); DrawGlyphCircle(val, 16, 16, 3, color, 2); break; default: DrawGlyphCircle(val, 16, 16, 9, color, 1); DrawGlyphCircle(val, 16, 16, 3, color, 1); DrawGlyphLine(val, 16, 4, 16, 10, color, 1); DrawGlyphLine(val, 16, 22, 16, 28, color, 1); DrawGlyphLine(val, 4, 16, 10, 16, color, 1); DrawGlyphLine(val, 22, 16, 28, 16, color, 1); break; } val.Apply(false, true); return val; } private static void DrawGlyphPin(Texture2D texture, int x, int y, Color32 color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) DrawGlyphCircle(texture, x, y, 4, color, 2); DrawGlyphLine(texture, x, y - 4, x, y - 10, color, 2); DrawGlyphLine(texture, x - 3, y - 6, x, y - 10, color, 1); DrawGlyphLine(texture, x + 3, y - 6, x, y - 10, color, 1); } private static void DrawGlyphDiamond(Texture2D texture, int cx, int cy, int radius, Color32 color, int thickness) { //IL_0009: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) DrawGlyphLine(texture, cx, cy + radius, cx + radius, cy, color, thickness); DrawGlyphLine(texture, cx + radius, cy, cx, cy - radius, color, thickness); DrawGlyphLine(texture, cx, cy - radius, cx - radius, cy, color, thickness); DrawGlyphLine(texture, cx - radius, cy, cx, cy + radius, color, thickness); } private static void DrawGlyphEllipse(Texture2D texture, int cx, int cy, int rx, int ry, Color32 color, int thickness) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 360; i += 3) { float num = (float)i * ((float)Math.PI / 180f); int x = Mathf.RoundToInt((float)cx + Mathf.Cos(num) * (float)rx); int y = Mathf.RoundToInt((float)cy + Mathf.Sin(num) * (float)ry); SetGlyphPixel(texture, x, y, color, thickness); } } private static void DrawGlyphCircle(Texture2D texture, int cx, int cy, int radius, Color32 color, int thickness) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DrawGlyphEllipse(texture, cx, cy, radius, radius, color, thickness); } private static void DrawGlyphLine(Texture2D texture, int x0, int y0, int x1, int y1, Color32 color, int thickness) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Abs(x1 - x0); int num2 = ((x0 < x1) ? 1 : (-1)); int num3 = -Mathf.Abs(y1 - y0); int num4 = ((y0 < y1) ? 1 : (-1)); int num5 = num + num3; while (true) { SetGlyphPixel(texture, x0, y0, color, thickness); if (x0 == x1 && y0 == y1) { break; } int num6 = 2 * num5; if (num6 >= num3) { num5 += num3; x0 += num2; } if (num6 <= num) { num5 += num; y0 += num4; } } } private static void SetGlyphPixel(Texture2D texture, int x, int y, Color32 color, int thickness) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null) { return; } int num = Mathf.Max(0, thickness - 1); for (int i = -num; i <= num; i++) { for (int j = -num; j <= num; j++) { int num2 = x + j; int num3 = y + i; if (num2 >= 0 && num3 >= 0 && num2 < ((Texture)texture).width && num3 < ((Texture)texture).height) { texture.SetPixel(num2, num3, Color32.op_Implicit(color)); } } } } private static Texture2D MakeSolidTexture(Color color, string name) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); ((Object)val).name = name; val.SetPixel(0, 0, color); val.Apply(false, true); return val; } private static Texture2D LoadEmbeddedRgba(string resourceName, int width, int height, string textureName, bool flipVertical) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown try { Assembly assembly = typeof(WayfinderPinManagerUI).Assembly; using Stream stream = assembly.GetManifestResourceStream(resourceName); if (stream == null) { return null; } int num = width * height * 4; byte[] array = new byte[num]; int i; int num2; for (i = 0; i < array.Length; i += num2) { num2 = stream.Read(array, i, array.Length - i); if (num2 <= 0) { break; } } if (i != num) { return null; } Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); ((Object)val).name = textureName; if (flipVertical) { int num3 = width * 4; byte[] array2 = new byte[num3]; for (int j = 0; j < height / 2; j++) { int num4 = j * num3; int num5 = (height - 1 - j) * num3; Buffer.BlockCopy(array, num4, array2, 0, num3); Buffer.BlockCopy(array, num5, array, num4, num3); Buffer.BlockCopy(array2, 0, array, num5, num3); } } val.LoadRawTextureData(array); val.Apply(false, true); return val; } catch { return null; } } private void DrawPanelBorder(Rect rect) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = Color32.op_Implicit(new Color32((byte)184, (byte)144, (byte)88, (byte)210)); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 1f, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private void DrawBronzeRect(Rect rect, float thickness) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = Color32.op_Implicit(new Color32((byte)188, (byte)151, (byte)99, (byte)235)); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private void DrawHorizontalRule() { //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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(1f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); Color color = GUI.color; GUI.color = Color32.op_Implicit(new Color32((byte)129, (byte)103, (byte)67, (byte)190)); GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color; GUILayout.Space(5f); } private static string FriendlyCategoryName(WayfinderPinCategory category) { return category switch { WayfinderPinCategory.Creature => "Creature Marker", WayfinderPinCategory.Sighting => "Enemy Sightings", WayfinderPinCategory.Boss => "Bosses", WayfinderPinCategory.PointOfInterest => "Point of Interest", _ => category.ToString(), }; } private static string GetIconDisplayName(WayfinderIconEntry entry) { if (entry == null) { return "Unknown icon"; } string text = CleanIconName(entry); string text2 = (entry.IsTrophy ? " [Trophy]" : string.Empty); return text + text2; } private static string CleanIconName(WayfinderIconEntry entry) { if (entry == null) { return "Wayfinder Pin"; } string text = string.Empty; try { if (Localization.instance != null && !string.IsNullOrEmpty(entry.RawName)) { text = Localization.instance.Localize(entry.RawName); } } catch { } string text2 = ((!string.IsNullOrEmpty(text) && !text.StartsWith("$")) ? text : entry.PrefabName); if (string.IsNullOrEmpty(text2)) { text2 = entry.Key; } if (string.IsNullOrEmpty(text2)) { return "Wayfinder Pin"; } if (text2.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring("Trophy".Length); } return text2.Replace("_", " ").Trim(); } private void Select(WayfinderPinRecord record) { //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) if (record != null) { _selectedId = record.id ?? string.Empty; _selectedCategory = record.category; _selectedPosition = record.Position; _renameBuffer = (string.IsNullOrEmpty(record.displayName) ? record.subtype : record.displayName); _scaleBuffer = Mathf.Clamp(record.scale, 0.5f, 2.5f); _deleteArmedId = string.Empty; _cachedEditIconQuery = null; _cachedEditIconCategory = (WayfinderPinCategory)(-1); } } private WayfinderPinRecord RecoverSelectedRecord() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList records = _manager.Records; if (records == null) { return null; } WayfinderPinRecord result = null; float num = 2.25f; for (int i = 0; i < records.Count; i++) { WayfinderPinRecord wayfinderPinRecord = records[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == _selectedCategory) { float num2 = wayfinderPinRecord.Position.x - _selectedPosition.x; float num3 = wayfinderPinRecord.Position.z - _selectedPosition.z; float num4 = num2 * num2 + num3 * num3; if (!(num4 > num)) { num = num4; result = wayfinderPinRecord; } } } return result; } private void ApplyColor(string id, Color color) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _manager.SetColor(id, color); ForceMapRefresh(); } private void ResetColorToDefault(string id) { _manager.ResetColorToDefault(id); ForceMapRefresh(); } private static WayfinderPinCategory NextCategory(WayfinderPinCategory current) { Array values = Enum.GetValues(typeof(WayfinderPinCategory)); int num = Array.IndexOf(values, current); num = (num + 1) % values.Length; return (WayfinderPinCategory)values.GetValue(num); } private static WayfinderPinCategory PreviousCategory(WayfinderPinCategory current) { Array values = Enum.GetValues(typeof(WayfinderPinCategory)); int num = Array.IndexOf(values, current); num = (num - 1 + values.Length) % values.Length; return (WayfinderPinCategory)values.GetValue(num); } private static bool IsLargeMapOpen() { Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return false; } try { if (LargeRootField != null) { object? value = LargeRootField.GetValue(instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { return val.activeInHierarchy; } } } catch { } return false; } private static void ForceMapRefresh() { if (WayfinderPlugin.Map != null) { WayfinderPlugin.Map.ForceRefresh(); } } } } namespace JoeyBadManners.Wayfinder.Map { internal sealed class WayfinderMapController { private static readonly FieldInfo PinsField = AccessTools.Field(typeof(Minimap), "m_pins"); private static readonly MethodInfo GetSpriteMethod = AccessTools.Method(typeof(Minimap), "GetSprite", new Type[1] { typeof(PinType) }, (Type[])null); private static readonly MethodInfo GetClosestPinMethod = AccessTools.Method(typeof(Minimap), "GetClosestPin", new Type[3] { typeof(Vector3), typeof(float), typeof(bool) }, (Type[])null); private static readonly FieldInfo PinUpdateRequiredField = AccessTools.Field(typeof(Minimap), "m_pinUpdateRequired"); private static readonly FieldInfo LargeShipMarkerField = AccessTools.Field(typeof(Minimap), "m_largeShipMarker"); private static readonly FieldInfo SmallShipMarkerField = AccessTools.Field(typeof(Minimap), "m_smallShipMarker"); private static readonly FieldInfo LargeRootField = AccessTools.Field(typeof(Minimap), "m_largeRoot"); private static readonly FieldInfo MapLargeField = typeof(Minimap).GetField("m_mapLarge", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly Type UiInputHandlerType = AccessTools.TypeByName("UIInputHandler"); private static readonly Type UiInputHintType = AccessTools.TypeByName("UIInputHint"); private static readonly FieldInfo UiInputHintMouseKeyboardField = ((UiInputHintType == null) ? null : AccessTools.Field(UiInputHintType, "m_mouseKeyboardHint")); private static readonly FieldInfo[] VanillaSelectedIconFields = new FieldInfo[7] { AccessTools.Field(typeof(Minimap), "m_selectedIcon0"), AccessTools.Field(typeof(Minimap), "m_selectedIcon1"), AccessTools.Field(typeof(Minimap), "m_selectedIcon2"), AccessTools.Field(typeof(Minimap), "m_selectedIcon3"), AccessTools.Field(typeof(Minimap), "m_selectedIcon4"), AccessTools.Field(typeof(Minimap), "m_selectedIconDeath"), AccessTools.Field(typeof(Minimap), "m_selectedIconBoss") }; private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly RuntimeIconRegistry _icons; private readonly WayfinderPinDatabase _database; private readonly WayfinderPinManagementService _manager; private readonly Dictionary _recordPins = new Dictionary(); private readonly Dictionary _recordsByPin = new Dictionary(); private readonly Dictionary _nativeBossKeys = new Dictionary(); private readonly Dictionary _trackedOverlays = new Dictionary(); private readonly Dictionary _lastKnownVehicleOverlays = new Dictionary(); private Sprite _trackedDotSprite; private Sprite _lastKnownVehicleBadgeSprite; private GUIStyle _mapNameStyle; private readonly Dictionary _vanillaPinUiOriginalActive = new Dictionary(); private bool _vanillaPinUiCacheBuilt; private float _nextVanillaPinUiProbeTime; private GameObject _persistentPinPanel; private bool _didDumpPersistentPanelDiagnostics; private float _nextPersistentPanelDiagnosticAttemptTime; private static readonly FieldInfo NamePinDataField = AccessTools.Field(typeof(PinData), "m_NamePinData"); private static readonly FieldInfo NamePinField = typeof(PinData).GetField("m_namePin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private Minimap _map; private int _lastRevision = -1; private long _lastWorldUid = long.MinValue; private bool _internalPinRemoval; private float _nextVisualRefreshTime; private string _lastHiddenCategories; internal WayfinderMapController(ManualLogSource log, WayfinderConfig config, RuntimeIconRegistry icons, WayfinderPinDatabase database, WayfinderPinManagementService manager) { _log = log; _config = config; _icons = icons; _database = database; _manager = manager; } internal void Tick() { if (!_config.Enabled.Value) { return; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { if ((Object)(object)_map != (Object)null) { Detach(); } return; } if (!object.ReferenceEquals(instance, _map) || _lastWorldUid != _database.WorldUid) { Detach(); _map = instance; _lastWorldUid = _database.WorldUid; _lastRevision = -1; } string text = _config.HiddenCategories.Value ?? string.Empty; if (!string.Equals(text, _lastHiddenCategories, StringComparison.Ordinal)) { _lastHiddenCategories = text; _lastRevision = -1; } if (_database.WorldUid != 0 && _lastRevision != _database.Revision) { SyncDatabasePins(); } } internal void OnMinimapStarted(Minimap map) { if (!((Object)(object)map == (Object)null)) { if (!object.ReferenceEquals(_map, map)) { Detach(); _map = map; _lastRevision = -1; _lastWorldUid = _database.WorldUid; } SyncDatabasePins(); } } internal void AfterUpdatePins(Minimap map) { if (_config.Enabled.Value && !((Object)(object)map == (Object)null) && !(Time.unscaledTime < _nextVisualRefreshTime)) { _nextVisualRefreshTime = Time.unscaledTime + Mathf.Max(0.05f, _config.MapVisualRefreshInterval.Value); ApplyWayfinderPinVisuals(); ApplyVanillaSpecialPinVisuals(map); ApplyVanillaBossVisuals(map); EnforceVanillaPersistentPinUiVisibility(map); } } internal void EnforceVanillaPersistentPinUiVisibility(Minimap map) { if ((Object)(object)map == (Object)null) { return; } if (!_config.ReplaceVanillaPersistentPinControls.Value) { RestoreVanillaPersistentPinUi(); return; } if (!_vanillaPinUiCacheBuilt || _vanillaPinUiOriginalActive.Count == 0 || Time.unscaledTime >= _nextVanillaPinUiProbeTime) { _nextVanillaPinUiProbeTime = Time.unscaledTime + 0.75f; CacheVanillaPersistentPinUi(map); } foreach (KeyValuePair item in _vanillaPinUiOriginalActive) { GameObject key = item.Key; if ((Object)(object)key != (Object)null && key.activeSelf) { key.SetActive(false); } } } private void CacheVanillaPersistentPinUi(Minimap map) { if ((Object)(object)map == (Object)null) { return; } int count = _vanillaPinUiOriginalActive.Count; List list = new List(); for (int i = 0; i < VanillaSelectedIconFields.Length; i++) { FieldInfo fieldInfo = VanillaSelectedIconFields[i]; if (fieldInfo == null) { continue; } object value = null; try { value = fieldInfo.GetValue(map); } catch { } GameObject unityGameObject = GetUnityGameObject(value); if ((Object)(object)unityGameObject == (Object)null) { continue; } GameObject val = FindVanillaPinControlRoot(unityGameObject); if ((Object)(object)val != (Object)null) { RememberVanillaPinUiObject(val); if (!list.Contains(val)) { list.Add(val); } } } GameObject largeMapUiRoot = GetLargeMapUiRoot(map); if ((Object)(object)largeMapUiRoot != (Object)null) { CacheOldPersistentPinHintLabels(largeMapUiRoot); CacheBottomMouseHintGroup(largeMapUiRoot); } CachePersistentPinPanelChrome(list); CachePersistentPanelMouseGlyphs(); CacheBottomMouseHintItemsGlobal(); _vanillaPinUiCacheBuilt = _vanillaPinUiOriginalActive.Count > count || _vanillaPinUiOriginalActive.Count > 0; } internal bool TryGetPersistentPinPanelGuiRect(out Rect rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) rect = default(Rect); if ((Object)(object)_persistentPinPanel == (Object)null || !_persistentPinPanel.activeInHierarchy) { return false; } RectTransform component = _persistentPinPanel.GetComponent(); return TryGetGuiRect(component, out rect); } private void CachePersistentPinPanelChrome(List selectorRoots) { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_05dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) if (((Object)(object)_persistentPinPanel != (Object)null && _persistentPinPanel.activeInHierarchy) || selectorRoots == null || selectorRoots.Count == 0) { return; } Dictionary dictionary = new Dictionary(); for (int i = 0; i < selectorRoots.Count; i++) { GameObject val = selectorRoots[i]; if ((Object)(object)val == (Object)null) { continue; } Transform val2 = val.transform; int num = 0; while ((Object)(object)val2 != (Object)null && num < 8) { GameObject gameObject = ((Component)val2).gameObject; RectTransform component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)gameObject.GetComponent() != (Object)null && TryGetGuiRect(component, out var rect) && ((Rect)(ref rect)).width >= 42f && ((Rect)(ref rect)).width <= 150f && ((Rect)(ref rect)).height >= 100f && ((Rect)(ref rect)).height <= 420f && ((Rect)(ref rect)).xMin >= (float)Screen.width * 0.55f && ((Rect)(ref rect)).xMax <= (float)Screen.width + 8f) { dictionary[gameObject] = rect; } val2 = val2.parent; num++; } } GameObject val3 = null; Rect val4 = default(Rect); int num2 = 0; float num3 = float.MinValue; foreach (KeyValuePair item in dictionary) { GameObject key = item.Key; if ((Object)(object)key == (Object)null || !key.activeInHierarchy) { continue; } int num4 = 0; for (int j = 0; j < selectorRoots.Count; j++) { GameObject val5 = selectorRoots[j]; if (!((Object)(object)val5 == (Object)null)) { Transform transform = val5.transform; if ((Object)(object)transform == (Object)(object)key.transform || transform.IsChildOf(key.transform)) { num4++; } } } if (num4 >= 3) { Rect value = item.Value; float num5 = (float)num4 * 1000f + ((Rect)(ref value)).height - ((Rect)(ref value)).width * 0.25f; if (num5 > num3) { val3 = key; val4 = value; num2 = num4; num3 = num5; } } } if ((Object)(object)val3 == (Object)null) { Image[] array = Resources.FindObjectsOfTypeAll(); Rect val8 = default(Rect); foreach (Image val6 in array) { if ((Object)(object)val6 == (Object)null || (Object)(object)((Component)val6).gameObject == (Object)null || !((Component)val6).gameObject.activeInHierarchy || !TryGetGuiRect(((Graphic)val6).rectTransform, out var rect2) || ((Rect)(ref rect2)).width < 42f || ((Rect)(ref rect2)).width > 150f || ((Rect)(ref rect2)).height < 100f || ((Rect)(ref rect2)).height > 420f || ((Rect)(ref rect2)).xMin < (float)Screen.width * 0.55f || ((Rect)(ref rect2)).xMax > (float)Screen.width + 8f) { continue; } int num6 = 0; for (int l = 0; l < selectorRoots.Count; l++) { GameObject val7 = selectorRoots[l]; if ((Object)(object)val7 == (Object)null) { continue; } RectTransform component2 = val7.GetComponent(); if (!((Object)(object)component2 == (Object)null) && TryGetGuiRect(component2, out var rect3)) { ((Rect)(ref val8))..ctor(((Rect)(ref rect2)).x - 12f, ((Rect)(ref rect2)).y - 12f, ((Rect)(ref rect2)).width + 24f, ((Rect)(ref rect2)).height + 24f); if (((Rect)(ref val8)).Contains(((Rect)(ref rect3)).center)) { num6++; } } } if (num6 >= 3) { float num7 = (float)num6 * 1000f + ((Rect)(ref rect2)).height - ((Rect)(ref rect2)).width * 0.25f; if (num7 > num3) { val3 = ((Component)val6).gameObject; val4 = rect2; num2 = num6; num3 = num7; } } } } if ((Object)(object)val3 == (Object)null) { return; } _persistentPinPanel = val3; Image[] array2 = Resources.FindObjectsOfTypeAll(); GameObject val9 = null; float num8 = float.MaxValue; foreach (Image val10 in array2) { if ((Object)(object)val10 == (Object)null || (Object)(object)((Component)val10).gameObject == (Object)null || !((Component)val10).gameObject.activeInHierarchy || (Object)(object)((Component)val10).gameObject == (Object)(object)val3) { continue; } RectTransform rectTransform = ((Graphic)val10).rectTransform; if ((Object)(object)rectTransform == (Object)null || !TryGetGuiRect(rectTransform, out var rect4) || ((Rect)(ref rect4)).width < 38f || ((Rect)(ref rect4)).width > 125f || ((Rect)(ref rect4)).height < 48f || ((Rect)(ref rect4)).height > 145f) { continue; } float num9 = Mathf.Abs(((Rect)(ref rect4)).center.x - ((Rect)(ref val4)).center.x); float num10 = Mathf.Abs(((Rect)(ref rect4)).xMax - ((Rect)(ref val4)).xMax); float num11 = ((Rect)(ref val4)).yMin - ((Rect)(ref rect4)).yMax; if (!(num9 > 24f) && !(num10 > 24f) && !(num11 < -4f) && !(num11 > 90f)) { float num12 = num11 + num9 + num10; if (num12 < num8) { val9 = ((Component)val10).gameObject; num8 = num12; } } } if ((Object)(object)val9 != (Object)null) { RememberVanillaPinUiObject(val9); } if (_config.DebugLogging.Value) { _log.LogDebug((object)("Wayfinder persistent panel: " + ((Object)val3).name + " descendants=" + num2 + " rect=" + val4)); } } private void CachePersistentPanelMouseGlyphs() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_persistentPinPanel == (Object)null || !_persistentPinPanel.activeInHierarchy) { return; } RectTransform component = _persistentPinPanel.GetComponent(); if ((Object)(object)component == (Object)null || !TryGetGuiRect(component, out var rect)) { return; } int num = CacheExactPersistentPanelMouseGlyphs(); if (num >= 2) { return; } CacheUiInputHintMouseKeyboardGlyphs(rect); Graphic[] array = Resources.FindObjectsOfTypeAll(); foreach (Graphic val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { TryCachePersistentPanelMouseGlyphObject(((Component)val).gameObject, rect); } } CanvasRenderer[] array2 = Resources.FindObjectsOfTypeAll(); foreach (CanvasRenderer val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && !((Object)(object)((Component)val2).gameObject.GetComponent() != (Object)null)) { TryCachePersistentPanelMouseGlyphObject(((Component)val2).gameObject, rect); } } } private void DumpPersistentPanelDiagnostics(Rect panelRect) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) if (_didDumpPersistentPanelDiagnostics || Time.unscaledTime < _nextPersistentPanelDiagnosticAttemptTime) { return; } _nextPersistentPanelDiagnosticAttemptTime = Time.unscaledTime + 1f; try { Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref panelRect)).xMin - 180f, ((Rect)(ref panelRect)).yMin - 120f, ((Rect)(ref panelRect)).width + 240f, ((Rect)(ref panelRect)).height + 240f); RectTransform[] array = Resources.FindObjectsOfTypeAll(); List list = new List(); foreach (RectTransform val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && TryGetGuiRect(val2, out var rect) && (((Rect)(ref val)).Overlaps(rect, true) || ((Rect)(ref val)).Contains(((Rect)(ref rect)).center))) { list.Add(val2); } } list.Sort(delegate(RectTransform a, RectTransform b) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Rect rect3 = default(Rect); Rect rect4 = default(Rect); bool flag = (Object)(object)a != (Object)null && TryGetGuiRect(a, out rect3); bool flag2 = (Object)(object)b != (Object)null && TryGetGuiRect(b, out rect4); if (!flag && !flag2) { return 0; } if (!flag) { return 1; } if (!flag2) { return -1; } int num2 = ((Rect)(ref rect3)).yMin.CompareTo(((Rect)(ref rect4)).yMin); return (num2 != 0) ? num2 : ((Rect)(ref rect3)).xMin.CompareTo(((Rect)(ref rect4)).xMin); }); _log.LogWarning((object)"[JW UI DIAG 1.33.10] ===== BEGIN RIGHT PANEL DUMP ====="); _log.LogWarning((object)string.Concat("[JW UI DIAG 1.33.10] panel=", panelRect, " probe=", val, " candidates=", list.Count)); for (int num = 0; num < list.Count; num++) { RectTransform val3 = list[num]; if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((Component)val3).gameObject == (Object)null)) { GameObject gameObject = ((Component)val3).gameObject; if (TryGetGuiRect(val3, out var rect2)) { string diagnosticComponentSummary = GetDiagnosticComponentSummary(gameObject); _log.LogWarning((object)string.Concat("[JW UI DIAG 1.33.10] OBJ ", num, " name=\"", ((Object)gameObject).name, "\" activeSelf=", gameObject.activeSelf, " activeHierarchy=", gameObject.activeInHierarchy, " rect=", rect2, " path=\"", GetDiagnosticHierarchyPath(gameObject.transform), "\" components=[", diagnosticComponentSummary, "]")); LogDiagnosticSpecialComponents(gameObject); } } } _log.LogWarning((object)"[JW UI DIAG 1.33.10] ===== END RIGHT PANEL DUMP ====="); _didDumpPersistentPanelDiagnostics = true; } catch (Exception ex) { _log.LogWarning((object)("[JW UI DIAG 1.33.10] dump failed: " + ex)); } } private static string GetDiagnosticHierarchyPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return ""; } List list = new List(); Transform val = transform; int num = 0; while ((Object)(object)val != (Object)null && num < 32) { list.Add(((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : ""); val = val.parent; num++; } list.Reverse(); return string.Join("/", list.ToArray()); } private static string GetDiagnosticComponentSummary(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } Component[] array = null; try { array = gameObject.GetComponents(); } catch { return ""; } if (array == null || array.Length == 0) { return string.Empty; } List list = new List(); foreach (Component val in array) { if ((Object)(object)val == (Object)null) { list.Add(""); continue; } Type type = ((object)val).GetType(); list.Add((type != null) ? type.FullName : ((object)val).GetType().Name); } return string.Join(", ", list.ToArray()); } private void LogDiagnosticSpecialComponents(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return; } Component[] array = null; try { array = gameObject.GetComponents(); } catch { return; } if (array == null) { return; } foreach (Component val in array) { if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); if (!(type == null)) { string text = type.Name ?? string.Empty; if (text.IndexOf("UIInputHint", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("InputLayoutElement", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("UIInputHandler", StringComparison.OrdinalIgnoreCase) >= 0) { _log.LogWarning((object)("[JW UI DIAG 1.33.10] SPECIAL type=" + type.FullName + " fields={" + GetDiagnosticFieldSummary(val, type) + "}")); } } } Image component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { string text2 = (((Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name : ""); string text3 = (((Object)(object)((Graphic)component).mainTexture != (Object)null) ? ((Object)((Graphic)component).mainTexture).name : ""); _log.LogWarning((object)("[JW UI DIAG 1.33.10] IMAGE sprite=\"" + text2 + "\" texture=\"" + text3 + "\" enabled=" + ((Behaviour)component).enabled + " raycast=" + ((Graphic)component).raycastTarget)); } RawImage component2 = gameObject.GetComponent(); if ((Object)(object)component2 != (Object)null) { string text4 = (((Object)(object)component2.texture != (Object)null) ? ((Object)component2.texture).name : ""); _log.LogWarning((object)("[JW UI DIAG 1.33.10] RAWIMAGE texture=\"" + text4 + "\" enabled=" + ((Behaviour)component2).enabled + " raycast=" + ((Graphic)component2).raycastTarget)); } TMP_Text component3 = gameObject.GetComponent(); if ((Object)(object)component3 != (Object)null) { string text5 = component3.text ?? string.Empty; text5 = text5.Replace("\r", " ").Replace("\n", " "); _log.LogWarning((object)("[JW UI DIAG 1.33.10] TMP text=\"" + text5 + "\" enabled=" + ((Behaviour)component3).enabled)); } } private static string GetDiagnosticFieldSummary(object instance, Type type) { if (instance == null || type == null) { return string.Empty; } FieldInfo[] array = null; try { array = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } catch { return ""; } if (array == null || array.Length == 0) { return string.Empty; } List list = new List(); foreach (FieldInfo fieldInfo in array) { if (!(fieldInfo == null)) { object obj2 = null; try { obj2 = fieldInfo.GetValue(instance); } catch { list.Add(fieldInfo.Name + "="); continue; } list.Add(fieldInfo.Name + "=" + GetDiagnosticValueSummary(obj2)); } } return string.Join(", ", list.ToArray()); } private static string GetDiagnosticValueSummary(object value) { if (value == null) { return ""; } Object val = (Object)((value is Object) ? value : null); if (val != (Object)null) { Component val2 = (Component)(object)((val is Component) ? val : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.gameObject != (Object)null) { return ((object)val).GetType().Name + "(\"" + ((Object)val2.gameObject).name + "\")"; } GameObject val3 = (GameObject)(object)((val is GameObject) ? val : null); if ((Object)(object)val3 != (Object)null) { return "GameObject(\"" + ((Object)val3).name + "\")"; } return ((object)val).GetType().Name + "(\"" + val.name + "\")"; } string text = value.ToString(); if (text == null) { return ""; } text = text.Replace("\r", " ").Replace("\n", " "); if (text.Length > 180) { text = text.Substring(0, 180) + "..."; } return text; } private int CacheExactPersistentPanelMouseGlyphs() { if ((Object)(object)_persistentPinPanel == (Object)null) { return 0; } Transform transform = _persistentPinPanel.transform; if ((Object)(object)transform == (Object)null) { return 0; } int num = 0; string[] array = new string[2] { "iconhints/keyboardhints/mouse1", "iconhints/keyboardhints/mouse2" }; for (int i = 0; i < array.Length; i++) { Transform val = transform.Find(array[i]); if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { GameObject gameObject = ((Component)val).gameObject; num++; RememberVanillaPinUiObject(gameObject); if (gameObject.activeSelf) { gameObject.SetActive(false); } if (_config.DebugLogging.Value) { Image component = gameObject.GetComponent(); string text = (((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name : ""); _log.LogDebug((object)("Wayfinder hid exact persistent map mouse glyph: " + array[i] + " sprite=" + text)); } } } return num; } private void CacheUiInputHintMouseKeyboardGlyphs(Rect panelRect) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) if (UiInputHintType == null || UiInputHintMouseKeyboardField == null) { return; } MonoBehaviour[] array = null; try { array = Resources.FindObjectsOfTypeAll(); } catch { return; } if (array == null) { return; } foreach (MonoBehaviour val in array) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !((Component)val).gameObject.activeInHierarchy || !UiInputHintType.IsInstanceOfType(val)) { continue; } Component val2 = (Component)(object)val; object value = null; try { value = UiInputHintMouseKeyboardField.GetValue(val2); } catch { } GameObject unityGameObject = GetUnityGameObject(value); if ((Object)(object)unityGameObject == (Object)null || !unityGameObject.activeInHierarchy || IsPartOfVisiblePlayersControl(unityGameObject)) { continue; } RectTransform component = unityGameObject.GetComponent(); if ((Object)(object)component == (Object)null || !TryGetGuiRect(component, out var rect)) { continue; } float num = ((Rect)(ref panelRect)).xMin - ((Rect)(ref rect)).xMax; float num2 = ((Rect)(ref panelRect)).yMin - 24f; float num3 = ((Rect)(ref panelRect)).yMin + Mathf.Min(170f, ((Rect)(ref panelRect)).height * 0.58f); if (!(num < -14f) && !(num > 70f) && !(((Rect)(ref rect)).center.x >= ((Rect)(ref panelRect)).xMin + 4f) && !(((Rect)(ref rect)).center.y < num2) && !(((Rect)(ref rect)).center.y > num3)) { RememberVanillaPinUiObject(unityGameObject); if (_config.DebugLogging.Value) { _log.LogDebug((object)string.Concat("Wayfinder hid UIInputHint mouse/keyboard glyph: owner=", ((Object)val2.gameObject).name, " target=", ((Object)unityGameObject).name, " rect=", rect, " panel=", panelRect)); } } } } private void TryCachePersistentPanelMouseGlyphObject(GameObject gameObject, Rect panelRect) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy || (Object)(object)gameObject == (Object)(object)_persistentPinPanel) { return; } Transform transform = gameObject.transform; if ((Object)(object)transform == (Object)null || ((Object)(object)_persistentPinPanel != (Object)null && (Object)(object)transform == (Object)(object)_persistentPinPanel.transform) || IsPartOfVisiblePlayersControl(gameObject)) { return; } RectTransform component = gameObject.GetComponent(); if ((Object)(object)component == (Object)null || !TryGetGuiRect(component, out var rect) || ((Rect)(ref rect)).width < 2f || ((Rect)(ref rect)).width > 78f || ((Rect)(ref rect)).height < 2f || ((Rect)(ref rect)).height > 78f) { return; } float num = ((Rect)(ref panelRect)).xMin - ((Rect)(ref rect)).xMax; if (num < -10f || num > 58f || ((Rect)(ref rect)).center.x >= ((Rect)(ref panelRect)).xMin + 2f) { return; } float num2 = ((Rect)(ref panelRect)).yMin - 18f; float num3 = ((Rect)(ref panelRect)).yMin + Mathf.Min(155f, ((Rect)(ref panelRect)).height * 0.52f); if (!(((Rect)(ref rect)).center.y < num2) && !(((Rect)(ref rect)).center.y > num3)) { RememberVanillaPinUiObject(gameObject); if (_config.DebugLogging.Value) { _log.LogDebug((object)string.Concat("Wayfinder hid orphaned map mouse glyph candidate: ", ((Object)gameObject).name, " rect=", rect, " panel=", panelRect)); } } } private static bool IsPartOfVisiblePlayersControl(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } Transform val = gameObject.transform; int num = 0; while ((Object)(object)val != (Object)null && num < 6) { if (ContainsVisiblePlayersText(((Component)val).gameObject)) { return true; } val = val.parent; num++; } return false; } private void CacheBottomMouseHintItemsGlobal() { TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { CacheBottomMouseHintItem(((Component)val).gameObject, NormalizeUiText(val.text)); } } Text[] array2 = Resources.FindObjectsOfTypeAll(); foreach (Text val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && ((Component)val2).gameObject.activeInHierarchy) { CacheBottomMouseHintItem(((Component)val2).gameObject, NormalizeUiText(val2.text)); } } } private void CacheBottomMouseHintItem(GameObject labelObject, string text) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)labelObject == (Object)null || !IsBottomMouseHintText(text)) { return; } RectTransform component = labelObject.GetComponent(); if ((Object)(object)component != (Object)null && TryGetGuiRect(component, out var rect) && ((Rect)(ref rect)).center.y < (float)Screen.height * 0.62f) { return; } Transform val = labelObject.transform; GameObject val2 = null; int num = 0; while ((Object)(object)val != (Object)null && num < 5) { Transform parent = val.parent; if ((Object)(object)parent == (Object)null) { break; } GameObject gameObject = ((Component)parent).gameObject; if (ContainsVisiblePlayersText(gameObject) || CountBottomMouseHintTexts(gameObject) > 1) { val2 = gameObject; break; } RectTransform component2 = gameObject.GetComponent(); if ((Object)(object)component2 != (Object)null && TryGetGuiRect(component2, out var rect2) && (((Rect)(ref rect2)).width > 260f || ((Rect)(ref rect2)).height > 90f)) { break; } val = parent; num++; } if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null) { RememberVanillaPinUiObject(((Component)val).gameObject); } if ((Object)(object)val2 != (Object)null) { CacheNearestMouseHintImage(labelObject, val2); } } private void CacheNearestMouseHintImage(GameObject labelObject, GameObject sharedHintRoot) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)labelObject == (Object)null || (Object)(object)sharedHintRoot == (Object)null) { return; } RectTransform component = labelObject.GetComponent(); if ((Object)(object)component == (Object)null || !TryGetGuiRect(component, out var rect)) { return; } Image[] componentsInChildren = sharedHintRoot.GetComponentsInChildren(true); Image val = null; float num = float.MaxValue; foreach (Image val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).gameObject == (Object)null || !((Component)val2).gameObject.activeInHierarchy || ContainsVisiblePlayersText(((Component)val2).gameObject) || !TryGetGuiRect(((Graphic)val2).rectTransform, out var rect2) || ((Rect)(ref rect2)).width < 8f || ((Rect)(ref rect2)).width > 46f || ((Rect)(ref rect2)).height < 8f || ((Rect)(ref rect2)).height > 46f) { continue; } float num2 = Mathf.Abs(((Rect)(ref rect2)).center.x - ((Rect)(ref rect)).center.x); float num3 = Mathf.Abs(((Rect)(ref rect2)).center.y - ((Rect)(ref rect)).center.y); if (!(num2 > 62f) && !(num3 > 28f)) { float num4 = num2 + num3 * 2f; if (num4 < num) { val = val2; num = num4; } } } if ((Object)(object)val != (Object)null) { RememberVanillaPinUiObject(((Component)val).gameObject); } } private static bool IsBottomMouseHintText(string text) { if (string.IsNullOrEmpty(text)) { return false; } if (!text.Contains("addpin") && !text.Contains("crossoffpin") && !text.Contains("crossoff") && !text.Contains("crossoutpin") && !text.Contains("crossout") && !text.Contains("removepin")) { return string.Equals(text, "ping", StringComparison.Ordinal); } return true; } private static int CountBottomMouseHintTexts(GameObject root) { if ((Object)(object)root == (Object)null) { return 0; } int num = 0; TMP_Text[] componentsInChildren = root.GetComponentsInChildren(true); foreach (TMP_Text val in componentsInChildren) { if ((Object)(object)val != (Object)null && IsBottomMouseHintText(NormalizeUiText(val.text))) { num++; } } Text[] componentsInChildren2 = root.GetComponentsInChildren(true); foreach (Text val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null && IsBottomMouseHintText(NormalizeUiText(val2.text))) { num++; } } return num; } private static bool TryGetGuiRect(RectTransform rectTransform, out Rect rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) rect = default(Rect); if ((Object)(object)rectTransform == (Object)null) { return false; } Vector3[] array = (Vector3[])(object)new Vector3[4]; rectTransform.GetWorldCorners(array); Canvas componentInParent = ((Component)rectTransform).GetComponentInParent(); Camera val = null; if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) { val = componentInParent.worldCamera; } Vector2 val2 = RectTransformUtility.WorldToScreenPoint(val, array[0]); float num = val2.x; float num2 = val2.x; float num3 = val2.y; float num4 = val2.y; for (int i = 1; i < 4; i++) { Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val, array[i]); num = Mathf.Min(num, val3.x); num2 = Mathf.Max(num2, val3.x); num3 = Mathf.Min(num3, val3.y); num4 = Mathf.Max(num4, val3.y); } float num5 = num2 - num; float num6 = num4 - num3; if (num5 <= 0.5f || num6 <= 0.5f) { return false; } rect = new Rect(num, (float)Screen.height - num4, num5, num6); return true; } private static GameObject FindVanillaPinControlRoot(GameObject selectedIcon) { if ((Object)(object)selectedIcon == (Object)null) { return null; } Transform transform = selectedIcon.transform; if ((Object)(object)transform == (Object)null) { return selectedIcon; } if (UiInputHandlerType != null) { try { Component componentInParent = ((Component)transform).GetComponentInParent(UiInputHandlerType); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.gameObject != (Object)null) { return componentInParent.gameObject; } } catch { } } Button componentInParent2 = selectedIcon.GetComponentInParent