Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Joeys Wayfinder v1.1.1
JoeyBadManners.Wayfinder.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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.Security; using System.Security.Permissions; 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 Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace JoeyBadManners.Wayfinder { [BepInPlugin("com.joeybadmanners.wayfinder", "Joey's Wayfinder", "1.1.1")] public sealed class WayfinderPlugin : BaseUnityPlugin { public const string PluginGuid = "com.joeybadmanners.wayfinder"; public const string PluginName = "Joey's Wayfinder"; public const string PluginVersion = "1.1.1"; 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; private bool _runtimeSuspended; 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.1.1 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 (Settings == null || !Settings.Enabled.Value) { SuspendRuntime(); return; } if (_runtimeSuspended) { _runtimeSuspended = false; _nextBootstrapCheck = 0f; _nextDiscoverySliceTime = 0f; if (Map != null) { Map.ForceRefresh(); } if (Vehicles != null) { Vehicles.ResetSession(); } } 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 (Settings == null || !Settings.Enabled.Value || 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 == 0L) { CloseWorldSessionIfNeeded(); } else if ((num != _loadedWorldUid || Pins.WorldUid != num) && Pins.LoadWorld(num, ZNet.instance.GetWorldName())) { _loadedWorldUid = num; LiveDiscoveryRegistry.ResetForWorld(num); PhysicalSpawnerIndex.Reset(); LiveDiscoveryRegistry.SyncFromLoadedLocations(); LiveDiscoveryRegistry.SyncVehiclesFromGameInstances(); LiveDiscoveryRegistry.SeedLoadedPortals(); 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 != 0L) && 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 (Settings != null && Settings.Enabled.Value && Map != null && (Object)(object)Minimap.instance != (Object)null) { Map.EnforceNativeShipMarkerVisibility(Minimap.instance); Map.EnforceVanillaPersistentPinUiVisibility(Minimap.instance); } } private void OnGUI() { if (Settings != null && Settings.Enabled.Value) { if (Map != null) { Map.DrawLargeMapNameLabels(); } if (CompassHUD != null) { CompassHUD.Draw(); } if (PinManagerUI != null) { PinManagerUI.DrawMapButton(); PinManagerUI.Draw(); } } } private void SuspendRuntime() { if (!_runtimeSuspended) { _runtimeSuspended = true; if (TrackingBeacons != null) { TrackingBeacons.Clear(); } if (Vehicles != null) { Vehicles.ClearSession(); } if (Map != null) { Map.Suspend(); } } } private void OnDestroy() { try { if (TrackingBeacons != null) { TrackingBeacons.Clear(); } if (Pins != null) { Pins.Save(); } if (Harmony != null) { Harmony.UnpatchSelf(); } } catch { } } } } namespace JoeyBadManners.Wayfinder.Tracking { internal sealed class TrackedBeaconController { private sealed class Beam { internal GameObject GameObject; internal LineRenderer Line; internal Material Material; } private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private readonly WayfinderPinManagementService _manager; private readonly Dictionary<string, Beam> _beams = new Dictionary<string, Beam>(StringComparer.Ordinal); internal TrackedBeaconController(ManualLogSource log, WayfinderConfig config, WayfinderPinManagementService manager) { _log = log; _config = config; _manager = manager; } internal void Tick() { if (!_config.Enabled.Value || !_config.BeaconEnabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null) { Clear(); return; } List<WayfinderPinRecord> trackedPins = _manager.GetTrackedPins(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < trackedPins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = trackedPins[i]; if (wayfinderPinRecord == null || string.IsNullOrEmpty(wayfinderPinRecord.id)) { continue; } hashSet.Add(wayfinderPinRecord.id); if (!_beams.TryGetValue(wayfinderPinRecord.id, out var value) || value == null || (Object)(object)value.GameObject == (Object)null || (Object)(object)value.Line == (Object)null) { value = CreateBeam(wayfinderPinRecord.id); if (value == null) { continue; } _beams[wayfinderPinRecord.id] = value; } UpdateBeam(value, wayfinderPinRecord); } List<string> list = new List<string>(); foreach (KeyValuePair<string, Beam> beam in _beams) { if (!hashSet.Contains(beam.Key)) { list.Add(beam.Key); } } for (int j = 0; j < list.Count; j++) { DestroyBeam(_beams[list[j]]); _beams.Remove(list[j]); } } internal void Clear() { foreach (Beam value in _beams.Values) { DestroyBeam(value); } _beams.Clear(); } private Beam CreateBeam(string id) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown Shader val = Shader.Find("Sprites/Default"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Transparent"); } if ((Object)(object)val == (Object)null) { if (_config.DebugLogging.Value) { _log.LogWarning((object)"Tracked beacon could not find a transparent Unity shader."); } return null; } GameObject val2 = new GameObject("Wayfinder Tracked Beacon " + id); LineRenderer val3 = val2.AddComponent<LineRenderer>(); Material val4 = new Material(val); ((Object)val4).name = "Wayfinder Tracked Beacon Material"; val4.renderQueue = 4000; ((Renderer)val3).material = val4; val3.positionCount = 2; val3.useWorldSpace = true; val3.loop = false; val3.numCapVertices = 4; val3.textureMode = (LineTextureMode)0; val3.alignment = (LineAlignment)0; ((Renderer)val3).receiveShadows = false; return new Beam { GameObject = val2, Line = val3, Material = val4 }; } private void UpdateBeam(Beam beam, WayfinderPinRecord record) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) if (beam != null && !((Object)(object)beam.Line == (Object)null) && record != null) { float num = Mathf.Clamp01(_config.BeaconOpacity.Value); bool num2 = WayfinderDynamicState.IsLastKnownVehicle(record); if (num2) { num *= Mathf.Clamp01(_config.LastKnownVehicleOpacity.Value); } float num3 = Mathf.Max(0.08f, _config.BeaconWidth.Value); float num4 = Mathf.Max(5f, _config.BeaconHeight.Value); if (num2) { num3 *= 0.82f; } Color color = TrackedColorPalette.GetColor(record.trackSlot, num); Color color2 = TrackedColorPalette.GetColor(record.trackSlot, num * 0.15f); beam.Line.startWidth = num3; beam.Line.endWidth = num3 * 0.55f; beam.Line.startColor = color; beam.Line.endColor = color2; beam.Line.SetPosition(0, record.Position + Vector3.up * 0.5f); beam.Line.SetPosition(1, record.Position + Vector3.up * num4); if ((Object)(object)beam.Material != (Object)null) { beam.Material.color = color; } } } private static void DestroyBeam(Beam beam) { if (beam != null) { if ((Object)(object)beam.GameObject != (Object)null) { Object.Destroy((Object)(object)beam.GameObject); } if ((Object)(object)beam.Material != (Object)null) { Object.Destroy((Object)(object)beam.Material); } } } } internal static class TrackedColorPalette { internal const int SlotCount = 5; internal static Color GetColor(int slot, float alpha) { //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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //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_00a6: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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) //IL_00c2: 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) Color result = (Color)(slot switch { 0 => new Color(0.32f, 1f, 0.34f, 1f), 1 => new Color(0.18f, 0.82f, 1f, 1f), 2 => new Color(1f, 0.72f, 0.18f, 1f), 3 => new Color(0.82f, 0.38f, 1f, 1f), 4 => new Color(1f, 0.28f, 0.22f, 1f), _ => new Color(0.32f, 1f, 0.34f, 1f), }); result.a = Mathf.Clamp01(alpha); return result; } internal static string GetName(int slot) { return slot switch { 0 => "Green", 1 => "Cyan", 2 => "Gold", 3 => "Violet", 4 => "Red", _ => "Green", }; } } } namespace JoeyBadManners.Wayfinder.Pins { [Serializable] internal sealed class WayfinderClusterMember { public string key; public float x; public float y; public float z; public Vector3 Position => new Vector3(x, y, z); public WayfinderClusterMember() { } public WayfinderClusterMember(string memberKey, Vector3 position) { //IL_0017: 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_002f: Unknown result type (might be due to invalid IL or missing references) key = memberKey ?? string.Empty; x = position.x; y = position.y; z = position.z; } } internal static class WayfinderDynamicState { private const long UnixEpochTicks = 621355968000000000L; private const long TicksPerSecond = 10000000L; internal static bool IsDynamicVehicle(WayfinderPinRecord record) { if (record != null && record.category == WayfinderPinCategory.Vehicle) { return record.source == WayfinderPinSource.Dynamic; } return false; } internal static bool IsLiveVehicle(WayfinderPinRecord record) { if (IsDynamicVehicle(record)) { return record.dynamicLive; } return false; } internal static bool IsLastKnownVehicle(WayfinderPinRecord record) { if (IsDynamicVehicle(record) && !record.dynamicLive) { return record.lastSeenUnix > 0; } return false; } internal static long NowUnixSeconds() { try { long num = DateTime.UtcNow.Ticks - 621355968000000000L; return (num <= 0) ? 0 : (num / 10000000); } catch { return 0L; } } internal static long AgeSeconds(WayfinderPinRecord record) { if (record == null || record.lastSeenUnix <= 0) { return -1L; } long num = NowUnixSeconds(); if (num <= 0) { return -1L; } long num2 = num - record.lastSeenUnix; if (num2 >= 0) { return num2; } return 0L; } internal static string FormatLastSeen(WayfinderPinRecord record) { if (record == null) { return "unknown"; } if (record.dynamicLive) { return "now"; } long num = AgeSeconds(record); if (num < 0) { return "unknown"; } if (num < 10) { return "just now"; } if (num < 60) { return num + "s ago"; } long num2 = num / 60; if (num2 < 60) { return num2 + "m ago"; } long num3 = num2 / 60; if (num3 < 24) { return num3 + "h ago"; } long num4 = num3 / 24; if (num4 < 30) { return num4 + "d ago"; } long num5 = num4 / 30; if (num5 < 12) { return num5 + "mo ago"; } return num4 / 365 + "y ago"; } internal static string GetVehicleStateLabel(WayfinderPinRecord record) { if (!IsDynamicVehicle(record)) { return string.Empty; } if (record.dynamicLive) { return "LIVE"; } return "LAST KNOWN"; } } internal enum WayfinderPinCategory { Resource, Creature, Boss, Dungeon, PointOfInterest, Spawner, Habitat, Sighting, Vehicle, Trader, Portal, Base, Outpost, Farm, Dock, Road, Bridge, Camp, Storage, Danger, Custom, Unknown } internal sealed class WayfinderPinDatabase { private const float DuplicatePositionTolerance = 0.25f; private const string PortalRightClickProtectionMigrationSentinel = "__jw_1350_portal_rightclick_protection__"; private readonly ManualLogSource _log; private readonly WayfinderConfig _config; private WayfinderWorldData _data = new WayfinderWorldData(); private string _filePath; private bool _dirty; private float _nextScheduledSaveTime; private readonly HashSet<string> _suppressedKeyCache = new HashSet<string>(StringComparer.Ordinal); private readonly List<WayfinderPinRecord> _visibleRecords = new List<WayfinderPinRecord>(); private int _visibleRecordsRevision = -1; internal IReadOnlyList<WayfinderPinRecord> Records { get { if (_visibleRecordsRevision != Revision) { _visibleRecords.Clear(); if (_data != null && _data.pins != null) { for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord != null && (wayfinderPinRecord.category != WayfinderPinCategory.Portal || wayfinderPinRecord.portalVerified)) { _visibleRecords.Add(wayfinderPinRecord); } } } _visibleRecordsRevision = Revision; } return _visibleRecords; } } internal long WorldUid => _data.worldUid; internal int Revision { get; private set; } internal WayfinderPinDatabase(ManualLogSource log, WayfinderConfig config) { _log = log; _config = config; } internal bool LoadWorld(long worldUid, string worldName) { if (!Save()) { _log.LogWarning((object)"Wayfinder postponed world switch because the previous world database could not be saved. The in-memory records were kept and the save/switch will retry automatically."); return false; } string text = Path.Combine(Paths.ConfigPath, "JoeysWayfinder", "worlds"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, worldUid + ".wfd"); WayfinderWorldData wayfinderWorldData = new WayfinderWorldData { worldUid = worldUid, worldName = (worldName ?? string.Empty) }; bool flag = false; bool flag2 = false; bool flag3 = false; if (File.Exists(text2)) { WayfinderWorldData wayfinderWorldData2 = null; try { wayfinderWorldData2 = WayfinderSaveCodec.Read(text2, worldUid, worldName); } catch (Exception ex) { flag3 = true; _log.LogWarning((object)("Could not read the primary Wayfinder world database: " + ex.Message)); } if (wayfinderWorldData2 == null) { flag3 = true; } if (wayfinderWorldData2 == null) { string text3 = text2 + ".bak"; if (File.Exists(text3)) { try { wayfinderWorldData2 = WayfinderSaveCodec.Read(text3, worldUid, worldName); if (wayfinderWorldData2 != null && wayfinderWorldData2.worldUid == worldUid) { flag = true; try { File.Copy(text3, text2, overwrite: true); } catch (Exception ex2) { flag2 = true; _log.LogWarning((object)("Wayfinder loaded the backup database but could not restore it over the primary file yet: " + ex2.Message)); } } } catch (Exception ex3) { _log.LogWarning((object)("Could not read the Wayfinder backup database: " + ex3.Message)); } } } if (wayfinderWorldData2 != null && wayfinderWorldData2.worldUid == worldUid) { wayfinderWorldData = wayfinderWorldData2; } else if (flag3) { PreserveUnreadableWorldFile(text2); _log.LogWarning((object)"Wayfinder could not validate either the primary world database or its backup. The unreadable primary was preserved before starting a clean in-memory database for this world."); } } else { string path = Path.Combine(text, worldUid + ".json"); if (File.Exists(path)) { try { WayfinderWorldData wayfinderWorldData3 = JsonUtility.FromJson<WayfinderWorldData>(File.ReadAllText(path)); if (wayfinderWorldData3 != null && wayfinderWorldData3.worldUid == worldUid && wayfinderWorldData3.suppressedKeys != null) { wayfinderWorldData.suppressedKeys = new List<string>(wayfinderWorldData3.suppressedKeys); } } catch (Exception ex4) { _log.LogWarning((object)("Could not migrate legacy Wayfinder suppression data: " + ex4.Message)); } } } _filePath = text2; _data = wayfinderWorldData; if (_data.pins == null) { _data.pins = new List<WayfinderPinRecord>(); } if (_data.suppressedKeys == null) { _data.suppressedKeys = new List<string>(); } if (_data.schemaVersion < 2) { _data.schemaVersion = 2; } _dirty = false; _nextScheduledSaveTime = 0f; if (flag2) { _dirty = true; ScheduleSave(); } int repairedIds; int repairedMembers; int repairedSuppressions; int num = RepairLoadedPersistenceIntegrity(out repairedIds, out repairedMembers, out repairedSuppressions); if (num > 0) { _dirty = true; ScheduleSave(); } int mergedResourceRecords; int removedStaticPointRecords; int num2 = RepairLoadedDiscoveryDuplicates(out mergedResourceRecords, out removedStaticPointRecords); if (num2 > 0) { _dirty = true; ScheduleSave(); } int num3 = RepairPre1350PortalRightClickSuppressions(); if (num3 >= 0) { _dirty = true; ScheduleSave(); } int preservedCustomNames; int num4 = RepairPre1351VehicleNaming(out preservedCustomNames); if (num4 >= 0) { _data.schemaVersion = 10; _dirty = true; ScheduleSave(); } int num5 = MigratePortalVerification(); if (num5 >= 0) { _dirty = true; ScheduleSave(); } RebuildSuppressionCache(); Revision++; _log.LogInfo((object)("Wayfinder world database ready: " + _data.pins.Count + " records for " + (_data.worldName ?? worldUid.ToString()) + ".")); if (flag) { _log.LogWarning((object)"Recovered this world's Wayfinder database from the .bak safety copy after the primary file failed validation."); } if (num > 0) { _log.LogInfo((object)("Persistence integrity repaired " + repairedIds + " pin id(s), " + repairedMembers + " invalid/duplicate member(s), and " + repairedSuppressions + " invalid/duplicate suppression key(s).")); } if (num2 > 0) { _log.LogInfo((object)("Discovery regression cleanup merged " + mergedResourceRecords + " exact duplicate resource cluster record(s) and removed " + removedStaticPointRecords + " exact duplicate generated-location/spawner record(s).")); } if (num3 > 0) { _log.LogInfo((object)("Portal recovery restored " + num3 + " previously suppressed portal pin(s). Map right-click can no longer permanently suppress managed portals; deliberate removal remains available from the Pins panel.")); } if (num5 > 0) { _log.LogWarning((object)("Portal migration quarantined " + num5 + " legacy position-only portal record(s). They are preserved in the .wfd file but hidden until a valid live portal confirms them; matching records recover their names, favorites, icon overrides, and tracking state in place.")); } if (num4 > 0 || preservedCustomNames > 0) { _log.LogInfo((object)("Vehicle-name migration repaired " + num4 + " automatic vehicle label(s) and preserved " + preservedCustomNames + " existing custom label(s).")); } return true; } private void PreserveUnreadableWorldFile(string path) { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return; } try { string text = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"); string destFileName = path + ".corrupt-" + text; File.Copy(path, destFileName, overwrite: false); } catch { } } private int RepairLoadedPersistenceIntegrity(out int repairedIds, out int repairedMembers, out int repairedSuppressions) { repairedIds = 0; repairedMembers = 0; repairedSuppressions = 0; if (_data == null) { return 0; } if (_data.pins == null) { _data.pins = new List<WayfinderPinRecord>(); } if (_data.suppressedKeys == null) { _data.suppressedKeys = new List<string>(); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null) { continue; } if (string.IsNullOrEmpty(wayfinderPinRecord.id) || hashSet.Contains(wayfinderPinRecord.id)) { wayfinderPinRecord.id = Guid.NewGuid().ToString("N"); repairedIds++; } hashSet.Add(wayfinderPinRecord.id); if (wayfinderPinRecord.members == null) { wayfinderPinRecord.members = new List<WayfinderClusterMember>(); continue; } HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); bool flag = false; for (int num = wayfinderPinRecord.members.Count - 1; num >= 0; num--) { string text = wayfinderPinRecord.members[num]?.key; if (string.IsNullOrEmpty(text) || hashSet2.Contains(text)) { wayfinderPinRecord.members.RemoveAt(num); repairedMembers++; flag = true; } else { hashSet2.Add(text); } } if (flag && (wayfinderPinRecord.category == WayfinderPinCategory.Resource || wayfinderPinRecord.category == WayfinderPinCategory.Sighting)) { RecalculateCluster(wayfinderPinRecord); } } HashSet<string> hashSet3 = new HashSet<string>(StringComparer.Ordinal); for (int num2 = _data.suppressedKeys.Count - 1; num2 >= 0; num2--) { string text2 = _data.suppressedKeys[num2]; if (string.IsNullOrEmpty(text2) || hashSet3.Contains(text2)) { _data.suppressedKeys.RemoveAt(num2); repairedSuppressions++; } else { hashSet3.Add(text2); } } return repairedIds + repairedMembers + repairedSuppressions; } private int RepairLoadedDiscoveryDuplicates(out int mergedResourceRecords, out int removedStaticPointRecords) { mergedResourceRecords = 0; removedStaticPointRecords = 0; if (_data == null || _data.pins == null || _data.pins.Count < 2) { return 0; } Dictionary<string, WayfinderPinRecord> ownerByMember = new Dictionary<string, WayfinderPinRecord>(StringComparer.OrdinalIgnoreCase); Dictionary<string, WayfinderPinRecord> dictionary = new Dictionary<string, WayfinderPinRecord>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (!IsRepairableAutoDiscoveryRecord(wayfinderPinRecord) || wayfinderPinRecord.members == null || wayfinderPinRecord.members.Count == 0) { continue; } if (wayfinderPinRecord.category == WayfinderPinCategory.Resource) { WayfinderPinRecord wayfinderPinRecord2 = FindExistingResourceOwner(wayfinderPinRecord, ownerByMember); if (wayfinderPinRecord2 != null && wayfinderPinRecord2 != wayfinderPinRecord) { MergeExactDuplicateResourceRecord(wayfinderPinRecord2, wayfinderPinRecord); _data.pins.RemoveAt(i); i--; mergedResourceRecords++; IndexResourceMembers(wayfinderPinRecord2, ownerByMember); } else { IndexResourceMembers(wayfinderPinRecord, ownerByMember); } } else { if (!IsStaticGeneratedOrSpawnerRecord(wayfinderPinRecord)) { continue; } string exactStaticIdentityKey = GetExactStaticIdentityKey(wayfinderPinRecord); if (!string.IsNullOrEmpty(exactStaticIdentityKey)) { if (!dictionary.TryGetValue(exactStaticIdentityKey, out var value)) { dictionary[exactStaticIdentityKey] = wayfinderPinRecord; } else if (value != wayfinderPinRecord) { PreserveDuplicateUserState(value, wayfinderPinRecord); _data.pins.RemoveAt(i); i--; removedStaticPointRecords++; } } } } return mergedResourceRecords + removedStaticPointRecords; } private static bool IsRepairableAutoDiscoveryRecord(WayfinderPinRecord record) { if (record != null && record.source != WayfinderPinSource.Manual && record.source != WayfinderPinSource.Imported && record.source != WayfinderPinSource.Vanilla) { return record.source != WayfinderPinSource.Dynamic; } return false; } private static bool IsStaticGeneratedOrSpawnerRecord(WayfinderPinRecord record) { if (record == null || record.members == null) { return false; } for (int i = 0; i < record.members.Count; i++) { string text = ((record.members[i] == null) ? null : record.members[i].key); if (!string.IsNullOrEmpty(text) && (text.StartsWith("location:", StringComparison.OrdinalIgnoreCase) || text.StartsWith("spawner:", StringComparison.OrdinalIgnoreCase))) { return true; } } return false; } private static string GetExactStaticIdentityKey(WayfinderPinRecord record) { if (record == null || record.members == null) { return null; } for (int i = 0; i < record.members.Count; i++) { string text = ((record.members[i] == null) ? null : record.members[i].key); if (!string.IsNullOrEmpty(text) && (text.StartsWith("location:", StringComparison.OrdinalIgnoreCase) || text.StartsWith("spawner:", StringComparison.OrdinalIgnoreCase))) { return text; } } return null; } private static string BuildResourceMemberIdentity(WayfinderPinRecord record, WayfinderClusterMember member) { if (record == null || member == null || string.IsNullOrEmpty(member.key)) { return null; } return (record.subtype ?? string.Empty).ToLowerInvariant() + "|" + member.key; } private static WayfinderPinRecord FindExistingResourceOwner(WayfinderPinRecord record, Dictionary<string, WayfinderPinRecord> ownerByMember) { if (record == null || record.members == null || ownerByMember == null) { return null; } for (int i = 0; i < record.members.Count; i++) { string text = BuildResourceMemberIdentity(record, record.members[i]); if (!string.IsNullOrEmpty(text) && ownerByMember.TryGetValue(text, out var value) && value != null && value != record) { return value; } } return null; } private static void IndexResourceMembers(WayfinderPinRecord record, Dictionary<string, WayfinderPinRecord> ownerByMember) { if (record == null || record.members == null || ownerByMember == null) { return; } for (int i = 0; i < record.members.Count; i++) { string text = BuildResourceMemberIdentity(record, record.members[i]); if (!string.IsNullOrEmpty(text)) { ownerByMember[text] = record; } } } private void MergeExactDuplicateResourceRecord(WayfinderPinRecord keep, WayfinderPinRecord duplicate) { if (keep == null || duplicate == null) { return; } if (keep.members == null) { keep.members = new List<WayfinderClusterMember>(); } if (duplicate.members != null) { for (int i = 0; i < duplicate.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = duplicate.members[i]; if (wayfinderClusterMember != null && !string.IsNullOrEmpty(wayfinderClusterMember.key) && !HasResourceMember(keep, wayfinderClusterMember.key)) { keep.members.Add(wayfinderClusterMember); } } } PreserveDuplicateUserState(keep, duplicate); if (duplicate.source == WayfinderPinSource.InteractionDiscovery) { keep.source = WayfinderPinSource.InteractionDiscovery; } if (string.IsNullOrEmpty(keep.displayName) && !string.IsNullOrEmpty(duplicate.displayName)) { keep.displayName = duplicate.displayName; } if (string.IsNullOrEmpty(keep.iconKey) && !string.IsNullOrEmpty(duplicate.iconKey)) { keep.iconKey = duplicate.iconKey; } RecalculateCluster(keep); } private static bool HasResourceMember(WayfinderPinRecord record, string memberKey) { if (record == null || record.members == null || string.IsNullOrEmpty(memberKey)) { return false; } for (int i = 0; i < record.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = record.members[i]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, memberKey, StringComparison.Ordinal)) { return true; } } return false; } private static void PreserveDuplicateUserState(WayfinderPinRecord keep, WayfinderPinRecord duplicate) { if (keep == null || duplicate == null) { return; } if (duplicate.favorite) { keep.favorite = true; } if (duplicate.iconOverride && !keep.iconOverride) { keep.iconOverride = true; keep.iconOverrideKey = duplicate.iconOverrideKey ?? string.Empty; } if (duplicate.tracked) { if (!keep.tracked) { keep.tracked = true; keep.trackSlot = duplicate.trackSlot; } else if (duplicate.trackSlot >= 0 && (keep.trackSlot < 0 || duplicate.trackSlot < keep.trackSlot)) { keep.trackSlot = duplicate.trackSlot; } } } private void RebuildSuppressionCache() { _suppressedKeyCache.Clear(); if (_data == null || _data.suppressedKeys == null) { return; } for (int i = 0; i < _data.suppressedKeys.Count; i++) { string text = _data.suppressedKeys[i]; if (!string.IsNullOrEmpty(text)) { _suppressedKeyCache.Add(text); } } } private int RepairPre1350PortalRightClickSuppressions() { if (_data == null || _data.suppressedKeys == null) { return -1; } for (int i = 0; i < _data.suppressedKeys.Count; i++) { if (string.Equals(_data.suppressedKeys[i], "__jw_1350_portal_rightclick_protection__", StringComparison.Ordinal)) { return -1; } } int num = 0; for (int num2 = _data.suppressedKeys.Count - 1; num2 >= 0; num2--) { string text = _data.suppressedKeys[num2]; if (!string.IsNullOrEmpty(text) && text.StartsWith("portal:", StringComparison.OrdinalIgnoreCase)) { _data.suppressedKeys.RemoveAt(num2); num++; } } _data.suppressedKeys.Add("__jw_1350_portal_rightclick_protection__"); return num; } private int MigratePortalVerification() { if (_data == null || _data.pins == null || _data.schemaVersion >= 11) { return -1; } int num = 0; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Portal) { wayfinderPinRecord.portalVerified = HasStablePortalIdentity(wayfinderPinRecord); if (!wayfinderPinRecord.portalVerified) { num++; } } } _data.schemaVersion = 11; return num; } private static bool HasStablePortalIdentity(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("portal:zdo:", StringComparison.Ordinal)) { return true; } } return false; } private int RepairPre1351VehicleNaming(out int preservedCustomNames) { preservedCustomNames = 0; if (_data == null || _data.pins == null || _data.schemaVersion >= 9) { return -1; } int num = 0; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != WayfinderPinCategory.Vehicle || wayfinderPinRecord.source != WayfinderPinSource.Dynamic) { continue; } if (WayfinderVehicleNaming.LooksLikeAutomaticOrLegacyName(wayfinderPinRecord)) { wayfinderPinRecord.vehicleNameOverride = false; string automaticDisplayName = WayfinderVehicleNaming.GetAutomaticDisplayName(wayfinderPinRecord); if (!string.Equals(wayfinderPinRecord.displayName ?? string.Empty, automaticDisplayName, StringComparison.Ordinal)) { wayfinderPinRecord.displayName = automaticDisplayName; num++; } } else { wayfinderPinRecord.vehicleNameOverride = true; preservedCustomNames++; } } return num; } internal bool UnloadWorld() { int num = ((_data != null && _data.pins != null) ? _data.pins.Count : 0); long num2 = ((_data == null) ? 0 : _data.worldUid); if (!Save()) { return false; } _data = new WayfinderWorldData(); _filePath = null; _dirty = false; _suppressedKeyCache.Clear(); _nextScheduledSaveTime = 0f; Revision++; if (num2 != 0L) { _log.LogInfo((object)("Wayfinder world session closed after saving " + num + " records.")); } return true; } internal void Tick() { if (_dirty && !(_nextScheduledSaveTime <= 0f) && Time.unscaledTime >= _nextScheduledSaveTime) { Save(); } } private void ScheduleSave() { if (_dirty && !string.IsNullOrEmpty(_filePath) && !(_nextScheduledSaveTime > 0f)) { _nextScheduledSaveTime = Time.unscaledTime + Mathf.Max(0.5f, _config.PersistenceFlushInterval.Value); } } internal bool Save() { if (!_dirty || string.IsNullOrEmpty(_filePath)) { _nextScheduledSaveTime = 0f; return true; } try { WayfinderSaveCodec.Write(_filePath, _data); _dirty = false; _nextScheduledSaveTime = 0f; if (_config.DebugLogging.Value && _config.VerboseRuntimeLogging.Value) { _log.LogInfo((object)("Wayfinder persisted " + _data.pins.Count + " pins to " + _filePath)); } return true; } catch (Exception ex) { _dirty = true; _nextScheduledSaveTime = Time.unscaledTime + Mathf.Max(1f, _config.PersistenceFlushInterval.Value); _log.LogWarning((object)("Could not save Wayfinder world database; keeping changes in memory and retrying automatically: " + ex.Message)); return false; } } internal WayfinderPinRecord AddResourceObservation(string resourceKey, string displayName, Vector3 position, string iconKey, WayfinderPinSource source, string memberKey = null, float? linkDistanceOverride = null) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(resourceKey)) { throw new ArgumentException("resourceKey is required", "resourceKey"); } memberKey = (string.IsNullOrEmpty(memberKey) ? BuildPositionKey(resourceKey, position) : memberKey); if (IsSuppressed(memberKey)) { return null; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord.category != WayfinderPinCategory.Resource || wayfinderPinRecord.subtype != resourceKey || wayfinderPinRecord.members == null) { continue; } for (int j = 0; j < wayfinderPinRecord.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[j]; if (wayfinderClusterMember.key == memberKey || HorizontalDistance(wayfinderClusterMember.Position, position) <= 0.25f) { RepairResourceMetadata(wayfinderPinRecord, displayName, iconKey, source); return wayfinderPinRecord; } } } float num = linkDistanceOverride ?? _config.ResourceClusterLinkDistance.Value; if (!_config.ResourceClustering.Value) { num = 0f; } List<WayfinderPinRecord> list = new List<WayfinderPinRecord>(); if (num > 0f) { for (int k = 0; k < _data.pins.Count; k++) { WayfinderPinRecord wayfinderPinRecord2 = _data.pins[k]; if (wayfinderPinRecord2.category == WayfinderPinCategory.Resource && !(wayfinderPinRecord2.subtype != resourceKey) && ClusterTouches(wayfinderPinRecord2, position, num)) { list.Add(wayfinderPinRecord2); } } } WayfinderPinRecord wayfinderPinRecord3; if (list.Count == 0) { wayfinderPinRecord3 = NewResourceRecord(resourceKey, displayName, position, iconKey, source); wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); _data.pins.Add(wayfinderPinRecord3); } else { wayfinderPinRecord3 = list[0]; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); if (!string.IsNullOrEmpty(displayName)) { wayfinderPinRecord3.displayName = displayName; } if (!string.IsNullOrEmpty(iconKey)) { wayfinderPinRecord3.iconKey = iconKey; } if (source == WayfinderPinSource.InteractionDiscovery) { wayfinderPinRecord3.source = source; } for (int l = 1; l < list.Count; l++) { WayfinderPinRecord wayfinderPinRecord4 = list[l]; if (wayfinderPinRecord4.members != null) { wayfinderPinRecord3.members.AddRange(wayfinderPinRecord4.members); } _data.pins.Remove(wayfinderPinRecord4); } } RecalculateCluster(wayfinderPinRecord3); _dirty = true; Revision++; ScheduleSave(); return wayfinderPinRecord3; } internal WayfinderPinRecord AddSightingObservation(string creatureKey, string displayName, Vector3 position, string iconKey, string memberKey, float linkDistance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(creatureKey)) { return null; } memberKey = (string.IsNullOrEmpty(memberKey) ? BuildPositionKey("sighting:" + creatureKey, position) : memberKey); if (IsSuppressed(memberKey)) { return null; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != WayfinderPinCategory.Sighting || !string.Equals(wayfinderPinRecord.subtype, creatureKey, StringComparison.OrdinalIgnoreCase) || wayfinderPinRecord.members == null) { continue; } for (int j = 0; j < wayfinderPinRecord.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[j]; if (wayfinderClusterMember != null && wayfinderClusterMember.key == memberKey) { RepairPointMetadata(wayfinderPinRecord, displayName, iconKey, WayfinderPinSource.Dynamic); return wayfinderPinRecord; } } } List<WayfinderPinRecord> list = new List<WayfinderPinRecord>(); for (int k = 0; k < _data.pins.Count; k++) { WayfinderPinRecord wayfinderPinRecord2 = _data.pins[k]; if (wayfinderPinRecord2 != null && wayfinderPinRecord2.category == WayfinderPinCategory.Sighting && string.Equals(wayfinderPinRecord2.subtype, creatureKey, StringComparison.OrdinalIgnoreCase) && ClusterTouches(wayfinderPinRecord2, position, linkDistance)) { list.Add(wayfinderPinRecord2); } } WayfinderPinRecord wayfinderPinRecord3; if (list.Count == 0) { wayfinderPinRecord3 = new WayfinderPinRecord { id = Guid.NewGuid().ToString("N"), displayName = (displayName ?? creatureKey), subtype = creatureKey, iconKey = (iconKey ?? string.Empty), category = WayfinderPinCategory.Sighting, source = WayfinderPinSource.Dynamic, Position = position, count = 1, isCluster = false, scale = 0.9f }; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); _data.pins.Add(wayfinderPinRecord3); } else { wayfinderPinRecord3 = list[0]; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); if (!string.IsNullOrEmpty(displayName)) { wayfinderPinRecord3.displayName = displayName; } if (!string.IsNullOrEmpty(iconKey)) { wayfinderPinRecord3.iconKey = iconKey; } for (int l = 1; l < list.Count; l++) { WayfinderPinRecord wayfinderPinRecord4 = list[l]; if (wayfinderPinRecord4.members != null) { wayfinderPinRecord3.members.AddRange(wayfinderPinRecord4.members); } _data.pins.Remove(wayfinderPinRecord4); } } RecalculateCluster(wayfinderPinRecord3); _dirty = true; Revision++; ScheduleSave(); return wayfinderPinRecord3; } internal int CollapseNearbyAutoPoints(Func<WayfinderPinRecord, string> groupSelector, float radius) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0163: Unknown result type (might be due to invalid IL or missing references) if (groupSelector == null || radius <= 0f) { return 0; } float num = radius * radius; float num2 = 1f / radius; Dictionary<string, Dictionary<long, List<WayfinderPinRecord>>> dictionary = new Dictionary<string, Dictionary<long, List<WayfinderPinRecord>>>(StringComparer.OrdinalIgnoreCase); HashSet<WayfinderPinRecord> hashSet = new HashSet<WayfinderPinRecord>(); for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category == WayfinderPinCategory.Portal || wayfinderPinRecord.source == WayfinderPinSource.Manual || wayfinderPinRecord.source == WayfinderPinSource.Imported || wayfinderPinRecord.source == WayfinderPinSource.Dynamic) { continue; } string text = null; try { text = groupSelector(wayfinderPinRecord); } catch { } if (string.IsNullOrEmpty(text)) { continue; } int num3 = Mathf.FloorToInt(wayfinderPinRecord.Position.x * num2); int num4 = Mathf.FloorToInt(wayfinderPinRecord.Position.z * num2); if (!dictionary.TryGetValue(text, out var value)) { Dictionary<long, List<WayfinderPinRecord>> dictionary2 = (dictionary[text] = new Dictionary<long, List<WayfinderPinRecord>>()); value = dictionary2; } bool flag = false; for (int j = -1; j <= 1; j++) { if (flag) { break; } for (int k = -1; k <= 1; k++) { if (flag) { break; } long key = BuildSpatialCellKey(num3 + j, num4 + k); if (!value.TryGetValue(key, out var value2)) { continue; } for (int l = 0; l < value2.Count; l++) { WayfinderPinRecord wayfinderPinRecord2 = value2[l]; if (wayfinderPinRecord2 != null) { float num5 = wayfinderPinRecord2.Position.x - wayfinderPinRecord.Position.x; float num6 = wayfinderPinRecord2.Position.z - wayfinderPinRecord.Position.z; if (num5 * num5 + num6 * num6 <= num) { flag = true; break; } } } } } if (flag) { hashSet.Add(wayfinderPinRecord); continue; } long key2 = BuildSpatialCellKey(num3, num4); if (!value.TryGetValue(key2, out var value3)) { List<WayfinderPinRecord> list = (value[key2] = new List<WayfinderPinRecord>()); value3 = list; } value3.Add(wayfinderPinRecord); } int num7 = 0; if (hashSet.Count > 0) { for (int num8 = _data.pins.Count - 1; num8 >= 0; num8--) { if (hashSet.Contains(_data.pins[num8])) { _data.pins.RemoveAt(num8); num7++; } } } if (num7 > 0) { _dirty = true; Revision++; ScheduleSave(); } return num7; } private static long BuildSpatialCellKey(int x, int z) { return ((long)x << 32) ^ (uint)z; } internal WayfinderPinRecord AddPointObservation(string subtype, string displayName, Vector3 position, string iconKey, WayfinderPinCategory category, WayfinderPinSource source, string memberKey) { //IL_0018: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: 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_00a2: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(subtype)) { return null; } memberKey = (string.IsNullOrEmpty(memberKey) ? BuildPositionKey(subtype, position) : memberKey); if (IsSuppressed(memberKey)) { return null; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != category || wayfinderPinRecord.subtype != subtype) { continue; } if (wayfinderPinRecord.members != null) { for (int j = 0; j < wayfinderPinRecord.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[j]; if (wayfinderClusterMember.key == memberKey || HorizontalDistance(wayfinderClusterMember.Position, position) <= 0.75f) { RepairPointMetadata(wayfinderPinRecord, displayName, iconKey, source); return wayfinderPinRecord; } } } if (HorizontalDistance(wayfinderPinRecord.Position, position) <= 0.75f) { RepairPointMetadata(wayfinderPinRecord, displayName, iconKey, source); return wayfinderPinRecord; } } WayfinderPinRecord wayfinderPinRecord2 = new WayfinderPinRecord(); wayfinderPinRecord2.id = Guid.NewGuid().ToString("N"); wayfinderPinRecord2.displayName = displayName ?? subtype; wayfinderPinRecord2.subtype = subtype; wayfinderPinRecord2.iconKey = iconKey ?? string.Empty; wayfinderPinRecord2.category = category; wayfinderPinRecord2.source = source; wayfinderPinRecord2.Position = position; wayfinderPinRecord2.count = 1; wayfinderPinRecord2.isCluster = false; float scale; switch (category) { default: scale = 1f; break; case WayfinderPinCategory.Dungeon: case WayfinderPinCategory.PointOfInterest: scale = 0.95f; break; case WayfinderPinCategory.Boss: scale = Mathf.Max(1f, _config.BossPinScale.Value); break; } wayfinderPinRecord2.scale = scale; WayfinderPinRecord wayfinderPinRecord3 = wayfinderPinRecord2; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); _data.pins.Add(wayfinderPinRecord3); _dirty = true; Revision++; ScheduleSave(); return wayfinderPinRecord3; } internal void BeginPortalObservationPass() { if (_data == null || _data.pins == null) { return; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Portal) { wayfinderPinRecord.portalLive = false; } } } internal WayfinderPinRecord UpsertPortalObservation(string subtype, string portalTag, Vector3 position, string stableMemberKey, string positionMemberKey, bool connectionKnown, bool connected, bool syncDisplayName, out string action) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_049e: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) action = "rejected"; if (string.IsNullOrEmpty(subtype)) { subtype = "Portal"; } positionMemberKey = (string.IsNullOrEmpty(positionMemberKey) ? BuildPositionKey("portal-pos:" + subtype, position) : positionMemberKey); if (string.IsNullOrEmpty(stableMemberKey) || IsSuppressed(stableMemberKey) || IsSuppressed(positionMemberKey)) { action = "suppressed"; return null; } WayfinderPinRecord wayfinderPinRecord = null; float num = float.MaxValue; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord2 = _data.pins[i]; if (wayfinderPinRecord2 != null && wayfinderPinRecord2.category == WayfinderPinCategory.Portal) { float num2 = HorizontalDistance(wayfinderPinRecord2.Position, position); if ((RecordHasMemberKey(wayfinderPinRecord2, stableMemberKey) && num2 <= 3f) || RecordHasMemberKey(wayfinderPinRecord2, positionMemberKey)) { wayfinderPinRecord = wayfinderPinRecord2; break; } float num3 = num2; float num4 = (wayfinderPinRecord2.portalVerified ? 1f : 3f); if (num3 <= num4 && num3 < num && string.Equals(wayfinderPinRecord2.subtype ?? string.Empty, subtype, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord = wayfinderPinRecord2; num = num3; } } } string text = (string.IsNullOrEmpty(portalTag) ? string.Empty : portalTag.Trim()); string text2 = (string.IsNullOrEmpty(text) ? "Portal" : text); long num5 = WayfinderDynamicState.NowUnixSeconds(); if (wayfinderPinRecord == null) { WayfinderPinRecord wayfinderPinRecord3 = new WayfinderPinRecord { id = Guid.NewGuid().ToString("N"), displayName = text2, subtype = subtype, iconKey = "wayfinder:portal", category = WayfinderPinCategory.Portal, source = WayfinderPinSource.InteractionDiscovery, Position = position, count = 1, isCluster = false, scale = 1f, portalTag = text, portalConnectionKnown = connectionKnown, portalConnected = (connectionKnown && connected), portalStatusSeenUnix = (connectionKnown ? num5 : 0), portalLive = true, portalVerified = true }; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(stableMemberKey, position)); wayfinderPinRecord3.members.Add(new WayfinderClusterMember(positionMemberKey, position)); _data.pins.Add(wayfinderPinRecord3); _dirty = true; Revision++; if (_config != null) { ScheduleSave(); } action = "created"; return wayfinderPinRecord3; } bool flag = false; bool flag2 = false; wayfinderPinRecord.portalLive = true; if (!wayfinderPinRecord.portalVerified) { wayfinderPinRecord.portalVerified = true; flag = true; } if (wayfinderPinRecord.category != WayfinderPinCategory.Portal) { wayfinderPinRecord.category = WayfinderPinCategory.Portal; flag = true; } if (wayfinderPinRecord.source != WayfinderPinSource.InteractionDiscovery) { wayfinderPinRecord.source = WayfinderPinSource.InteractionDiscovery; flag = true; } if (!string.Equals(wayfinderPinRecord.subtype, subtype, StringComparison.Ordinal)) { wayfinderPinRecord.subtype = subtype; flag = true; } if (!string.Equals(wayfinderPinRecord.iconKey, "wayfinder:portal", StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = "wayfinder:portal"; flag = true; } if (!string.Equals(wayfinderPinRecord.portalTag ?? string.Empty, text, StringComparison.Ordinal)) { wayfinderPinRecord.portalTag = text; flag = true; } if (syncDisplayName && !wayfinderPinRecord.portalNameOverride && !string.Equals(wayfinderPinRecord.displayName, text2, StringComparison.Ordinal)) { wayfinderPinRecord.displayName = text2; flag = true; } else if (string.IsNullOrEmpty(wayfinderPinRecord.displayName)) { wayfinderPinRecord.displayName = text2; flag = true; } if (connectionKnown) { if (!wayfinderPinRecord.portalConnectionKnown || wayfinderPinRecord.portalConnected != connected) { wayfinderPinRecord.portalConnectionKnown = true; wayfinderPinRecord.portalConnected = connected; flag = true; } if (num5 > 0 && (wayfinderPinRecord.portalStatusSeenUnix <= 0 || num5 - wayfinderPinRecord.portalStatusSeenUnix >= 5)) { wayfinderPinRecord.portalStatusSeenUnix = num5; flag2 = true; } } else if (wayfinderPinRecord.portalConnectionKnown) { wayfinderPinRecord.portalConnectionKnown = false; flag = true; } if (HorizontalDistance(wayfinderPinRecord.Position, position) > 0.01f || Mathf.Abs(wayfinderPinRecord.y - position.y) > 0.01f) { wayfinderPinRecord.Position = position; flag = true; } if (wayfinderPinRecord.members == null) { wayfinderPinRecord.members = new List<WayfinderClusterMember>(); } for (int num6 = wayfinderPinRecord.members.Count - 1; num6 >= 0; num6--) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[num6]; if (wayfinderClusterMember == null || (!string.Equals(wayfinderClusterMember.key, stableMemberKey, StringComparison.Ordinal) && !string.Equals(wayfinderClusterMember.key, positionMemberKey, StringComparison.Ordinal))) { wayfinderPinRecord.members.RemoveAt(num6); flag = true; } } if (!RecordHasMemberKey(wayfinderPinRecord, stableMemberKey)) { wayfinderPinRecord.members.Add(new WayfinderClusterMember(stableMemberKey, position)); flag = true; } if (!RecordHasMemberKey(wayfinderPinRecord, positionMemberKey)) { wayfinderPinRecord.members.Add(new WayfinderClusterMember(positionMemberKey, position)); flag = true; } int num7 = MergePortalDuplicates(wayfinderPinRecord, stableMemberKey, positionMemberKey, position, subtype); if (num7 > 0) { flag = true; } if (flag || flag2) { _dirty = true; if (_config != null) { ScheduleSave(); } if (flag) { Revision++; } } action = (flag ? "updated" : "matched") + ((num7 > 0) ? (";merged=" + num7) : string.Empty); return wayfinderPinRecord; } private static bool RecordHasMemberKey(WayfinderPinRecord record, string key) { if (record == null || record.members == null || string.IsNullOrEmpty(key)) { return false; } for (int i = 0; i < record.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = record.members[i]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, key, StringComparison.Ordinal)) { return true; } } return false; } private int MergePortalDuplicates(WayfinderPinRecord keep, string stableKey, string positionKey, Vector3 position, string subtype) { //IL_0044: 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) int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord != keep && wayfinderPinRecord.category == WayfinderPinCategory.Portal) { float num3 = HorizontalDistance(wayfinderPinRecord.Position, position); bool num4 = (RecordHasMemberKey(wayfinderPinRecord, stableKey) && num3 <= 3f) || RecordHasMemberKey(wayfinderPinRecord, positionKey); bool flag = !wayfinderPinRecord.portalVerified && num3 <= 3f && string.Equals(wayfinderPinRecord.subtype ?? string.Empty, subtype, StringComparison.OrdinalIgnoreCase); if (num4 || flag) { MergePortalUserState(keep, wayfinderPinRecord); _data.pins.RemoveAt(num2); num++; } } } return num; } private static void MergePortalUserState(WayfinderPinRecord keep, WayfinderPinRecord duplicate) { if (duplicate.favorite) { keep.favorite = true; } if (duplicate.tracked && !keep.tracked) { keep.tracked = true; keep.trackSlot = duplicate.trackSlot; } if (duplicate.iconOverride && !keep.iconOverride) { keep.iconOverride = true; keep.iconOverrideKey = duplicate.iconOverrideKey; } if (duplicate.portalNameOverride && !keep.portalNameOverride) { keep.portalNameOverride = true; keep.displayName = duplicate.displayName; } } internal bool RemovePortalObservation(string stableMemberKey, string positionMemberKey, Vector3 position) { //IL_0038: 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) for (int num = _data.pins.Count - 1; num >= 0; num--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Portal) { float num2 = HorizontalDistance(wayfinderPinRecord.Position, position); if ((RecordHasMemberKey(wayfinderPinRecord, stableMemberKey) && num2 <= 3f) || RecordHasMemberKey(wayfinderPinRecord, positionMemberKey) || (wayfinderPinRecord.portalVerified && num2 <= 1f)) { _data.pins.RemoveAt(num); _dirty = true; Revision++; if (_config != null) { ScheduleSave(); } return true; } } } return false; } internal bool RemovePointByMemberKey(WayfinderPinCategory category, string memberKey) { if (string.IsNullOrEmpty(memberKey)) { return false; } for (int num = _data.pins.Count - 1; num >= 0; num--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.members != null) { for (int i = 0; i < wayfinderPinRecord.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[i]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, memberKey, StringComparison.Ordinal)) { _data.pins.RemoveAt(num); _dirty = true; Revision++; ScheduleSave(); return true; } } } } return false; } internal void BeginDynamicObservationPass(WayfinderPinCategory category) { if (_data == null || _data.pins == null) { return; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic) { wayfinderPinRecord.dynamicLive = false; } } } internal bool HasDynamicPointByMemberKey(WayfinderPinCategory category, string memberKey) { if (string.IsNullOrEmpty(memberKey) || _data == null || _data.pins == null) { return false; } for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != category || wayfinderPinRecord.source != WayfinderPinSource.Dynamic || wayfinderPinRecord.members == null) { continue; } for (int j = 0; j < wayfinderPinRecord.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[j]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, memberKey, StringComparison.Ordinal)) { return true; } } } return false; } internal int FinalizeDynamicObservationPass(WayfinderPinCategory category, HashSet<string> seenMemberKeys, float staleGraceSeconds) { if (_data == null || _data.pins == null) { return 0; } float unscaledTime = Time.unscaledTime; float num = Mathf.Max(0f, staleGraceSeconds); int num2 = 0; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord == null || wayfinderPinRecord.category != category || wayfinderPinRecord.source != WayfinderPinSource.Dynamic) { continue; } bool flag = false; if (wayfinderPinRecord.members != null && seenMemberKeys != null) { for (int j = 0; j < wayfinderPinRecord.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[j]; if (wayfinderClusterMember != null && seenMemberKeys.Contains(wayfinderClusterMember.key)) { flag = true; break; } } } if (!flag && wayfinderPinRecord.dynamicLive) { float dynamicLastObservedRealtime = wayfinderPinRecord.dynamicLastObservedRealtime; if (!(dynamicLastObservedRealtime > 0f) || !(unscaledTime - dynamicLastObservedRealtime < num)) { wayfinderPinRecord.dynamicLive = false; num2++; } } } return num2; } internal WayfinderPinRecord UpsertDynamicPoint(string subtype, string displayName, Vector3 position, string iconKey, WayfinderPinCategory category, string memberKey, float movementTolerance) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(subtype) || string.IsNullOrEmpty(memberKey)) { return null; } if (IsSuppressed(memberKey)) { return null; } WayfinderPinRecord wayfinderPinRecord = null; WayfinderClusterMember wayfinderClusterMember = null; for (int i = 0; i < _data.pins.Count; i++) { if (wayfinderPinRecord != null) { break; } WayfinderPinRecord wayfinderPinRecord2 = _data.pins[i]; if (wayfinderPinRecord2 == null || wayfinderPinRecord2.source != WayfinderPinSource.Dynamic || wayfinderPinRecord2.members == null) { continue; } for (int j = 0; j < wayfinderPinRecord2.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember2 = wayfinderPinRecord2.members[j]; if (wayfinderClusterMember2 != null && string.Equals(wayfinderClusterMember2.key, memberKey, StringComparison.Ordinal)) { wayfinderPinRecord = wayfinderPinRecord2; wayfinderClusterMember = wayfinderClusterMember2; break; } } } if (wayfinderPinRecord == null) { WayfinderPinRecord wayfinderPinRecord3 = new WayfinderPinRecord { id = Guid.NewGuid().ToString("N"), displayName = (string.IsNullOrEmpty(displayName) ? subtype : displayName), subtype = subtype, iconKey = (iconKey ?? string.Empty), category = category, source = WayfinderPinSource.Dynamic, Position = position, count = 1, isCluster = false, scale = 1f, lastSeenUnix = WayfinderDynamicState.NowUnixSeconds(), vehicleNameOverride = false, dynamicLive = true, dynamicLastObservedRealtime = Time.unscaledTime }; wayfinderPinRecord3.members.Add(new WayfinderClusterMember(memberKey, position)); _data.pins.Add(wayfinderPinRecord3); _dirty = true; Revision++; ScheduleSave(); return wayfinderPinRecord3; } bool flag = false; bool flag2 = false; bool flag3 = false; wayfinderPinRecord.dynamicLive = true; wayfinderPinRecord.dynamicLastObservedRealtime = Time.unscaledTime; long num = WayfinderDynamicState.NowUnixSeconds(); if (num > 0 && (wayfinderPinRecord.lastSeenUnix <= 0 || num - wayfinderPinRecord.lastSeenUnix >= 5)) { wayfinderPinRecord.lastSeenUnix = num; flag3 = true; } if (category == WayfinderPinCategory.Vehicle) { if (!wayfinderPinRecord.vehicleNameOverride && !string.IsNullOrEmpty(displayName) && !string.Equals(wayfinderPinRecord.displayName, displayName, StringComparison.Ordinal)) { wayfinderPinRecord.displayName = displayName; flag = true; } } else if (string.IsNullOrEmpty(wayfinderPinRecord.displayName) && !string.IsNullOrEmpty(displayName)) { wayfinderPinRecord.displayName = displayName; flag = true; } if (!string.Equals(wayfinderPinRecord.subtype, subtype, StringComparison.Ordinal)) { wayfinderPinRecord.subtype = subtype; flag = true; } if (!string.IsNullOrEmpty(iconKey) && !string.Equals(wayfinderPinRecord.iconKey, iconKey, StringComparison.OrdinalIgnoreCase)) { wayfinderPinRecord.iconKey = iconKey; flag = true; } if (wayfinderPinRecord.category != category) { wayfinderPinRecord.category = category; flag = true; } if (wayfinderPinRecord.source != WayfinderPinSource.Dynamic) { wayfinderPinRecord.source = WayfinderPinSource.Dynamic; flag = true; } float num2 = Mathf.Max(0f, movementTolerance); Vector3 position2 = wayfinderPinRecord.Position; float num3 = position2.x - position.x; float num4 = position2.y - position.y; float num5 = position2.z - position.z; if (num3 * num3 + num4 * num4 + num5 * num5 > num2 * num2) { wayfinderPinRecord.Position = position; if (wayfinderClusterMember == null) { wayfinderClusterMember = new WayfinderClusterMember(memberKey, position); wayfinderPinRecord.members.Add(wayfinderClusterMember); flag = true; } else { wayfinderClusterMember.x = position.x; wayfinderClusterMember.y = position.y; wayfinderClusterMember.z = position.z; } flag2 = true; } if (flag || flag2 || flag3) { _dirty = true; ScheduleSave(); if (flag) { Revision++; } } return wayfinderPinRecord; } internal int CollapseDuplicateDynamicMemberKeys(WayfinderPinCategory category) { Dictionary<string, WayfinderPinRecord> dictionary = new Dictionary<string, WayfinderPinRecord>(StringComparer.Ordinal); int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic && wayfinderPinRecord.members != null && wayfinderPinRecord.members.Count != 0) { string text = ((wayfinderPinRecord.members[0] == null) ? null : wayfinderPinRecord.members[0].key); if (!string.IsNullOrEmpty(text)) { if (!dictionary.TryGetValue(text, out var value)) { dictionary[text] = wayfinderPinRecord; } else { if (wayfinderPinRecord.tracked && !value.tracked) { value.tracked = true; value.trackSlot = wayfinderPinRecord.trackSlot; } if (wayfinderPinRecord.favorite) { value.favorite = true; } if (wayfinderPinRecord.iconOverride && !value.iconOverride) { value.iconOverride = true; value.iconOverrideKey = wayfinderPinRecord.iconOverrideKey ?? string.Empty; } if (wayfinderPinRecord.lastSeenUnix > value.lastSeenUnix) { value.lastSeenUnix = wayfinderPinRecord.lastSeenUnix; } if (wayfinderPinRecord.dynamicLive) { value.dynamicLive = true; } if (wayfinderPinRecord.dynamicLastObservedRealtime > value.dynamicLastObservedRealtime) { value.dynamicLastObservedRealtime = wayfinderPinRecord.dynamicLastObservedRealtime; } if (category == WayfinderPinCategory.Vehicle && wayfinderPinRecord.vehicleNameOverride && !value.vehicleNameOverride) { value.vehicleNameOverride = true; value.displayName = wayfinderPinRecord.displayName; } _data.pins.RemoveAt(num2); num++; } } } } if (num > 0) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveStaleDynamicAliasesNear(WayfinderPinCategory category, Vector3 livePosition, float radius, string subtypePrefix, string currentMemberKey, HashSet<string> liveMemberKeys) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0100: 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_0114: 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) float num = Mathf.Max(0f, radius); num *= num; int num2 = 0; for (int num3 = _data.pins.Count - 1; num3 >= 0; num3--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num3]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic && wayfinderPinRecord.members != null && wayfinderPinRecord.members.Count != 0 && (string.IsNullOrEmpty(subtypePrefix) || (wayfinderPinRecord.subtype != null && wayfinderPinRecord.subtype.StartsWith(subtypePrefix, StringComparison.OrdinalIgnoreCase)))) { string text = ((wayfinderPinRecord.members[0] == null) ? null : wayfinderPinRecord.members[0].key); if (!string.IsNullOrEmpty(text) && !string.Equals(text, currentMemberKey, StringComparison.Ordinal) && (liveMemberKeys == null || !liveMemberKeys.Contains(text))) { float num4 = wayfinderPinRecord.Position.x - livePosition.x; float num5 = wayfinderPinRecord.Position.y - livePosition.y; float num6 = wayfinderPinRecord.Position.z - livePosition.z; if (!(num4 * num4 + num5 * num5 + num6 * num6 > num)) { WayfinderPinRecord wayfinderPinRecord2 = null; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord3 = _data.pins[i]; if (wayfinderPinRecord3 == null || wayfinderPinRecord3.members == null) { continue; } for (int j = 0; j < wayfinderPinRecord3.members.Count; j++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord3.members[j]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, currentMemberKey, StringComparison.Ordinal)) { wayfinderPinRecord2 = wayfinderPinRecord3; break; } } if (wayfinderPinRecord2 != null) { break; } } if (wayfinderPinRecord2 != null) { if (wayfinderPinRecord.tracked && !wayfinderPinRecord2.tracked) { wayfinderPinRecord2.tracked = true; wayfinderPinRecord2.trackSlot = wayfinderPinRecord.trackSlot; } if (wayfinderPinRecord.favorite) { wayfinderPinRecord2.favorite = true; } if (wayfinderPinRecord.iconOverride && !wayfinderPinRecord2.iconOverride) { wayfinderPinRecord2.iconOverride = true; wayfinderPinRecord2.iconOverrideKey = wayfinderPinRecord.iconOverrideKey ?? string.Empty; } if (wayfinderPinRecord.lastSeenUnix > wayfinderPinRecord2.lastSeenUnix) { wayfinderPinRecord2.lastSeenUnix = wayfinderPinRecord.lastSeenUnix; } if (category == WayfinderPinCategory.Vehicle && wayfinderPinRecord.vehicleNameOverride && !wayfinderPinRecord2.vehicleNameOverride) { wayfinderPinRecord2.vehicleNameOverride = true; wayfinderPinRecord2.displayName = wayfinderPinRecord.displayName; } } _data.pins.RemoveAt(num3); num2++; } } } } if (num2 > 0) { _dirty = true; Revision++; ScheduleSave(); } return num2; } internal bool RemoveDynamicPointByMemberKey(WayfinderPinCategory category, string memberKey) { if (string.IsNullOrEmpty(memberKey)) { return false; } for (int num = _data.pins.Count - 1; num >= 0; num--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic && wayfinderPinRecord.members != null) { for (int i = 0; i < wayfinderPinRecord.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[i]; if (wayfinderClusterMember != null && string.Equals(wayfinderClusterMember.key, memberKey, StringComparison.Ordinal)) { _data.pins.RemoveAt(num); _dirty = true; Revision++; ScheduleSave(); return true; } } } } return false; } internal bool RemoveNearestDynamicPoint(WayfinderPinCategory category, Vector3 position, float radius, string subtypePrefix) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_007f: 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_0093: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0f, radius); float num2 = num * num; int num3 = -1; float num4 = num2; for (int i = 0; i < _data.pins.Count; i++) { WayfinderPinRecord wayfinderPinRecord = _data.pins[i]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic && (string.IsNullOrEmpty(subtypePrefix) || (wayfinderPinRecord.subtype != null && wayfinderPinRecord.subtype.StartsWith(subtypePrefix, StringComparison.OrdinalIgnoreCase)))) { float num5 = wayfinderPinRecord.Position.x - position.x; float num6 = wayfinderPinRecord.Position.y - position.y; float num7 = wayfinderPinRecord.Position.z - position.z; float num8 = num5 * num5 + num6 * num6 + num7 * num7; if (num8 <= num4) { num4 = num8; num3 = i; } } } if (num3 < 0) { return false; } _data.pins.RemoveAt(num3); _dirty = true; Revision++; ScheduleSave(); return true; } internal int RemoveUnseenDynamicPoints(WayfinderPinCategory category, HashSet<string> seenMemberKeys) { int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == category && wayfinderPinRecord.source == WayfinderPinSource.Dynamic) { bool flag = false; if (wayfinderPinRecord.members != null) { for (int i = 0; i < wayfinderPinRecord.members.Count; i++) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[i]; if (wayfinderClusterMember != null && seenMemberKeys != null && seenMemberKeys.Contains(wayfinderClusterMember.key)) { flag = true; break; } } } if (!flag && !wayfinderPinRecord.dynamicLive) { _data.pins.RemoveAt(num2); num++; } } } if (num > 0) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal void MarkMetadataChanged() { _dirty = true; ScheduleSave(); } internal void AddOrUpdate(WayfinderPinRecord record) { if (record != null) { if (string.IsNullOrEmpty(record.id)) { record.id = Guid.NewGuid().ToString("N"); } int num = _data.pins.FindIndex((WayfinderPinRecord p) => p.id == record.id); if (num >= 0) { _data.pins[num] = record; } else { _data.pins.Add(record); } _dirty = true; Revision++; ScheduleSave(); } } internal bool Remove(string id) { return Remove(id, _config.SuppressRemovedAutoPins.Value); } internal bool Remove(string id, bool suppressAutoRediscovery) { WayfinderPinRecord wayfinderPinRecord = _data.pins.Find((WayfinderPinRecord p) => p != null && p.id == id); if (wayfinderPinRecord == null) { return false; } if (suppressAutoRediscovery && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Vanilla && wayfinderPinRecord.source != WayfinderPinSource.Imported) { SuppressMembers(wayfinderPinRecord); } _data.pins.Remove(wayfinderPinRecord); _dirty = true; Revision++; ScheduleSave(); return true; } internal int RemoveResourceMembers(Func<string, Vector3, bool> covered) { //IL_00cb: 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 (covered == null) { return 0; } int num = 0; bool flag = false; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Resource) { if (wayfinderPinRecord.members == null || wayfinderPinRecord.members.Count == 0) { bool flag2 = false; try { flag2 = covered(wayfinderPinRecord.subtype, wayfinderPinRecord.Position); } catch { } if (flag2) { _data.pins.RemoveAt(num2); num++; flag = true; } } else { bool flag3 = false; for (int num3 = wayfinderPinRecord.members.Count - 1; num3 >= 0; num3--) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[num3]; if (wayfinderClusterMember != null) { bool flag4 = false; try { flag4 = covered(wayfinderPinRecord.subtype, wayfinderClusterMember.Position); } catch { } if (flag4) { wayfinderPinRecord.members.RemoveAt(num3); num++; flag = true; flag3 = true; } } } if (wayfinderPinRecord.members.Count == 0) { _data.pins.RemoveAt(num2); } else if (flag3) { RecalculateCluster(wayfinderPinRecord); } } } } if (flag) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveSightingMembers(Func<string, Vector3, bool> covered) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (covered == null) { return 0; } int num = 0; bool flag = false; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Sighting) { if (wayfinderPinRecord.members == null || wayfinderPinRecord.members.Count == 0) { bool flag2 = false; try { flag2 = covered(wayfinderPinRecord.subtype, wayfinderPinRecord.Position); } catch { } if (flag2) { _data.pins.RemoveAt(num2); num++; flag = true; } } else { bool flag3 = false; for (int num3 = wayfinderPinRecord.members.Count - 1; num3 >= 0; num3--) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[num3]; if (wayfinderClusterMember != null) { bool flag4 = false; try { flag4 = covered(wayfinderPinRecord.subtype, wayfinderClusterMember.Position); } catch { } if (flag4) { wayfinderPinRecord.members.RemoveAt(num3); num++; flag = true; flag3 = true; } } } if (wayfinderPinRecord.members.Count == 0) { _data.pins.RemoveAt(num2); } else if (flag3) { RecalculateCluster(wayfinderPinRecord); } } } } if (flag) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveAutoResourceSubtype(string subtype) { if (string.IsNullOrEmpty(subtype)) { return 0; } int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Resource && string.Equals(wayfinderPinRecord.subtype, subtype, StringComparison.OrdinalIgnoreCase) && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported) { _data.pins.RemoveAt(num2); num++; } } if (num > 0) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveAutoResourceSubtypeBySource(string subtype, WayfinderPinSource source) { if (string.IsNullOrEmpty(subtype)) { return 0; } int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Resource && string.Equals(wayfinderPinRecord.subtype, subtype, StringComparison.OrdinalIgnoreCase) && wayfinderPinRecord.source == source) { _data.pins.RemoveAt(num2); num++; } } if (num > 0) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveAutoPointSubtypesContaining(params string[] normalizedNeedles) { if (normalizedNeedles == null || normalizedNeedles.Length == 0) { return 0; } int num = 0; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && (wayfinderPinRecord.category == WayfinderPinCategory.PointOfInterest || wayfinderPinRecord.category == WayfinderPinCategory.Dungeon) && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported && wayfinderPinRecord.source != WayfinderPinSource.Vanilla) { string text = NormalizeKey(wayfinderPinRecord.subtype); bool flag = false; for (int i = 0; i < normalizedNeedles.Length; i++) { string value = NormalizeKey(normalizedNeedles[i]); if (!string.IsNullOrEmpty(value) && text.Contains(value)) { flag = true; break; } } if (flag) { _data.pins.RemoveAt(num2); num++; } } } if (num > 0) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveAutoResourceSubtypeAboveY(string subtype, float yThreshold) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(subtype)) { return 0; } int num = 0; bool flag = false; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Resource && string.Equals(wayfinderPinRecord.subtype, subtype, StringComparison.OrdinalIgnoreCase) && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported) { if (wayfinderPinRecord.members == null || wayfinderPinRecord.members.Count == 0) { if (wayfinderPinRecord.Position.y > yThreshold) { _data.pins.RemoveAt(num2); num++; flag = true; } } else { bool flag2 = false; for (int num3 = wayfinderPinRecord.members.Count - 1; num3 >= 0; num3--) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[num3]; if (wayfinderClusterMember != null && wayfinderClusterMember.Position.y > yThreshold) { wayfinderPinRecord.members.RemoveAt(num3); num++; flag2 = true; flag = true; } } if (wayfinderPinRecord.members.Count == 0) { _data.pins.RemoveAt(num2); } else if (flag2) { RecalculateCluster(wayfinderPinRecord); } } } } if (flag) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal int RemoveAutoDungeonResources(float interiorYThreshold) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) int num = 0; bool flag = false; for (int num2 = _data.pins.Count - 1; num2 >= 0; num2--) { WayfinderPinRecord wayfinderPinRecord = _data.pins[num2]; if (wayfinderPinRecord != null && wayfinderPinRecord.category == WayfinderPinCategory.Resource && wayfinderPinRecord.source != WayfinderPinSource.Manual && wayfinderPinRecord.source != WayfinderPinSource.Imported) { if (wayfinderPinRecord.members == null || wayfinderPinRecord.members.Count == 0) { if (wayfinderPinRecord.Position.y > interiorYThreshold) { _data.pins.RemoveAt(num2); num++; flag = true; } } else { bool flag2 = false; for (int num3 = wayfinderPinRecord.members.Count - 1; num3 >= 0; num3--) { WayfinderClusterMember wayfinderClusterMember = wayfinderPinRecord.members[num3]; if (wayfinderClusterMember != null && wayfinderClusterMember.Position.y > interiorYThreshold) { wayfinderPinRecord.members.RemoveAt(num3); num++; flag2 = true; flag = true; } } if (wayfinderPinRecord.members.Count == 0) { _data.pins.RemoveAt(num2); } else if (flag2) { RecalculateCluster(wayfinderPinRecord); } } } } if (flag) { _dirty = true; Revision++; ScheduleSave(); } return num; } internal bool IsSuppressed(string memberKey) { if (_config == null || _config.SuppressRemovedAutoPins == null || !_config.SuppressRemovedAutoPins.Value) { return false; } if (string.IsNullOrEmpty(memberKey)) { return false; } return _suppressedKeyCache.Contains(memberKey); } private void RepairResourceMetadata(WayfinderPinRecord record, string displayName, string iconKey, WayfinderPinSource source) { if (record != null)