using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace NativeEnemyHealthUI; [BepInPlugin("chatgpt.repo.nativeenemyhealthui", "Native Enemy Health UI", "1.0.8")] public sealed class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry MaxDistance; internal static ConfigEntry VisibleSeconds; internal static ConfigEntry UiScale; internal static ConfigEntry ExtraHeight; internal static ConfigEntry ScreenXOffset; private Harmony _harmony; private void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; MaxDistance = ((BaseUnityPlugin)this).Config.Bind("Enemy Health UI", "Max Distance", 40f, new ConfigDescription("Maximum camera distance for the health display.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), new object[0])); VisibleSeconds = ((BaseUnityPlugin)this).Config.Bind("Enemy Health UI", "Visible Seconds After Damage", 4f, new ConfigDescription("How long the native bar stays visible after a hit.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 15f), new object[0])); UiScale = ((BaseUnityPlugin)this).Config.Bind("Enemy Health UI", "UI Scale", 0.5f, new ConfigDescription("Base scale of the native enemy health UI.", (AcceptableValueBase)(object)new AcceptableValueRange(0.2f, 1f), new object[0])); ExtraHeight = ((BaseUnityPlugin)this).Config.Bind("Enemy Health UI", "Extra Height Offset", 1.25f, new ConfigDescription("DamageShow-style extra height added above the enemy bounds.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), new object[0])); ScreenXOffset = ((BaseUnityPlugin)this).Config.Bind("Enemy Health UI", "Screen X Offset", 10f, new ConfigDescription("Small horizontal correction in HUD units. Positive values move the enemy bar to the right.", (AcceptableValueBase)(object)new AcceptableValueRange(-50f, 50f), new object[0])); _harmony = new Harmony("chatgpt.repo.nativeenemyhealthui.v108"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Native Enemy Health UI 1.0.8 loaded - final stabilized placement."); } } [HarmonyPatch] internal static class EnemyHealthPatches { [HarmonyPatch(typeof(EnemyHealth), "Hurt")] [HarmonyPostfix] private static void HurtPostfix(EnemyHealth __instance, int _damage) { if ((Object)(object)__instance == (Object)null || _damage <= 0) { return; } try { NativeEnemyBar nativeEnemyBar = ((Component)__instance).GetComponent(); if ((Object)(object)nativeEnemyBar == (Object)null) { nativeEnemyBar = ((Component)__instance).gameObject.AddComponent(); } nativeEnemyBar.ShowAfterDamage(); if (Plugin.Log != null) { Plugin.Log.LogDebug((object)"[NativeEnemyHealthUI] Hurt detected; requested native health bar."); } } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("[NativeEnemyHealthUI] Hurt hook failed: " + ex.Message)); } } } } public sealed class NativeEnemyBar : MonoBehaviour { private EnemyHealth _health; private Enemy _enemy; private EnemyParent _parent; private Transform _followTransform; private float _nextFollowResolveTime; private float _nextOffsetResolveTime; private float _worldOffsetY = 1.25f; private float _targetWorldOffsetY = 1.25f; private float _worldOffsetVelocity; private bool _hasOffsetSample; private Vector3 _currentWorldAnchor; private GameObject _healthClone; private RectTransform _cloneRect; private RectTransform _uiParentRect; private Canvas _hostCanvas; private Camera _worldCamera; private float _visibleUntil = -1000f; private int _lastCurrent = int.MinValue; private int _lastMax = int.MinValue; public void ShowAfterDamage() { EnsureReferences(); EnsureUi(); _visibleUntil = Time.realtimeSinceStartup + ((Plugin.VisibleSeconds != null) ? Plugin.VisibleSeconds.Value : 4f); ResolveFollowTarget(force: true); UpdateWorldOffsetFromBounds(force: true); UpdateCurrentWorldAnchor(); RefreshHealthText(force: true); UpdateScreenPose(log: true); if ((Object)(object)_healthClone != (Object)null) { _healthClone.SetActive(true); } } private void EnsureReferences() { if ((Object)(object)_health == (Object)null) { _health = ((Component)this).GetComponent(); } if ((Object)(object)_health != (Object)null && (Object)(object)_enemy == (Object)null) { _enemy = EnemyAccess.GetEnemy(_health); } if ((Object)(object)_enemy == (Object)null) { _enemy = ((Component)this).GetComponentInParent(); } if ((Object)(object)_parent == (Object)null) { _parent = EnemyAccess.GetEnemyParent(_enemy); if ((Object)(object)_parent == (Object)null) { _parent = ((Component)this).GetComponentInParent(); } } if ((Object)(object)_worldCamera == (Object)null) { _worldCamera = Camera.main; } if ((Object)(object)_followTransform == (Object)null) { _followTransform = ResolveFollowTransform(); } } private Transform ResolveFollowTransform() { Transform damageShowFollowTransform = EnemyAccess.GetDamageShowFollowTransform(_enemy); if ((Object)(object)damageShowFollowTransform != (Object)null) { return damageShowFollowTransform; } if (!((Object)(object)_health != (Object)null)) { return ((Component)this).transform; } return ((Component)_health).transform; } private void ResolveFollowTarget(bool force) { if (!force && Time.time < _nextFollowResolveTime) { return; } _nextFollowResolveTime = Time.time + 0.5f; Transform val = ResolveFollowTransform(); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)_followTransform) { _followTransform = val; UpdateWorldOffsetFromBounds(force: true); if (Plugin.Log != null) { Plugin.Log.LogDebug((object)("[NativeEnemyHealthUI] DamageShow follow target rebound: " + HierarchyPath(_followTransform))); } } } private Transform ResolveBoundsRoot() { if ((Object)(object)_health != (Object)null && (Object)(object)((Component)_health).transform != (Object)null && (Object)(object)((Component)_health).transform.parent != (Object)null) { return ((Component)_health).transform.parent; } if ((Object)(object)_parent != (Object)null) { return ((Component)_parent).transform; } if ((Object)(object)_health != (Object)null) { return ((Component)_health).transform; } return null; } private void UpdateWorldOffsetFromBounds(bool force) { //IL_004e: 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) if (!force && Time.time < _nextOffsetResolveTime) { return; } _nextOffsetResolveTime = Time.time + 0.25f; Transform val = ResolveBoundsRoot(); if (!((Object)(object)val == (Object)null) && !((Object)(object)_followTransform == (Object)null) && TryGetWorldBounds(val, out var bounds)) { float num = ((Bounds)(ref bounds)).max.y - _followTransform.position.y + 0.15f; num = Mathf.Clamp(num, 0.35f, 12f); float num2 = ((Plugin.ExtraHeight != null) ? Plugin.ExtraHeight.Value : 1.25f); if (num2 < 0f) { num2 = 0f; } float num3 = Mathf.Clamp(num + num2, 0.35f, 20f); if (!_hasOffsetSample || force) { _targetWorldOffsetY = num3; _worldOffsetY = num3; _worldOffsetVelocity = 0f; _hasOffsetSample = true; } else { num3 = Mathf.Clamp(num3, _targetWorldOffsetY - 0.55f, _targetWorldOffsetY + 0.55f); _targetWorldOffsetY = num3; } } } private static bool TryGetWorldBounds(Transform root, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool flag = false; if ((Object)(object)root == (Object)null) { return false; } Collider[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); Collider[] array = componentsInChildren; foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !val.isTrigger) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } if (flag) { return true; } Renderer[] componentsInChildren2 = ((Component)root).GetComponentsInChildren(true); Renderer[] array2 = componentsInChildren2; foreach (Renderer val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { if (!flag) { bounds = val2.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val2.bounds); } } } return flag; } private void UpdateCurrentWorldAnchor() { //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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_followTransform == (Object)null)) { if (_hasOffsetSample) { _worldOffsetY = Mathf.SmoothDamp(_worldOffsetY, _targetWorldOffsetY, ref _worldOffsetVelocity, 0.1f, 8f, Mathf.Max(Time.deltaTime, 0.001f)); } _currentWorldAnchor = _followTransform.position + Vector3.up * _worldOffsetY; } } private void EnsureUi() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_healthClone != (Object)null) { return; } GameObject template = NativeTemplateResolver.GetTemplate(); if ((Object)(object)template == (Object)null) { return; } try { Transform parent = template.transform.parent; if (!((Object)(object)parent == (Object)null)) { _healthClone = Object.Instantiate(template, parent, false); ((Object)_healthClone).name = "Native Enemy Health"; _healthClone.SetActive(true); RemoveNativeHealthControllerDirect(_healthClone); _cloneRect = _healthClone.GetComponent(); _uiParentRect = (RectTransform)(object)((parent is RectTransform) ? parent : null); _hostCanvas = _healthClone.GetComponentInParent(); if ((Object)(object)_cloneRect != (Object)null) { _cloneRect.anchorMin = new Vector2(0.5f, 0.5f); _cloneRect.anchorMax = new Vector2(0.5f, 0.5f); _cloneRect.pivot = new Vector2(0.5f, 0.5f); _cloneRect.anchoredPosition = Vector2.zero; ((Transform)_cloneRect).localScale = Vector3.one * 0.5f; ((Transform)_cloneRect).localRotation = Quaternion.identity; } _healthClone.transform.SetAsLastSibling(); ForceCanvasGroupsVisible(_healthClone); if (Plugin.Log != null) { string text = (((Object)(object)_parent != (Object)null) ? (_parent.enemyName ?? ((Object)_parent).name) : ((Object)((Component)this).gameObject).name); Plugin.Log.LogInfo((object)("[NativeEnemyHealthUI] Native bar attached with DamageShow placement: " + text + " | follow=" + (((Object)(object)_followTransform != (Object)null) ? HierarchyPath(_followTransform) : "") + " | canvas=" + (((Object)(object)_hostCanvas != (Object)null) ? ((object)_hostCanvas.renderMode).ToString() : ""))); } } } catch (Exception ex) { if ((Object)(object)_healthClone != (Object)null) { Object.Destroy((Object)(object)_healthClone); } _healthClone = null; _cloneRect = null; _uiParentRect = null; _hostCanvas = null; if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("[NativeEnemyHealthUI] UI creation failed: " + ex)); } } } private void LateUpdate() { //IL_00af: 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) if ((Object)(object)_health == (Object)null || (Object)(object)_healthClone == (Object)null) { return; } int currentHealth = EnemyAccess.GetCurrentHealth(_health); if (Time.realtimeSinceStartup > _visibleUntil || currentHealth <= 0) { if (_healthClone.activeSelf) { _healthClone.SetActive(false); } return; } EnsureReferences(); if ((Object)(object)_worldCamera == (Object)null) { return; } ResolveFollowTarget(force: false); UpdateWorldOffsetFromBounds(force: false); UpdateCurrentWorldAnchor(); float num = ((Plugin.MaxDistance != null) ? Plugin.MaxDistance.Value : 40f); if ((Object)(object)_followTransform == (Object)null || Vector3.Distance(((Component)_worldCamera).transform.position, _followTransform.position) > num) { if (_healthClone.activeSelf) { _healthClone.SetActive(false); } } else { UpdateScreenPose(log: false); RefreshHealthText(force: false); } } private void UpdateScreenPose(bool log) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0071: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_healthClone == (Object)null || (Object)(object)_cloneRect == (Object)null || (Object)(object)_worldCamera == (Object)null || (Object)(object)_followTransform == (Object)null) { return; } Vector3 val = _worldCamera.WorldToViewportPoint(_currentWorldAnchor); if (val.z <= 0f) { _healthClone.SetActive(false); return; } float num = Vector3.Distance(((Component)_worldCamera).transform.position, _currentWorldAnchor); if ((Object)(object)_uiParentRect != (Object)null) { Rect rect = _uiParentRect.rect; float num2 = ((Plugin.ScreenXOffset != null) ? Plugin.ScreenXOffset.Value : 10f); _cloneRect.anchoredPosition = new Vector2((val.x - 0.5f) * ((Rect)(ref rect)).width + num2, (val.y - 0.5f) * ((Rect)(ref rect)).height); } float num3 = ((Plugin.UiScale != null) ? Plugin.UiScale.Value : 0.5f); float num4 = Mathf.InverseLerp(5f, 40f, num); float num5 = Mathf.Lerp(1f, 0.45f, num4); float num6 = num3 * num5; ((Transform)_cloneRect).localScale = Vector3.one * num6; if (!_healthClone.activeSelf) { _healthClone.SetActive(true); } if (log && Plugin.Log != null) { Plugin.Log.LogInfo((object)string.Concat("[NativeEnemyHealthUI] DAMAGESHOW FOLLOW SHOW follow=", _followTransform.position, " anchor=", _currentWorldAnchor, " viewport=", val, " offsetY=", _worldOffsetY.ToString("0.00"), " targetOffsetY=", _targetWorldOffsetY.ToString("0.00"), " anchored=", _cloneRect.anchoredPosition, " scale=", num6.ToString("0.00"), " distance=", num.ToString("0.00"))); } } private void RefreshHealthText(bool force) { if ((Object)(object)_healthClone == (Object)null || (Object)(object)_health == (Object)null) { return; } int num = Mathf.Max(0, EnemyAccess.GetCurrentHealth(_health)); int num2 = Mathf.Max(1, EnemyAccess.GetMaxHealth(_health, num)); if (force || num != _lastCurrent || num2 != _lastMax) { _lastCurrent = num; _lastMax = num2; TextUtil.SetText(_healthClone, num.ToString()); GameObject val = TextUtil.FindChild(_healthClone.transform, "HealthMax"); if ((Object)(object)val != (Object)null) { TextUtil.SetText(val, num2.ToString()); } } } private static void RemoveNativeHealthControllerDirect(GameObject clone) { if ((Object)(object)clone == (Object)null) { return; } Component[] components = clone.GetComponents(); Component[] array = components; foreach (Component val in array) { if (!((Object)(object)val == (Object)null) && string.Equals(((object)val).GetType().Name, "HealthUI", StringComparison.OrdinalIgnoreCase)) { Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); if ((Object)(object)val2 != (Object)null) { val2.enabled = false; } Object.Destroy((Object)(object)val); } } } private static void ForceCanvasGroupsVisible(GameObject root) { if ((Object)(object)root == (Object)null) { return; } CanvasGroup[] componentsInChildren = root.GetComponentsInChildren(true); CanvasGroup[] array = componentsInChildren; foreach (CanvasGroup val in array) { if (!((Object)(object)val == (Object)null)) { val.alpha = 1f; val.interactable = false; val.blocksRaycasts = false; } } } private static string HierarchyPath(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } List list = new List(); Transform val = t; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list.ToArray()); } private void OnDestroy() { if ((Object)(object)_healthClone != (Object)null) { Object.Destroy((Object)(object)_healthClone); } _healthClone = null; _cloneRect = null; _uiParentRect = null; _hostCanvas = null; _followTransform = null; } } internal static class EnemyAccess { private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo HealthEnemyField = typeof(EnemyHealth).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo HealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo EnemyParentField = typeof(Enemy).GetField("EnemyParent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo EnemyHasRigidbodyField = typeof(Enemy).GetField("HasRigidbody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly PropertyInfo EnemyHasRigidbodyProperty = typeof(Enemy).GetProperty("HasRigidbody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo EnemyRigidbodyField = typeof(Enemy).GetField("Rigidbody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly PropertyInfo EnemyRigidbodyProperty = typeof(Enemy).GetProperty("Rigidbody", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo EnemyCenterTransformField = typeof(Enemy).GetField("CenterTransform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly PropertyInfo EnemyCenterTransformProperty = typeof(Enemy).GetProperty("CenterTransform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static bool _loggedLayout; public static Enemy GetEnemy(EnemyHealth health) { if ((Object)(object)health == (Object)null) { return null; } try { if (HealthEnemyField != null) { object? value = HealthEnemyField.GetValue(health); Enemy val = (Enemy)((value is Enemy) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } } catch { } return ((Component)health).GetComponentInParent(); } public static Transform GetDamageShowFollowTransform(Enemy enemy) { if ((Object)(object)enemy == (Object)null) { return null; } try { bool flag = false; if (EnemyHasRigidbodyField != null) { flag = Convert.ToBoolean(EnemyHasRigidbodyField.GetValue(enemy)); } else if (EnemyHasRigidbodyProperty != null) { flag = Convert.ToBoolean(EnemyHasRigidbodyProperty.GetValue(enemy, null)); } if (flag) { object obj = null; if (EnemyRigidbodyField != null) { obj = EnemyRigidbodyField.GetValue(enemy); } else if (EnemyRigidbodyProperty != null) { obj = EnemyRigidbodyProperty.GetValue(enemy, null); } Rigidbody val = (Rigidbody)((obj is Rigidbody) ? obj : null); if ((Object)(object)val != (Object)null) { return ((Component)val).transform; } } object obj2 = null; if (EnemyCenterTransformField != null) { obj2 = EnemyCenterTransformField.GetValue(enemy); } else if (EnemyCenterTransformProperty != null) { obj2 = EnemyCenterTransformProperty.GetValue(enemy, null); } Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if ((Object)(object)val2 != (Object)null) { return val2; } } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogDebug((object)("[NativeEnemyHealthUI] DamageShow follow resolve fallback: " + ex.Message)); } } return ((Component)enemy).transform; } public static EnemyParent GetEnemyParent(Enemy enemy) { if ((Object)(object)enemy == (Object)null) { return null; } try { if (EnemyParentField != null) { object? value = EnemyParentField.GetValue(enemy); EnemyParent val = (EnemyParent)((value is EnemyParent) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } } catch { } return ((Component)enemy).GetComponentInParent(); } public static int GetCurrentHealth(EnemyHealth health) { if ((Object)(object)health == (Object)null) { return 0; } LogLayoutOnce(); try { if (HealthCurrentField != null) { return Convert.ToInt32(HealthCurrentField.GetValue(health)); } } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("[NativeEnemyHealthUI] Could not read private healthCurrent: " + ex.Message)); } } return Mathf.Max(0, health.health); } public static int GetMaxHealth(EnemyHealth health, int current) { if ((Object)(object)health == (Object)null) { return Mathf.Max(1, current); } try { return Mathf.Max(current, health.health); } catch { return Mathf.Max(1, current); } } private static void LogLayoutOnce() { if (!_loggedLayout && Plugin.Log != null) { _loggedLayout = true; Plugin.Log.LogInfo((object)("[NativeEnemyHealthUI] Enemy layout: healthCurrent=" + ((HealthCurrentField != null) ? "found" : "missing") + ", EnemyHealth.enemy=" + ((HealthEnemyField != null) ? "found" : "missing") + ", Enemy.EnemyParent=" + ((EnemyParentField != null) ? "found" : "missing"))); } } } internal static class NativeTemplateResolver { private static GameObject _template; private static float _nextSearchAt; private static bool _pathLogged; public static GameObject GetTemplate() { if ((Object)(object)_template != (Object)null) { return _template; } if (Time.realtimeSinceStartup < _nextSearchAt) { return null; } _nextSearchAt = Time.realtimeSinceStartup + 2f; string[] array = new string[2] { "UI/HUD/HUD Canvas/HUD/Game Hud/Health", "HUD Canvas/HUD/Game Hud/Health" }; string[] array2 = array; foreach (string text in array2) { GameObject val = GameObject.Find(text); if (IsHealthTemplate(val)) { _template = val; LogPath(val.transform); return _template; } } Transform[] array3 = Object.FindObjectsOfType(); Transform[] array4 = array3; foreach (Transform val2 in array4) { if (!((Object)(object)val2 == (Object)null) && string.Equals(((Object)val2).name, "Health", StringComparison.OrdinalIgnoreCase) && IsHealthTemplate(((Component)val2).gameObject)) { _template = ((Component)val2).gameObject; LogPath(val2); return _template; } } return null; } private static bool IsHealthTemplate(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } Component[] components = go.GetComponents(); Component[] array = components; foreach (Component val in array) { if ((Object)(object)val != (Object)null && string.Equals(((object)val).GetType().Name, "HealthUI", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void LogPath(Transform t) { if (!_pathLogged && Plugin.Log != null && !((Object)(object)t == (Object)null)) { _pathLogged = true; Plugin.Log.LogInfo((object)("[NativeEnemyHealthUI] Native template resolved: " + HierarchyPath(t))); } } private static string HierarchyPath(Transform t) { List list = new List(); Transform val = t; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list.ToArray()); } } internal static class HealthUiGeometry { public static Vector3 GetAnchor(EnemyParent parent, Enemy enemy, Transform fallback, float extraHeight) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: 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) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)parent != (Object)null) ? ((Component)parent).transform : (((Object)(object)enemy != (Object)null) ? ((Component)enemy).transform : fallback)); if ((Object)(object)val == (Object)null) { return Vector3.zero; } Collider[] colliders = (Collider[])(((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentsInChildren(true) : (((Object)(object)enemy != (Object)null) ? ((Array)((Component)enemy).GetComponentsInChildren(true)) : ((Array)new Collider[0]))); if (TryCompactBounds(colliders, val.position, includeTriggers: false, out var bounds) || TryCompactBounds(colliders, val.position, includeTriggers: true, out bounds)) { return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + extraHeight, ((Bounds)(ref bounds)).center.z); } Renderer[] array = (Renderer[])(((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentsInChildren(true) : (((Object)(object)enemy != (Object)null) ? ((Array)((Component)enemy).GetComponentsInChildren(true)) : ((Array)new Renderer[0]))); bool flag = false; Bounds val2 = default(Bounds); ((Bounds)(ref val2))..ctor(val.position, Vector3.zero); Renderer[] array2 = array; foreach (Renderer val3 in array2) { if ((Object)(object)val3 == (Object)null || !val3.enabled || !((Component)val3).gameObject.activeInHierarchy) { continue; } Bounds bounds2 = val3.bounds; Vector3 size = ((Bounds)(ref bounds2)).size; if (!(((Vector3)(ref size)).magnitude > 6f) && !(Vector3.Distance(((Bounds)(ref bounds2)).center, val.position) > 4f)) { if (!flag) { val2 = bounds2; flag = true; } else { ((Bounds)(ref val2)).Encapsulate(bounds2); } } } if (flag) { return new Vector3(((Bounds)(ref val2)).center.x, ((Bounds)(ref val2)).max.y + extraHeight, ((Bounds)(ref val2)).center.z); } return val.position + Vector3.up * (1.6f + extraHeight); } private static bool TryCompactBounds(Collider[] colliders, Vector3 origin, bool includeTriggers, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0057: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool flag = false; if (colliders == null) { return false; } foreach (Collider val in colliders) { if ((Object)(object)val == (Object)null || !val.enabled || (!includeTriggers && val.isTrigger)) { continue; } Bounds bounds2 = val.bounds; Vector3 size = ((Bounds)(ref bounds2)).size; float magnitude = ((Vector3)(ref size)).magnitude; float num = Vector3.Distance(((Bounds)(ref bounds2)).center, origin); if (!(magnitude < 0.05f) && !(magnitude > 6f) && !(num > 4f)) { if (!flag) { bounds = bounds2; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(bounds2); } } } return flag; } } internal static class TextUtil { public static GameObject FindChild(Transform root, string name) { if ((Object)(object)root == (Object)null) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); Transform[] array = componentsInChildren; foreach (Transform val in array) { if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, name, StringComparison.OrdinalIgnoreCase)) { return ((Component)val).gameObject; } } return null; } public static void SetText(GameObject obj, string text) { if ((Object)(object)obj == (Object)null) { return; } Component[] components = obj.GetComponents(); Component[] array = components; foreach (Component val in array) { if ((Object)(object)val == (Object)null) { continue; } PropertyInfo property = ((object)val).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property == null) && property.CanWrite && !(property.PropertyType != typeof(string))) { try { property.SetValue(val, text, null); break; } catch { } } } } }