Decompiled source of AutoPOIMarker v1.7.1

plugins/AutoPOIMarker.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyTitle("Auto POI Marker - Valheim point-of-interest discovery")]
[assembly: AssemblyDescription("Client-side Valheim mod that announces nearby dungeons and marks configurable points of interest on the map.")]
[assembly: AssemblyCompany("Rollstuhltreter")]
[assembly: AssemblyProduct("Auto POI Marker")]
[assembly: AssemblyCopyright("Copyright (c) Rollstuhltreter")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.7.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("kann.valheim.autopoimarker", "Auto POI Marker", "1.7.1")]
public sealed class AutoPOIMarkerPlugin : BaseUnityPlugin
{
	private sealed class CachedLocation
	{
		internal object ZoneLocation;

		internal string SourceName;

		internal string PrefabName;

		internal Vector3 Position;

		internal bool IsDungeon;

		internal int Biome;
	}

	[HarmonyPatch(typeof(ResourceRoot), "Awake")]
	private static class ResourceRootAwakePatch
	{
		private static void Postfix(ResourceRoot __instance)
		{
			Instance?.RegisterYggdrasilRoot(__instance);
		}
	}

	[HarmonyPatch(typeof(ZNetScene), "CreateObject", new Type[] { typeof(ZDO) })]
	private static class ZNetSceneCreateObjectPatch
	{
		private static void Postfix(GameObject __result)
		{
			Instance?.RegisterYggdrasilRoot(__result);
		}
	}

	[HarmonyPatch(typeof(Location), "Awake")]
	private static class LocationAwakePatch
	{
		private static void Postfix(Location __instance)
		{
			Instance?.RegisterNetworkLocation(__instance);
		}
	}

	[HarmonyPatch(typeof(LocationProxy), "SpawnLocation")]
	private static class LocationProxySpawnPatch
	{
		private static void Postfix(LocationProxy __instance)
		{
			Instance?.RegisterLocationProxy(__instance);
		}
	}

