Decompiled source of HexRareScanner v1.0.0

Plugins/HexRareScanner.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HexRareScanner;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("HexRareScanner")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HexRareScanner")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3c9bc10-9d16-4f15-b8bd-d4fe1c71af33")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[HarmonyPatch(typeof(Character), "OnDeath")]
internal static class PatchCharacterOnDeath
{
	internal static void Prefix(Character __instance)
	{
		if (Plugin.IsModEnabled && !((Object)(object)__instance == (Object)null))
		{
			string prefabNameFromClone = PrefabNameHelper.GetPrefabNameFromClone(((Object)((Component)__instance).gameObject).name);
			int creatureLevel = Plugin.GetCreatureLevel(__instance);
			if (Plugin.IsTrackedPrefab(prefabNameFromClone, creatureLevel))
			{
				PinManager.RemoveCreaturePin(__instance);
			}
		}
	}
}
namespace HexRareScanner
{
	internal static class PinManager
	{
		private static readonly Dictionary<ZDOID, PinData> PinsByZdoid = new Dictionary<ZDOID, PinData>();

		private static readonly FieldInfo CharacterNViewField = typeof(Character).GetField("m_nview", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo RemovePinMethod = typeof(Minimap).GetMethod("RemovePin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(PinData) }, null);

		internal static void AddCreaturePin(Character character, Vector3 position, string pinName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0032: 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)
			//IL_006c: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			ZDOID zdoId = GetZdoId(character);
			string text = (((Object)(object)character != (Object)null) ? ((Object)((Component)character).gameObject).name : "null");
			if (zdoId == ZDOID.None)
			{
				Plugin.Log.LogWarning((object)("Could not get ZDOID for character " + text + ". Pin will not be added."));
			}
			else if (!PinsByZdoid.ContainsKey(zdoId))
			{
				PinData val = Minimap.instance.AddPin(position, (PinType)12, pinName, false, false, 0L, default(PlatformUserID));
				if (val != null)
				{
					PinsByZdoid[zdoId] = val;
				}
			}
		}

		internal static void RemoveCreaturePin(Character character)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0032: 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)
			//IL_00a0: 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)
			if (!((Object)(object)Minimap.instance == (Object)null))
			{
				ZDOID zdoId = GetZdoId(character);
				string text = (((Object)(object)character != (Object)null) ? ((Object)((Component)character).gameObject).name : "null");
				PinData value;
				if (zdoId == ZDOID.None)
				{
					Plugin.Log.LogWarning((object)("Could not get ZDOID for character " + text + ". Pin will not be removed."));
				}
				else if (PinsByZdoid.TryGetValue(zdoId, out value))
				{
					RemovePinMethod?.Invoke(Minimap.instance, new object[1] { value });
					PinsByZdoid.Remove(zdoId);
				}
				else
				{
					Plugin.Log.LogWarning((object)$"No pin found for {text}. ZDOID: {zdoId}");
				}
			}
		}

