Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ShowEnemyHealth Fixed v1.0.5
REPO_ShowEnemyHealth.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; 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: AssemblyCompany("REPO_ShowEnemyHealth")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+16aa0046be6243b25b8d3c518bf413179277c854")] [assembly: AssemblyProduct("ShowEnemyHealth Fixed Edition")] [assembly: AssemblyTitle("REPO_ShowEnemyHealth")] [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 ShowEnemyHealth { internal static class ConfigManager { public static ConfigFile ConfigFile { get; private set; } public static ConfigEntry<bool> ShowEnemyHealth { get; private set; } public static ConfigEntry<float> HealthFontSize { get; private set; } public static ConfigEntry<string> HealthFontColor { get; private set; } public static ConfigEntry<bool> ShowEnemyDamage { get; private set; } public static ConfigEntry<float> DamageFontSize { get; private set; } public static ConfigEntry<string> DamageFontHighlightColor { get; private set; } public static ConfigEntry<string> DamageFontColor { get; private set; } public static void Initialize(ConfigFile configFile) { ConfigFile = configFile; BindConfigs(); } private static void BindConfigs() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown AcceptableValueList<string> val = new AcceptableValueList<string>(new string[10] { "black", "blue", "cyan", "green", "grey", "magenta", "orange", "red", "white", "yellow" }); ShowEnemyHealth = ConfigFile.Bind<bool>("Health", "ShowEnemyHealth", true, "Show enemy health text"); HealthFontSize = ConfigFile.Bind<float>("Health", "HealthFontSize", 15f, new ConfigDescription("Health text size", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 50f), Array.Empty<object>())); HealthFontColor = ConfigFile.Bind<string>("Health", "HealthFontColor", "grey", new ConfigDescription("Health text color", (AcceptableValueBase)(object)val, Array.Empty<object>())); ShowEnemyDamage = ConfigFile.Bind<bool>("Damage", "ShowEnemyDamage", true, "Show damage text"); DamageFontSize = ConfigFile.Bind<float>("Damage", "DamageFontSize", 25f, new ConfigDescription("Damage text size", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 50f), Array.Empty<object>())); DamageFontHighlightColor = ConfigFile.Bind<string>("Damage", "DamageFontHighlightColor", "red", new ConfigDescription("Damage highlight color", (AcceptableValueBase)(object)val, Array.Empty<object>())); DamageFontColor = ConfigFile.Bind<string>("Damage", "DamageFontColor", "orange", new ConfigDescription("Damage text color", (AcceptableValueBase)(object)val, Array.Empty<object>())); } } internal class CustomDamageTextOcclusion : MonoBehaviour { private WorldSpaceUIValueLost valueLost; private CanvasGroup canvasGroup; private Enemy currEnemy; private EnemyHealth currEnemyHealth; private EnemyRigidbody enemyRigidBody; private int occlusionMask; private bool isOccluded; private bool wasOccluded; private float baseAlpha = 1f; private float nextOcclusionCheckTime; private void Awake() { occlusionMask = UIOcclusionHelper.BuildOcclusionMask(); } internal void Initialize(WorldSpaceUIValueLost component, Enemy enemy, EnemyHealth enemyHealth, EnemyRigidbody rigidBody) { valueLost = component; currEnemy = enemy; currEnemyHealth = enemyHealth; enemyRigidBody = rigidBody; EnsureCanvasGroup(); } private void LateUpdate() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)valueLost == (Object)null) { ((Behaviour)this).enabled = false; return; } EnsureCanvasGroup(); if (!((Object)(object)canvasGroup == (Object)null)) { if (!wasOccluded || canvasGroup.alpha > 0f) { baseAlpha = canvasGroup.alpha; } if (Time.time >= nextOcclusionCheckTime) { nextOcclusionCheckTime = Time.time + 0.1f; isOccluded = UIOcclusionHelper.IsOccluded(Camera.main, GetWorldPosition(), occlusionMask, currEnemy, currEnemyHealth, enemyRigidBody); } canvasGroup.alpha = (isOccluded ? 0f : baseAlpha); wasOccluded = isOccluded; } } private void EnsureCanvasGroup() { if (!((Object)(object)canvasGroup != (Object)null)) { canvasGroup = ((Component)this).GetComponent<CanvasGroup>(); if ((Object)(object)canvasGroup == (Object)null) { canvasGroup = ((Component)this).gameObject.AddComponent<CanvasGroup>(); } } } private Vector3 GetWorldPosition() { //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_005f: Unknown result type (might be due to invalid IL or missing references) //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) //IL_004a: 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) if ((Object)(object)valueLost != (Object)null && Patches.WorldSpaceUIValueLostWorldPositionField != null && Patches.WorldSpaceUIValueLostWorldPositionField.GetValue(valueLost) is Vector3 result) { return result; } return ((Component)this).transform.position; } } [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(TextMeshProUGUI))] internal class CustomEnemyHealthUI : WorldSpaceUIChild { private Vector3 scale = default(Vector3); internal Vector3 newWorldPosition = default(Vector3); private Enemy currEnemy; private EnemyHealth currEnemyHealth; private EnemyRigidbody enemyRigidBody; private TextMeshProUGUI text; private bool initialized = false; private bool isOccluded = false; private float nextOcclusionCheckTime = 0f; private int occlusionMask; private Vector3 worldspaceOffset = default(Vector3); private Vector3 targetWorldspaceOffset = default(Vector3); public int recalcIntervalFrames = 50; private int frameCounter = 0; public float worldspaceOffsetLerpRate = 5f; private void Awake() { //IL_0011: 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_0022: Unknown result type (might be due to invalid IL or missing references) Patches.SetWorldSpaceUIChildPositionOffset((WorldSpaceUIChild)(object)this, new Vector3(0f, -0.05f, 0f)); scale = Vector3.one; text = ((Component)this).GetComponent<TextMeshProUGUI>(); ((TMP_Text)text).alpha = 0f; occlusionMask = UIOcclusionHelper.BuildOcclusionMask(); } public void Show(EnemyHealth enemyHealth, Vector3 screenspaceOffset) { //IL_003b: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_011e: 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) if (!ConfigManager.ShowEnemyHealth.Value) { Patches.RemoveHealthUI(((Object)(object)currEnemyHealth != (Object)null) ? currEnemyHealth : enemyHealth); return; } ((Graphic)text).color = Color.gray; Color32 val = default(Color32); if (ColorUtility.DoTryParseHtmlColor(ConfigManager.HealthFontColor.Value, ref val)) { ((Graphic)text).color = Color32.op_Implicit(val); } ((TMP_Text)text).fontSize = ConfigManager.HealthFontSize.Value; if (!Object.op_Implicit((Object)(object)currEnemy) || !Object.op_Implicit((Object)(object)currEnemyHealth)) { Patches.SetWorldSpaceUIChildPositionOffset((WorldSpaceUIChild)(object)this, screenspaceOffset); currEnemyHealth = enemyHealth; currEnemy = Patches.GetEnemy(enemyHealth); if ((Object)(object)currEnemy == (Object)null) { Patches.RemoveHealthUI(this); return; } newWorldPosition = Patches.GetEnemyPosition(currEnemy, currEnemyHealth); Patches.SetWorldSpaceUIChildPosition((WorldSpaceUIChild)(object)this, newWorldPosition); enemyRigidBody = Patches.GetEnemyRigidbody(currEnemy); UpdateTargetWorldSpaceOffset(); worldspaceOffset = targetWorldspaceOffset; UpdateTextValues(); initialized = true; ((TMP_Text)text).alpha = 0f; } } internal void UpdateTargetWorldSpaceOffset() { //IL_001a: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_006d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemyRigidBody == (Object)null) { return; } Bounds val = default(Bounds); bool flag = false; Collider[] componentsInChildren = ((Component)enemyRigidBody).GetComponentsInChildren<Collider>(); Collider[] array = componentsInChildren; foreach (Collider val2 in array) { if (!((Object)(object)val2 == (Object)null) && !val2.isTrigger) { if (!flag) { val = val2.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val2.bounds); } } } if (!flag) { targetWorldspaceOffset = new Vector3(0f, 1f, 0f); return; } float y = Patches.GetEnemyPosition(currEnemy, currEnemyHealth).y; float num = Mathf.Clamp(((Bounds)(ref val)).max.y - y + 0.15f, 0.5f, 12f); targetWorldspaceOffset = new Vector3(0f, num, 0f); } internal void UpdateTextValues() { if (!((Object)(object)currEnemy == (Object)null) && !((Object)(object)currEnemyHealth == (Object)null) && !((Object)(object)text == (Object)null)) { int num = Mathf.Max(0, Patches.GetHealthCurrent(currEnemyHealth)); int health = currEnemyHealth.health; ((TMP_Text)text).text = $"{num}/{health}"; } } protected override void Update() { //IL_00a3: 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_00ba: 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) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) if (!initialized) { return; } if ((Object)(object)currEnemyHealth == (Object)null || (Object)(object)currEnemy == (Object)null) { Patches.RemoveHealthUI(this); return; } if (!Patches.IsEnemyHealthDisplayable(currEnemyHealth)) { Patches.RemoveHealthUI(currEnemyHealth); return; } frameCounter++; if (frameCounter >= recalcIntervalFrames) { frameCounter = 0; UpdateTargetWorldSpaceOffset(); } worldspaceOffset = Vector3.Lerp(worldspaceOffset, targetWorldspaceOffset, Time.deltaTime * worldspaceOffsetLerpRate); if ((Object)(object)currEnemy != (Object)null) { newWorldPosition = Patches.GetEnemyPosition(currEnemy, currEnemyHealth); } Patches.SetWorldSpaceUIChildPosition((WorldSpaceUIChild)(object)this, newWorldPosition + worldspaceOffset); ((WorldSpaceUIChild)this).Update(); float num = 1f; float num2 = 5f; float num3 = 20f; Camera main = Camera.main; float num4 = (((Object)(object)main != (Object)null) ? Vector3.Distance(((Component)main).transform.position, newWorldPosition) : num2); Vector3 localScale = scale; if (num4 > num2) { num4 = Mathf.Clamp(num4, num2, num3); num = 1f - (num4 - num2) / (num3 - num2); ((Vector3)(ref localScale))..ctor(num, num, num); } if (Patches.ShouldBypassHealthOcclusion(currEnemyHealth, main, newWorldPosition)) { isOccluded = false; } else if (num4 < num3 && Time.time >= nextOcclusionCheckTime) { nextOcclusionCheckTime = Time.time + 0.1f; isOccluded = UIOcclusionHelper.IsOccluded(main, newWorldPosition + worldspaceOffset, occlusionMask, currEnemy, currEnemyHealth, enemyRigidBody); } else if (num4 >= num3) { isOccluded = false; } ((TMP_Text)text).alpha = ((!isOccluded) ? num : 0f); ((Component)this).transform.localScale = localScale; UpdateTextValues(); } } [HarmonyPatch] public static class Patches { internal static readonly Dictionary<EnemyHealth, CustomEnemyHealthUI> healthUIElements = new Dictionary<EnemyHealth, CustomEnemyHealthUI>(); internal static readonly Dictionary<EnemyHealth, Coroutine> createHealthCoroutines = new Dictionary<EnemyHealth, Coroutine>(); internal static readonly HashSet<EnemyHealth> damagedEnemyHealth = new HashSet<EnemyHealth>(); internal const float DamagedOcclusionBypassDistance = 10f; internal static readonly FieldInfo EnemyHealthEnemyField = typeof(EnemyHealth).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyHealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyHealthDeadField = typeof(EnemyHealth).GetField("dead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyHealthField = typeof(Enemy).GetField("Health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyEnemyParentField = typeof(Enemy).GetField("EnemyParent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyRigidbodyField = typeof(Enemy).GetField("Rigidbody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyParentEnemyField = typeof(EnemyParent).GetField("Enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(EnemyParent).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo EnemyBangEnemyField = typeof(EnemyBang).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIChildWorldPositionField = typeof(WorldSpaceUIChild).GetField("worldPosition", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIChildPositionOffsetField = typeof(WorldSpaceUIChild).GetField("positionOffset", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIValueLostWorldPositionField = typeof(WorldSpaceUIValueLost).GetField("worldPosition", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIValueLostValueField = typeof(WorldSpaceUIValueLost).GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIValueLostTextField = typeof(WorldSpaceUIValueLost).GetField("text", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIValueLostTextColorField = typeof(WorldSpaceUIValueLost).GetField("textColor", BindingFlags.Instance | BindingFlags.NonPublic); internal static readonly FieldInfo WorldSpaceUIParentValueLostListField = typeof(WorldSpaceUIParent).GetField("valueLostList", BindingFlags.Instance | BindingFlags.NonPublic); internal static Enemy GetEnemy(EnemyHealth enemyHealth) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if ((Object)(object)enemyHealth == (Object)null) { return null; } return (Enemy)(EnemyHealthEnemyField?.GetValue(enemyHealth)); } internal static int GetHealthCurrent(EnemyHealth enemyHealth) { if ((Object)(object)enemyHealth == (Object)null) { return 0; } return (int)(EnemyHealthCurrentField?.GetValue(enemyHealth) ?? ((object)0)); } internal static void SetHealthCurrent(EnemyHealth enemyHealth, int value) { if (!((Object)(object)enemyHealth == (Object)null)) { EnemyHealthCurrentField?.SetValue(enemyHealth, value); } } internal static bool GetDead(EnemyHealth enemyHealth) { if ((Object)(object)enemyHealth == (Object)null) { return false; } return (bool)(EnemyHealthDeadField?.GetValue(enemyHealth) ?? ((object)false)); } internal static Vector3 GetEnemyPosition(Enemy enemy, EnemyHealth enemyHealth) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0084: 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_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemy != (Object)null && (Object)(object)enemy.CenterTransform != (Object)null) { return enemy.CenterTransform.position; } if ((Object)(object)enemy != (Object)null && (Object)(object)((Component)enemy).transform != (Object)null) { return ((Component)enemy).transform.position; } if ((Object)(object)enemyHealth != (Object)null && (Object)(object)((Component)enemyHealth).transform != (Object)null) { return ((Component)enemyHealth).transform.position; } return Vector3.zero; } internal static EnemyRigidbody GetEnemyRigidbody(Enemy enemy) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if ((Object)(object)enemy == (Object)null) { return null; } return (EnemyRigidbody)(EnemyRigidbodyField?.GetValue(enemy)); } internal static void MarkDamaged(EnemyHealth enemyHealth) { if ((Object)(object)enemyHealth != (Object)null) { damagedEnemyHealth.Add(enemyHealth); } } internal static bool ShouldBypassHealthOcclusion(EnemyHealth enemyHealth, Camera mainCamera, Vector3 enemyPosition) { //IL_0032: 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 ((Object)(object)enemyHealth == (Object)null || (Object)(object)mainCamera == (Object)null || !damagedEnemyHealth.Contains(enemyHealth)) { return false; } if (Vector3.Distance(((Component)mainCamera).transform.position, enemyPosition) <= 10f) { return true; } damagedEnemyHealth.Remove(enemyHealth); return false; } internal static bool IsEnemyHealthDisplayable(EnemyHealth enemyHealth) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 if (!ConfigManager.ShowEnemyHealth.Value || (Object)(object)enemyHealth == (Object)null || GetDead(enemyHealth)) { return false; } Enemy enemy = GetEnemy(enemyHealth); if ((Object)(object)enemy == (Object)null || (int)enemy.CurrentState == 11) { return false; } return true; } internal static void SetWorldSpaceUIChildPosition(WorldSpaceUIChild child, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)child == (Object)null) && !(WorldSpaceUIChildWorldPositionField == null)) { WorldSpaceUIChildWorldPositionField.SetValue(child, position); } } internal static void SetWorldSpaceUIChildPositionOffset(WorldSpaceUIChild child, Vector3 offset) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)child == (Object)null) && !(WorldSpaceUIChildPositionOffsetField == null)) { WorldSpaceUIChildPositionOffsetField.SetValue(child, offset); } } [HarmonyPatch(typeof(EnemyHealth), "Awake")] [HarmonyPostfix] public static void CreateEnemyHealthOnAwake(EnemyHealth __instance) { if (!((Object)(object)__instance == (Object)null)) { StartOrRestartCreateHealthCoroutine(__instance); } } [HarmonyPatch(typeof(EnemyHealth), "OnSpawn")] [HarmonyPostfix] public static void CreateEnemyHealthOnSpawn(EnemyHealth __instance) { if (!((Object)(object)__instance == (Object)null)) { RemoveHealthUI(__instance); StartOrRestartCreateHealthCoroutine(__instance); } } [HarmonyPatch(typeof(EnemyHealth), "DeathRPC")] [HarmonyPostfix] public static void RemoveEnemyHealthOnDeath(EnemyHealth __instance) { RemoveHealthUI(__instance); } [HarmonyPatch(typeof(EnemyHealth), "DeathImpulseRPC")] [HarmonyPostfix] public static void RemoveEnemyHealthOnDeathImpulse(EnemyHealth __instance) { RemoveHealthUI(__instance); } [HarmonyPatch(typeof(EnemyHealth), "Hurt")] [HarmonyPostfix] public static void ShowEnemyHealthOnHurt(int _damage, Vector3 _hurtDirection, EnemyHealth __instance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //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_00af: 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) if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } try { Enemy enemy = GetEnemy(__instance); if (!((Object)(object)enemy == (Object)null)) { EnemyParent val = (EnemyParent)(EnemyEnemyParentField?.GetValue(enemy)); int healthCurrent = GetHealthCurrent(__instance); bool flag = healthCurrent <= 0 || GetDead(__instance); Vector3 enemyPosition = GetEnemyPosition(enemy, __instance); Plugin.Logger.LogDebug((object)string.Format("{0} HURT by {1}, now has {2}, willDie:{3} ...", val?.enemyName ?? "Enemy", _damage, healthCurrent, flag)); CreateDamageUI(_damage, enemyPosition, enemy, __instance); if (_damage > 0) { MarkDamaged(__instance); } if (flag) { RemoveHealthUI(__instance); } else { CreateHealthUI(__instance, enemyPosition); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"LogEnemyHurt failed:\n{arg}"); } } [HarmonyPatch(typeof(EnemyHealth), "HurtRPC")] [HarmonyPostfix] public static void ShowEnemyHealthOnHurtRPC(int _damage, Vector3 _hurtDirection, EnemyHealth __instance) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0054: 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) //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMasterClientOrSingleplayer() || _damage <= 0) { return; } try { Enemy enemy = GetEnemy(__instance); if (!((Object)(object)enemy == (Object)null)) { EnemyParent val = (EnemyParent)(EnemyEnemyParentField?.GetValue(enemy)); Vector3 enemyPosition = GetEnemyPosition(enemy, __instance); int healthCurrent = GetHealthCurrent(__instance); int num = Math.Max(0, healthCurrent - _damage); SetHealthCurrent(__instance, num); CreateDamageUI(_damage, enemyPosition, enemy, __instance); MarkDamaged(__instance); bool flag = num <= 0 || GetDead(__instance); Plugin.Logger.LogDebug((object)string.Format("{0} HURT by {1}, currHealth: {2}, now has {3}, willDie:{4} ...", val?.enemyName ?? "Enemy", _damage, healthCurrent, num, flag)); if (flag) { RemoveHealthUI(__instance); } else { CreateHealthUI(__instance, enemyPosition); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"LogEnemyHurt failed:\n{arg}"); } } [HarmonyPatch(typeof(EnemyBang), "ExplodeRPC")] [HarmonyPostfix] public static void HandleBangerExplode(EnemyBang __instance) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown try { if (!((Object)(object)__instance == (Object)null)) { Enemy val = (Enemy)(EnemyBangEnemyField?.GetValue(__instance)); if (!((Object)(object)val == (Object)null) && !(EnemyHealthField == null)) { EnemyHealth enemyHealth = (EnemyHealth)EnemyHealthField.GetValue(val); RemoveHealthUI(enemyHealth); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"HandleBangerExplode failed:\n{arg}"); } } [HarmonyPatch(typeof(EnemyParent), "DespawnRPC")] [HarmonyPostfix] public static void RemoveEnemyHealthOnDespawn(EnemyParent __instance) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown try { if (!((Object)(object)__instance == (Object)null)) { Enemy val = null; if (EnemyParentEnemyField != null) { val = (Enemy)EnemyParentEnemyField.GetValue(__instance); } if ((Object)(object)val == (Object)null) { val = ((Component)__instance).GetComponentInChildren<Enemy>(true); } if (!((Object)(object)val == (Object)null) && !(EnemyHealthField == null)) { EnemyHealth enemyHealth = (EnemyHealth)EnemyHealthField.GetValue(val); RemoveHealthUI(enemyHealth); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"RemoveEnemyHealthOnDespawn failed:\n{arg}"); } } internal static void StartOrRestartCreateHealthCoroutine(EnemyHealth enemyHealth) { if (!((Object)(object)enemyHealth == (Object)null)) { StopCreateHealthCoroutine(enemyHealth); Coroutine val = Plugin.StartPluginCoroutine(DelayedCreateHealthUI(enemyHealth)); if (val != null) { createHealthCoroutines[enemyHealth] = val; } } } internal static void StopCreateHealthCoroutine(EnemyHealth enemyHealth) { if (!((Object)(object)enemyHealth == (Object)null) && createHealthCoroutines.TryGetValue(enemyHealth, out var value)) { createHealthCoroutines.Remove(enemyHealth); Plugin.StopPluginCoroutine(value); } } internal static void CreateHealthUI(EnemyHealth enemyHealth, Vector3 enemyPosition) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) if (!IsEnemyHealthDisplayable(enemyHealth)) { RemoveHealthUI(enemyHealth); } else { if ((Object)(object)WorldSpaceUIParent.instance == (Object)null || (Object)(object)WorldSpaceUIParent.instance.TTSPrefab == (Object)null) { return; } Vector3 val = new Vector3 { x = 0f, y = 0f, z = 0f }; if (healthUIElements.TryGetValue(enemyHealth, out var value)) { if (!((Object)(object)value == (Object)null) && !((Object)(object)((Component)value).gameObject == (Object)null)) { value.Show(enemyHealth, val); return; } healthUIElements.Remove(enemyHealth); } if (healthUIElements.ContainsKey(enemyHealth)) { return; } GameObject val2 = Object.Instantiate<GameObject>(WorldSpaceUIParent.instance.TTSPrefab, ((Component)WorldSpaceUIParent.instance).transform.position, ((Component)WorldSpaceUIParent.instance).transform.rotation, ((Component)WorldSpaceUIParent.instance).transform); Object.Destroy((Object)(object)val2.GetComponent<WorldSpaceUITTS>()); CustomEnemyHealthUI customEnemyHealthUI = val2.AddComponent<CustomEnemyHealthUI>(); if (WorldSpaceUIChildWorldPositionField == null) { Object.Destroy((Object)(object)val2); return; } SetWorldSpaceUIChildPosition((WorldSpaceUIChild)(object)customEnemyHealthUI, enemyPosition); healthUIElements.Add(enemyHealth, customEnemyHealthUI); if (Plugin.StartPluginCoroutine(DelayedShowHealthUI(healthUIElements[enemyHealth], enemyHealth, val)) == null) { try { customEnemyHealthUI.Show(enemyHealth, val); } catch (Exception arg) { Plugin.Logger.LogDebug((object)$"Handling exception in immediate ShowHealthUI fallback: {arg}"); RemoveHealthUI(enemyHealth); } } } } internal static void RemoveHealthUI(EnemyHealth enemyHealth) { if ((Object)(object)enemyHealth == (Object)null) { CleanupStaleHealthUIEntries(); return; } StopCreateHealthCoroutine(enemyHealth); damagedEnemyHealth.Remove(enemyHealth); if (healthUIElements.TryGetValue(enemyHealth, out var value)) { healthUIElements.Remove(enemyHealth); if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } } } internal static void RemoveHealthUI(CustomEnemyHealthUI healthUI) { if ((Object)(object)healthUI == (Object)null) { CleanupStaleHealthUIEntries(); return; } EnemyHealth val = null; bool flag = false; foreach (KeyValuePair<EnemyHealth, CustomEnemyHealthUI> healthUIElement in healthUIElements) { if ((Object)(object)healthUIElement.Value == (Object)(object)healthUI) { val = healthUIElement.Key; flag = true; break; } } if (flag) { StopCreateHealthCoroutine(val); damagedEnemyHealth.Remove(val); healthUIElements.Remove(val); } if ((Object)(object)((Component)healthUI).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)healthUI).gameObject); } } internal static void CleanupStaleHealthUIEntries() { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Invalid comparison between Unknown and I4 List<EnemyHealth> list = new List<EnemyHealth>(); List<EnemyHealth> list2 = new List<EnemyHealth>(); List<EnemyHealth> list3 = new List<EnemyHealth>(); foreach (KeyValuePair<EnemyHealth, CustomEnemyHealthUI> healthUIElement in healthUIElements) { if ((Object)(object)healthUIElement.Key == (Object)null || (Object)(object)healthUIElement.Value == (Object)null || (Object)(object)((Component)healthUIElement.Value).gameObject == (Object)null) { list.Add(healthUIElement.Key); } } foreach (KeyValuePair<EnemyHealth, Coroutine> createHealthCoroutine in createHealthCoroutines) { if ((Object)(object)createHealthCoroutine.Key == (Object)null || createHealthCoroutine.Value == null) { list2.Add(createHealthCoroutine.Key); } } foreach (EnemyHealth item in damagedEnemyHealth) { if ((Object)(object)item == (Object)null || GetDead(item)) { list3.Add(item); continue; } Enemy enemy = GetEnemy(item); if ((Object)(object)enemy == (Object)null || (int)enemy.CurrentState == 11) { list3.Add(item); } } foreach (EnemyHealth item2 in list2) { createHealthCoroutines.Remove(item2); } foreach (EnemyHealth item3 in list) { if (healthUIElements.TryGetValue(item3, out var value) && (Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } healthUIElements.Remove(item3); } foreach (EnemyHealth item4 in list3) { damagedEnemyHealth.Remove(item4); } } internal static void CreateDamageUI(int damage, Vector3 worldPosition, Enemy enemy, EnemyHealth enemyHealth) { //IL_004e: 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_00b8: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.ShowEnemyDamage.Value) { return; } WorldSpaceUIParent instance = WorldSpaceUIParent.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.valueLostPrefab == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(instance.valueLostPrefab, ((Component)instance).transform.position, ((Component)instance).transform.rotation, ((Component)instance).transform); WorldSpaceUIValueLost component = val.GetComponent<WorldSpaceUIValueLost>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val); return; } CustomDamageTextOcclusion customDamageTextOcclusion = val.GetComponent<CustomDamageTextOcclusion>() ?? val.AddComponent<CustomDamageTextOcclusion>(); customDamageTextOcclusion.Initialize(component, enemy, enemyHealth, GetEnemyRigidbody(enemy)); WorldSpaceUIValueLostWorldPositionField?.SetValue(component, worldPosition); WorldSpaceUIValueLostValueField?.SetValue(component, damage); if (Plugin.StartPluginCoroutine(DelayedSetText(WorldSpaceUIValueLostTextField, component, $"-{damage}")) == null) { TrySetDamageText(WorldSpaceUIValueLostTextField, component, $"-{damage}"); } ((List<WorldSpaceUIValueLost>)(WorldSpaceUIParentValueLostListField?.GetValue(instance)))?.Add(component); } internal static IEnumerator DelayedSetText(FieldInfo fieldInfo, object target, string targetText) { yield return null; if (!TrySetDamageText(fieldInfo, target, targetText)) { yield return null; TrySetDamageText(fieldInfo, target, targetText); } } internal static bool TrySetDamageText(FieldInfo fieldInfo, object target, string targetText) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) //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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = (TextMeshProUGUI)(fieldInfo?.GetValue(target)); if ((Object)(object)val == (Object)null) { return false; } ((TMP_Text)val).text = targetText; ((TMP_Text)val).fontSize = ConfigManager.DamageFontSize.Value; ((Graphic)val).color = Color.blue; Color32 val2 = default(Color32); if (ColorUtility.DoTryParseHtmlColor(ConfigManager.DamageFontHighlightColor.Value, ref val2)) { ((Graphic)val).color = Color32.op_Implicit(val2); } Color32 val3 = default(Color32); if (ColorUtility.DoTryParseHtmlColor(ConfigManager.DamageFontColor.Value, ref val3)) { Color val4 = Color32.op_Implicit(val3); WorldSpaceUIValueLostTextColorField?.SetValue(target, val4); } else { WorldSpaceUIValueLostTextColorField?.SetValue(target, Color.cyan); } return true; } internal static IEnumerator DelayedShowHealthUI(CustomEnemyHealthUI healthUI, EnemyHealth enemyHealth, Vector3 offset) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) yield return null; try { if (!IsEnemyHealthDisplayable(enemyHealth)) { RemoveHealthUI(enemyHealth); } else { healthUI.Show(enemyHealth, offset); } } catch (Exception ex) { Exception e = ex; Plugin.Logger.LogDebug((object)$"Handling exception in DelayedShowHealthUI: {e}"); try { RemoveHealthUI(enemyHealth); } catch { } } } internal static IEnumerator DelayedCreateHealthUI(EnemyHealth enemyHealth) { float fastRetryUntil = Time.realtimeSinceStartup + 3f; while (ConfigManager.ShowEnemyHealth.Value && (Object)(object)enemyHealth != (Object)null && !GetDead(enemyHealth)) { Enemy enemy = GetEnemy(enemyHealth); if ((Object)(object)enemy != (Object)null && (int)enemy.CurrentState == 11) { break; } if ((Object)(object)enemy != (Object)null && (Object)(object)WorldSpaceUIParent.instance != (Object)null && (Object)(object)WorldSpaceUIParent.instance.TTSPrefab != (Object)null) { CreateHealthUI(enemyHealth, GetEnemyPosition(enemy, enemyHealth)); if (healthUIElements.ContainsKey(enemyHealth)) { break; } } if (Time.realtimeSinceStartup < fastRetryUntil) { yield return null; } else { yield return (object)new WaitForSeconds(0.5f); } } createHealthCoroutines.Remove(enemyHealth); } } [BepInPlugin("REPO_ShowEnemyHealth", "ShowEnemyHealth Fixed Edition", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static Coroutine StartPluginCoroutine(IEnumerator routine) { if ((Object)(object)Instance == (Object)null || routine == null || !((Behaviour)Instance).isActiveAndEnabled) { return null; } return ((MonoBehaviour)Instance).StartCoroutine(routine); } internal static void StopPluginCoroutine(Coroutine coroutine) { if (!((Object)(object)Instance == (Object)null) && coroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(coroutine); } } private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"Plugin ShowEnemyHealth Fixed Edition by Rozza is loaded!"); ConfigManager.Initialize(((BaseUnityPlugin)this).Config); _harmony = new Harmony("EnemyHealth"); PatchAll(); } private void PatchAll() { IEnumerable<Type> enumerable; try { enumerable = Assembly.GetExecutingAssembly().GetTypes(); } catch (ReflectionTypeLoadException ex) { enumerable = ex.Types.Where((Type t) => t != null); } foreach (Type item in enumerable) { _harmony.PatchAll(item); } } } internal static class UIOcclusionHelper { internal static int BuildOcclusionMask() { int mask = 0; AddLayerToMask(ref mask, "Default"); AddLayerToMask(ref mask, "StaticGrabObject"); AddLayerToMask(ref mask, "PhysGrabObject"); AddLayerToMask(ref mask, "PhysGrabObjectCart"); AddLayerToMask(ref mask, "PhysGrabObjectHinge"); return mask; } internal static bool IsOccluded(Camera mainCamera, Vector3 labelPosition, int occlusionMask, Enemy enemy, EnemyHealth enemyHealth, EnemyRigidbody enemyRigidBody) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mainCamera == (Object)null || occlusionMask == 0) { return false; } Vector3 position = ((Component)mainCamera).transform.position; Vector3 val = labelPosition - position; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= Mathf.Epsilon) { return false; } RaycastHit[] array = Physics.RaycastAll(position, ((Vector3)(ref val)).normalized, magnitude, occlusionMask, (QueryTriggerInteraction)1); RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val2 = array2[i]; if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !IsCurrentEnemyCollider(((RaycastHit)(ref val2)).collider, enemy, enemyHealth, enemyRigidBody)) { return true; } } return false; } private static void AddLayerToMask(ref int mask, string layerName) { int num = LayerMask.NameToLayer(layerName); if (num >= 0) { mask |= 1 << num; } } private static bool IsCurrentEnemyCollider(Collider collider, Enemy enemy, EnemyHealth enemyHealth, EnemyRigidbody enemyRigidBody) { Transform transform = ((Component)collider).transform; return IsInHierarchy(transform, ((Object)(object)enemy != (Object)null) ? ((Component)enemy).transform : null) || IsInHierarchy(transform, ((Object)(object)enemyRigidBody != (Object)null) ? ((Component)enemyRigidBody).transform : null) || IsInHierarchy(transform, ((Object)(object)enemyHealth != (Object)null) ? ((Component)enemyHealth).transform : null); } private static bool IsInHierarchy(Transform colliderTransform, Transform ownerTransform) { return (Object)(object)colliderTransform != (Object)null && (Object)(object)ownerTransform != (Object)null && colliderTransform.IsChildOf(ownerTransform); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "REPO_ShowEnemyHealth"; public const string PLUGIN_NAME = "ShowEnemyHealth Fixed Edition"; public const string PLUGIN_VERSION = "1.0.0"; } }