Decompiled source of Custom Enemy Location v1.0.5

CustomEnemyLocation.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
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: AssemblyCompany("CustomEnemyLocation")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5")]
[assembly: AssemblyProduct("CustomEnemyLocation")]
[assembly: AssemblyTitle("CustomEnemyLocation")]
[assembly: AssemblyVersion("1.0.5.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace com.ugleh.repo
{
	[BepInPlugin("com.ugleh.repo.customenemylocation", "Custom Enemy Location", "1.0.5")]
	public sealed class CustomEnemyLocationPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <ScanLoop>d__20 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public CustomEnemyLocationPlugin <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ScanLoop>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Expected O, but got Unknown
				int num = <>1__state;
				CustomEnemyLocationPlugin customEnemyLocationPlugin = <>4__this;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
				}
				else
				{
					<>1__state = -1;
				}
				if (customEnemyLocationPlugin._enableEnemyIcons.Value)
				{
					customEnemyLocationPlugin.TryTrackEnemies();
				}
				else
				{
					ClearAllMarkers();
				}
				<>2__current = (object)new WaitForSeconds(Mathf.Clamp(customEnemyLocationPlugin._scanIntervalSeconds.Value, 0.1f, 10f));
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string PluginGuid = "com.ugleh.repo.customenemylocation";

		public const string PluginName = "Custom Enemy Location";

		public const string PluginVersion = "1.0.5";

		private static readonly FieldInfo? EnemyDeadField = typeof(EnemyHealth).GetField("dead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo? EnemyParentSpawnedField = typeof(EnemyParent).GetField("Spawned", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo? PlayerDeathHeadField = typeof(PlayerAvatar).GetField("playerDeathHead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private ConfigEntry<bool> _enableEnemyIcons;

		private ConfigEntry<string> _enemyIconColorHex;

		private ConfigEntry<float> _scanIntervalSeconds;

		private ConfigEntry<bool> _hideDeadEnemies;

		private ConfigEntry<bool> _onlyTrackSpawnedEnemies;

		private Sprite? _markerSprite;

		private Sprite? _fallbackSprite;

		private bool _loggedMissingSprite;

		private bool _loggedReady;

		internal static ManualLogSource Log { get; private set; } = null;


		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			_enableEnemyIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Enemy Icons", true, "Toggle visibility of enemies on the minimap");
			_enemyIconColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Enemy Icon Color (Hex)", "#8000FF", "Hex color code for enemy icons (e.g. #FF0000 for red)");
			_scanIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Scan Interval Seconds", 0.75f, "How often to look for new enemy instances.");
			_hideDeadEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Dead Enemies", true, "Hide dots for enemies whose EnemyHealth.dead flag is set.");
			_onlyTrackSpawnedEnemies = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Only Track Spawned Enemies", true, "Only show dots for enemies whose EnemyParent.Spawned flag is set.");
			((MonoBehaviour)this).StartCoroutine(ScanLoop());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Enemy Location 1.0.5 loaded.");
		}

		[IteratorStateMachine(typeof(<ScanLoop>d__20))]
		private IEnumerator ScanLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScanLoop>d__20(0)
			{
				<>4__this = this
			};
		}

		private void TryTrackEnemies()
		{
			//IL_005e: 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_0068: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (!MapIsReady())
			{
				return;
			}
			Sprite val = ResolveMarkerSprite();
			if ((Object)(object)val == (Object)null)
			{
				if (!_loggedMissingSprite)
				{
					_loggedMissingSprite = true;
					Log.LogWarning((object)"Could not resolve a map marker sprite yet; enemy dots will appear once the map/player marker is available.");
				}
				return;
			}
			_loggedMissingSprite = false;
			Color color = ParseColor(_enemyIconColorHex.Value, new Color(0.5f, 0f, 1f, 1f));
			Enemy[] array = Object.FindObjectsOfType<Enemy>();
			foreach (Enemy val2 in array)
			{
				if (!ShouldTrack(val2))
				{
					HideMarker(val2);
					continue;
				}
				EnemyMapMarker enemyMapMarker = ((Component)val2).GetComponent<EnemyMapMarker>();
				if ((Object)(object)enemyMapMarker == (Object)null)
				{
					enemyMapMarker = ((Component)val2).gameObject.AddComponent<EnemyMapMarker>();
				}
				enemyMapMarker.Configure(val, color, GetIconScaleByEnemyType(val2.Type));
				enemyMapMarker.TryAddToMap();
			}
		}

		private bool MapIsReady()
		{
			try
			{
				if (!SemiFunc.RunIsLevel() || SemiFunc.RunIsShop() || SemiFunc.RunIsArena() || SemiFunc.RunIsLobby())
				{
					return false;
				}
			}
			catch (Exception)
			{
				return false;
			}
			if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
			{
				return false;
			}
			if ((Object)(object)Map.Instance == (Object)null || (Object)(object)Map.Instance.CustomObject == (Object)null || (Object)(object)Map.Instance.OverLayerParent == (Object)null)
			{
				return false;
			}
			if (!_loggedReady)
			{
				_loggedReady = true;
				Log.LogInfo((object)"Map is ready; enemy tracker is attaching markers.");
			}
			return true;
		}

		private bool ShouldTrack(Enemy enemy)
		{
			if ((Object)(object)enemy == (Object)null || !((Behaviour)enemy).isActiveAndEnabled || !((Component)enemy).gameObject.activeInHierarchy)
			{
				return false;
			}
			if (_onlyTrackSpawnedEnemies.Value && !IsSpawned(enemy))
			{
				return false;
			}
			if (_hideDeadEnemies.Value)
			{
				return !IsDead(enemy);
			}
			return true;
		}

		private static bool IsSpawned(Enemy enemy)
		{
			if (EnemyParentSpawnedField == null)
			{
				return true;
			}
			EnemyParent componentInParent = ((Component)enemy).GetComponentInParent<EnemyParent>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			object value = EnemyParentSpawnedField.GetValue(componentInParent);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static bool IsDead(Enemy enemy)
		{
			if (EnemyDeadField == null)
			{
				return false;
			}
			EnemyHealth component = ((Component)enemy).GetComponent<EnemyHealth>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			object value = EnemyDeadField.GetValue(component);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private Sprite? ResolveMarkerSprite()
		{
			if ((Object)(object)_markerSprite != (Object)null)
			{
				return _markerSprite;
			}
			_markerSprite = FindPlayerDeathHeadSprite();
			if ((Object)(object)_markerSprite != (Object)null)
			{
				return _markerSprite;
			}
			if (_fallbackSprite == null)
			{
				_fallbackSprite = CreateCircleSprite();
			}
			return _fallbackSprite;
		}

		private static Sprite? FindPlayerDeathHeadSprite()
		{
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null || GameDirector.instance.PlayerList.Count == 0)
			{
				return null;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if (!((Object)(object)player == (Object)null))
				{
					object? obj = PlayerDeathHeadField?.GetValue(player);
					PlayerDeathHead val = (PlayerDeathHead)((obj is PlayerDeathHead) ? obj : null);
					if (val != null && (Object)(object)val.mapCustom != (Object)null && (Object)(object)val.mapCustom.sprite != (Object)null)
					{
						return val.mapCustom.sprite;
					}
				}
			}
			return null;
		}

		private static Sprite CreateCircleSprite()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false);
			Color32[] array = (Color32[])(object)new Color32[1024];
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					float num = (float)j - 15.5f;
					float num2 = (float)i - 15.5f;
					byte b = (byte)Mathf.RoundToInt(Mathf.Clamp01(13.94f - Mathf.Sqrt(num * num + num2 * num2)) * 255f);
					array[i * 32 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			((Object)val).name = "CustomEnemyLocationFallbackCircle";
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 32f);
			((Object)obj).name = "CustomEnemyLocationFallbackCircle";
			return obj;
		}

		private static Color ParseColor(string value, Color fallback)
		{
			//IL_002d: 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)
			string text = value.Trim();
			if (!text.StartsWith("#", StringComparison.Ordinal))
			{
				text = "#" + text;
			}
			Color result = default(Color);
			if (!ColorUtility.TryParseHtmlString(text, ref result))
			{
				return fallback;
			}
			return result;
		}

		private static float GetIconScaleByEnemyType(EnemyType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Expected I4, but got Unknown
			return (int)type switch
			{
				0 => 0.65f, 
				1 => 0.85f, 
				2 => 1f, 
				3 => 1.25f, 
				4 => 1.5f, 
				_ => 1f, 
			};
		}

		private static void HideMarker(Enemy enemy)
		{
			if (!((Object)(object)enemy == (Object)null))
			{
				EnemyMapMarker component = ((Component)enemy).GetComponent<EnemyMapMarker>();
				if ((Object)(object)component != (Object)null)
				{
					component.ResetMarker();
				}
			}
		}

		private static void ClearAllMarkers()
		{
			EnemyMapMarker[] array = Object.FindObjectsOfType<EnemyMapMarker>();
			foreach (EnemyMapMarker obj in array)
			{
				obj.ResetMarker();
				Object.Destroy((Object)(object)obj);
			}
		}
	}
	internal sealed class EnemyMapMarker : MonoBehaviour
	{
		private static readonly FieldInfo? MapCustomEntityField = typeof(MapCustom).GetField("mapCustomEntity", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private MapCustom? _mapCustom;

		private Sprite? _sprite;

		private Color _color;

		private float _scale = 1f;

		private bool _added;

		private bool _loggedAddFailure;

		public void Configure(Sprite sprite, Color color, float scale)
		{
			//IL_0008: 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_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)
			_sprite = sprite;
			_color = color;
			_scale = scale;
			EnsureMapCustom();
			if (!((Object)(object)_mapCustom == (Object)null))
			{
				_mapCustom.autoAdd = false;
				_mapCustom.sprite = sprite;
				_mapCustom.color = color;
				ApplyScale(_mapCustom, _scale);
			}
		}

		public void TryAddToMap()
		{
			//IL_005a: 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)
			if (_added || (Object)(object)_sprite == (Object)null || !((Behaviour)this).isActiveAndEnabled || !((Component)this).gameObject.activeInHierarchy)
			{
				return;
			}
			EnsureMapCustom();
			if ((Object)(object)_mapCustom == (Object)null)
			{
				return;
			}
			try
			{
				_mapCustom.sprite = _sprite;
				_mapCustom.color = _color;
				_mapCustom.Add();
				_added = HasMapEntity(_mapCustom);
				ApplyScale(_mapCustom, _scale);
				_loggedAddFailure = false;
			}
			catch (Exception ex)
			{
				if (!_loggedAddFailure)
				{
					_loggedAddFailure = true;
					CustomEnemyLocationPlugin.Log.LogWarning((object)("Failed to add enemy marker for " + ((Object)((Component)this).gameObject).name + ": " + ex.Message));
				}
			}
		}

		public void ResetMarker()
		{
			if ((Object)(object)_mapCustom != (Object)null)
			{
				_mapCustom.Hide();
				DestroyMapEntity(_mapCustom);
			}
			_added = false;
		}

		private void OnDisable()
		{
			ResetMarker();
		}

		private void OnDestroy()
		{
			ResetMarker();
		}

		private void EnsureMapCustom()
		{
			if (!((Object)(object)_mapCustom != (Object)null))
			{
				_mapCustom = ((Component)this).GetComponent<MapCustom>();
				if ((Object)(object)_mapCustom == (Object)null)
				{
					_mapCustom = ((Component)this).gameObject.AddComponent<MapCustom>();
				}
			}
		}

		private static bool HasMapEntity(MapCustom mapCustom)
		{
			if (MapCustomEntityField == null)
			{
				return true;
			}
			object? value = MapCustomEntityField.GetValue(mapCustom);
			MapCustomEntity val = (MapCustomEntity)((value is MapCustomEntity) ? value : null);
			if (val != null)
			{
				return (Object)(object)val != (Object)null;
			}
			return false;
		}

		private static void ApplyScale(MapCustom mapCustom, float scale)
		{
			//IL_0031: 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)
			if (!(MapCustomEntityField == null))
			{
				object? value = MapCustomEntityField.GetValue(mapCustom);
				MapCustomEntity val = (MapCustomEntity)((value is MapCustomEntity) ? value : null);
				if (val != null && (Object)(object)val != (Object)null)
				{
					((Component)val).transform.localScale = Vector3.one * scale;
				}
			}
		}

		private static void DestroyMapEntity(MapCustom mapCustom)
		{
			if (!(MapCustomEntityField == null))
			{
				object? value = MapCustomEntityField.GetValue(mapCustom);
				MapCustomEntity val = (MapCustomEntity)((value is MapCustomEntity) ? value : null);
				if (val != null && (Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
				MapCustomEntityField.SetValue(mapCustom, null);
			}
		}
	}
}