Decompiled source of Pin It AutomaticMapPins v1.12.2

PinIt.dll

Decompiled a week ago
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.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PinIt
{
	internal static class ConfigCleanup
	{
		private static readonly Dictionary<string, string> SectionNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "0 - Map", "Map" },
			{ "1 - General", "Forageables" },
			{ "2 - Pins", "Forageables" },
			{ "1b - Forageable Items", "Forageable Items" },
			{ "3 - Dungeons", "Dungeons" },
			{ "3b - Dungeon Items", "Dungeon Items" },
			{ "4 - Ores", "Ores" },
			{ "4b - Ore Items", "Ore Items" },
			{ "5 - Extras", "Extras" },
			{ "5b - Extra Items", "Extra Items" },
			{ "6 - Extra Ranges", "Extra Ranges" },
			{ "9 - Debug", "Debug" }
		};

		private static readonly Dictionary<string, string> RenamedMapSettings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "ForageablePinColor", "ForageableTextColor" },
			{ "DungeonPinColor", "DungeonTextColor" },
			{ "OrePinColor", "OreTextColor" },
			{ "ExtraPinColor", "ExtraTextColor" }
		};

		private static readonly HashSet<string> ItemSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Forageable Items", "Dungeon Items", "Ore Items", "Extra Items" };

		private static readonly string[] ExcludedNames = new string[5] { "MushroomBlue", "Blue Mushroom", "Blue Mushrooms", "RoyalJelly", "Royal Jelly" };

		internal static void Run(ConfigFile config, Action<string> logInfo, Action<string> logWarning)
		{
			try
			{
				string configFilePath = config.ConfigFilePath;
				if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(configFilePath);
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
				string text = null;
				string[] array2 = array;
				foreach (string text2 in array2)
				{
					string text3 = text2.Trim();
					if (text3.StartsWith("[") && text3.EndsWith("]"))
					{
						string text4 = text3.Substring(1, text3.Length - 2);
						text = (SectionNames.TryGetValue(text4, out var value) ? value : text4);
						if (!dictionary.ContainsKey(text))
						{
							dictionary[text] = new List<string>();
							list2.Add(text);
						}
					}
					else if (text == null)
					{
						list.Add(text2);
					}
					else
					{
						dictionary[text].Add(text2);
					}
				}
				bool flag = false;
				foreach (string item in new List<string>(list2))
				{
					List<string> list3 = dictionary[item];
					List<string> list4 = new List<string>();
					foreach (string item2 in list3)
					{
						string text5 = item2;
						string line = text5.Trim();
						if (TryGetEntry(line, out var key, out var value2))
						{
							if (item.Equals("Forageables", StringComparison.OrdinalIgnoreCase) && key.Equals("AllowedItems", StringComparison.OrdinalIgnoreCase))
							{
								string text6 = CleanAllowedItems(value2);
								if (!text6.Equals(value2.Trim(), StringComparison.Ordinal))
								{
									flag = true;
								}
								text5 = key + " = " + text6;
							}
							if (item.Equals("Ores", StringComparison.OrdinalIgnoreCase) && key.Equals("OreTypes", StringComparison.OrdinalIgnoreCase))
							{
								string text7 = CleanOreTypes(value2);
								if (!text7.Equals(value2.Trim(), StringComparison.Ordinal))
								{
									flag = true;
								}
								text5 = key + " = " + text7;
							}
							if (item.Equals("Extras", StringComparison.OrdinalIgnoreCase) && key.Equals("ExtraTypes", StringComparison.OrdinalIgnoreCase))
							{
								string text8 = MigrateExtraTypes(value2);
								if (!text8.Equals(value2.Trim(), StringComparison.Ordinal))
								{
									flag = true;
								}
								text5 = key + " = " + text8;
							}
							if (item.Equals("Map", StringComparison.OrdinalIgnoreCase) && RenamedMapSettings.TryGetValue(key, out var value3))
							{
								RemoveEntryComments(list4);
								text5 = value3 + " = " + value2;
								flag = true;
							}
							if (ItemSections.Contains(item) && (IsExcludedItemKey(key) || key.EndsWith(" - Delete all pins", StringComparison.OrdinalIgnoreCase)))
							{
								RemoveEntryComments(list4);
								flag = true;
								continue;
							}
						}
						list4.Add(text5);
					}
					CollapseBlankLines(list4);
					dictionary[item] = list4;
				}
				foreach (string oldName in SectionNames.Keys)
				{
					if (Array.Exists(array, (string l) => l.Trim().Equals("[" + oldName + "]", StringComparison.OrdinalIgnoreCase)))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return;
				}
				List<string> list5 = new List<string>();
				list5.AddRange(list);
				CollapseBlankLines(list5);
				foreach (string item3 in list2)
				{
					List<string> list6 = dictionary[item3];
					if (ContainsSetting(list6))
					{
						if (list5.Count > 0 && list5[list5.Count - 1].Length != 0)
						{
							list5.Add("");
						}
						list5.Add("[" + item3 + "]");
						list5.AddRange(list6);
						CollapseBlankLines(list5);
					}
				}
				File.WriteAllLines(configFilePath, list5.ToArray());
				config.Reload();
				logInfo?.Invoke("Organized the Pin It! config and moved destructive controls to [Cleanup].");
			}
			catch (Exception ex)
			{
				logWarning?.Invoke("Could not organize the Pin It! config: " + ex.Message);
			}
		}

		private static bool TryGetEntry(string line, out string key, out string value)
		{
			key = null;
			value = null;
			if (line.Length == 0 || line.StartsWith("#"))
			{
				return false;
			}
			int num = line.IndexOf('=');
			if (num <= 0)
			{
				return false;
			}
			key = line.Substring(0, num).Trim();
			value = line.Substring(num + 1).Trim();
			return true;
		}

		private static string CleanAllowedItems(string value)
		{
			List<string> list = new List<string>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = value.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length != 0 && !IsExcludedItem(text2) && hashSet.Add(text2))
				{
					list.Add(text2);
				}
			}
			return string.Join(",", list.ToArray());
		}

		private static string CleanOreTypes(string value)
		{
			List<string> list = new List<string>();
			bool flag = false;
			string[] array = value.Split(new char[1] { ';' });
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length == 0)
				{
					continue;
				}
				string text3 = text2.Split(new char[1] { ':' })[0].Trim();
				if (!text3.StartsWith("giant_skull", StringComparison.OrdinalIgnoreCase))
				{
					if (text3.StartsWith("giant_ribs", StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
					}
					list.Add(text2);
				}
			}
			if (!flag)
			{
				list.Add("giant_ribs:Black Marble:BlackMarble");
			}
			return string.Join(";", list.ToArray());
		}

		private static string MigrateExtraTypes(string value)
		{
			return value.Replace("CharredFortress:Charred Fortress:TrophyCharredMelee,CharredBone", "CharredFortress:Charred Fortress:BellFragment");
		}

		private static bool IsExcludedItem(string item)
		{
			return item.Equals("MushroomBlue", StringComparison.OrdinalIgnoreCase) || item.Equals("RoyalJelly", StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsExcludedItemKey(string key)
		{
			string[] excludedNames = ExcludedNames;
			foreach (string text in excludedNames)
			{
				if (key.StartsWith(text + " - ", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static void RemoveEntryComments(List<string> output)
		{
			while (output.Count > 0 && output[output.Count - 1].Trim().Length == 0)
			{
				output.RemoveAt(output.Count - 1);
			}
			while (output.Count > 0 && output[output.Count - 1].TrimStart(Array.Empty<char>()).StartsWith("#"))
			{
				output.RemoveAt(output.Count - 1);
			}
		}

		private static bool ContainsSetting(List<string> lines)
		{
			foreach (string line in lines)
			{
				if (TryGetEntry(line.Trim(), out var _, out var _))
				{
					return true;
				}
			}
			return false;
		}

		private static void CollapseBlankLines(List<string> lines)
		{
			for (int num = lines.Count - 1; num > 0; num--)
			{
				if (lines[num].Trim().Length == 0 && lines[num - 1].Trim().Length == 0)
				{
					lines.RemoveAt(num);
				}
			}
			while (lines.Count > 0 && lines[0].Trim().Length == 0)
			{
				lines.RemoveAt(0);
			}
			while (lines.Count > 0 && lines[lines.Count - 1].Trim().Length == 0)
			{
				lines.RemoveAt(lines.Count - 1);
			}
		}
	}
	internal static class DungeonPinner
	{
		private const float ScanInterval = 1f;

		private const float SameDungeonDistance = 10f;

		private static float _nextScan;

		private static readonly HashSet<string> LoggedLocations = new HashSet<string>();

		internal static void ClearCache()
		{
		}

		internal static void Tick()
		{
			//IL_0070: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			if (!PinItPlugin.DungeonPinsEnabled.Value || Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 1f;
			Player localPlayer = Player.m_localPlayer;
			Minimap instance = Minimap.instance;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float value = PinItPlugin.DungeonPinRange.Value;
			IList list = WorldScan.Locations();
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					object? obj = list[i];
					Check(instance, (Location)((obj is Location) ? obj : null), position, value);
				}
				return;
			}
			Location[] array = WorldScan.FindLocationsSlow();
			foreach (Location location in array)
			{
				Check(instance, location, position, value);
			}
		}

		private unsafe static void Check(Minimap minimap, Location location, Vector3 playerPos, float range)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)location == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)location).transform.position;
			if (PinIcons.FlatDistance(playerPos, position) > range)
			{
				return;
			}
			string prefabName = GetPrefabName(((Component)location).gameObject);
			if (PinItPlugin.DebugLogging.Value)
			{
				HashSet<string> loggedLocations = LoggedLocations;
				Vector3 val = position;
				if (loggedLocations.Add(prefabName + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString()))
				{
					PinItPlugin.Log.LogInfo((object)$"Nearby location '{prefabName}' at {position}");
				}
			}
			DungeonType dungeonType = DungeonTypes.Match(prefabName);
			if (dungeonType != null && ItemSettings.IsPinEnabled(PinKind.Dungeon, dungeonType.Key))
			{
				TryPin(minimap, dungeonType, position);
			}
		}

		private static void TryPin(Minimap minimap, DungeonType type, Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			PinRecord pinRecord = FindRecord(pos);
			if (pinRecord != null)
			{
				if (!PinItPlugin.RepinDeletedDungeons.Value || PinIcons.FindPinForRecord(minimap, pinRecord) != null)
				{
					return;
				}
				PinRecordStore.Remove(pinRecord);
			}
			PinData pin = minimap.AddPin(pos, PinItPlugin.GetDungeonPinType(), type.Name, true, false, 0L, default(PlatformUserID));
			PinRecordStore.Add(PinKind.Dungeon, type.Key, type.Name, pos);
			if (PinItPlugin.UseDungeonIcons.Value)
			{
				PinIcons.ApplyIcon(pin, type.IconItem);
			}
			PinEffects.OnNewPin(pin, PinKind.Dungeon, type.Key);
			if (PinItPlugin.DebugLogging.Value)
			{
				PinItPlugin.Log.LogInfo((object)$"Pinned '{type.Name}' at {pos}");
			}
		}

		private static PinRecord FindRecord(Vector3 pos)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			foreach (PinRecord item in PinRecordStore.GetAll())
			{
				if (item.Kind != PinKind.Dungeon || !(PinIcons.FlatDistance(item.Pos, pos) <= 10f))
				{
					continue;
				}
				return item;
			}
			return null;
		}

		private static string GetPrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}
	}
	internal class DungeonType
	{
		public string Key;

		public string Name;

		public string IconItem;
	}
	internal static class DungeonTypes
	{
		internal const string Default = "Crypt:Burial Chambers:TrophySkeleton;SunkenCrypt:Sunken Crypt:TrophyDraugr;TrollCave:Troll Cave:TrophyFrostTroll;BearCave:Bear Cave:TrophyBjorn;MountainCave:Frost Cave:TrophyCultist;Mistlands_DvergrTownEntrance:Infested Mine:TrophySeeker;Mistlands_DvergrBossEntrance:Infested Citadel:TrophySeekerBrute;MorgenHole:Putrid Hole:TrophyMorgen;TheHole:Winding Tunnels:TrophyElaking;MorkBorg:Mörkhalla:TrophyJotunWitch;Hildir_crypt:Smouldering Tomb:TrophySkeleton;Hildir_cave:Howling Cavern:TrophyCultist";

		private static readonly List<DungeonType> Types = new List<DungeonType>();

		internal static void Rebuild(string config)
		{
			Types.Clear();
			ItemSettings.BeginRebuild(PinKind.Dungeon);
			string[] array = (config ?? "").Split(new char[1] { ';' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length >= 2)
				{
					string text2 = array2[0].Trim();
					string text3 = array2[1].Trim();
					string iconItem = ((array2.Length > 2) ? array2[2].Trim() : "");
					if (text2.Length != 0 && text3.Length != 0)
					{
						Types.Add(new DungeonType
						{
							Key = text2,
							Name = text3,
							IconItem = iconItem
						});
						ItemSettings.Register(PinKind.Dungeon, text2, text3);
					}
				}
			}
			Types.Sort((DungeonType a, DungeonType b) => b.Key.Length.CompareTo(a.Key.Length));
			ItemSettings.EndRebuild(PinKind.Dungeon);
			DungeonPinner.ClearCache();
		}

		internal static DungeonType Match(string locationPrefabName)
		{
			if (string.IsNullOrEmpty(locationPrefabName))
			{
				return null;
			}
			foreach (DungeonType type in Types)
			{
				if (locationPrefabName.StartsWith(type.Key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}

		internal static DungeonType GetByKey(string key)
		{
			foreach (DungeonType type in Types)
			{
				if (string.Equals(type.Key, key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}
	}
	internal static class ExtraPinner
	{
		private const float ScanInterval = 1f;

		private const int ScanFrames = 12;

		private const int MinChunk = 300;

		private const float SameLocationDistance = 10f;

		private const float SameObjectDistance = 3f;

		private static float _nextScan;

		private static readonly HashSet<string> Logged = new HashSet<string>();

		private static readonly List<ZNetView> Snapshot = new List<ZNetView>(8192);

		private static bool _scanning;

		private static int _cursor;

		private static Vector3 _scanPlayerPos;

		private static float _scanMaxRange;

		private static readonly Dictionary<int, ExtraType> TypeByPrefab = new Dictionary<int, ExtraType>();

		internal static void ClearCache()
		{
			TypeByPrefab.Clear();
		}

		internal static void Tick()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			if (!PinItPlugin.ExtrasEnabled.Value)
			{
				_scanning = false;
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Minimap instance = Minimap.instance;
			if (!_scanning)
			{
				if (Time.time < _nextScan)
				{
					return;
				}
				_nextScan = Time.time + 1f;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null)
				{
					return;
				}
				_scanPlayerPos = ((Component)localPlayer).transform.position;
				_scanMaxRange = ExtraTypes.MaxRange();
				IList list = WorldScan.Locations();
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						object? obj = list[i];
						CheckLocation(instance, (Location)((obj is Location) ? obj : null));
					}
				}
				else
				{
					Location[] array = WorldScan.FindLocationsSlow();
					foreach (Location location in array)
					{
						CheckLocation(instance, location);
					}
				}
				if ((Object)(object)ZNetScene.instance == (Object)null)
				{
					return;
				}
				WorldScan.CopyViews(Snapshot);
				_cursor = 0;
				_scanning = true;
			}
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				_scanning = false;
				return;
			}
			int num = Math.Max(300, Snapshot.Count / 12 + 1);
			int num2 = Math.Min(Snapshot.Count, _cursor + num);
			while (_cursor < num2)
			{
				ZNetView val = Snapshot[_cursor];
				if (!((Object)(object)val == (Object)null))
				{
					ZDO zDO = val.GetZDO();
					if (zDO != null)
					{
						Vector3 position = zDO.GetPosition();
						float num3 = PinIcons.FlatDistance(_scanPlayerPos, position);
						if (!(num3 > _scanMaxRange))
						{
							ExtraType extraType = GetExtraType(zDO.GetPrefab(), val);
							if (extraType != null && ItemSettings.IsPinEnabled(PinKind.Extra, extraType.Key) && !(num3 > ExtraTypes.GetRange(extraType)))
							{
								TryPin(instance, extraType, position, 3f);
							}
						}
					}
				}
				_cursor++;
			}
			if (_cursor >= Snapshot.Count)
			{
				_scanning = false;
				Snapshot.Clear();
			}
		}

		private static ExtraType GetExtraType(int prefabHash, ZNetView view)
		{
			if (TypeByPrefab.TryGetValue(prefabHash, out var value))
			{
				return value;
			}
			ExtraType extraType = ExtraTypes.Match(GetPrefabName(((Component)view).gameObject));
			TypeByPrefab[prefabHash] = extraType;
			return extraType;
		}

		private static void CheckLocation(Minimap minimap, Location location)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)location == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)location).transform.position;
			float num = PinIcons.FlatDistance(_scanPlayerPos, position);
			if (!(num > _scanMaxRange))
			{
				ExtraType extraType = ExtraTypes.Match(GetPrefabName(((Component)location).gameObject));
				if (extraType != null && ItemSettings.IsPinEnabled(PinKind.Extra, extraType.Key) && !(num > ExtraTypes.GetRange(extraType)))
				{
					TryPin(minimap, extraType, position, 10f);
				}
			}
		}

		private unsafe static void TryPin(Minimap minimap, ExtraType type, Vector3 pos, float sameDistance)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			PinRecord pinRecord = FindRecord(type, pos, sameDistance);
			if (pinRecord != null)
			{
				if (!PinItPlugin.RepinDeletedExtras.Value || PinIcons.FindPinForRecord(minimap, pinRecord) != null)
				{
					return;
				}
				PinRecordStore.Remove(pinRecord);
			}
			PinData pin = minimap.AddPin(pos, PinItPlugin.GetExtraPinType(), type.Name, true, false, 0L, default(PlatformUserID));
			PinRecordStore.Add(PinKind.Extra, type.Key, type.Name, pos);
			if (PinItPlugin.UseExtraIcons.Value)
			{
				PinIcons.ApplyIcon(pin, type.IconItem);
			}
			PinEffects.OnNewPin(pin, PinKind.Extra, type.Key);
			if (PinItPlugin.DebugLogging.Value)
			{
				HashSet<string> logged = Logged;
				string key = type.Key;
				Vector3 val = pos;
				if (logged.Add(key + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString()))
				{
					PinItPlugin.Log.LogInfo((object)$"Pinned extra '{type.Name}' at {pos}");
				}
			}
		}

		private static PinRecord FindRecord(ExtraType type, Vector3 pos, float sameDistance)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			foreach (PinRecord item in PinRecordStore.GetAll())
			{
				if (item.Kind != PinKind.Extra || item.Id != type.Key || !(PinIcons.FlatDistance(item.Pos, pos) <= sameDistance))
				{
					continue;
				}
				return item;
			}
			return null;
		}

		private static string GetPrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}
	}
	internal class ExtraType
	{
		public string Key;

		public bool Exact;

		public string Name;

		public string IconItem;
	}
	internal static class ExtraTypes
	{
		internal const string Default = "GoblinCamp:Fuling Camp:TrophyGoblin;Hildir_plainsfortress:Black Tower:TrophyGoblinBrute;CharredFortress:Charred Fortress:BellFragment;Spawner_GreydwarfNest:Greydwarf Spawner:TrophyGreydwarf;DrakeNest:Moder Egg:DragonEgg;TarPit:Tar Pit:Tar;Mistlands_Excavation:Dvergr Camp:TrophyDvergr;Mistlands_GuardTower:Dvergr Tower:TrophyDvergr;Mistlands_Lighthouse:Dvergr Lighthouse:TrophyDvergr;Mistlands_Harbour:Dvergr Dock:TrophyDvergr;=Leviathan:Leviathan:Chitin;FireHole:Surtling Spawner:SurtlingCore,TrophySurtling;Spawner_CharredStone:Charred Spawner:CharredSkull,TrophyCharredMelee,CharredBone;Spawner_CharredCross:Charred Spawner:CharredSkull,TrophyCharredMelee,CharredBone";

		private static readonly Dictionary<string, float> DefaultRanges = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Charred Fortress", 150f },
			{ "Black Tower", 150f },
			{ "Fuling Camp", 100f },
			{ "Dvergr Camp", 100f },
			{ "Dvergr Tower", 100f },
			{ "Dvergr Lighthouse", 150f },
			{ "Dvergr Dock", 100f },
			{ "Leviathan", 150f },
			{ "Tar Pit", 80f },
			{ "Surtling Spawner", 80f },
			{ "Moder Egg", 40f },
			{ "Greydwarf Spawner", 80f },
			{ "Charred Spawner", 80f }
		};

		private static readonly Dictionary<string, ConfigEntry<float>> RangeEntries = new Dictionary<string, ConfigEntry<float>>(StringComparer.OrdinalIgnoreCase);

		private static ConfigFile _config;

		private static float _maxRange;

		private static readonly List<ExtraType> Types = new List<ExtraType>();

		internal static void Init(ConfigFile config)
		{
			_config = config;
		}

		internal static float GetRange(ExtraType type)
		{
			ConfigEntry<float> value;
			return RangeEntries.TryGetValue(type.Name, out value) ? value.Value : PinItPlugin.ExtrasPinRange.Value;
		}

		internal static float MaxRange()
		{
			return _maxRange;
		}

		private static void BindRanges()
		{
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Expected O, but got Unknown
			if (_config == null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (ExtraType type in Types)
			{
				hashSet.Add(type.Name);
			}
			List<string> list = null;
			foreach (string key in RangeEntries.Keys)
			{
				if (!hashSet.Contains(key))
				{
					if (list == null)
					{
						list = new List<string>();
					}
					list.Add(key);
				}
			}
			if (list != null)
			{
				foreach (string item in list)
				{
					ItemSettings.RemoveEntry((ConfigEntryBase)(object)RangeEntries[item]);
					RangeEntries.Remove(item);
				}
				ItemSettings.SaveConfig();
			}
			foreach (ExtraType type2 in Types)
			{
				if (!RangeEntries.ContainsKey(type2.Name))
				{
					float value;
					float num = (DefaultRanges.TryGetValue(type2.Name, out value) ? value : PinItPlugin.ExtrasPinRange.Value);
					ConfigEntry<float> val = _config.Bind<float>("Extra Ranges", SafeKey(type2.Name), num, new ConfigDescription("Distance in metres at which '" + type2.Name + "' gets pinned. Areas beyond roughly 150 m may not be loaded by the game yet, so higher values may not take effect.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
					val.SettingChanged += delegate
					{
						UpdateMaxRange();
					};
					RangeEntries[type2.Name] = val;
				}
			}
			UpdateMaxRange();
		}

		internal static void UpdateMaxRange()
		{
			float num = 0f;
			foreach (ExtraType type in Types)
			{
				num = Math.Max(num, GetRange(type));
			}
			_maxRange = num;
		}

		private static string SafeKey(string name)
		{
			return ItemSettings.SafeKey(name);
		}

		internal static void Rebuild(string config)
		{
			Types.Clear();
			ItemSettings.BeginRebuild(PinKind.Extra);
			string[] array = (config ?? "").Split(new char[1] { ';' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length >= 2)
				{
					string text2 = array2[0].Trim();
					bool flag = text2.StartsWith("=");
					if (flag)
					{
						text2 = text2.Substring(1).Trim();
					}
					string text3 = array2[1].Trim();
					string iconItem = ((array2.Length > 2) ? array2[2].Trim() : "");
					if (text2.Length != 0 && text3.Length != 0)
					{
						Types.Add(new ExtraType
						{
							Key = text2,
							Exact = flag,
							Name = text3,
							IconItem = iconItem
						});
						ItemSettings.Register(PinKind.Extra, text2, text3);
					}
				}
			}
			Types.Sort((ExtraType a, ExtraType b) => b.Key.Length.CompareTo(a.Key.Length));
			ItemSettings.EndRebuild(PinKind.Extra);
			BindRanges();
			ExtraPinner.ClearCache();
		}

		internal static ExtraType Match(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			foreach (ExtraType type in Types)
			{
				if (type.Exact ? string.Equals(prefabName, type.Key, StringComparison.OrdinalIgnoreCase) : prefabName.StartsWith(type.Key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}

		internal static ExtraType GetByKey(string key)
		{
			foreach (ExtraType type in Types)
			{
				if (string.Equals(type.Key, key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}
	}
	internal static class ForagePinner
	{
		private static readonly Regex LabelPattern = new Regex("^\\d+x$");

		internal static bool IsForageLabel(string name)
		{
			return name != null && LabelPattern.IsMatch(name);
		}

		internal static void OnHarvested(Pickable pickable)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: 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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			if (!PinItPlugin.Enabled.Value)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = pickable.m_itemPrefab;
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return;
			}
			string name = ((Object)itemPrefab).name;
			Vector3 position = ((Component)pickable).transform.position;
			if (PinItPlugin.DebugLogging.Value)
			{
				PinItPlugin.Log.LogInfo((object)$"Harvested '{((Object)pickable).name}' -> item '{name}' at {position}");
			}
			if (!PinItPlugin.IsAllowed(name) || (PinItPlugin.IgnoreCultivatedGround.Value && IsOnCultivatedGround(position)))
			{
				return;
			}
			float value = PinItPlugin.ClusterRadius.Value;
			PinData val = FindClusterPin(instance, name, position, value);
			if (val == null)
			{
				List<Vector3> clusterMembers = GetClusterMembers(name, position, value);
				Vector3 val2 = ((clusterMembers.Count > 0) ? Centroid(clusterMembers) : position);
				int count = Mathf.Max(1, GetClusterMembers(name, val2, value).Count);
				PinData pin = instance.AddPin(val2, PinItPlugin.GetPinType(), BuildLabel(count), true, false, 0L, default(PlatformUserID));
				PinRecordStore.Add(PinKind.Forage, name, null, val2);
				if (PinItPlugin.UseItemIcons.Value)
				{
					PinIcons.ApplyIcon(pin, name);
				}
				PinEffects.OnNewPin(pin, PinKind.Forage, name);
				if (PinItPlugin.DebugLogging.Value)
				{
					PinItPlugin.Log.LogInfo((object)$"Created '{name}' pin '{BuildLabel(count)}' at {val2}");
				}
				return;
			}
			int count2 = Mathf.Max(1, GetClusterMembers(name, val.m_pos, value).Count);
			string text = BuildLabel(count2);
			if (!(val.m_name == text))
			{
				Vector3 pos = val.m_pos;
				PinType type = val.m_type;
				bool flag = val.m_checked;
				PinIcons.Forget(val);
				instance.RemovePin(val);
				PinData pin2 = instance.AddPin(pos, type, text, true, flag, 0L, default(PlatformUserID));
				if (PinItPlugin.UseItemIcons.Value)
				{
					PinIcons.ApplyIcon(pin2, name);
				}
				if (PinItPlugin.DebugLogging.Value)
				{
					PinItPlugin.Log.LogInfo((object)$"Updated '{name}' pin to '{text}' at {pos}");
				}
			}
		}

		private static string BuildLabel(int count)
		{
			return $"{count}x";
		}

		private static PinData FindClusterPin(Minimap minimap, string itemId, Vector3 pos, float radius)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			PinData result = null;
			float num = float.MaxValue;
			PinRecord[] array = PinRecordStore.GetAll().ToArray();
			foreach (PinRecord pinRecord in array)
			{
				if (pinRecord.Kind != PinKind.Forage || pinRecord.Id != itemId)
				{
					continue;
				}
				float num2 = PinIcons.FlatDistance(pinRecord.Pos, pos);
				if (!(num2 > radius))
				{
					PinData val = PinIcons.FindPinForRecord(minimap, pinRecord);
					if (val == null)
					{
						PinRecordStore.Remove(pinRecord);
					}
					else if (num2 < num)
					{
						result = val;
						num = num2;
					}
				}
			}
			return result;
		}

		private static List<Vector3> GetClusterMembers(string itemId, Vector3 center, float radius)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			bool value = PinItPlugin.IgnoreCultivatedGround.Value;
			foreach (Pickable item in PickableRegistry.GetLoaded())
			{
				if (!((Object)(object)item.m_itemPrefab == (Object)null) && !(((Object)item.m_itemPrefab).name != itemId))
				{
					Vector3 position = ((Component)item).transform.position;
					if (!(PinIcons.FlatDistance(position, center) > radius) && (!value || !IsOnCultivatedGround(position)))
					{
						list.Add(position);
					}
				}
			}
			return list;
		}

		private static bool IsOnCultivatedGround(Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			Heightmap val = Heightmap.FindHeightmap(pos);
			return (Object)(object)val != (Object)null && val.IsCultivated(pos);
		}

		private static Vector3 Centroid(List<Vector3> points)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.zero;
			foreach (Vector3 point in points)
			{
				val += point;
			}
			return val / (float)points.Count;
		}
	}
	internal class ItemToggle
	{
		public PinKind Kind;

		public string Name;

		public ConfigEntry<bool> Pin;

		public ConfigEntry<bool> Visible;

		public ConfigEntry<bool> DeleteAll;

		public readonly HashSet<string> Ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
	}
	internal static class ItemSettings
	{
		private const float DeleteCheckInterval = 0.5f;

		private static ConfigFile _config;

		private static readonly Dictionary<string, ItemToggle> ByName = new Dictionary<string, ItemToggle>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, ItemToggle>[] ById = CreateIdMaps();

		private static readonly HashSet<string>[] Seen = CreateSeenSets();

		private static readonly List<ItemToggle> Scratch = new List<ItemToggle>();

		private static float _nextDeleteCheck;

		private static readonly MethodInfo RemoveMethod = typeof(ConfigFile).GetMethod("Remove", new Type[1] { typeof(ConfigDefinition) });

		private static readonly PropertyInfo EntriesProperty = AccessTools.Property(typeof(ConfigFile), "Entries");

		private static readonly FieldInfo EntriesField = AccessTools.Field(typeof(ConfigFile), "Entries");

		private static bool _removeWarned;

		private static Dictionary<string, ItemToggle>[] CreateIdMaps()
		{
			Dictionary<string, ItemToggle>[] array = new Dictionary<string, ItemToggle>[Enum.GetValues(typeof(PinKind)).Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Dictionary<string, ItemToggle>(StringComparer.OrdinalIgnoreCase);
			}
			return array;
		}

		private static HashSet<string>[] CreateSeenSets()
		{
			HashSet<string>[] array = new HashSet<string>[Enum.GetValues(typeof(PinKind)).Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			}
			return array;
		}

		internal static void Init(ConfigFile config)
		{
			_config = config;
		}

		internal static void BeginRebuild(PinKind kind)
		{
			Seen[(int)kind].Clear();
			ById[(int)kind].Clear();
			foreach (ItemToggle value in ByName.Values)
			{
				if (value.Kind == kind)
				{
					value.Ids.Clear();
				}
			}
		}

		internal static void Register(PinKind kind, string id, string name)
		{
			if (_config != null && !string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(name))
			{
				string text = kind.ToString() + "|" + name;
				Seen[(int)kind].Add(text);
				if (!ByName.TryGetValue(text, out var value))
				{
					string text2 = SectionFor(kind);
					string text3 = SafeKey(name);
					value = new ItemToggle
					{
						Kind = kind,
						Name = name
					};
					value.Pin = _config.Bind<bool>(text2, text3 + " - Pin", true, "Ticked = " + name + " gets pinned. Unticked = new " + name + " pins are no longer added (existing pins stay; use the matching permanent action in [Cleanup] to remove them).");
					value.Visible = _config.Bind<bool>(text2, text3 + " - Visible", true, "Ticked = " + name + " pins are shown. Unticked = all " + name + " pins are made invisible and can't be clicked, crossed out or removed until they're shown again.");
					value.DeleteAll = _config.Bind<bool>("Cleanup", CleanupKey(kind, text3), false, "WARNING — PERMANENT: Tick to delete EVERY " + name + " pin made by Pin It! for the character and world you're playing. This cannot be undone. The setting resets itself after running. If ticked at the main menu it runs after entering a world. Deleted dungeon, ore and extra pins only return when that category's RepinDeleted setting is enabled; forageable pins return after harvesting there again.");
					ByName[text] = value;
				}
				value.Ids.Add(id);
				ById[(int)kind][id] = value;
			}
		}

		internal static bool EndRebuild(PinKind kind)
		{
			Scratch.Clear();
			foreach (ItemToggle value in ByName.Values)
			{
				if (value.Kind == kind && !Seen[(int)kind].Contains(kind.ToString() + "|" + value.Name))
				{
					Scratch.Add(value);
				}
			}
			foreach (ItemToggle item in Scratch)
			{
				RemoveEntry((ConfigEntryBase)(object)item.Pin);
				RemoveEntry((ConfigEntryBase)(object)item.Visible);
				RemoveEntry((ConfigEntryBase)(object)item.DeleteAll);
				ByName.Remove(kind.ToString() + "|" + item.Name);
				if (PinItPlugin.DebugLogging != null && PinItPlugin.DebugLogging.Value)
				{
					PinItPlugin.Log.LogInfo((object)("Removed settings for '" + item.Name + "' (no longer in the list)."));
				}
			}
			bool flag = Scratch.Count > 0;
			Scratch.Clear();
			if (flag)
			{
				SaveConfig();
			}
			return flag;
		}

		internal static void RemoveEntry(ConfigEntryBase entry)
		{
			if (_config == null || entry == null)
			{
				return;
			}
			try
			{
				if (RemoveMethod != null)
				{
					RemoveMethod.Invoke(_config, new object[1] { entry.Definition });
					return;
				}
				object obj = ((EntriesProperty != null) ? EntriesProperty.GetValue(_config, null) : ((EntriesField != null) ? EntriesField.GetValue(_config) : null));
				if (obj is IDictionary dictionary)
				{
					dictionary.Remove(entry.Definition);
					return;
				}
			}
			catch (Exception ex)
			{
				if (!_removeWarned)
				{
					_removeWarned = true;
					PinItPlugin.Log.LogWarning((object)("Could not remove an unused setting from the config: " + ex.Message));
				}
				return;
			}
			if (!_removeWarned)
			{
				_removeWarned = true;
				PinItPlugin.Log.LogWarning((object)"This BepInEx version can't remove unused settings; they will stay in the config.");
			}
		}

		internal static void SaveConfig()
		{
			try
			{
				ConfigFile config = _config;
				if (config != null)
				{
					config.Save();
				}
			}
			catch (Exception ex)
			{
				PinItPlugin.Log.LogWarning((object)("Could not save the config: " + ex.Message));
			}
		}

		internal static ItemToggle Get(PinKind kind, string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return null;
			}
			ItemToggle value;
			return ById[(int)kind].TryGetValue(id, out value) ? value : null;
		}

		internal static bool IsPinEnabled(PinKind kind, string id)
		{
			return Get(kind, id)?.Pin.Value ?? true;
		}

		internal static bool IsVisible(PinKind kind, string id)
		{
			return Get(kind, id)?.Visible.Value ?? true;
		}

		internal static void Tick()
		{
			if (Time.time < _nextDeleteCheck)
			{
				return;
			}
			_nextDeleteCheck = Time.time + 0.5f;
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			Scratch.Clear();
			foreach (ItemToggle value in ByName.Values)
			{
				if (value.DeleteAll.Value)
				{
					Scratch.Add(value);
				}
			}
			foreach (ItemToggle item in Scratch)
			{
				DeleteAll(instance, item);
			}
			Scratch.Clear();
		}

		private static void DeleteAll(Minimap minimap, ItemToggle toggle)
		{
			int num = 0;
			PinRecord[] array = PinRecordStore.GetAll().ToArray();
			foreach (PinRecord pinRecord in array)
			{
				if (pinRecord.Kind == toggle.Kind && toggle.Ids.Contains(pinRecord.Id))
				{
					PinData val = PinIcons.FindPinForRecord(minimap, pinRecord);
					if (val != null)
					{
						PinIcons.Forget(val);
						minimap.RemovePin(val);
						num++;
					}
					if (pinRecord.Kind == PinKind.Forage)
					{
						PinRecordStore.Remove(pinRecord);
					}
				}
			}
			toggle.DeleteAll.Value = false;
			string text = string.Format("Pin It!: deleted {0} {1} pin{2}", num, toggle.Name, (num == 1) ? "" : "s");
			PinItPlugin.Log.LogInfo((object)text);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}

		internal static string SectionFor(PinKind kind)
		{
			return kind switch
			{
				PinKind.Dungeon => "Dungeon Items", 
				PinKind.Ore => "Ore Items", 
				PinKind.Extra => "Extra Items", 
				_ => "Forageable Items", 
			};
		}

		private static string CleanupKey(PinKind kind, string key)
		{
			return kind switch
			{
				PinKind.Dungeon => "Dungeons", 
				PinKind.Ore => "Ores", 
				PinKind.Extra => "Extras", 
				_ => "Forageables", 
			} + " - " + key + " - DELETE all pins";
		}

		internal static string SafeKey(string name)
		{
			char[] array = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };
			foreach (char oldChar in array)
			{
				name = name.Replace(oldChar, '_');
			}
			return name.Trim();
		}
	}
	internal static class OreHitPatches
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			Type[] array = new Type[3]
			{
				typeof(MineRock),
				typeof(MineRock5),
				typeof(Destructible)
			};
			MethodInfo methodInfo = AccessTools.Method(typeof(OreHitPatches), "DamagePrefix", (Type[])null, (Type[])null);
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					MethodInfo methodInfo2 = AccessTools.Method(type, "Damage", new Type[1] { typeof(HitData) }, (Type[])null);
					if (methodInfo2 == null)
					{
						PinItPlugin.Log.LogWarning((object)(type.Name + ".Damage not found; ore hit detection for it is off."));
					}
					else
					{
						harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
				catch (Exception ex)
				{
					PinItPlugin.Log.LogWarning((object)("Could not patch " + type.Name + ".Damage: " + ex.Message));
				}
			}
		}

		private static void DamagePrefix(Component __instance, HitData hit)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					OrePinner.LogDebug(__instance.gameObject, __instance.transform.position);
					OrePinner.OnHit(__instance, hit);
				}
			}
			catch (Exception arg)
			{
				PinItPlugin.Log.LogError((object)$"Error while pinning ore on hit: {arg}");
			}
		}
	}
	internal static class OrePinner
	{
		private const float ScanInterval = 1f;

		private const int ScanFrames = 12;

		private const int MinChunk = 300;

		private const float SameDepositDistance = 2f;

		private static readonly Regex CountOnlyPattern = new Regex("^\\d+x$");

		private static float _nextScan;

		private static readonly HashSet<string> LoggedDeposits = new HashSet<string>();

		private static readonly List<ZNetView> Snapshot = new List<ZNetView>(8192);

		private static bool _scanning;

		private static int _cursor;

		private static Vector3 _scanPlayerPos;

		private static readonly Dictionary<int, OreType> TypeByPrefab = new Dictionary<int, OreType>();

		internal static void ClearCache()
		{
			TypeByPrefab.Clear();
		}

		internal static void Tick()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (!PinItPlugin.OrePinsEnabled.Value)
			{
				_scanning = false;
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Minimap instance = Minimap.instance;
			if (!_scanning)
			{
				if (Time.time < _nextScan)
				{
					return;
				}
				_nextScan = Time.time + 1f;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
				{
					return;
				}
				WorldScan.CopyViews(Snapshot);
				_scanPlayerPos = ((Component)localPlayer).transform.position;
				_cursor = 0;
				_scanning = true;
			}
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				_scanning = false;
				return;
			}
			float value = PinItPlugin.OrePinRange.Value;
			bool value2 = PinItPlugin.SilverRequiresPickaxeHit.Value;
			int num = Math.Max(300, Snapshot.Count / 12 + 1);
			int num2 = Math.Min(Snapshot.Count, _cursor + num);
			while (_cursor < num2)
			{
				ZNetView val = Snapshot[_cursor];
				if (!((Object)(object)val == (Object)null))
				{
					ZDO zDO = val.GetZDO();
					if (zDO != null)
					{
						Vector3 position = zDO.GetPosition();
						if (!(PinIcons.FlatDistance(_scanPlayerPos, position) > value))
						{
							OreType oreType = GetOreType(zDO.GetPrefab(), val);
							if (oreType != null && ItemSettings.IsPinEnabled(PinKind.Ore, oreType.Key) && (!(oreType.Hidden && value2) || IsKnownDeposit(oreType, position)))
							{
								AddDeposit(instance, oreType, position);
							}
						}
					}
				}
				_cursor++;
			}
			if (_cursor >= Snapshot.Count)
			{
				_scanning = false;
				Snapshot.Clear();
			}
		}

		private static OreType GetOreType(int prefabHash, ZNetView view)
		{
			if (TypeByPrefab.TryGetValue(prefabHash, out var value))
			{
				return value;
			}
			OreType oreType = OreTypes.Match(GetPrefabName(((Component)view).gameObject));
			TypeByPrefab[prefabHash] = oreType;
			return oreType;
		}

		internal static void OnHit(Component target, HitData hit)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if (!PinItPlugin.OrePinsEnabled.Value || (Object)(object)target == (Object)null || hit == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Minimap instance = Minimap.instance;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null || hit.m_attacker != ((Character)localPlayer).GetZDOID() || hit.m_damage.m_pickaxe <= 0f)
			{
				return;
			}
			Transform val = target.transform;
			while ((Object)(object)val != (Object)null)
			{
				OreType oreType = OreTypes.Match(GetPrefabName(((Component)val).gameObject));
				if (oreType != null)
				{
					if (ItemSettings.IsPinEnabled(PinKind.Ore, oreType.Key))
					{
						AddDeposit(instance, oreType, val.position);
					}
					break;
				}
				val = val.parent;
			}
		}

		private static void AddDeposit(Minimap minimap, OreType type, Vector3 pos)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: 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_0198: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(pos.x, pos.z);
			float value = PinItPlugin.OreClusterRadius.Value;
			bool value2 = PinItPlugin.RepinDeletedOres.Value;
			PinRecord pinRecord = FindRecordContaining(type, val);
			if (pinRecord != null)
			{
				if (!value2 || PinIcons.FindPinForRecord(minimap, pinRecord) != null)
				{
					return;
				}
				PinRecordStore.Remove(pinRecord);
			}
			PinRecord pinRecord2 = FindNearestCluster(type, pos, value);
			if (pinRecord2 != null)
			{
				PinData val2 = PinIcons.FindPinForRecord(minimap, pinRecord2);
				if (val2 != null)
				{
					pinRecord2.Deposits.Add(val);
					Vector3 val3 = Centroid(pinRecord2.Deposits, pos.y);
					PinType type2 = val2.m_type;
					bool flag = val2.m_checked;
					PinIcons.Forget(val2);
					minimap.RemovePin(val2);
					pinRecord2.Pos = val3;
					PinRecordStore.Save();
					PinData pin = minimap.AddPin(val3, type2, BuildLabel(pinRecord2.Label, pinRecord2.Deposits.Count), true, flag, 0L, default(PlatformUserID));
					if (PinItPlugin.UseOreIcons.Value)
					{
						PinIcons.ApplyIcon(pin, type.IconItem);
					}
					if (PinItPlugin.DebugLogging.Value)
					{
						PinItPlugin.Log.LogInfo((object)$"Updated '{type.Name}' pin to {pinRecord2.Deposits.Count}x at {val3}");
					}
					return;
				}
				if (!value2)
				{
					pinRecord2.Deposits.Add(val);
					PinRecordStore.Save();
					return;
				}
				PinRecordStore.Remove(pinRecord2);
			}
			PinData pin2 = minimap.AddPin(pos, PinItPlugin.GetOrePinType(), BuildLabel(type.Name, 1), true, false, 0L, default(PlatformUserID));
			PinRecordStore.Add(PinKind.Ore, type.Key, type.Name, pos, new List<Vector2> { val });
			if (PinItPlugin.UseOreIcons.Value)
			{
				PinIcons.ApplyIcon(pin2, type.IconItem);
			}
			PinEffects.OnNewPin(pin2, PinKind.Ore, type.Key);
			if (PinItPlugin.DebugLogging.Value)
			{
				PinItPlugin.Log.LogInfo((object)$"Pinned '{type.Name}' at {pos}");
			}
		}

		private static string BuildLabel(string name, int count)
		{
			count = Math.Max(1, count);
			if (!PinItPlugin.ShowOreName.Value)
			{
				return $"{count}x";
			}
			return (count > 1) ? $"{name} {count}x" : name;
		}

		internal static bool IsOreLabel(string label, string name)
		{
			if (label == null)
			{
				return false;
			}
			if (CountOnlyPattern.IsMatch(label))
			{
				return true;
			}
			if (name == null)
			{
				return false;
			}
			if (label == name)
			{
				return true;
			}
			return Regex.IsMatch(label, "^" + Regex.Escape(name) + " \\d+x$");
		}

		internal static void RelabelAll(Minimap minimap)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			PinRecord[] array = PinRecordStore.GetAll().ToArray();
			foreach (PinRecord pinRecord in array)
			{
				if (pinRecord.Kind != PinKind.Ore)
				{
					continue;
				}
				PinData val = PinIcons.FindPinForRecord(minimap, pinRecord);
				if (val != null)
				{
					string text = BuildLabel(pinRecord.Label, pinRecord.Deposits.Count);
					if (!(val.m_name == text))
					{
						Vector3 pos = val.m_pos;
						PinType type = val.m_type;
						bool flag = val.m_checked;
						PinIcons.Forget(val);
						minimap.RemovePin(val);
						minimap.AddPin(pos, type, text, true, flag, 0L, default(PlatformUserID));
					}
				}
			}
		}

		private static bool IsKnownDeposit(OreType type, Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return FindRecordContaining(type, new Vector2(pos.x, pos.z)) != null;
		}

		private static PinRecord FindRecordContaining(OreType type, Vector2 deposit)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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)
			foreach (PinRecord item in PinRecordStore.GetAll())
			{
				if (item.Kind != PinKind.Ore || item.Id != type.Key)
				{
					continue;
				}
				foreach (Vector2 deposit2 in item.Deposits)
				{
					if (Vector2.Distance(deposit2, deposit) <= 2f)
					{
						return item;
					}
				}
			}
			return null;
		}

		private static PinRecord FindNearestCluster(OreType type, Vector3 pos, float radius)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			PinRecord result = null;
			float num = float.MaxValue;
			foreach (PinRecord item in PinRecordStore.GetAll())
			{
				if (item.Kind == PinKind.Ore && !(item.Id != type.Key))
				{
					float num2 = PinIcons.FlatDistance(item.Pos, pos);
					if (num2 <= radius && num2 < num)
					{
						result = item;
						num = num2;
					}
				}
			}
			return result;
		}

		private static string GetPrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}

		private static Vector3 Centroid(List<Vector2> points, float y)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = Vector2.zero;
			foreach (Vector2 point in points)
			{
				val += point;
			}
			val /= (float)points.Count;
			return new Vector3(val.x, y, val.y);
		}

		internal unsafe static void LogDebug(GameObject go, Vector3 pos)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			string prefabName = GetPrefabName(go);
			if (PinItPlugin.DebugLogging.Value)
			{
				HashSet<string> loggedDeposits = LoggedDeposits;
				Vector3 val = pos;
				if (loggedDeposits.Add(prefabName + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString()))
				{
					PinItPlugin.Log.LogInfo((object)$"Hit '{prefabName}' at {pos}");
				}
			}
		}
	}
	internal class OreType
	{
		public string Key;

		public string Name;

		public string IconItem;

		public bool Hidden;
	}
	internal static class OreTypes
	{
		internal const string Default = "rock4_copper:Copper:CopperOre;MineRock_Tin:Tin:TinOre;silvervein:Silver:SilverOre:hidden;giant_brain:Soft Tissue:Softtissue;giant_ribs:Black Marble:BlackMarble;LeviathanLava:Flametal:FlametalOreNew,FlametalOre";

		private static readonly List<OreType> Types = new List<OreType>();

		internal static void Rebuild(string config)
		{
			Types.Clear();
			ItemSettings.BeginRebuild(PinKind.Ore);
			string[] array = (config ?? "").Split(new char[1] { ';' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length >= 2)
				{
					string text2 = array2[0].Trim();
					string text3 = array2[1].Trim();
					string iconItem = ((array2.Length > 2) ? array2[2].Trim() : "");
					bool hidden = array2.Length > 3 && array2[3].Trim().Equals("hidden", StringComparison.OrdinalIgnoreCase);
					if (text2.Length != 0 && text3.Length != 0)
					{
						Types.Add(new OreType
						{
							Key = text2,
							Name = text3,
							IconItem = iconItem,
							Hidden = hidden
						});
						ItemSettings.Register(PinKind.Ore, text2, text3);
					}
				}
			}
			Types.Sort((OreType a, OreType b) => b.Key.Length.CompareTo(a.Key.Length));
			ItemSettings.EndRebuild(PinKind.Ore);
			OrePinner.ClearCache();
		}

		internal static OreType Match(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			foreach (OreType type in Types)
			{
				if (prefabName.StartsWith(type.Key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}

		internal static OreType GetByKey(string key)
		{
			foreach (OreType type in Types)
			{
				if (string.Equals(type.Key, key, StringComparison.OrdinalIgnoreCase))
				{
					return type;
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Pickable), "Awake")]
	internal static class Pickable_Awake_Patch
	{
		private static void Postfix(Pickable __instance)
		{
			PickableRegistry.Add(__instance);
		}
	}
	[HarmonyPatch(typeof(Pickable), "Interact")]
	internal static class Pickable_Interact_Patch
	{
		private static readonly FieldRef<Pickable, bool> PickedRef = AccessTools.FieldRefAccess<Pickable, bool>("m_picked");

		private static void Prefix(Pickable __instance, out bool __state)
		{
			__state = false;
			try
			{
				__state = (Object)(object)__instance != (Object)null && !PickedRef.Invoke(__instance);
			}
			catch (Exception ex)
			{
				PinItPlugin.Log.LogWarning((object)("Could not read pick state: " + ex.Message));
			}
		}

		private static void Postfix(Pickable __instance, bool __state)
		{
			if (!__state || (Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			try
			{
				ForagePinner.OnHarvested(__instance);
			}
			catch (Exception arg)
			{
				PinItPlugin.Log.LogError((object)$"Error while pinning forageable: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Minimap), "Update")]
	internal static class Minimap_Update_Patch
	{
		private static void Postfix()
		{
			Run(PinIcons.Tick, "updating pin icons");
			Run(DungeonPinner.Tick, "pinning dungeons");
			Run(OrePinner.Tick, "pinning ores");
			Run(ExtraPinner.Tick, "pinning extras");
			Run(ItemSettings.Tick, "deleting pins");
			Run(PinVisibility.Tick, "updating pin visibility");
		}

		private static void Run(Action action, string what)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				PinItPlugin.Log.LogError((object)$"Error while {what}: {arg}");
			}
		}
	}
	internal static class Minimap_GetClosestPin_Patch
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Minimap_GetClosestPin_Patch), "Postfix", (Type[])null, (Type[])null);
			int num = 0;
			MethodInfo[] methods = typeof(Minimap).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (methodInfo2.Name != "GetClosestPin" || methodInfo2.ReturnType != typeof(PinData))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo2.GetParameters();
				if (parameters.Length >= 2 && !(parameters[0].ParameterType != typeof(Vector3)) && !(parameters[1].ParameterType != typeof(float)))
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
					}
					catch (Exception ex)
					{
						PinItPlugin.Log.LogWarning((object)("Could not patch Minimap.GetClosestPin: " + ex.Message));
					}
				}
			}
			if (num == 0)
			{
				PinItPlugin.Log.LogWarning((object)"Minimap.GetClosestPin not found; hidden pins may still be clickable.");
			}
		}

		private static void Postfix(Minimap __instance, object[] __args, ref PinData __result)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (__result == null || !PinVisibility.IsHidden(__result))
				{
					return;
				}
				Vector3 a = (Vector3)__args[0];
				float num = (float)__args[1];
				bool flag = default(bool);
				int num3;
				if (__args.Length >= 3)
				{
					object obj = __args[2];
					int num2;
					if (obj is bool)
					{
						flag = (bool)obj;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					num3 = ((num2 == 0) ? 1 : 0);
				}
				else
				{
					num3 = 1;
				}
				bool flag2 = (byte)((uint)num3 | (flag ? 1u : 0u)) != 0;
				PinData val = null;
				float num4 = float.MaxValue;
				List<PinData> pins = PinIcons.GetPins(__instance);
				if (pins != null)
				{
					foreach (PinData item in pins)
					{
						if (item != null && !PinVisibility.IsHidden(item) && (!flag2 || (!((Object)(object)item.m_uiElement == (Object)null) && ((Component)item.m_uiElement).gameObject.activeInHierarchy)))
						{
							float num5 = PinIcons.FlatDistance(a, item.m_pos);
							if (num5 < num && num5 < num4)
							{
								val = item;
								num4 = num5;
							}
						}
					}
				}
				__result = val;
			}
			catch (Exception arg)
			{
				PinItPlugin.Log.LogError((object)$"Error while protecting hidden pins: {arg}");
			}
		}
	}
	internal static class PickableRegistry
	{
		private static readonly HashSet<Pickable> Pickables = new HashSet<Pickable>();

		public static void Add(Pickable pickable)
		{
			if ((Object)(object)pickable != (Object)null)
			{
				Pickables.Add(pickable);
			}
		}

		public static IEnumerable<Pickable> GetLoaded()
		{
			Pickables.RemoveWhere((Pickable p) => (Object)(object)p == (Object)null);
			return Pickables;
		}
	}
	internal enum PinSound
	{
		Off,
		SoftChime,
		GentleBell,
		SoftPop,
		WaterDrop
	}
	internal static class PinEffects
	{
		private sealed class Appear
		{
			public float Created;

			public float Start = -1f;

			public bool RingSpawned;

			public GameObject Ring;

			public Image RingImage;
		}

		private const float MinSoundGap = 0.2f;

		private const float MarkerWaitTimeout = 4f;

		private const float MaxAge = 15f;

		private static readonly Dictionary<PinData, Appear> Active = new Dictionary<PinData, Appear>();

		private static Sprite _ringSprite;

		private static readonly Type AudioSourceType = AccessTools.TypeByName("UnityEngine.AudioSource");

		private static readonly Type AudioClipType = AccessTools.TypeByName("UnityEngine.AudioClip");

		private static readonly Type MixerGroupType = AccessTools.TypeByName("UnityEngine.Audio.AudioMixerGroup");

		private static readonly MethodInfo ClipCreate = ((AudioClipType == null) ? null : AccessTools.Method(AudioClipType, "Create", new Type[5]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null));

		private static readonly MethodInfo ClipSetData = ((AudioClipType == null) ? null : AccessTools.Method(AudioClipType, "SetData", new Type[2]
		{
			typeof(float[]),
			typeof(int)
		}, (Type[])null));

		private static readonly MethodInfo PlayOneShotMethod = ((AudioSourceType == null || AudioClipType == null) ? null : AccessTools.Method(AudioSourceType, "PlayOneShot", new Type[2]
		{
			AudioClipType,
			typeof(float)
		}, (Type[])null));

		private static readonly PropertyInfo MixerProperty = ((AudioSourceType == null) ? null : AccessTools.Property(AudioSourceType, "outputAudioMixerGroup"));

		private static Component _source;

		private static readonly Dictionary<PinSound, Object> Clips = new Dictionary<PinSound, Object>();

		private static object _mixerGroup;

		private static float _nextMixerLookup;

		private static float _lastSound = -10f;

		internal static void OnNewPin(PinData pin, PinKind kind, string itemId)
		{
			if (pin != null && PinItPlugin.ShowPins.Value && PinItPlugin.IsCategoryVisible(kind) && ItemSettings.IsVisible(kind, itemId))
			{
				if (PinItPlugin.AppearAnimation.Value)
				{
					Active[pin] = new Appear
					{
						Created = Time.unscaledTime
					};
				}
				PlaySound(PinItPlugin.GetSelectedSound(), preview: false);
			}
		}

		internal static void Prune(HashSet<PinData> livePins)
		{
			if (Active.Count == 0)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			List<PinData> list = new List<PinData>();
			foreach (KeyValuePair<PinData, Appear> item in Active)
			{
				Appear value = item.Value;
				bool flag = item.Key == null || livePins == null || !livePins.Contains(item.Key);
				bool flag2 = value.Start < 0f && unscaledTime - value.Created > 4f;
				bool flag3 = unscaledTime - value.Created > 15f;
				if (flag || flag2 || flag3)
				{
					list.Add(item.Key);
				}
			}
			foreach (PinData item2 in list)
			{
				Finish(item2);
			}
		}

		internal static void Reset()
		{
			foreach (PinData item in new List<PinData>(Active.Keys))
			{
				Finish(item);
			}
			Active.Clear();
		}

		internal static void UpdateAppear(PinData pin, Color tint, out float scale, out float alpha)
		{
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			scale = 1f;
			alpha = 1f;
			if (!Active.TryGetValue(pin, out var value))
			{
				return;
			}
			if (!PinItPlugin.AppearAnimation.Value)
			{
				Finish(pin);
				return;
			}
			RectTransform uiElement = pin.m_uiElement;
			if (!((Object)(object)uiElement == (Object)null) && ((Component)uiElement).gameObject.activeInHierarchy)
			{
				float unscaledTime = Time.unscaledTime;
				if (value.Start < 0f)
				{
					value.Start = unscaledTime;
				}
				float appearDuration = PinItPlugin.GetAppearDuration();
				float num = appearDuration * 1.4f;
				float num2 = unscaledTime - value.Start;
				float x = Mathf.Clamp01(num2 / appearDuration);
				scale = Mathf.LerpUnclamped(0.35f, 1f, EaseOutBack(x));
				alpha = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(num2 / (appearDuration * 0.45f)));
				bool value2 = PinItPlugin.AppearRing.Value;
				if (value2)
				{
					UpdateRing(value, uiElement, tint, num2, num);
				}
				float num3 = (value2 ? Mathf.Max(appearDuration, num) : appearDuration);
				if (num2 >= num3)
				{
					Finish(pin);
				}
			}
		}

		private static void UpdateRing(Appear a, RectTransform marker, Color tint, float t, float ringLife)
		{
			//IL_008a: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			if (!a.RingSpawned)
			{
				a.RingSpawned = true;
				SpawnRing(a, marker);
			}
			if (!((Object)(object)a.Ring == (Object)null) && !((Object)(object)a.RingImage == (Object)null))
			{
				float num = Mathf.Clamp01(t / ringLife);
				float num2 = Mathf.Lerp(0.9f, 2.6f, 1f - Mathf.Pow(1f - num, 3f));
				a.Ring.transform.localScale = new Vector3(num2, num2, 1f);
				Color color = tint;
				color.a = tint.a * 0.5f * Mathf.Pow(1f - num, 2f);
				((Graphic)a.RingImage).color = color;
			}
		}

		private static void SpawnRing(Appear a, RectTransform marker)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_006a: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = new GameObject("PinIt_Ring", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				RectTransform component = val.GetComponent<RectTransform>();
				((Transform)component).SetParent((Transform)(object)marker, false);
				Vector2 anchorMin = (component.anchorMax = new Vector2(0.5f, 0.5f));
				component.anchorMin = anchorMin;
				component.pivot = new Vector2(0.5f, 0.5f);
				component.anchoredPosition = Vector2.zero;
				Rect rect = marker.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				if (size.x < 1f || size.y < 1f)
				{
					((Vector2)(ref size))..ctor(32f, 32f);
				}
				component.sizeDelta = size * 1.1f;
				Image component2 = val.GetComponent<Image>();
				component2.sprite = GetRingSprite();
				((Graphic)component2).raycastTarget = false;
				((Graphic)component2).color = new Color(1f, 1f, 1f, 0f);
				a.Ring = val;
				a.RingImage = component2;
			}
			catch (Exception ex)
			{
				PinItPlugin.Log.LogWarning((object)("Could not create the pin ring effect: " + ex.Message));
			}
		}

		private static void Finish(PinData pin)
		{
			if (pin != null && Active.TryGetValue(pin, out var value) && (Object)(object)value.Ring != (Object)null)
			{
				Object.Destroy((Object)(object)value.Ring);
			}
			if (pin != null)
			{
				Active.Remove(pin);
				return;
			}
			List<PinData> list = new List<PinData>();
			foreach (PinData key in Active.Keys)
			{
				if (key == null)
				{
					list.Add(key);
				}
			}
			foreach (PinData item in list)
			{
				Active.Remove(item);
			}
		}

		private static float EaseOutBack(float x)
		{
			float num = x - 1f;
			return 1f + 3.4f * num * num * num + 2.4f * num * num;
		}

		private static Sprite GetRingSprite()
		{
			//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_002f: 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_0041: Expected O, but got Unknown
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_ringSprite != (Object)null)
			{
				return _ringSprite;
			}
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)61
			};
			float num = 31.5f;
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num2 = ((float)j - num) / num;
					float num3 = ((float)i - num) / num;
					float num4 = Mathf.Abs(Mathf.Sqrt(num2 * num2 + num3 * num3) - 0.8f);
					float num5 = Mathf.Clamp01(1f - num4 / 0.1f);
					num5 = num5 * num5 * (3f - 2f * num5);
					array[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(num5 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			_ringSprite = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f);
			((Object)_ringSprite).hideFlags = (HideFlags)61;
			return _ringSprite;
		}

		internal static void PlaySound(PinSound sound, bool preview)
		{
			if (sound == PinSound.Off)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (!preview && unscaledTime - _lastSound < 0.2f)
			{
				return;
			}
			float soundVolume = PinItPlugin.GetSoundVolume();
			if (soundVolume <= 0f)
			{
				return;
			}
			try
			{
				Component source = GetSource();
				Object clip = GetClip(sound);
				if (!((Object)(object)source == (Object)null) && !(clip == (Object)null) && !(PlayOneShotMethod == null))
				{
					PlayOneShotMethod.Invoke(source, new object[2] { clip, soundVolume });
					_lastSound = unscaledTime;
				}
			}
			catch (Exception ex)
			{
				PinItPlugin.Log.LogWarning((object)("Could not play the pin sound: " + ex.Message));
			}
		}

		private static Component GetSource()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (AudioSourceType == null)
			{
				return null;
			}
			if ((Object)(object)_source == (Object)null)
			{
				GameObject val = new GameObject("PinIt_Audio");
				Object.DontDestroyOnLoad((Object)(object)val);
				_source = val.AddComponent(AudioSourceType);
				SetSourceValue("playOnAwake", false);
				SetSourceValue("loop", false);
				SetSourceValue("spatialBlend", 0f);
				SetSourceValue("volume", 1f);
			}
			if (MixerProperty != null && Time.unscaledTime >= _nextMixerLookup && MixerProperty.GetValue(_source, null) == null)
			{
				_nextMixerLookup = Time.unscaledTime + 5f;
				if (_mixerGroup == null)
				{
					_mixerGroup = FindGameMixerGroup();
				}
				if (_mixerGroup != null)
				{
					MixerProperty.SetValue(_source, _mixerGroup, null);
				}
			}
			return _source;
		}

		private static void SetSourceValue(string property, object value)
		{
			PropertyInfo propertyInfo = AccessTools.Property(AudioSourceType, property);
			if (propertyInfo != null && propertyInfo.CanWrite)
			{
				propertyInfo.SetValue(_source, value, null);
			}
		}

		private static object FindGameMixerGroup()
		{
			try
			{
				Type type = AccessTools.TypeByName("AudioMan");
				if (type == null || MixerGroupType == null)
				{
					return null;
				}
				object obj = AccessTools.Field(type, "instance")?.GetValue(null) ?? AccessTools.Property(type, "instance")?.GetValue(null, null) ?? AccessTools.Field(type, "s_instance")?.GetValue(null);
				if (obj == null)
				{
					return null;
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!(fieldInfo.FieldType != MixerGroupType) && fieldInfo.Name.IndexOf("gui", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						object value = fieldInfo.GetValue(obj);
						if (value != null)
						{
							return value;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static Object GetClip(PinSound sound)
		{
			if (Clips.TryGetValue(sound, out var value) && value != (Object)null)
			{
				return value;
			}
			Object val = BuildClip(sound);
			if (val != (Object)null)
			{
				Clips[sound] = val;
			}
			return val;
		}

		private static Object BuildClip(PinSound sound)
		{
			if (ClipCreate == null || ClipSetData == null)
			{
				return null;
			}
			float num;
			switch (sound)
			{
			case PinSound.SoftChime:
				num = 0.75f;
				break;
			case PinSound.GentleBell:
				num = 1.1f;
				break;
			case PinSound.SoftPop:
				num = 0.2f;
				break;
			case PinSound.WaterDrop:
				num = 0.3f;
				break;
			default:
				return null;
			}
			int num2 = Mathf.CeilToInt(num * 44100f);
			float[] array = new float[num2];
			float num3 = 0f;
			for (int i = 0; i < num2; i++)
			{
				float num4 = (float)i / 44100f;
				float num6;
				switch (sound)
				{
				case PinSound.SoftChime:
					num6 = Mathf.Sin((float)Math.PI * 1760f * num4) * Mathf.Exp(-5.5f * num4) + 0.45f * Mathf.Sin((float)Math.PI * 2637f * num4) * Mathf.Exp(-7.5f * num4);
					break;
				case PinSound.GentleBell:
					num6 = Mathf.Sin(3690.1147f * num4) * Mathf.Exp(-3.4f * num4) + 0.45f * Mathf.Sin(10184.717f * num4) * Mathf.Exp(-5.5f * num4) + 0.2f * Mathf.Sin(19926.62f * num4) * Mathf.Exp(-9f * num4);
					break;
				case PinSound.SoftPop:
				{
					float num7 = 220f + 380f * Mathf.Exp((0f - num4) * 38f);
					num3 += (float)Math.PI * 2f * num7 / 44100f;
					num6 = Mathf.Sin(num3) * Mathf.Exp((0f - num4) * 30f);
					break;
				}
				default:
				{
					float num5 = 650f + 900f * (1f - Mathf.Exp((0f - num4) * 55f));
					num3 += (float)Math.PI * 2f * num5 / 44100f;
					num6 = Mathf.Sin(num3) * Mathf.Exp((0f - num4) * 20f);
					break;
				}
				}
				array[i] = num6 * Mathf.Clamp01(num4 / 0.004f);
			}
			int num8 = Mathf.Min(num2, 352);
			for (int j = 0; j < num8; j++)
			{
				array[num2 - 1 - j] *= (float)j / (float)num8;
			}
			float num9 = 0f;
			for (int k = 0; k < num2; k++)
			{
				num9 = Mathf.Max(num9, Mathf.Abs(array[k]));
			}
			if (num9 > 0f)
			{
				float num10 = 0.5f / num9;
				for (int l = 0; l < num2; l++)
				{
					array[l] *= num10;
				}
			}
			object? obj = ClipCreate.Invoke(null, new object[5]
			{
				"PinIt_" + sound,
				num2,
				1,
				44100,
				false
			});
			Object val = (Object)((obj is Object) ? obj : null);
			if (val == (Object)null)
			{
				return null;
			}
			ClipSetData.Invoke(val, new object[2] { array, 0 });
			val.hideFlags = (HideFlags)61;
			return val;
		}
	}
	internal static class PinIcons
	{
		private static readonly FieldRef<Minimap, List<PinData>> PinsRef = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");

		private const float PinMatchTolerance = 0.5f;

		private static readonly Dictionary<string, Sprite> IconCache = new Dictionary<string, Sprite>();

		private static readonly HashSet<string> MissingIconsLogged = new HashSet<string>();

		private static readonly Dictionary<PinData, Sprite> Decorated = new Dictionary<PinData, Sprite>();

		private static bool _refreshPending;

		private static Minimap _lastMinimap;

		private static List<PinData> _lastPinList;

		private static int _lastPinCount = -1;

		private const float GridCell = 4f;

		private static readonly Dictionary<long, List<PinData>> Grid = new Dictionary<long, List<PinData>>();

		private static readonly Stack<List<PinData>> GridListPool = new Stack<List<PinData>>();

		private static List<PinData> _gridSource;

		private static int _gridCount = -1;

		private static bool _gridDirty = true;

		private static readonly FieldInfo PrefabsField = AccessTools.Field(typeof(ZNetScene), "m_prefabs");

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

		internal static List<PinData> GetPins(Minimap minimap)
		{
			return PinsRef.Invoke(minimap);
		}

		private static int Cell(float v)
		{
			return Mathf.FloorToInt(v / 4f);
		}

		private static long CellKey(int cx, int cz)
		{
			return ((long)cx << 32) | (uint)cz;
		}

		private static void EnsureGrid(List<PinData> pins)
		{
			if (!_gridDirty && pins == _gridSource && pins.Count == _gridCount)
			{
				return;
			}
			foreach (List<PinData> value2 in Grid.Values)
			{
				value2.Clear();
				GridListPool.Push(value2);
			}
			Grid.Clear();
			foreach (PinData pin in pins)
			{
				if (pin != null && pin.m_name != null)
				{
					long key = CellKey(Cell(pin.m_pos.x), Cell(pin.m_pos.z));
					if (!Grid.TryGetValue(key, out var value))
					{
						value = ((GridListPool.Count > 0) ? GridListPool.Pop() : new List<PinData>(2));
						Grid[key] = value;
					}
					value.Add(pin);
				}
			}
			_gridSource = pins;
			_gridCount = pins.Count;
			_gridDirty = false;
		}

		internal static PinData FindPinAt(Minimap minimap, Vector3 pos, Func<string, bool> labelMatches)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			List<PinData> list = PinsRef.Invoke(minimap);
			if (list == null)
			{
				return null;
			}
			EnsureGrid(list);
			int num = Cell(pos.x);
			int num2 = Cell(pos.z);
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (!Grid.TryGetValue(CellKey(num + i, num2 + j), out var value))
					{
						continue;
					}
					for (int k = 0; k < value.Count; k++)
					{
						PinData val = value[k];
						if (!(FlatDistance(val.m_pos, pos) > 0.5f) && labelMatches(val.m_name))
						{
							return val;
						}
					}
				}
			}
			return null;
		}

		internal static PinData FindPinForRecord(Minimap minimap, PinRecord record)
		{
			//IL_0039: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			switch (record.Kind)
			{
			case PinKind.Dungeon:
			case PinKind.Extra:
				return FindPinAt(minimap, record.Pos, (string name) => name == record.Label);
			case PinKind.Ore:
				return FindPinAt(minimap, record.Pos, (string name) => OrePinner.IsOreLabel(name, record.Label));
			default:
				return FindPinAt(minimap, record.Pos, ForagePinner.IsForageLabel);
			}
		}

		internal static void RequestRefresh()
		{
			_refreshPending = true;
		}

		internal static void Forget(PinData pin)
		{
			Decorated.Remove(pin);
			_gridDirty = true;
			PinVisibility.Invalidate();
		}

		internal static void Tick()
		{
			DetectMapReload();
			if (_refreshPending && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ObjectDB.instance != (Object)null)
			{
				_refreshPending = false;
				RefreshAllIcons();
			}
			if (Decorated.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<PinData, Sprite> item in Decorated)
			{
				PinData key = item.Key;
				if ((Object)(object)key.m_icon != (Object)(object)item.Value)
				{
					key.m_icon = item.Value;
				}
				if ((Object)(object)key.m_iconElement != (Object)null && (Object)(object)key.m_iconElement.sprite != (Object)(object)item.Value)
				{
					key.m_iconElement.sprite = item.Value;
				}
			}
		}

		private static void DetectMapReload()
		{
			Minimap instance = Minimap.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if ((Object)(object)instance != (Object)(object)_lastMinimap)
				{
					_lastMinimap = instance;
					NoPieceIcon.Clear();
					Decorated.Clear();
					_lastPinList = null;
					_lastPinCount = -1;
					_gridDirty = true;
				}
				List<PinData> list = PinsRef.Invoke(instance);
				int num = list?.Count ?? 0;
				if (list != _lastPinList || num != _lastPinCount)
				{
					_lastPinList = list;
					_lastPinCount = num;
					_refreshPending = true;
				}
			}
		}

		private static void RefreshAllIcons()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			OrePinner.RelabelAll(instance);
			foreach (PinData key in Decorated.Keys)
			{
				Sprite sprite = (key.m_icon = GetStandardSprite(instance, key.m_type));
				if ((Object)(object)key.m_iconElement != (Object)null)
				{
					key.m_iconElement.sprite = sprite;
				}
			}
			Decorated.Clear();
			foreach (PinRecord item in PinRecordStore.GetAll())
			{
				PinData val = FindPinForRecord(instance, item);
				if (val == null)
				{
					continue;
				}
				switch (item.Kind)
				{
				case PinKind.Extra:
					if (PinItPlugin.UseExtraIcons.Value)
					{
						ExtraType byKey = ExtraTypes.GetByKey(item.Id);
						if (byKey != null)
						{
							ApplyIcon(val, byKey.IconItem);
						}
					}
					break;
				case PinKind.Ore:
					if (PinItPlugin.UseOreIcons.Value)
					{
						OreType byKey3 = OreTypes.GetByKey(item.Id);
						if (byKey3 != null)
						{
							ApplyIcon(val, byKey3.IconItem);
						}
					}
					break;
				case PinKind.Dungeon:
					if (PinItPlugin.UseDungeonIcons.Value)
					{
						DungeonType byKey2 = DungeonTypes.GetByKey(item.Id);
						if (byKey2 != null)
						{
							ApplyIcon(val, byKey2.IconItem);
						}
					}
					break;
				default:
					if (PinItPlugin.UseItemIcons.Value)
					{
						ApplyIcon(val, item.Id);
					}
					break;
				}
			}
		}

		internal static void ApplyIcon(PinData pin, string itemId)
		{
			if (pin == null || string.IsNullOrEmpty(itemId))
			{
				return;
			}
			Sprite val = null;
			string[] array = itemId.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length != 0)
				{
					val = GetItemIcon(text2);
					if ((Object)(object)val != (Object)null)
					{
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				if (MissingIconsLogged.Add(itemId))
				{
					PinItPlugin.Log.LogWarning((object)("No inventory or build-menu icon found for '" + itemId + "', using the standard pin marker."));
				}
				return;
			}
			pin.m_icon = val;
			if ((Object)(object)pin.m_iconElement != (Object)null)
			{
				pin.m_iconElement.sprite = val;
			}
			Decorated[pin] = val;
		}

		private static Sprite GetItemIcon(string name)
		{
			if (IconCache.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			string text = "item";
			Sprite val = FindItemIcon(name);
			if ((Object)(object)val == (Object)null)
			{
				val = FindPieceIcon(name);
				text = "build piece";
			}
			if ((Object)(object)val != (Object)null)
			{
				IconCache[name] = val;
				if (PinItPlugin.DebugLogging.Value)
				{
					PinItPlugin.Log.LogInfo((object)("Icon for '" + name + "' found as " + text + "."));
				}
			}
			return val;
		}

		private static Sprite FindItemIcon(string itemId)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemId);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			Sprite[] array = ((!((Object)(object)val != (Object)null)) ? null : val.m_itemData?.m_shared?.m_icons);
			return (array != null && array.Length != 0) ? array[0] : null;
		}

		private static Sprite FindPieceIcon(string prefabName)
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (NoPieceIcon.Contains(prefabName))
			{
				return null;
			}
			GameObject val = instance.GetPrefab(prefabName);
			if ((Object)(object)val == (Object)null && PrefabsField != null && PrefabsField.GetValue(instance) is IEnumerable enumerable)
			{
				foreach (object item in enumerable)
				{
					GameObject val2 = (GameObject)((item is GameObject) ? item : null);
					if ((Object)(object)val2 != (Object)null && string.Equals(((Object)val2).name, prefabName, StringComparison.OrdinalIgnoreCase))
					{
						val = val2;
						break;
					}
				}
			}
			Piece val3 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
			Sprite val4 = (((Object)(object)val3 != (Object)null) ? val3.m_icon : null);
			if ((Object)(object)val4 == (Object)null)
			{
				NoPieceIcon.Add(prefabName);
			}
			return val4;
		}

		private static Sprite GetStandardSprite(Minimap minimap, PinType type)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			return Traverse.Create((object)minimap).Method("GetSprite", new object[1] { type }).GetValue<Sprite>();
		}

		internal static float FlatDistance(Vector3 a, Vector3 b)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return Mathf.Sqrt(num * num + num2 * num2);
		}
	}
	[BepInPlugin("pinit.automaticmappins", "Pin It! - Automatic Map Pins", "1.12.2")]
	public class PinItPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "pinit.automaticmappins";

		public const string ModName = "Pin It! - Automatic Map Pins";

		public const string ModVersion = "1.12.2";

		internal const string DefaultAllowedItems = "Raspberry,Blueberries,Cloudberry,Lingonberry,Mushroom,MushroomMagecap,MushroomJotunPuffs,MushroomSmokePuff,Thistle,Dandelion,Fiddleheadfern";

		private static readonly HashSet<string> ExcludedForageables = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MushroomBlue", "RoyalJelly" };

		private static readonly Dictionary<string, string> ForageNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Raspberry", "Raspberries" },
			{ "Blueberries", "Blueberries" },
			{ "Cloudberry", "Cloudberries" },
			{ "Lingonberry", "Lingonberries" },
			{ "Mushroom", "Mushrooms" },
			{ "MushroomMagecap", "Magecap" },
			{ "MushroomJotunPuffs", "Jotun Puffs" },
			{ "MushroomSmokePuff", "Smoke Puffs" },
			{ "Thistle", "Thistle" },
			{ "Dandelion", "Dandelion" },
			{ "Fiddleheadfern", "Fiddlehead" }
		};

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> ShowPins;

		internal static ConfigEntry<bool> ShowForagePins;

		internal static ConfigEntry<bool> ShowDungeonPins;

		internal static ConfigEntry<bool> ShowOrePins;

		internal static ConfigEntry<bool> ShowExtraPins;

		internal static ConfigEntry<float> ForagePinOpacity;

		internal static ConfigEntry<float> DungeonPinOpacity;

		internal static ConfigEntry<float> OrePinOpacity;

		internal static ConfigEntry<float> ExtraPinOpacity;

		internal static ConfigEntry<Color> ForageTextColor;

		internal static ConfigEntry<Color> DungeonTextColor;

		internal static ConfigEntry<Color> OreTextColor;

		internal static ConfigEntry<Color> ExtraTextColor;

		internal static ConfigEntry<bool> HoverGrow;

		internal static ConfigEntry<float> HoverGrowScale;

		internal static ConfigEntry<bool> AppearAnimation;

		internal static ConfigEntry<bool> AppearRing;

		internal static ConfigEntry<float> AppearDuration;

		internal static ConfigEntry<bool> SoundSoftChime;

		internal static ConfigEntry<bool> SoundGentleBell;

		internal static ConfigEntry<bool> SoundSoftPop;

		internal static ConfigEntry<bool> SoundWaterDrop;

		internal static ConfigEntry<float> SoundVolume;

		private static bool _syncingSounds;

		internal static ConfigEntry<bool> LabelsOnHoverOnly;

		internal static ConfigEntry<float> LabelTextScale;

		internal static ConfigEntry<bool> ShowMapToggle;

		internal static ConfigEntry<string> MapToggleLabel;

		internal static ConfigEntry<float> MapToggleOffsetX;

		internal static ConfigEntry<float> MapToggleOffsetY;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<string> AllowedItems;

		internal static ConfigEntry<float> ClusterRadius;

		internal static ConfigEntry<bool> IgnoreCultivatedGround;

		internal static ConfigEntry<bool> UseItemIcons;

		internal static ConfigEntry<PinType> PinIcon;

		internal static ConfigEntry<bool> DungeonPinsEnabled;

		internal static ConfigEntry<float> DungeonPinRange;

		internal static ConfigEntry<string> DungeonTypeList;

		internal static ConfigEntry<bool> UseDungeonIcons;

		internal static ConfigEntry<PinType> DungeonPinIcon;

		internal static ConfigEntry<bool> RepinDeletedDungeons;

		internal static ConfigEntry<bool> OrePinsEnabled;

		internal static ConfigEntry<float> OrePinRange;

		internal static ConfigEntry<float> OreClusterRadius;

		internal static ConfigEntry<bool> SilverRequiresPickaxeHit;

		internal static ConfigEntry<bool> ShowOreName;

		internal static ConfigEntry<string> OreTypeList;

		internal static ConfigEntry<bool> UseOreIcons;

		internal static ConfigEntry<PinType> OrePinIcon;

		internal static ConfigEntry<bool> RepinDeletedOres;

		internal static ConfigEntry<bool> ExtrasEnabled;

		internal static ConfigEntry<float> ExtrasPinRange;

		internal static ConfigEntry<string> ExtraTypeList;

		internal static ConfigEntry<bool> UseExtraIcons;

		internal static ConfigEntry<PinType> ExtraPinIcon;

		internal static ConfigEntry<bool> RepinDeletedExtras;

		internal static ConfigEntry<bool> DebugLogging;

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

		private Harmony _harmony;

		private const double ApplyDelay = 5.0;

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static readonly Dictionary<Action, double> PendingApply = new Dictionary<Action, double>();

		privat