Decompiled source of SpawnTracker v0.1.2

plugins/SpawnTracker.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[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 SpawnTracker
{
	[BepInPlugin("nnikoloo.SpawnTracker", "SpawnTracker", "0.1.2")]
	public class SpawnTrackerPlugin : BaseUnityPlugin
	{
		private class Entry
		{
			public Vector3 Position;

			public string Title;

			public string Status;

			public Color Color;

			public float Distance;
		}

		public const string Guid = "nnikoloo.SpawnTracker";

		public const string ModName = "SpawnTracker";

		public const string Version = "0.1.2";

		private static readonly FieldRef<CreatureSpawner, ZNetView> CreatureSpawnerView = AccessTools.FieldRefAccess<CreatureSpawner, ZNetView>("m_nview");

		private static readonly FieldRef<SpawnArea, ZNetView> SpawnAreaView = AccessTools.FieldRefAccess<SpawnArea, ZNetView>("m_nview");

		private static readonly FieldRef<SpawnArea, float> SpawnAreaTimer = AccessTools.FieldRefAccess<SpawnArea, float>("m_spawnTimer");

		private static readonly List<CreatureSpawner> AllCreatureSpawners = (List<CreatureSpawner>)AccessTools.Field(typeof(CreatureSpawner), "m_creatureSpawners").GetValue(null);

		private static readonly Color Alive = new Color(0.45f, 0.9f, 0.45f);

		private static readonly Color Waiting = new Color(1f, 0.82f, 0.3f);

		private static readonly Color Ready = new Color(1f, 0.45f, 0.35f);

		private static readonly Color Inactive = new Color(0.7f, 0.7f, 0.7f);

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<float> _range;

		private ConfigEntry<bool> _showLabels;

		private ConfigEntry<bool> _showList;

		private ConfigEntry<int> _listSize;

		private bool _visible = true;

		private float _nextScan;

		private readonly List<Entry> _entries = new List<Entry>();

		private GUIStyle _labelStyle;

		private GUIStyle _listStyle;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Key to show or hide spawn information.");
			_range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 80f, "Maximum distance (metres) at which spawners are shown.");
			_showLabels = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowLabels", true, "Show floating labels above each spawner.");
			_showList = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowList", true, "Show the on-screen list of nearby spawners.");
			_listSize = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "ListSize", 8, "How many spawners to show in the list.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Press {2} to toggle.", "SpawnTracker", "0.1.2", _toggleKey.Value));
		}

		private void Update()
		{
			//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)
			KeyboardShortcut value = _toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_visible = !_visible;
			}
			if (_visible && !(Time.time < _nextScan))
			{
				_nextScan = Time.time + 0.5f;
				Scan();
			}
		}

		private void Scan()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			_entries.Clear();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null)
			{
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			float value = _range.Value;
			foreach (CreatureSpawner allCreatureSpawner in AllCreatureSpawners)
			{
				if ((Object)(object)allCreatureSpawner == (Object)null)
				{
					continue;
				}
				float num = Vector3.Distance(position, ((Component)allCreatureSpawner).transform.position);
				if (!(num > value))
				{
					Entry entry = DescribeCreatureSpawner(allCreatureSpawner, num);
					if (entry != null)
					{
						_entries.Add(entry);
					}
				}
			}
			SpawnArea[] array = Object.FindObjectsOfType<SpawnArea>();
			foreach (SpawnArea val in array)
			{
				float num2 = Vector3.Distance(position, ((Component)val).transform.position);
				if (!(num2 > value))
				{
					Entry entry2 = DescribeSpawnArea(val, num2);
					if (entry2 != null)
					{
						_entries.Add(entry2);
					}
				}
			}
			_entries.Sort((Entry a, Entry b) => a.Distance.CompareTo(b.Distance));
		}

		private static Entry DescribeCreatureSpawner(CreatureSpawner spawner, float distance)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Invalid comparison between Unknown and I4
			//IL_0074: 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)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			ZNetView obj = CreatureSpawnerView.Invoke(spawner);
			ZDO val = ((obj != null) ? obj.GetZDO() : null);
			if (val == null || (Object)(object)spawner.m_creaturePrefab == (Object)null)
			{
				return null;
			}
			Entry entry = new Entry
			{
				Position = ((Component)spawner).transform.position,
				Title = CreatureName(spawner.m_creaturePrefab),
				Distance = distance
			};
			ZDOConnection connection = val.GetConnection();
			bool flag = connection != null && (int)connection.m_type == 3;
			ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)3);
			if (!((ZDOID)(ref connectionZDOID)).IsNone() && ZDOMan.instance.GetZDO(connectionZDOID) != null)
			{
				entry.Status = Texts.Get("alive");
				entry.Color = Alive;
				return entry;
			}
			if (spawner.m_respawnTimeMinuts <= 0f && flag)
			{
				entry.Status = Texts.Get("dead_no_respawn");
				entry.Color = Inactive;
				return entry;
			}
			if (spawner.m_respawnTimeMinuts > 0f && flag)
			{
				DateTime dateTime = new DateTime(val.GetLong(ZDOVars.s_aliveTime, 0L));
				double num = (double)spawner.m_respawnTimeMinuts * 60.0 - (ZNet.instance.GetTime() - dateTime).TotalSeconds;
				if (num > 0.0)
				{
					entry.Status = Texts.Get("respawns_in", FormatTime(num));
					entry.Color = Waiting;
					return entry;
				}
			}
			List<string> list = CreatureSpawnerBlockers(spawner, distance);
			entry.Status = ((list.Count == 0) ? Texts.Get("ready") : Texts.Get("ready_blocked", string.Join(", ", list.ToArray())));
			entry.Color = ((list.Count == 0) ? Ready : Waiting);
			return entry;
		}

		private static List<string> CreatureSpawnerBlockers(CreatureSpawner spawner, float distance)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			Vector3 position = ((Component)spawner).transform.position;
			if (!spawner.m_spawnAtDay && EnvMan.IsDay())
			{
				list.Add(Texts.Get("wait_night"));
			}
			if (!spawner.m_spawnAtNight && EnvMan.IsNight())
			{
				list.Add(Texts.Get("wait_day"));
			}
			if (!spawner.m_spawnInPlayerBase && (Object)(object)EffectArea.IsPointInsideArea(position, (Type)4, 0f) != (Object)null)
			{
				list.Add(Texts.Get("player_base"));
			}
			if (distance > spawner.m_triggerDistance)
			{
				list.Add(Texts.Get("get_within", spawner.m_triggerDistance.ToString("0")));
			}
			else if (spawner.m_triggerNoise > 0f)
			{
				list.Add(Texts.Get("needs_noise"));
			}
			List<string> list2 = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.GetGlobalKeys() : null);
			if (list2 != null)
			{
				if (spawner.m_requiredGlobalKey != "" && !list2.Contains(spawner.m_requiredGlobalKey))
				{
					list.Add(Texts.Get("needs_progress"));
				}
				if (spawner.m_blockingGlobalKey != "" && list2.Contains(spawner.m_blockingGlobalKey))
				{
					list.Add(Texts.Get("disabled_progress"));
				}
			}
			return list;
		}

		private static Entry DescribeSpawnArea(SpawnArea area, float distance)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = SpawnAreaView.Invoke(area);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return null;
			}
			string[] array = (from p in area.m_prefabs
				where (Object)(object)p.m_prefab != (Object)null
				select CreatureName(p.m_prefab)).Distinct().ToArray();
			Entry entry = new Entry();
			entry.Position = ((Component)area).transform.position;
			entry.Title = Texts.Get("spawner", (array.Length != 0) ? string.Join(", ", array) : "?");
			entry.Distance = distance;
			Entry entry2 = entry;
			CountInstances(area, out var near, out var total);
			string text = Texts.Get("counts", near, area.m_maxNear, total, area.m_maxTotal);
			if (near >= area.m_maxNear || total >= area.m_maxTotal)
			{
				entry2.Status = Texts.Get("full", text);
				entry2.Color = Alive;
			}
			else if (!val.IsOwner())
			{
				entry2.Status = Texts.Get("other_owner", text);
				entry2.Color = Inactive;
			}
			else if (!Player.IsPlayerInRange(((Component)area).transform.position, area.m_triggerDistance))
			{
				entry2.Status = Texts.Get("too_far", text);
				entry2.Color = Inactive;
			}
			else
			{
				float num = Mathf.Max(0f, area.m_spawnIntervalSec - SpawnAreaTimer.Invoke(area));
				entry2.Status = Texts.Get("next_in", FormatTime(num), text);
				entry2.Color = ((num <= 4f) ? Ready : Waiting);
			}
			return entry2;
		}

		private static void CountInstances(SpawnArea area, out int near, out int total)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			near = 0;
			total = 0;
			Vector3 position = ((Component)area).transform.position;
			foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances)
			{
				if (!((Object)(object)baseAIInstance == (Object)null) && IsFromArea(area, ((Component)baseAIInstance).gameObject))
				{
					float num = Utils.DistanceXZ(((Component)baseAIInstance).transform.position, position);
					if (num < area.m_nearRadius)
					{
						near++;
					}
					if (num < area.m_farRadius)
					{
						total++;
					}
				}
			}
		}

		private static bool IsFromArea(SpawnArea area, GameObject go)
		{
			Character component = go.GetComponent<Character>();
			if ((Object)(object)component != (Object)null && component.IsTamed())
			{
				return false;
			}
			foreach (SpawnData prefab in area.m_prefabs)
			{
				if ((Object)(object)prefab.m_prefab != (Object)null && ((Object)go).name.StartsWith(((Object)prefab.m_prefab).name, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private static string CreatureName(GameObject prefab)
		{
			Character component = prefab.GetComponent<Character>();
			if ((Object)(object)component != (Object)null && Localization.instance != null)
			{
				return Localization.instance.Localize(component.m_name);
			}
			return ((Object)prefab).name;
		}

		private static string FormatTime(double seconds)
		{
			TimeSpan timeSpan = TimeSpan.FromSeconds(Math.Ceiling(seconds));
			if (!(timeSpan.TotalHours >= 1.0))
			{
				return $"{timeSpan.Minutes}:{timeSpan.Seconds:00}";
			}
			return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
		}

		private void OnGUI()
		{
			//IL_0036: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			if (_visible && _entries.Count != 0 && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if (_labelStyle == null)
				{
					_labelStyle = new GUIStyle(GUI.skin.label)
					{
						alignment = (TextAnchor)4,
						fontSize = 14,
						fontStyle = (FontStyle)1,
						richText = true
					};
					_listStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = 14,
						richText = true
					};
				}
				if (_showLabels.Value)
				{
					DrawLabels();
				}
				if (_showList.Value)
				{
					DrawList();
				}
			}
		}

		private void DrawLabels()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			foreach (Entry entry in _entries)
			{
				Vector3 val = mainCamera.WorldToScreenPoint(entry.Position + Vector3.up * 2f);
				if (!(val.z <= 0f))
				{
					string text = $"{entry.Title} <size=12>({entry.Distance:0} m)</size>\n<color=#{ColorUtility.ToHtmlStringRGB(entry.Color)}>{entry.Status}</color>";
					DrawShadowed(new Rect(val.x - 160f, (float)Screen.height - val.y - 22f, 320f, 44f), text, _labelStyle);
				}
			}
		}

		private void DrawList()
		{
			//IL_0041: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.Min(_listSize.Value, _entries.Count);
			float num2 = (float)Screen.width - 460f - 20f;
			float num3 = 220f;
			DrawShadowed(new Rect(num2, num3, 460f, 20f), "<b>" + Texts.Get("list_title") + "</b> <size=11>(" + Texts.Get("list_hint", _toggleKey.Value) + ")</size>", _listStyle);
			for (int i = 0; i < num; i++)
			{
				Entry entry = _entries[i];
				num3 += 20f;
				string text = $"{entry.Distance,4:0} m  {entry.Title}: <color=#{ColorUtility.ToHtmlStringRGB(entry.Color)}>{entry.Status}</color>";
				DrawShadowed(new Rect(num2, num3, 460f, 20f), text, _listStyle);
			}
		}

		private static void DrawShadowed(Rect rect, string text, GUIStyle style)
		{
			//IL_0000: 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_004b: 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)
			Color color = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, 0.85f);
			GUI.Label(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), StripColors(text), style);
			GUI.color = color;
			GUI.Label(rect, text, style);
		}

		private static string StripColors(string text)
		{
			return Regex.Replace(text, "</?color[^>]*>", "");
		}
	}
	internal static class Texts
	{
		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			["alive"] = "Alive",
			["dead_no_respawn"] = "Dead · does not respawn",
			["respawns_in"] = "Respawns in {0}",
			["ready"] = "Ready to spawn",
			["ready_blocked"] = "Ready · {0}",
			["wait_night"] = "waiting for night",
			["wait_day"] = "waiting for day",
			["player_base"] = "blocked by a player base",
			["get_within"] = "get within {0} m",
			["needs_noise"] = "needs noise nearby",
			["needs_progress"] = "needs more world progress",
			["disabled_progress"] = "disabled by world progress",
			["counts"] = "{0}/{1} near, {2}/{3} total",
			["full"] = "Full ({0})",
			["other_owner"] = "Timer owned by another player ({0})",
			["too_far"] = "Inactive, too far ({0})",
			["next_in"] = "Next in ~{0} ({1})",
			["spawner"] = "Spawner: {0}",
			["list_title"] = "Nearby spawners",
			["list_hint"] = "{0} to hide"
		};

		private static readonly Dictionary<string, string> Spanish = new Dictionary<string, string>
		{
			["alive"] = "Vivo",
			["dead_no_respawn"] = "Muerto · no reaparece",
			["respawns_in"] = "Reaparece en {0}",
			["ready"] = "Listo para aparecer",
			["ready_blocked"] = "Listo · {0}",
			["wait_night"] = "espera la noche",
			["wait_day"] = "espera el día",
			["player_base"] = "bloqueado por una base",
			["get_within"] = "acércate a {0} m",
			["needs_noise"] = "necesita ruido cerca",
			["needs_progress"] = "requiere más progreso",
			["disabled_progress"] = "desactivado por el progreso",
			["counts"] = "{0}/{1} cerca, {2}/{3} total",
			["full"] = "Lleno ({0})",
			["other_owner"] = "Temporizador en otro jugador ({0})",
			["too_far"] = "Inactivo, muy lejos ({0})",
			["next_in"] = "Próximo en ~{0} ({1})",
			["spawner"] = "Spawner: {0}",
			["list_title"] = "Spawners cercanos",
			["list_hint"] = "{0} para ocultar"
		};

		private static readonly Dictionary<string, Dictionary<string, string>> Languages = new Dictionary<string, Dictionary<string, string>>
		{
			["English"] = English,
			["Spanish"] = Spanish
		};

		public static string Get(string key, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null);
			if (text == null || !Languages.TryGetValue(text, out var value) || !value.TryGetValue(key, out var value2))
			{
				value2 = English[key];
			}
			if (args.Length != 0)
			{
				return string.Format(value2, args);
			}
			return value2;
		}
	}
}