		internal static ZDOID GetZdoId(Character character)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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)
			if ((Object)(object)character == (Object)null || CharacterNViewField == null)
			{
				return ZDOID.None;
			}
			object? value = CharacterNViewField.GetValue(character);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || val.GetZDO() == null)
			{
				return ZDOID.None;
			}
			return val.GetZDO().m_uid;
		}

		internal static bool HasCreaturePin(Character character)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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)
			ZDOID zdoId = GetZdoId(character);
			if (zdoId == ZDOID.None)
			{
				return false;
			}
			return PinsByZdoid.ContainsKey(zdoId);
		}

		internal static void Clear()
		{
			PinsByZdoid.Clear();
		}
	}
	[BepInPlugin("com.hex.rarescanner", "HexRareScanner", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class TrackedCreatureSetting
		{
			internal ConfigEntry<bool> Enabled { get; }

			internal string DisplayName { get; }

			internal string SoundEffectName { get; }

			internal int RarityLevel { get; }

			internal TrackedCreatureSetting(ConfigEntry<bool> enabled, string displayName, string soundEffectName, int rarityLevel)
			{
				Enabled = enabled;
				DisplayName = displayName;
				SoundEffectName = soundEffectName;
				RarityLevel = rarityLevel;
			}
		}

		private const string PluginGuid = "com.hex.rarescanner";

		private const string PluginName = "HexRareScanner";

		private const string PluginVersion = "1.0.0";

		private Harmony _harmonyInstance;

		private static ConfigEntry<bool> _isModEnabled;

		private static ConfigEntry<bool> _playTrackedCreatureSound;

		private static readonly Dictionary<string, TrackedCreatureSetting> TrackedCreatures = new Dictionary<string, TrackedCreatureSetting>();

		internal static readonly FieldInfo CharacterMLevelField = typeof(Character).GetField("m_level", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static bool IsModEnabled => _isModEnabled?.Value ?? false;

		internal static bool PlayTrackedCreatureSound => _playTrackedCreatureSound?.Value ?? false;

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			InitializeConfig();
			_harmonyInstance = new Harmony("com.hex.rarescanner");
			_harmonyInstance.PatchAll();
			Log.LogInfo((object)"HexRareScanner v1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			Log.LogInfo((object)"HexRareScanner v1.0.0 unloaded.");
			Harmony harmonyInstance = _harmonyInstance;
			if (harmonyInstance != null)
			{
				harmonyInstance.UnpatchSelf();
			}
			_harmonyInstance = null;
			Instance = null;
			Log = null;
		}

		private void InitializeConfig()
		{
			TrackedCreatures.Clear();
			_isModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IsModEnabled", true, "Enable or disable the mod.");
			_playTrackedCreatureSound = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PlayTrackedCreatureSound", true, "Enable or disable the tracked creature spawn sound.");
			AddTrackedCreature("Serpent", "Track Sea Serpents", "Sea Serpent", "sfx_serpent_taunt");
			AddTrackedCreature("BonemawSerpent", "Track Bonemaw Serpents", "Bonemaw Serpent", "sfx_bonemaw_serpent_alert");
			AddTrackedCreature("Troll", "Track Trolls", "Troll", "sfx_troll_idle");
			AddTrackedCreature("Bjorn", "Track Black Forest Bears", "Black Forest Bear", "sfx_bear_bite_attack");
			AddTrackedCreature("Unbjorn", "Track Vile Bears", "Vile Bear", "sfx_bear_bite_attack");
			AddTrackedCreature("Abomination", "Track Abominations", "Abomination", "sfx_abomination_arise_end");
			AddTrackedCreature("StoneGolem", "Track Stone Golems", "Stone Golem", "sfx_stonegolem_idle");
			AddTrackedCreature("Morgen", "Track Morgens", "Morgen", "sfx_morgen_idle");
			AddTrackedCreature("Wolf", "Track 2-star Wolves", "Wolf", "sfx_wolf_alerted", 3);
			AddTrackedCreature("Boar", "Track 2-star Boars", "Boar", "sfx_boar_idle", 3);
			AddTrackedCreature("Deer", "Track 2-star Deer", "Deer", "sfx_deer_idle", 3);
			AddTrackedCreature("Asksvin", "Track 2-star Asksvin", "Asksvin", "sfx_asksvin_idle", 3);
			AddTrackedCreature("FallenValkyrie", "Track Fallen Valkyrie", "FallenValkyrie", "sfx_valkyrie_flapwing");
		}

		private void AddTrackedCreature(string prefabName, string configName, string displayName, string soundEffectName, int rarityLevel = 1)
		{
			TrackedCreatures[prefabName] = new TrackedCreatureSetting(((BaseUnityPlugin)this).Config.Bind<bool>("Tracking", configName, true, "Enable or disable tracking of " + displayName + "."), displayName, soundEffectName, rarityLevel);
		}

		internal static bool IsTrackedPrefab(string prefabName, int creatureLevel)
		{
			if (!IsModEnabled)
			{
				return false;
			}
			if (TryGetTrackedCreature(prefabName, out var trackedCreature) && trackedCreature.Enabled != null && trackedCreature.Enabled.Value)
			{
				return creatureLevel >= trackedCreature.RarityLevel;
			}
			return false;
		}

		internal static string GetDisplayName(string prefabName)
		{
			if (!TryGetTrackedCreature(prefabName, out var trackedCreature))
			{
				return prefabName;
			}
			return trackedCreature.DisplayName;
		}

		internal static string GetSoundEffectName(string prefabName)
		{
			if (!TryGetTrackedCreature(prefabName, out var trackedCreature))
			{
				return null;
			}
			return trackedCreature.SoundEffectName;
		}

		internal static int GetCreatureLevel(Character character)
		{
			if ((Object)(object)character == (Object)null || CharacterMLevelField == null)
			{
				return 1;
			}
			object value = CharacterMLevelField.GetValue(character);
			if (value is int)
			{
				return (int)value;
			}
			return 1;
		}

		private static bool TryGetTrackedCreature(string prefabName, out TrackedCreatureSetting trackedCreature)
		{
			trackedCreature = null;
			if (string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			if (TrackedCreatures.TryGetValue(prefabName, out trackedCreature))
			{
				return trackedCreature != null;
			}
			return false;
		}
	}
	internal static class PrefabNameHelper
	{
		internal static string GetPrefabNameFromClone(string objectName)
		{
			if (string.IsNullOrEmpty(objectName))
			{
				return string.Empty;
			}
			if (objectName.EndsWith("(Clone)"))
			{
				return objectName.Substring(0, objectName.Length - "(Clone)".Length);
			}
			return objectName;
		}
	}
}
namespace HexRareScanner.Patches
{
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class PatchCharacterAwake
	{
		private static void Postfix(Character __instance)
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine(DelayedScan(__instance));
		}

		private static void PlayTrackedCreatureSound(string soundEffectName, Vector3 position)
		{
			//IL_0052: 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)
			if (string.IsNullOrEmpty(soundEffectName))
			{
				return;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				Plugin.Log.LogWarning((object)"ZNetScene.instance is null. Could not play tracked creature sound.");
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(soundEffectName);
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Could not find sound effect prefab: " + soundEffectName));
			}
			else
			{
				Object.Instantiate<GameObject>(prefab, position, Quaternion.identity);
			}
		}

		private static IEnumerator DelayedScan(Character character)
		{
			yield return null;
			yield return null;
			if (!Plugin.IsModEnabled || (Object)(object)character == (Object)null)
			{
				yield break;
			}
			string prefabNameFromClone = PrefabNameHelper.GetPrefabNameFromClone(((Object)((Component)character).gameObject).name);
			int creatureLevel = Plugin.GetCreatureLevel(character);
			if (Plugin.IsTrackedPrefab(prefabNameFromClone, creatureLevel) && !PinManager.HasCreaturePin(character))
			{
				string text = Plugin.GetDisplayName(prefabNameFromClone);
				if (creatureLevel > 1)
				{
					text = $"{creatureLevel - 1}-star {text}";
				}
				Vector3 position = ((Component)character).transform.position;
				if (Plugin.PlayTrackedCreatureSound)
				{
					PlayTrackedCreatureSound(Plugin.GetSoundEffectName(prefabNameFromClone), position);
				}
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "A " + text + " spawned!", 0, (Sprite)null);
				}
				PinManager.AddCreaturePin(character, position, text);
			}
		}
	}
}