Decompiled source of AutoPOI v1.0.0

plugins/AutoPOI.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("AutoPOI")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+90281b069b35b7b6cd4a175e69adb4f239147e05")]
[assembly: AssemblyProduct("AutoPOI")]
[assembly: AssemblyTitle("AutoPOI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AutoPOI
{
	internal enum PinIcon
	{
		Fire,
		House,
		Hammer,
		Dot,
		Portal,
		Boss
	}
	internal static class AutoPOIConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> DetectionRadius;

		public static ConfigEntry<bool> ShowMessage;

		public static ConfigEntry<bool> MarkEntered;

		public static ConfigEntry<bool> PinDungeons;

		public static ConfigEntry<PinIcon> DungeonIcon;

		public static ConfigEntry<bool> PinBosses;

		public static ConfigEntry<PinIcon> BossIcon;

		public static ConfigEntry<bool> PinVendors;

		public static ConfigEntry<PinIcon> VendorIcon;

		public static ConfigEntry<bool> LogNearbyLocations;

		public static void Bind(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Automatically pin points of interest you come near.");
			DetectionRadius = config.Bind<float>("General", "DetectionRadius", 50f, new ConfigDescription("How close you must get to a point of interest, in metres, before it is pinned.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			ShowMessage = config.Bind<bool>("General", "ShowMessage", true, "Show the usual \"Pin added\" message in the corner when a point of interest is pinned.");
			MarkEntered = config.Bind<bool>("General", "MarkEntered", true, "Cross out a dungeon or boss pin once you have been inside the dungeon or stepped into the boss arena.");
			PinDungeons = config.Bind<bool>("Dungeons", "Enabled", true, "Pin dungeons and caves: Burial Chambers, Troll Caves, Sunken Crypts, Frost Caves, Infested Mines, Hildir's dungeons, Ashlands and Deep North dungeons.");
			DungeonIcon = config.Bind<PinIcon>("Dungeons", "Icon", PinIcon.Dot, "Map icon for dungeon pins.");
			PinBosses = config.Bind<bool>("Bosses", "Enabled", true, "Pin boss altars and arenas.");
			BossIcon = config.Bind<PinIcon>("Bosses", "Icon", PinIcon.Boss, "Map icon for boss pins.");
			PinVendors = config.Bind<bool>("Vendors", "Enabled", true, "Pin Haldor, Hildir and the Bog Witch.");
			VendorIcon = config.Bind<PinIcon>("Vendors", "Icon", PinIcon.House, "Map icon for vendor pins.");
			LogNearbyLocations = config.Bind<bool>("Debug", "LogNearbyLocations", false, "Write every location inside the detection radius to the BepInEx log, pinned or not. Useful for reporting a point of interest the mod does not recognise.");
		}

		public static PinType ToPinType(PinIcon icon)
		{
			return (PinType)(icon switch
			{
				PinIcon.Fire => 0, 
				PinIcon.House => 1, 
				PinIcon.Hammer => 2, 
				PinIcon.Portal => 6, 
				PinIcon.Boss => 9, 
				_ => 3, 
			});
		}
	}
	[BepInPlugin("com.noodle.autopoi", "AutoPOI", "1.0.0")]
	internal class AutoPOIPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.noodle.autopoi";

		public const string PluginName = "AutoPOI";

		public const string PluginVersion = "1.0.0";

		private const float ScanInterval = 3f;

		private const float MatchRadius = 12f;

		internal static ManualLogSource Log;

		private readonly Dictionary<int, PoiInfo> m_kinds = new Dictionary<int, PoiInfo>();

		private readonly HashSet<int> m_logged = new HashSet<int>();

		private PoiMemory m_memory;

		private float m_nextScan;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			AutoPOIConfig.Bind(((BaseUnityPlugin)this).Config);
			Log.LogInfo((object)"AutoPOI 1.0.0 loaded.");
		}

		private void Update()
		{
			if (Time.time < m_nextScan)
			{
				return;
			}
			m_nextScan = Time.time + 3f;
			if (AutoPOIConfig.Enabled.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				Minimap instance = Minimap.instance;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && !((Character)localPlayer).IsDead())
				{
					m_memory = PoiMemory.For(m_memory, localPlayer, ZNet.instance.GetWorldUID());
					Scan(localPlayer, instance);
				}
			}
		}

		private void Scan(Player player, Minimap minimap)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: 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)
			Vector3 position = ((Component)player).transform.position;
			float value = AutoPOIConfig.DetectionRadius.Value;
			bool flag = ((Character)player).InInterior();
			Vector2s zone = ZoneSystem.GetZone(position);
			if (m_kinds.Count > 4096)
			{
				m_kinds.Clear();
				m_logged.Clear();
			}
			foreach (Location s_allLocation in Location.s_allLocations)
			{
				if ((Object)(object)s_allLocation == (Object)null)
				{
					continue;
				}
				Vector3 position2 = ((Component)s_allLocation).transform.position;
				float num = Utils.DistanceXZ(position, position2);
				bool flag2 = ((!s_allLocation.m_hasInterior) ? (!flag && num < s_allLocation.m_exteriorRadius) : (flag && ZoneSystem.GetZone(position2) == zone));
				if (num > value && !flag2)
				{
					continue;
				}
				PoiInfo poiInfo = Classify(s_allLocation);
				if (AutoPOIConfig.LogNearbyLocations.Value && m_logged.Add(((Object)s_allLocation).GetInstanceID()))
				{
					Log.LogInfo((object)($"Nearby location {PoiCatalog.PrefabName(s_allLocation)} at {position2:F0}: " + $"{poiInfo.Kind} \"{poiInfo.Label}\", interior={s_allLocation.m_hasInterior}, " + $"radius={s_allLocation.m_exteriorRadius:F0}, {num:F0} m away"));
				}
				if (poiInfo.Kind != PoiKind.None && PoiCatalog.IsEnabled(poiInfo.Kind))
				{
					PinData val = FindPin(minimap, position2);
					if (val == null && !m_memory.IsSeen(position2))
					{
						val = AddPin(player, minimap, position2, poiInfo);
					}
					m_memory.MarkSeen(position2);
					if (flag2 && poiInfo.Kind != PoiKind.Vendor && AutoPOIConfig.MarkEntered.Value && val != null && !m_memory.IsEntered(position2))
					{
						val.m_checked = true;
						minimap.m_pinUpdateRequired = true;
						m_memory.MarkEntered(position2);
					}
				}
			}
		}

		private PoiInfo Classify(Location location)
		{
			int instanceID = ((Object)location).GetInstanceID();
			if (!m_kinds.TryGetValue(instanceID, out var value))
			{
				value = PoiCatalog.Classify(location);
				m_kinds[instanceID] = value;
			}
			return value;
		}

		private static PinData AddPin(Player player, Minimap minimap, Vector3 pos, PoiInfo info)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0021: 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)
			PinType val = PoiCatalog.PinTypeFor(info.Kind);
			PinData result = minimap.AddPin(pos, val, info.Label, true, false, 0L, default(PlatformUserID));
			if (AutoPOIConfig.ShowMessage.Value)
			{
				((Character)player).Message((MessageType)1, "$msg_pin_added: " + info.Label, 0, minimap.GetSprite(val), false);
			}
			return result;
		}

		private static PinData FindPin(Minimap minimap, Vector3 pos)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			PinData result = null;
			float num = 12f;
			foreach (PinData pin in minimap.m_pins)
			{
				if (pin.m_save && IsMarkerType(pin.m_type))
				{
					float num2 = Utils.DistanceXZ(pos, pin.m_pos);
					if (num2 < num)
					{
						result = pin;
						num = num2;
					}
				}
			}
			return result;
		}

		private static bool IsMarkerType(PinType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected I4, but got Unknown
			switch ((int)type)
			{
			case 0:
			case 1:
			case 2:
			case 3:
			case 6:
			case 9:
			case 14:
			case 15:
			case 16:
				return true;
			default:
				return false;
			}
		}
	}
	internal enum PoiKind
	{
		None,
		Dungeon,
		Boss,
		Vendor
	}
	internal sealed class PoiInfo
	{
		public static readonly PoiInfo None = new PoiInfo(PoiKind.None, "");

		public readonly PoiKind Kind;

		public readonly string Label;

		public PoiInfo(PoiKind kind, string label)
		{
			Kind = kind;
			Label = label;
		}
	}
	internal static class PoiCatalog
	{
		private static readonly Dictionary<string, PoiInfo> s_known = new Dictionary<string, PoiInfo>
		{
			{
				"Eikthyrnir",
				new PoiInfo(PoiKind.Boss, "Eikthyr")
			},
			{
				"GDKing",
				new PoiInfo(PoiKind.Boss, "The Elder")
			},
			{
				"Bonemass",
				new PoiInfo(PoiKind.Boss, "Bonemass")
			},
			{
				"Dragonqueen",
				new PoiInfo(PoiKind.Boss, "Moder")
			},
			{
				"GoblinKing",
				new PoiInfo(PoiKind.Boss, "Yagluth")
			},
			{
				"Mistlands_DvergrBossEntrance1",
				new PoiInfo(PoiKind.Boss, "The Queen")
			},
			{
				"FaderLocation",
				new PoiInfo(PoiKind.Boss, "Fader")
			},
			{
				"Vendor_BlackForest",
				new PoiInfo(PoiKind.Vendor, "Haldor")
			},
			{
				"Hildir_camp",
				new PoiInfo(PoiKind.Vendor, "Hildir")
			},
			{
				"BogWitch_Camp",
				new PoiInfo(PoiKind.Vendor, "Bog Witch")
			},
			{
				"Crypt2",
				new PoiInfo(PoiKind.Dungeon, "Burial Chambers")
			},
			{
				"Crypt3",
				new PoiInfo(PoiKind.Dungeon, "Burial Chambers")
			},
			{
				"Crypt4",
				new PoiInfo(PoiKind.Dungeon, "Burial Chambers")
			},
			{
				"TrollCave02",
				new PoiInfo(PoiKind.Dungeon, "Troll Cave")
			},
			{
				"SunkenCrypt1",
				new PoiInfo(PoiKind.Dungeon, "Sunken Crypt")
			},
			{
				"SunkenCrypt2",
				new PoiInfo(PoiKind.Dungeon, "Sunken Crypt")
			},
			{
				"SunkenCrypt3",
				new PoiInfo(PoiKind.Dungeon, "Sunken Crypt")
			},
			{
				"SunkenCrypt4",
				new PoiInfo(PoiKind.Dungeon, "Sunken Crypt")
			},
			{
				"MountainCave02",
				new PoiInfo(PoiKind.Dungeon, "Frost Cave")
			},
			{
				"Mistlands_DvergrTownEntrance1",
				new PoiInfo(PoiKind.Dungeon, "Infested Mine")
			},
			{
				"Mistlands_DvergrTownEntrance2",
				new PoiInfo(PoiKind.Dungeon, "Infested Mine")
			},
			{
				"Hildir_crypt",
				new PoiInfo(PoiKind.Dungeon, "Smouldering Tomb")
			},
			{
				"Hildir_cave",
				new PoiInfo(PoiKind.Dungeon, "Howling Cavern")
			},
			{
				"Hildir_plainsfortress",
				new PoiInfo(PoiKind.Dungeon, "Sealed Tower")
			},
			{
				"CharredFortress",
				new PoiInfo(PoiKind.Dungeon, "Charred Fortress")
			},
			{
				"MorgenHole1",
				new PoiInfo(PoiKind.Dungeon, "Morgen Hole")
			},
			{
				"MorgenHole2",
				new PoiInfo(PoiKind.Dungeon, "Morgen Hole")
			},
			{
				"MorgenHole3",
				new PoiInfo(PoiKind.Dungeon, "Morgen Hole")
			}
		};

		public static string PrefabName(Location location)
		{
			return Utils.GetPrefabName(((Component)location).gameObject);
		}

		public static PoiInfo Classify(Location location)
		{
			string text = PrefabName(location);
			if (s_known.TryGetValue(text, out var value))
			{
				return value;
			}
			OfferingBowl componentInChildren = ((Component)location).GetComponentInChildren<OfferingBowl>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				string text2 = ((!((Object)(object)componentInChildren.m_bossPrefab != (Object)null)) ? null : componentInChildren.m_bossPrefab.GetComponent<Character>()?.m_name);
				return new PoiInfo(PoiKind.Boss, (!string.IsNullOrEmpty(text2)) ? text2 : FallbackLabel(location, text));
			}
			Trader componentInChildren2 = ((Component)location).GetComponentInChildren<Trader>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				return new PoiInfo(PoiKind.Vendor, (!string.IsNullOrEmpty(componentInChildren2.m_name)) ? componentInChildren2.m_name : FallbackLabel(location, text));
			}
			if (location.m_hasInterior)
			{
				return new PoiInfo(PoiKind.Dungeon, FallbackLabel(location, text));
			}
			return PoiInfo.None;
		}

		private static string FallbackLabel(Location location, string prefab)
		{
			if (!string.IsNullOrEmpty(location.m_discoverLabel))
			{
				return location.m_discoverLabel;
			}
			return Prettify(prefab);
		}

		private static string Prettify(string prefab)
		{
			string text = prefab.TrimEnd('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_');
			StringBuilder stringBuilder = new StringBuilder(text.Length + 8);
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (c == '_')
				{
					stringBuilder.Append(' ');
					continue;
				}
				if (i > 0 && char.IsUpper(c) && char.IsLower(text[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			if (stringBuilder.Length <= 0)
			{
				return prefab;
			}
			return stringBuilder.ToString();
		}

		public static bool IsEnabled(PoiKind kind)
		{
			return kind switch
			{
				PoiKind.Dungeon => AutoPOIConfig.PinDungeons.Value, 
				PoiKind.Boss => AutoPOIConfig.PinBosses.Value, 
				PoiKind.Vendor => AutoPOIConfig.PinVendors.Value, 
				_ => false, 
			};
		}

		public static PinType PinTypeFor(PoiKind kind)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			return (PinType)(kind switch
			{
				PoiKind.Boss => AutoPOIConfig.ToPinType(AutoPOIConfig.BossIcon.Value), 
				PoiKind.Vendor => AutoPOIConfig.ToPinType(AutoPOIConfig.VendorIcon.Value), 
				_ => AutoPOIConfig.ToPinType(AutoPOIConfig.DungeonIcon.Value), 
			});
		}
	}
	internal sealed class PoiMemory
	{
		private const string SeenPrefix = "AutoPOI.seen.";

		private const string EnteredPrefix = "AutoPOI.entered.";

		private readonly Player m_player;

		private readonly long m_worldUid;

		private readonly HashSet<long> m_seen;

		private readonly HashSet<long> m_entered;

		private PoiMemory(Player player, long worldUid)
		{
			m_player = player;
			m_worldUid = worldUid;
			m_seen = Load("AutoPOI.seen.");
			m_entered = Load("AutoPOI.entered.");
		}

		public static PoiMemory For(PoiMemory current, Player player, long worldUid)
		{
			if (current != null && (Object)(object)current.m_player == (Object)(object)player && current.m_worldUid == worldUid)
			{
				return current;
			}
			return new PoiMemory(player, worldUid);
		}

		public bool IsSeen(Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return m_seen.Contains(Key(pos));
		}

		public bool IsEntered(Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return m_entered.Contains(Key(pos));
		}

		public void MarkSeen(Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (m_seen.Add(Key(pos)))
			{
				Save("AutoPOI.seen.", m_seen);
			}
		}

		public void MarkEntered(Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (m_entered.Add(Key(pos)))
			{
				Save("AutoPOI.entered.", m_entered);
			}
		}

		private static long Key(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			long num = Mathf.RoundToInt(pos.x);
			long num2 = Mathf.RoundToInt(pos.z);
			return (num << 32) | (num2 & 0xFFFFFFFFu);
		}

		private HashSet<long> Load(string prefix)
		{
			HashSet<long> hashSet = new HashSet<long>();
			Dictionary<string, string> customData = m_player.m_customData;
			long worldUid = m_worldUid;
			if (!customData.TryGetValue(prefix + worldUid, out var value) || string.IsNullOrEmpty(value))
			{
				return hashSet;
			}
			string[] array = value.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				if (long.TryParse(array[i], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}

		private void Save(string prefix, HashSet<long> set)
		{
			StringBuilder stringBuilder = new StringBuilder(set.Count * 12);
			foreach (long item in set)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(';');
				}
				stringBuilder.Append(item.ToString(CultureInfo.InvariantCulture));
			}
			Dictionary<string, string> customData = m_player.m_customData;
			long worldUid = m_worldUid;
			customData[prefix + worldUid] = stringBuilder.ToString();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}