	[HarmonyPatch(typeof(ZoneSystem), "CreateLocationProxy")]
	private static class CreateLocationProxyPatch
	{
		private static void Postfix(ZoneLocation __0, Vector3 __2)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Instance?.RegisterZoneLocation(__0, __2);
		}
	}

	[HarmonyPatch(typeof(ZoneSystem), "SpawnLocation")]
	private static class SpawnLocationPatch
	{
		private static void Postfix(ZoneLocation __0, Vector3 __2)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Instance?.RegisterZoneLocation(__0, __2);
		}
	}

	public const string PluginGuid = "kann.valheim.autopoimarker";

	public const string PluginName = "Auto POI Marker";

	public const string PluginVersion = "1.7.1";

	internal static ConfigEntry<bool> Enable;

	internal static ConfigEntry<float> DetectionRadius;

	internal static ConfigEntry<bool> MarkDungeons;

	internal static ConfigEntry<string> DungeonNameFilter;

	internal static ConfigEntry<bool> MarkMeadowsAndBlackForestPointsOfInterest;

	internal static ConfigEntry<bool> MarkSwampPointsOfInterest;

	internal static ConfigEntry<bool> MarkSnowPointsOfInterest;

	internal static ConfigEntry<bool> MarkPlainsPointsOfInterest;

	internal static ConfigEntry<bool> MarkAshlandsPointsOfInterest;

	internal static ConfigEntry<bool> MarkMistlandsPointsOfInterest;

	internal static ConfigEntry<bool> MarkDeepNorthPointsOfInterest;

	internal static ConfigEntry<bool> MarkProgressionLandmarks;

	internal static ConfigEntry<string> LargePoiNameFilter;

	internal static ConfigEntry<string> CustomPoiNameFilter;

	internal static ConfigEntry<bool> MarkYggdrasilRoots;

	internal static ConfigEntry<bool> ShowNotification;

	internal static ConfigEntry<bool> MarkOnMap;

	internal static ConfigEntry<bool> EnableDebugLogging;

	private static readonly BindingFlags InstanceNonPublic = BindingFlags.Instance | BindingFlags.NonPublic;

	private static readonly BindingFlags InstanceAny = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private static readonly FieldInfo LocationField = FindNestedField("LocationInstance", "m_location");

	private static readonly FieldInfo PositionField = FindNestedField("LocationInstance", "m_position");

	private static readonly FieldInfo NameField = typeof(ZoneLocation).GetField("m_name", InstanceAny);

	private static readonly FieldInfo PrefabNameField = typeof(ZoneLocation).GetField("m_prefabName", InstanceAny);

	private static readonly FieldInfo BiomeField = typeof(ZoneLocation).GetField("m_biome", InstanceAny);

	private static readonly FieldInfo LocationBiomeField = typeof(Location).GetField("m_biome", InstanceAny);

	private static readonly FieldInfo LocationProxyInstanceField = typeof(LocationProxy).GetField("m_instance", InstanceAny);

	private static readonly MethodInfo HaveSimilarPinMethod = typeof(Minimap).GetMethod("HaveSimilarPin", InstanceNonPublic);

	private const string DefaultProgressionPoiFilter = "Eikthyr,GDKing,Bonemass,Dragonqueen,GoblinKing,Mistlands_Queen,FaderLocation,FimbulLocation,FireHole,Surtling,DrakeNest,TarPit,GoblinCamp,Mistlands_Giant,Mistlands_Excavation,Mistlands_Dvergr,Dvergr,CharredFortress,PlaceofMystery,SulfurArch,VoltureNest,Mistlands_Harbour,Mistlands_Lighthouse,Mistlands_StatueGroup,Mistlands_Swords,Mistlands_Viaduct,NorthVillage,MorkBorg,LumberCamp,FrozenShip,NorthMemorialPlace,HotSpring,IcePond";

	private const float LocationCellSize = 128f;

	private const float RescanDistance = 16f;

	private const int IndexEntriesPerFrame = 50;

	private readonly HashSet<string> _announcedLocations = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<string> _markedLocations = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<ResourceRoot> _knownYggdrasilRoots = new HashSet<ResourceRoot>();

	private readonly HashSet<string> _cachedLocationKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private readonly List<CachedLocation> _pendingNetworkLocations = new List<CachedLocation>();

	private Dictionary<Vector2Int, List<CachedLocation>> _locationBuckets;

	private ZoneSystem _indexedZoneSystem;

	private IEnumerator<LocationInstance> _locationIndexEnumerator;

	private bool _locationIndexStarted;

	private bool _locationIndexReady;

	private bool _hasLastScanPosition;

	private bool _scanRequested;

	private Vector3 _lastScanPosition;

	private Harmony _harmony;

	private int _indexedLocationCount;

	private int _networkLocationCount;

	private int _rootRegistrationCount;

	private static AutoPOIMarkerPlugin Instance;

	private void Awake()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		Instance = this;
		_harmony = new Harmony("kann.valheim.autopoimarker");
		_harmony.PatchAll();
		Enable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable nearby dungeon and point-of-interest discovery.");
		DetectionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DetectionRadius", 50f, "World-space radius in meters used to discover a location.");
		MarkDungeons = ((BaseUnityPlugin)this).Config.Bind<bool>("Dungeons", "MarkDungeons", true, "Announce and mark dungeon-type locations.");
		DungeonNameFilter = ((BaseUnityPlugin)this).Config.Bind<string>("Dungeons", "DungeonNameFilter", "Crypt,Cave,Dungeon,Mine,Catacomb,Tomb,Burial,Sunken,Infested,MorgenHole,TheDarkestHole,TheHole,Hildir_plainsfortress", "Comma-separated dungeon name fragments to mark. The default list covers every dungeon type recognised by this mod, including Black Forest Crypts and Swamp Sunken Crypts. Use * to mark every recognised dungeon.");
		MarkMeadowsAndBlackForestPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkMeadowsAndBlackForest", true, "Mark selected large non-dungeon points of interest in Meadows and Black Forest.");
		MarkSwampPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkSwamp", true, "Mark selected large non-dungeon points of interest in Swamp.");
		MarkSnowPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkSnow", true, "Mark non-dungeon points of interest in the snowy Mountain biome.");
		MarkPlainsPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkPlains", true, "Mark non-dungeon points of interest in Plains.");
		MarkMistlandsPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkMistlands", true, "Mark non-dungeon points of interest in Mistlands.");
		MarkAshlandsPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkAshlands", true, "Mark non-dungeon points of interest in Ashlands.");
		MarkDeepNorthPointsOfInterest = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkDeepNorth", true, "Mark non-dungeon points of interest in Deep North.");
		MarkProgressionLandmarks = ((BaseUnityPlugin)this).Config.Bind<bool>("PointsOfInterest", "MarkProgressionLandmarks", true, "Always mark the built-in progression landmark set for enabled biomes. This also updates existing configs when new landmarks are added.");
		LargePoiNameFilter = ((BaseUnityPlugin)this).Config.Bind<string>("PointsOfInterest", "LargePoiNameFilter", "Eikthyr,GDKing,Bonemass,Dragonqueen,GoblinKing,Mistlands_Queen,FaderLocation,FimbulLocation,FireHole,Surtling,DrakeNest,TarPit,GoblinCamp,Mistlands_Giant,Mistlands_Excavation,Mistlands_Dvergr,Dvergr,CharredFortress,PlaceofMystery,SulfurArch,VoltureNest,Mistlands_Harbour,Mistlands_Lighthouse,Mistlands_StatueGroup,Mistlands_Swords,Mistlands_Viaduct,NorthVillage,MorkBorg,LumberCamp,FrozenShip,NorthMemorialPlace,HotSpring,IcePond", "Comma-separated name fragments for progression-relevant non-dungeon points of interest. The default list includes boss altars, surtling spawns, key resource sites, Dvergr structures, Mistlands giant remains, plains camps and late-game landmarks. Use * only if you deliberately want every POI in an enabled biome.");
		CustomPoiNameFilter = ((BaseUnityPlugin)this).Config.Bind<string>("PointsOfInterest", "CustomPoiNameFilter", string.Empty, "Optional comma-separated location name fragments to mark regardless of biome. Use this for server or modded POIs not covered by the built-in list.");
		MarkYggdrasilRoots = ((BaseUnityPlugin)this).Config.Bind<bool>("Resources", "MarkYggdrasilRoots", true, "Mark nearby Mistlands Ancient Roots (Yggdrasil Roots), which provide Sap for magic progression.");
		ShowNotification = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowNotification", true, "Show a large centered notification when a configured location is discovered.");
		MarkOnMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "MarkOnMap", true, "Add the discovered location to the map using Valheim's hammer-style icon.");
		EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", false, "Write detailed local POI discovery diagnostics to BepInEx/LogOutput.log. Disable this after testing.");
		DebugLog("Plugin initialized. Waiting for a world and player.");
	}

	private void OnDestroy()
	{
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	private void DebugLog(string message)
	{
		if (EnableDebugLogging != null && EnableDebugLogging.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[POI Debug] " + message));
		}
	}

	private void Update()
	{
		//IL_0043: 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_006e: 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_0095: 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_0059: 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 (Enable == null || !Enable.Value)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		ZoneSystem instance = ZoneSystem.instance;
		if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance == (Object)null) && BuildLocationIndexStep(instance))
		{
			Vector3 position = ((Component)localPlayer).transform.position;
			if (_scanRequested || !_hasLastScanPosition || !(HorizontalDistanceSquared(position, _lastScanPosition) < 256f))
			{
				_lastScanPosition = position;
				_hasLastScanPosition = true;
				_scanRequested = false;
				float num = SanitizeRadius(DetectionRadius.Value);
				float radiusSquared = num * num;
				ScanNearbyLocations(position, radiusSquared);
				ScanKnownYggdrasilRoots(position, radiusSquared);
			}
		}
	}

	private bool BuildLocationIndexStep(ZoneSystem zoneSystem)
	{
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_indexedZoneSystem != (Object)(object)zoneSystem || _locationBuckets == null)
		{
			_indexedZoneSystem = zoneSystem;
			_locationBuckets = new Dictionary<Vector2Int, List<CachedLocation>>();
			_cachedLocationKeys.Clear();
			_indexedLocationCount = 0;
			foreach (CachedLocation pendingNetworkLocation in _pendingNetworkLocations)
			{
				AddCachedLocation(pendingNetworkLocation);
			}
			_pendingNetworkLocations.Clear();
			_locationIndexEnumerator = null;
			_locationIndexStarted = false;
			_locationIndexReady = false;
			_hasLastScanPosition = false;
			_scanRequested = true;
		}
		if (_locationIndexReady)
		{
			return true;
		}
		if (!zoneSystem.LocationsGenerated)
		{
			return true;
		}
		if (!_locationIndexStarted)
		{
			Dictionary<Vector2s, LocationInstance>.ValueCollection locationList = zoneSystem.GetLocationList();
			if (locationList == null)
			{
				DebugLog("World location list is unavailable. Continuing with server-loaded locations.");
				return true;
			}
			_locationIndexEnumerator = locationList.GetEnumerator();
			_locationIndexStarted = true;
			DebugLog("Starting location index build.");
		}
		int i;
		for (i = 0; i < 50; i++)
		{
			if (!_locationIndexEnumerator.MoveNext())
			{
				break;
			}
			AddLocationToIndex(_locationIndexEnumerator.Current);
		}
		if (i == 50)
		{
			return false;
		}
		_locationIndexEnumerator.Dispose();
		_locationIndexEnumerator = null;
		_locationIndexReady = true;
		DebugLog(string.Format(CultureInfo.InvariantCulture, "Location index ready. locations={0}, cells={1}, networkLocations={2}, roots={3}.", _indexedLocationCount, _locationBuckets.Count, _networkLocationCount, _rootRegistrationCount));
		return true;
	}

	private void AddLocationToIndex(LocationInstance instance)
	{
		//IL_0012: 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_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_00b2: 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)
		object obj = ((LocationField == null) ? null : LocationField.GetValue(instance));
		if (obj != null && ((PositionField == null) ? null : PositionField.GetValue(instance)) is Vector3 position)
		{
			string text = ReadString(obj, NameField);
			string text2 = ReadString(obj, PrefabNameField);
			string text3 = (string.IsNullOrWhiteSpace(text) ? text2 : text);
			if (!string.IsNullOrWhiteSpace(text3) && !IsExcludedLocation(text3, text2))
			{
				CachedLocation location = new CachedLocation
				{
					ZoneLocation = obj,
					SourceName = text3,
					PrefabName = text2,
					Position = position,
					IsDungeon = IsDungeon(text3, text2),
					Biome = ReadBiome(obj)
				};
				AddCachedLocation(location);
			}
		}
	}

	private void AddCachedLocation(CachedLocation location)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: 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)
		string item = BuildLocationKey(location.SourceName, location.Position);
		if (_cachedLocationKeys.Add(item))
		{
			Vector2Int locationCell = GetLocationCell(location.Position);
			if (!_locationBuckets.TryGetValue(locationCell, out var value))
			{
				value = new List<CachedLocation>();
				_locationBuckets.Add(locationCell, value);
			}
			value.Add(location);
			_indexedLocationCount++;
		}
	}

	private void ScanNearbyLocations(Vector3 playerPosition, float radiusSquared)
	{
		//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_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: 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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		int num = Mathf.CeilToInt(SanitizeRadius(DetectionRadius.Value) / 128f);
		Vector2Int locationCell = GetLocationCell(playerPosition);
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		for (int i = ((Vector2Int)(ref locationCell)).x - num; i <= ((Vector2Int)(ref locationCell)).x + num; i++)
		{
			for (int j = ((Vector2Int)(ref locationCell)).y - num; j <= ((Vector2Int)(ref locationCell)).y + num; j++)
			{
				if (!_locationBuckets.TryGetValue(new Vector2Int(i, j), out var value))
				{
					continue;
				}
				foreach (CachedLocation item2 in value)
				{
					num2++;
					float num6 = playerPosition.x - item2.Position.x;
					float num7 = playerPosition.z - item2.Position.z;
					if (num6 * num6 + num7 * num7 > radiusSquared)
					{
						continue;
					}
					num3++;
					if (!ShouldTrack(item2))
					{
						continue;
					}
					num4++;
					string item = BuildLocationKey(item2.SourceName, item2.Position);
					bool num8 = !ShowNotification.Value || _announcedLocations.Contains(item);
					bool flag = !MarkOnMap.Value || _markedLocations.Contains(item);
					if (!(num8 && flag))
					{
						string text = MakeShortName(item2.SourceName);
						if (MarkOnMap.Value && !_markedLocations.Contains(item) && (Object)(object)Minimap.instance != (Object)null)
						{
							AddHammerMarker(item2.Position, text);
							_markedLocations.Add(item);
							num5++;
						}
						if (ShowNotification.Value && !_announcedLocations.Contains(item) && (Object)(object)MessageHud.instance != (Object)null)
						{
							MessageHud.instance.ShowMessage((MessageType)2, text + " found!", 0, (Sprite)null, false, false);
							_announcedLocations.Add(item);
						}
					}
				}
			}
		}
		DebugLog(string.Format(CultureInfo.InvariantCulture, "Scan at ({0:0}, {1:0}): candidates={2}, nearby={3}, accepted={4}, pinsAdded={5}.", playerPosition.x, playerPosition.z, num2, num3, num4, num5));
	}

	private void ScanKnownYggdrasilRoots(Vector3 playerPosition, float radiusSquared)
	{
		//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_00a2: 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)
		//IL_00b0: 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_00d7: 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)
		if (MarkYggdrasilRoots == null || !MarkYggdrasilRoots.Value)
		{
			return;
		}
		_knownYggdrasilRoots.RemoveWhere((ResourceRoot root) => (Object)(object)root == (Object)null);
		int num = 0;
		int num2 = 0;
		foreach (ResourceRoot knownYggdrasilRoot in _knownYggdrasilRoots)
		{
			if ((Object)(object)knownYggdrasilRoot == (Object)null || (Object)(object)((Component)knownYggdrasilRoot).gameObject == (Object)null || ((Object)((Component)knownYggdrasilRoot).gameObject).name.IndexOf("YggdrasilRoot", StringComparison.OrdinalIgnoreCase) < 0)
			{
				continue;
			}
			Vector3 position = ((Component)knownYggdrasilRoot).transform.position;
			float num3 = playerPosition.x - position.x;
			float num4 = playerPosition.z - position.z;
			if (!(num3 * num3 + num4 * num4 > radiusSquared))
			{
				num++;
				string item = BuildLocationKey("YggdrasilRoot", position);
				if (MarkOnMap.Value && !_markedLocations.Contains(item) && (Object)(object)Minimap.instance != (Object)null)
				{
					AddHammerMarker(position, "Ancient Root");
					_markedLocations.Add(item);
					num2++;
				}
				if (ShowNotification.Value && !_announcedLocations.Contains(item) && (Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "Ancient Root found!", 0, (Sprite)null, false, false);
					_announcedLocations.Add(item);
				}
			}
		}
		DebugLog(string.Format(CultureInfo.InvariantCulture, "Root scan: registered={0}, nearby={1}, pinsAdded={2}.", _knownYggdrasilRoots.Count, num, num2));
	}

	private void RegisterYggdrasilRoot(ResourceRoot root)
	{
		//IL_004f: 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)root != (Object)null && ((Object)((Component)root).gameObject).name.IndexOf("YggdrasilRoot", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			if (_knownYggdrasilRoots.Add(root))
			{
				_rootRegistrationCount++;
				DebugLog(string.Format(CultureInfo.InvariantCulture, "Registered Ancient Root at ({0:0}, {1:0}).", ((Component)root).transform.position.x, ((Component)root).transform.position.z));
			}
			RequestScan();
		}
	}

	private void RegisterYggdrasilRoot(GameObject gameObject)
	{
		if (!((Object)(object)gameObject == (Object)null))
		{
			RegisterYggdrasilRoot(gameObject.GetComponentInChildren<ResourceRoot>(true));
		}
	}

	private void RegisterNetworkLocation(Location location)
	{
		//IL_0035: 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))
		{
			RegisterLoadedLocation(((Object)((Component)location).gameObject).name, ((Object)((Component)location).gameObject).name, ((Component)location).transform.position, ReadBiome(location, LocationBiomeField));
		}
	}

	private void RegisterZoneLocation(ZoneLocation zoneLocation, Vector3 position)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		if (zoneLocation != null)
		{
			string sourceName = ReadString(zoneLocation, NameField);
			string prefabName = ReadString(zoneLocation, PrefabNameField);
			RegisterLoadedLocation(sourceName, prefabName, position, ReadBiome(zoneLocation));
		}
	}

	private void RegisterLocationProxy(LocationProxy proxy)
	{
		if (!((Object)(object)proxy == (Object)null) && !(LocationProxyInstanceField == null))
		{
			object? value = LocationProxyInstanceField.GetValue(proxy);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				DebugLog("Location proxy spawned without an instantiated location object.");
			}
			else
			{
				RegisterNetworkLocation(val.GetComponentInChildren<Location>(true));
			}
		}
	}

	private void RegisterLoadedLocation(string sourceName, string prefabName, Vector3 position, int biome)
	{
		//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_0076: 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)
		if (string.IsNullOrWhiteSpace(sourceName))
		{
			sourceName = prefabName;
		}
		if (!string.IsNullOrWhiteSpace(sourceName) && !IsExcludedLocation(sourceName, prefabName))
		{
			CachedLocation cachedLocation = new CachedLocation
			{
				SourceName = sourceName,
				PrefabName = prefabName,
				Position = position,
				IsDungeon = IsDungeon(sourceName, prefabName),
				Biome = biome
			};
			_networkLocationCount++;
			DebugLog(string.Format(CultureInfo.InvariantCulture, "Registered server location '{0}' / '{1}' at ({2:0}, {3:0}), biome={4}, dungeon={5}, accepted={6}.", sourceName, prefabName, position.x, position.z, biome, cachedLocation.IsDungeon, ShouldTrack(cachedLocation)));
			if (_locationBuckets == null)
			{
				_pendingNetworkLocations.Add(cachedLocation);
				RequestScan();
			}
			else
			{
				AddCachedLocation(cachedLocation);
				RequestScan();
			}
		}
	}

	private void RequestScan()
	{
		_scanRequested = true;
	}

	private static Vector2Int GetLocationCell(Vector3 position)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		return new Vector2Int(Mathf.FloorToInt(position.x / 128f), Mathf.FloorToInt(position.z / 128f));
	}

	private static float HorizontalDistanceSquared(Vector3 first, Vector3 second)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		float num = first.x - second.x;
		float num2 = first.z - second.z;
		return num * num + num2 * num2;
	}

	private static void AddHammerMarker(Vector3 position, string shortName)
	{
		//IL_004c: 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_005b: 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)
		Minimap instance = Minimap.instance;
		if (!(HaveSimilarPinMethod != null) || !(bool)HaveSimilarPinMethod.Invoke(instance, new object[4]
		{
			position,
			(object)(PinType)2,
			shortName,
			true
		}))
		{
			instance.AddPin(position, (PinType)2, shortName, true, false, 0L, default(PlatformUserID));
		}
	}

	private static bool ShouldTrack(CachedLocation location)
	{
		if (location.IsDungeon)
		{
			if (MarkDungeons.Value)
			{
				return MatchesDungeonFilter(location.SourceName, location.PrefabName);
			}
			return false;
		}
		if (MatchesCustomPoiFilter(location.SourceName, location.PrefabName))
		{
			return true;
		}
		if (!IsBiomeEnabled(location.Biome))
		{
			return false;
		}
		if (MarkProgressionLandmarks == null || !MarkProgressionLandmarks.Value || !IsProgressionLandmark(location.SourceName, location.PrefabName))
		{
			return MatchesLargePoiFilter(location.SourceName, location.PrefabName);
		}
		return true;
	}

	private static bool IsBiomeEnabled(int biome)
	{
		switch (biome)
		{
		case 16:
			return MarkPlainsPointsOfInterest.Value;
		case 32:
			return MarkAshlandsPointsOfInterest.Value;
		case 512:
			return MarkMistlandsPointsOfInterest.Value;
		case 64:
			return MarkDeepNorthPointsOfInterest.Value;
		case 4:
			return MarkSnowPointsOfInterest.Value;
		case 1:
		case 8:
			return MarkMeadowsAndBlackForestPointsOfInterest.Value;
		case 2:
			return MarkSwampPointsOfInterest.Value;
		default:
			return false;
		}
	}

	private static bool MatchesDungeonFilter(string sourceName, string prefabName)
	{
		string value = DungeonNameFilter.Value;
		if (string.IsNullOrWhiteSpace(value) || value.Trim() == "*")
		{
			return true;
		}
		string text = sourceName + " " + prefabName;
		string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
		foreach (string text2 in array)
		{
			if (text.IndexOf(text2.Trim(), StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool MatchesLargePoiFilter(string sourceName, string prefabName)
	{
		string value = LargePoiNameFilter.Value;
		if (string.IsNullOrWhiteSpace(value))
		{
			return false;
		}
		if (value.Trim() == "*")
		{
			return true;
		}
		string text = sourceName + " " + prefabName;
		string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
		foreach (string text2 in array)
		{
			if (text.IndexOf(text2.Trim(), StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool MatchesCustomPoiFilter(string sourceName, string prefabName)
	{
		string value = CustomPoiNameFilter.Value;
		if (string.IsNullOrWhiteSpace(value))
		{
			return false;
		}
		string text = sourceName + " " + prefabName;
		string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
		foreach (string text2 in array)
		{
			if (text.IndexOf(text2.Trim(), StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsProgressionLandmark(string sourceName, string prefabName)
	{
		string text = sourceName + " " + prefabName;
		string[] array = "Eikthyr,GDKing,Bonemass,Dragonqueen,GoblinKing,Mistlands_Queen,FaderLocation,FimbulLocation,FireHole,Surtling,DrakeNest,TarPit,GoblinCamp,Mistlands_Giant,Mistlands_Excavation,Mistlands_Dvergr,Dvergr,CharredFortress,PlaceofMystery,SulfurArch,VoltureNest,Mistlands_Harbour,Mistlands_Lighthouse,Mistlands_StatueGroup,Mistlands_Swords,Mistlands_Viaduct,NorthVillage,MorkBorg,LumberCamp,FrozenShip,NorthMemorialPlace,HotSpring,IcePond".Split(new char[1] { ',' });
		foreach (string value in array)
		{
			if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsDungeon(string name, string prefabName)
	{
		string text = (name + " " + prefabName).ToLowerInvariant();
		string[] array = new string[13]
		{
			"crypt", "cave", "dungeon", "mine", "catacomb", "tomb", "burial", "sunken", "infested", "morgenhole",
			"thedarkesthole", "thehole", "hildir_plainsfortress"
		};
		foreach (string value in array)
		{
			if (text.Contains(value))
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsExcludedLocation(string name, string prefabName)
	{
		return (name + " " + prefabName).IndexOf("InfestedTree", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string MakeShortName(string rawName)
	{
		string text = rawName;
		int num = Math.Max(text.LastIndexOf('/'), text.LastIndexOf('\\'));
		if (num >= 0 && num + 1 < text.Length)
		{
			text = text.Substring(num + 1);
		}
		text = Regex.Replace(text, "\\s*\\(Clone\\)\\s*$", string.Empty, RegexOptions.IgnoreCase);
		text = Regex.Replace(text, "(?:[_\\-\\s]*\\d+)+\\s*$", string.Empty);
		text = Regex.Replace(text, "(?<!^)([A-Z])", " $1");
		text = Regex.Replace(text, "[_-]+", " ");
		text = Regex.Replace(text, "\\s+", " ").Trim();
		if (text.Length > 28)
		{
			text = text.Substring(0, 28).TrimEnd(Array.Empty<char>());
		}
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text;
		}
		return "Unknown location";
	}

	private static string BuildLocationKey(string name, Vector3 position)
	{
		//IL_0010: 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)
		return string.Format(CultureInfo.InvariantCulture, "{0}|{1:0}|{2:0}", MakeShortName(name), position.x, position.z);
	}

	private static int ReadBiome(object zoneLocation)
	{
		return ReadBiome(zoneLocation, BiomeField);
	}

	private static int ReadBiome(object instance, FieldInfo field)
	{
		if (instance == null || field == null)
		{
			return 0;
		}
		object value = field.GetValue(instance);
		if (value != null)
		{
			return Convert.ToInt32(value, CultureInfo.InvariantCulture);
		}
		return 0;
	}

	private static string ReadString(object instance, FieldInfo field)
	{
		object obj;
		if (!(field == null))
		{
			obj = field.GetValue(instance) as string;
			if (obj == null)
			{
				return string.Empty;
			}
		}
		else
		{
			obj = string.Empty;
		}
		return (string)obj;
	}

	private static FieldInfo FindNestedField(string nestedTypeName, string fieldName)
	{
		Type nestedType = typeof(ZoneSystem).GetNestedType(nestedTypeName, BindingFlags.Public | BindingFlags.NonPublic);
		if (!(nestedType == null))
		{
			return nestedType.GetField(fieldName, InstanceAny);
		}
		return null;
	}

	private static float SanitizeRadius(float value)
	{
		if (float.IsNaN(value) || float.IsInfinity(value))
		{
			return 100f;
		}
		return Mathf.Clamp(value, 1f, 1000f);
	}
}