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 EnemyHealthBar v1.2.0
EnemyHealthBar.dll
Decompiled 2 weeks agousing System; 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 EnemyHealthBar.Config; using EnemyHealthBar.Services; using EnemyHealthBar.UI; using EnemyHealthBar.Utilities; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace EnemyHealthBar { [BepInPlugin("DarkSpider90.EnemyHealthBar", "EnemyHealthBar", "1.2.0")] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "DarkSpider90.EnemyHealthBar"; public const string ModName = "EnemyHealthBar"; public const string ModVersion = "1.2.0"; private readonly Harmony _harmony = new Harmony("DarkSpider90.EnemyHealthBar"); internal static ManualLogSource Log { get; private set; } internal static ModConfig Settings { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); _harmony.PatchAll(); Log.LogInfo((object)"EnemyHealthBar by DarkSpider90 loaded. v1.2.0"); } private void OnDestroy() { _harmony.UnpatchSelf(); } } } namespace EnemyHealthBar.Utilities { internal static class ColorUtils { internal static Color Parse(string htmlColor, Color fallback) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (!ColorUtility.TryParseHtmlString(htmlColor, ref result)) { return fallback; } return result; } internal static Color WithAlpha(Color color, float alpha) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) color.a = Mathf.Clamp01(alpha); return color; } } } namespace EnemyHealthBar.UI { internal sealed class EnemyHealthBarDisplay : MonoBehaviour { private const float BoundsRefreshInterval = 0.65f; private EnemyHealth _enemyHealth; private GameObject _root; private RectTransform _canvasRect; private Image _foreground; private Image _background; private TextMeshProUGUI _healthText; private int _displayedCurrent; private int _displayedMax; private bool _hasHealthState; private float _targetFill = 1f; private float _smoothedFill = 1f; private float _lastRevealTime = -999f; private float _verticalBarOffset = 1.7f; private float _nextBoundsRefreshTime; private Transform _followAnchor; private static Camera _mainCamera; private static Sprite _whiteSprite; private void Awake() { _enemyHealth = ((Component)this).GetComponent<EnemyHealth>(); } private void Start() { if (Plugin.Settings.HealthBarEnabled.Value && !((Object)(object)_enemyHealth == (Object)null)) { SetHealth(_enemyHealth.health, _enemyHealth.health, silent: true); } } private void LateUpdate() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_enemyHealth == (Object)null) { DestroyRoot(); Object.Destroy((Object)(object)this); return; } if (!Plugin.Settings.HealthBarEnabled.Value || !_hasHealthState || _displayedCurrent <= 0) { HideRoot(); return; } if (!Plugin.Settings.AlwaysVisible.Value && Time.time - _lastRevealTime > Plugin.Settings.HideAfterSeconds.Value) { HideRoot(); return; } EnsureCanvas(); if ((Object)(object)_root == (Object)null || (Object)(object)_canvasRect == (Object)null) { return; } Vector3 val = CalculateTargetPosition(); _root.transform.position = val; Camera val2 = ResolveMainCamera(); if ((Object)(object)val2 != (Object)null) { _root.transform.rotation = ((Component)val2).transform.rotation; } float num = CalculateVisibilityAlpha(val, val2); if (num <= 0f) { HideRoot(); return; } if (!_root.activeSelf) { _root.SetActive(true); } ((Transform)_canvasRect).localScale = Vector3.one * Mathf.Max(0.001f, Plugin.Settings.BarScale.Value); RefreshVisuals(num); } internal void SetHealth(int current, int max, bool silent) { _displayedMax = Mathf.Max(1, max); _displayedCurrent = Mathf.Clamp(current, 0, _displayedMax); _hasHealthState = true; _targetFill = Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax); if (silent) { _smoothedFill = _targetFill; } else { Reveal(); } } internal void ApplyClientDamage(int damage, int max) { int num = Mathf.Max(1, max); if (!_hasHealthState || _displayedMax != num) { _displayedMax = num; _displayedCurrent = num; _hasHealthState = true; _smoothedFill = 1f; } _displayedCurrent = Mathf.Clamp(_displayedCurrent - Mathf.Max(0, damage), 0, _displayedMax); _targetFill = Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax); Reveal(); } internal void Reveal() { _lastRevealTime = Time.time; if ((Object)(object)_root != (Object)null && !_root.activeSelf) { _root.SetActive(true); } } internal void KillImmediately() { DestroyRoot(); Object.Destroy((Object)(object)this); } private float CalculateVisibilityAlpha(Vector3 barPosition, Camera camera) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) float num = 1f; if (!Plugin.Settings.AlwaysVisible.Value) { float num2 = _lastRevealTime + Mathf.Max(0.1f, Plugin.Settings.HideAfterSeconds.Value) - Time.time; if (num2 <= 0f) { return 0f; } float num3 = Mathf.Max(0.01f, Plugin.Settings.HideFadeDuration.Value); if (num2 < num3) { num *= Mathf.Clamp01(num2 / num3); } } if ((Object)(object)camera != (Object)null) { float num4 = Mathf.Max(1f, Plugin.Settings.FadeDistance.Value); float num5 = Vector3.Distance(((Component)camera).transform.position, barPosition); num *= Mathf.Clamp01(1f - num5 / num4); } return num; } private Vector3 CalculateTargetPosition() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_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) Transform val = ResolveFollowAnchor(); RefreshVerticalOffset(val); return val.position + Vector3.up * (_verticalBarOffset + Mathf.Max(0f, Plugin.Settings.HeightAboveEnemy.Value)); } private static Camera ResolveMainCamera() { if ((Object)(object)_mainCamera == (Object)null) { _mainCamera = Camera.main; } return _mainCamera; } private Transform ResolveFollowAnchor() { if ((Object)(object)_followAnchor != (Object)null) { return _followAnchor; } _followAnchor = FindFollowAnchor(); return _followAnchor; } private Transform FindFollowAnchor() { Transform val = ResolveEnemyRootForBounds(); if ((Object)(object)val != (Object)null) { EnemyRigidbody componentInChildren = ((Component)val).GetComponentInChildren<EnemyRigidbody>(true); if ((Object)(object)componentInChildren != (Object)null) { return ((Component)componentInChildren).transform; } Transform val2 = FindNamedDescendant(val, "Center"); if ((Object)(object)val2 != (Object)null) { return val2; } } return ((Component)this).transform; } private void RefreshVerticalOffset(Transform anchor) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)anchor == (Object)null) && !(Time.time < _nextBoundsRefreshTime)) { _nextBoundsRefreshTime = Time.time + 0.65f; Transform val = ResolveEnemyRootForBounds(); if (!((Object)(object)val == (Object)null) && TryResolveTopY(val, out var topY)) { _verticalBarOffset = Mathf.Clamp(topY - anchor.position.y, 0f, Mathf.Max(0f, Plugin.Settings.MaxAutoHeight.Value)); } } } private Transform ResolveEnemyRootForBounds() { if ((Object)(object)_enemyHealth != (Object)null && (Object)(object)((Component)_enemyHealth).transform != (Object)null && (Object)(object)((Component)_enemyHealth).transform.parent != (Object)null) { return ((Component)_enemyHealth).transform.parent; } return ((Component)this).transform; } private void EnsureCanvas() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0062: 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_0090: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("EnemyHealthBar_View"); Canvas val = _root.AddComponent<Canvas>(); val.renderMode = (RenderMode)2; _canvasRect = ((Component)val).GetComponent<RectTransform>(); _canvasRect.sizeDelta = new Vector2(Plugin.Settings.BarWidth.Value, Plugin.Settings.BarHeight.Value); ((Transform)_canvasRect).localScale = Vector3.one * Mathf.Max(0.001f, Plugin.Settings.BarScale.Value); _background = CreateImage((Transform)(object)_canvasRect, "Background"); Stretch(((Graphic)_background).rectTransform, 0f); _foreground = CreateImage((Transform)(object)_canvasRect, "Foreground"); Stretch(((Graphic)_foreground).rectTransform, 3f); _foreground.type = (Type)3; _foreground.fillMethod = (FillMethod)0; _foreground.fillOrigin = 0; _foreground.fillAmount = _smoothedFill; CreateHealthText((Transform)(object)_canvasRect); } } private static Image CreateImage(Transform parent, string name) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); Image obj = val.AddComponent<Image>(); obj.sprite = GetWhiteSprite(); ((Graphic)obj).raycastTarget = false; return obj; } private void CreateHealthText(Transform parent) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("HealthText"); val.transform.SetParent(parent, false); _healthText = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_healthText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_healthText).enableAutoSizing = false; ((Graphic)_healthText).raycastTarget = false; if (Plugin.Settings.TextBold.Value) { ((TMP_Text)_healthText).fontStyle = (FontStyles)1; } float num = Mathf.Clamp01(Plugin.Settings.TextOutlineWidth.Value); if (num > 0f) { ((TMP_Text)_healthText).outlineWidth = num; ((TMP_Text)_healthText).outlineColor = Color32.op_Implicit(ColorUtils.Parse(Plugin.Settings.TextOutlineColor.Value, Color.black)); } Stretch(((TMP_Text)_healthText).rectTransform, 0f); } private static void Stretch(RectTransform rect, float inset) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = new Vector2(inset, inset); rect.offsetMax = new Vector2(0f - inset, 0f - inset); } private void RefreshVisuals(float alpha) { //IL_00c9: 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_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_00d3: 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_0101: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015a: 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_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: 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) if (!((Object)(object)_foreground == (Object)null) && !((Object)(object)_background == (Object)null)) { _smoothedFill = Mathf.Lerp(_smoothedFill, _targetFill, Time.deltaTime * Mathf.Max(0.01f, Plugin.Settings.BarLerpSpeed.Value)); _foreground.fillAmount = _smoothedFill; bool num = ((_displayedMax > 0) ? Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax) : 0f) <= Plugin.Settings.LowHealthThreshold.Value; Color color = (num ? ColorUtils.Parse(Plugin.Settings.LowHealthBarColor.Value, Color.yellow) : ColorUtils.Parse(Plugin.Settings.BarColor.Value, Color.red)); Color val = ColorUtils.Parse(Plugin.Settings.BackgroundColor.Value, new Color(0f, 0f, 0f, 0.65f)); Color val2 = (num ? ColorUtils.Parse(Plugin.Settings.LowHealthTextColor.Value, Color.yellow) : ColorUtils.Parse(Plugin.Settings.TextColor.Value, Color.white)); ((Graphic)_foreground).color = ColorUtils.WithAlpha(color, alpha); ((Graphic)_background).color = ColorUtils.WithAlpha(val, val.a * alpha); if (!((Object)(object)_healthText == (Object)null)) { ((Component)_healthText).gameObject.SetActive(Plugin.Settings.ShowHealthText.Value); ((TMP_Text)_healthText).fontSize = Plugin.Settings.TextSize.Value; ((Graphic)_healthText).color = ColorUtils.WithAlpha(val2, val2.a * alpha); ((TMP_Text)_healthText).rectTransform.anchoredPosition = new Vector2(0f, Plugin.Settings.TextOffsetY.Value); ((TMP_Text)_healthText).text = (Plugin.Settings.ShowMaxHealth.Value ? $"{_displayedCurrent} / {_displayedMax}" : _displayedCurrent.ToString()); } } } private static Sprite GetWhiteSprite() { //IL_0032: 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) if ((Object)(object)_whiteSprite != (Object)null) { return _whiteSprite; } Texture2D whiteTexture = Texture2D.whiteTexture; _whiteSprite = Sprite.Create(whiteTexture, new Rect(0f, 0f, (float)((Texture)whiteTexture).width, (float)((Texture)whiteTexture).height), new Vector2(0.5f, 0.5f), 100f); return _whiteSprite; } private static bool TryResolveTopY(Transform root, out float topY) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) topY = 0f; bool flag = false; Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(false); Bounds bounds; foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(((object)val).GetType().Name == "ParticleSystemRenderer")) { bounds = val.bounds; float y = ((Bounds)(ref bounds)).max.y; if (!flag || y > topY) { topY = y; flag = true; } } } if (flag) { return true; } Collider[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<Collider>(false); foreach (Collider val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !val2.isTrigger) { bounds = val2.bounds; float y2 = ((Bounds)(ref bounds)).max.y; if (!flag || y2 > topY) { topY = y2; flag = true; } } } return flag; } private static Transform FindNamedDescendant(Transform root, string namePart) { if ((Object)(object)root == (Object)null) { return null; } string value = namePart.ToLowerInvariant(); Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)val).name.ToLowerInvariant().Contains(value)) { return val; } } return null; } private void HideRoot() { if ((Object)(object)_root != (Object)null && _root.activeSelf) { _root.SetActive(false); } } private void DestroyRoot() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } } private void OnDisable() { DestroyRoot(); } private void OnDestroy() { DestroyRoot(); } } internal sealed class FloatingDamageText : MonoBehaviour { private TextMeshProUGUI _text; private Vector3 _startWorldPosition; private float _age; private float _lifetime; private float _riseDistance; private float _fadeMultiplier; internal void Initialize(int damage, Vector3 position, DamageTextStyle style) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_0116: 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_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) _startWorldPosition = position + Random.insideUnitSphere * Plugin.Settings.DamageTextRandomOffset.Value; _lifetime = Mathf.Max(0.05f, style.Lifetime); _riseDistance = style.FloatSpeed; _fadeMultiplier = Mathf.Max(0.01f, style.FadeMultiplier); Canvas obj = ((Component)this).gameObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)2; RectTransform component = ((Component)obj).GetComponent<RectTransform>(); component.sizeDelta = new Vector2(150f, 72f); ((Transform)component).localScale = Vector3.one * style.CanvasScale; GameObject val = new GameObject("Text"); val.transform.SetParent((Transform)(object)component, false); _text = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ((TMP_Text)_text).enableAutoSizing = false; ((TMP_Text)_text).fontStyle = (FontStyles)1; ((Graphic)_text).raycastTarget = false; ((TMP_Text)_text).text = FormatDamage(style.Format, damage); ((Graphic)_text).color = style.Color; ((TMP_Text)_text).fontSize = style.Size; RectTransform rectTransform = ((TMP_Text)_text).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Component)this).transform.position = _startWorldPosition; FaceMainCamera(); } private void LateUpdate() { //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_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) _age += Time.deltaTime; float num = Mathf.Clamp01(_age / _lifetime); if (num >= 1f) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } ((Component)this).transform.position = _startWorldPosition + Vector3.up * (num * _riseDistance); FaceMainCamera(); if ((Object)(object)_text != (Object)null) { ((TMP_Text)_text).alpha = Mathf.Clamp01((1f - num * num) * _fadeMultiplier); } } private static string FormatDamage(string format, int damage) { if (string.IsNullOrWhiteSpace(format)) { format = "-{damage}"; } return format.Replace("{damage}", damage.ToString()); } private void FaceMainCamera() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main != (Object)null) { ((Component)this).transform.rotation = ((Component)main).transform.rotation; } } } } namespace EnemyHealthBar.Services { internal static class DamageTextSpawner { internal static void Spawn(Vector3 worldPosition, int damage) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings.DamageTextEnabled.Value) { DamageTextStyle style = DamageTextStyle.FromDamage(damage); new GameObject("EnemyHealthBar_DamageText").AddComponent<FloatingDamageText>().Initialize(damage, worldPosition, style); } } } internal readonly struct DamageTextStyle { internal readonly Color Color; internal readonly float Size; internal readonly float Lifetime; internal readonly float FloatSpeed; internal readonly float FadeMultiplier; internal readonly float CanvasScale; internal readonly string Format; private DamageTextStyle(Color color, float size, float lifetime, float floatSpeed, float fadeMultiplier, float canvasScale, string format) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Color = color; Size = size; Lifetime = lifetime; FloatSpeed = floatSpeed; FadeMultiplier = fadeMultiplier; CanvasScale = canvasScale; Format = format; } internal static DamageTextStyle FromDamage(int damage) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //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_00c7: 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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) ModConfig settings = Plugin.Settings; Color color = ColorUtils.Parse(settings.DamageTextColor.Value, new Color(1f, 0.6f, 0.18f)); float value = settings.DamageTextSize.Value; if (settings.UseDamageTiers.Value) { if (damage >= settings.CriticalDamageThreshold.Value) { color = ColorUtils.Parse(settings.CriticalDamageTextColor.Value, new Color(1f, 0.31f, 0.27f)); value = settings.CriticalDamageTextSize.Value; } else if (damage >= settings.MediumDamageThreshold.Value) { color = ColorUtils.Parse(settings.MediumDamageTextColor.Value, new Color(1f, 0.83f, 0.35f)); value = settings.MediumDamageTextSize.Value; } } return new DamageTextStyle(color, value, Mathf.Max(0.05f, settings.DamageTextLifetime.Value), settings.DamageTextFloatSpeed.Value, settings.DamageTextFadeMultiplier.Value, Mathf.Max(0.001f, settings.DamageTextScale.Value), settings.DamageTextFormat.Value); } } } namespace EnemyHealthBar.Patches { [HarmonyPatch] internal static class EnemyHealthPatches { private static readonly Dictionary<int, float> LastDamageTextTimeByEnemy = new Dictionary<int, float>(); private static readonly FieldRef<EnemyHealth, int> HealthCurrentRef = AccessTools.FieldRefAccess<EnemyHealth, int>("healthCurrent"); [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHealth), "Awake")] private static void EnemyHealthAwakePostfix(EnemyHealth __instance) { try { EnsureHealthBar(__instance); if (Plugin.Settings.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Initialized health bar for " + ((Object)__instance).name)); } } catch (Exception arg) { Plugin.Log.LogError((object)$"EnemyHealthBar Awake patch failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHealth), "OnSpawn")] private static void EnemyHealthOnSpawnPostfix(EnemyHealth __instance) { EnsureHealthBar(__instance).SetHealth(__instance.health, __instance.health, silent: true); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHealth), "HurtRPC")] private static void EnemyHealthHurtRpcPostfix(EnemyHealth __instance, int _damage, Vector3 _hurtDirection) { if ((Object)(object)__instance == (Object)null || _damage <= 0) { return; } try { EnemyHealthBarDisplay enemyHealthBarDisplay = EnsureHealthBar(__instance); if (SemiFunc.IsMasterClientOrSingleplayer()) { enemyHealthBarDisplay.SetHealth(HealthCurrentRef.Invoke(__instance), __instance.health, silent: false); } else { enemyHealthBarDisplay.ApplyClientDamage(_damage, __instance.health); } SpawnDamageText(__instance, _damage); } catch (Exception arg) { Plugin.Log.LogError((object)$"EnemyHealthBar HurtRPC patch failed: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHealth), "DeathRPC")] private static void EnemyHealthDeathRpcPostfix(EnemyHealth __instance) { DestroyHealthBar(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHealth), "Death")] private static void EnemyHealthDeathPostfix(EnemyHealth __instance) { DestroyHealthBar(__instance); } private static EnemyHealthBarDisplay EnsureHealthBar(EnemyHealth enemyHealth) { EnemyHealthBarDisplay enemyHealthBarDisplay = ((Component)enemyHealth).GetComponent<EnemyHealthBarDisplay>(); if ((Object)(object)enemyHealthBarDisplay == (Object)null) { enemyHealthBarDisplay = ((Component)enemyHealth).gameObject.AddComponent<EnemyHealthBarDisplay>(); } return enemyHealthBarDisplay; } private static void DestroyHealthBar(EnemyHealth enemyHealth) { if (!((Object)(object)enemyHealth == (Object)null)) { LastDamageTextTimeByEnemy.Remove(((Object)enemyHealth).GetInstanceID()); EnemyHealthBarDisplay component = ((Component)enemyHealth).GetComponent<EnemyHealthBarDisplay>(); if ((Object)(object)component != (Object)null) { component.KillImmediately(); } } } private static void SpawnDamageText(EnemyHealth enemyHealth, int damage) { //IL_004b: 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_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) if (Plugin.Settings.DamageTextEnabled.Value) { int instanceID = ((Object)enemyHealth).GetInstanceID(); float time = Time.time; if (!LastDamageTextTimeByEnemy.TryGetValue(instanceID, out var value) || !(time - value < 0.05f)) { LastDamageTextTimeByEnemy[instanceID] = time; DamageTextSpawner.Spawn(((Component)enemyHealth).transform.position + Vector3.up * Plugin.Settings.DamageTextHeightOffset.Value, Mathf.Clamp(damage, 1, 9999)); } } } } } namespace EnemyHealthBar.Config { internal sealed class ModConfig { internal readonly ConfigEntry<bool> HealthBarEnabled; internal readonly ConfigEntry<bool> AlwaysVisible; internal readonly ConfigEntry<bool> ShowHealthText; internal readonly ConfigEntry<bool> ShowMaxHealth; internal readonly ConfigEntry<float> BarWidth; internal readonly ConfigEntry<float> BarHeight; internal readonly ConfigEntry<float> BarScale; internal readonly ConfigEntry<float> BarLerpSpeed; internal readonly ConfigEntry<float> HeightAboveEnemy; internal readonly ConfigEntry<float> MaxAutoHeight; internal readonly ConfigEntry<float> HideAfterSeconds; internal readonly ConfigEntry<float> HideFadeDuration; internal readonly ConfigEntry<float> FadeDistance; internal readonly ConfigEntry<string> BarColor; internal readonly ConfigEntry<string> LowHealthBarColor; internal readonly ConfigEntry<string> BackgroundColor; internal readonly ConfigEntry<string> TextColor; internal readonly ConfigEntry<string> LowHealthTextColor; internal readonly ConfigEntry<float> LowHealthThreshold; internal readonly ConfigEntry<float> TextSize; internal readonly ConfigEntry<float> TextOffsetY; internal readonly ConfigEntry<bool> TextBold; internal readonly ConfigEntry<float> TextOutlineWidth; internal readonly ConfigEntry<string> TextOutlineColor; internal readonly ConfigEntry<bool> DamageTextEnabled; internal readonly ConfigEntry<string> DamageTextFormat; internal readonly ConfigEntry<bool> UseDamageTiers; internal readonly ConfigEntry<float> DamageTextHeightOffset; internal readonly ConfigEntry<float> DamageTextRandomOffset; internal readonly ConfigEntry<float> DamageTextLifetime; internal readonly ConfigEntry<float> DamageTextFloatSpeed; internal readonly ConfigEntry<float> DamageTextFadeMultiplier; internal readonly ConfigEntry<float> DamageTextScale; internal readonly ConfigEntry<string> DamageTextColor; internal readonly ConfigEntry<float> DamageTextSize; internal readonly ConfigEntry<int> MediumDamageThreshold; internal readonly ConfigEntry<string> MediumDamageTextColor; internal readonly ConfigEntry<float> MediumDamageTextSize; internal readonly ConfigEntry<int> CriticalDamageThreshold; internal readonly ConfigEntry<string> CriticalDamageTextColor; internal readonly ConfigEntry<float> CriticalDamageTextSize; internal readonly ConfigEntry<bool> VerboseLogging; internal ModConfig(ConfigFile config) { HealthBarEnabled = config.Bind<bool>("Health Bar", "Enabled", true, "Show enemy health bars."); AlwaysVisible = config.Bind<bool>("Health Bar", "AlwaysVisible", false, "Always show health bars, even without recent damage."); ShowHealthText = config.Bind<bool>("Health Bar", "ShowHealthText", true, "Show health numbers above the bar."); ShowMaxHealth = config.Bind<bool>("Health Bar", "ShowMaxHealth", true, "Show current/max health instead of current health only."); BarWidth = config.Bind<float>("Health Bar", "BarWidth", 210f, "World-space health bar canvas width."); BarHeight = config.Bind<float>("Health Bar", "BarHeight", 28f, "World-space health bar canvas height."); BarScale = config.Bind<float>("Health Bar", "BarScale", 0.0068f, "World-space health bar scale."); BarLerpSpeed = config.Bind<float>("Health Bar", "BarLerpSpeed", 11f, "Health bar fill smoothing speed."); HeightAboveEnemy = config.Bind<float>("Health Bar", "HeightAboveEnemy", 0.4f, "How far above the enemy's highest point the bar sits."); MaxAutoHeight = config.Bind<float>("Health Bar", "MaxAutoHeight", 3.5f, "Highest point the bar is allowed to be placed at above the enemy. Stops a stray effect or an oversized mesh from throwing the bar into the sky."); HideAfterSeconds = config.Bind<float>("Health Bar", "HideAfterSeconds", 8f, "Hide health bar after this many seconds without damage."); HideFadeDuration = config.Bind<float>("Health Bar", "HideFadeDuration", 0.45f, "Fade-out duration before the bar disappears."); FadeDistance = config.Bind<float>("Health Bar", "FadeDistance", 48f, "Distance where health bar fully fades out."); BarColor = config.Bind<string>("Health Bar", "BarColor", "#B32B24", "Health bar color in HTML format. Kept away from the bright end on purpose: the game's bloom turns a saturated colour into a glow that swallows the numbers."); LowHealthBarColor = config.Bind<string>("Health Bar", "LowHealthBarColor", "#C79A1E", "Health bar color below low health threshold."); BackgroundColor = config.Bind<string>("Health Bar", "BackgroundColor", "#16191ECC", "Health bar background color in HTML format."); TextColor = config.Bind<string>("Health Bar", "TextColor", "#F7F8FA", "Health text color in HTML format."); LowHealthTextColor = config.Bind<string>("Health Bar", "LowHealthTextColor", "#FFD447", "Health text color below low health threshold."); LowHealthThreshold = config.Bind<float>("Health Bar", "LowHealthThreshold", 0.3f, "Low health threshold from 0 to 1."); TextSize = config.Bind<float>("Health Bar", "TextSize", 14f, "Health text size."); TextOffsetY = config.Bind<float>("Health Bar", "TextOffsetY", 0f, "Health text vertical offset."); TextBold = config.Bind<bool>("Health Bar", "TextBold", true, "Draw the health numbers in bold."); TextOutlineWidth = config.Bind<float>("Health Bar", "TextOutlineWidth", 0.25f, "Thickness of the outline around the health numbers, from 0 to 1. This is what keeps them readable against the bar."); TextOutlineColor = config.Bind<string>("Health Bar", "TextOutlineColor", "#000000", "Colour of the outline around the health numbers."); DamageTextEnabled = config.Bind<bool>("Damage Text", "Enabled", true, "Show floating damage numbers."); DamageTextFormat = config.Bind<string>("Damage Text", "TextFormat", "-{damage}", "Damage text format. Use {damage} as placeholder."); UseDamageTiers = config.Bind<bool>("Damage Text", "UseDamageTiers", true, "Use separate colors/sizes for medium and critical damage."); DamageTextHeightOffset = config.Bind<float>("Damage Text", "HeightOffset", 1.65f, "Damage text height above enemy transform."); DamageTextRandomOffset = config.Bind<float>("Damage Text", "RandomOffsetRadius", 0.28f, "Random offset radius for damage text."); DamageTextLifetime = config.Bind<float>("Damage Text", "Lifetime", 1.05f, "Damage text lifetime."); DamageTextFloatSpeed = config.Bind<float>("Damage Text", "FloatSpeed", 1.25f, "Damage text upward movement distance."); DamageTextFadeMultiplier = config.Bind<float>("Damage Text", "FadeMultiplier", 1f, "Damage text fade multiplier."); DamageTextScale = config.Bind<float>("Damage Text", "Scale", 0.0105f, "World-space damage text canvas scale."); DamageTextColor = config.Bind<string>("Damage Text", "Color", "#FF9A2F", "Default damage text color."); DamageTextSize = config.Bind<float>("Damage Text", "Size", 30f, "Default damage text size."); MediumDamageThreshold = config.Bind<int>("Damage Text", "MediumDamageThreshold", 30, "Minimum damage for medium damage text style."); MediumDamageTextColor = config.Bind<string>("Damage Text", "MediumColor", "#FFD35A", "Medium damage text color."); MediumDamageTextSize = config.Bind<float>("Damage Text", "MediumSize", 35f, "Medium damage text size."); CriticalDamageThreshold = config.Bind<int>("Damage Text", "CriticalDamageThreshold", 60, "Minimum damage for critical damage text style."); CriticalDamageTextColor = config.Bind<string>("Damage Text", "CriticalColor", "#FF4E45", "Critical damage text color."); CriticalDamageTextSize = config.Bind<float>("Damage Text", "CriticalSize", 42f, "Critical damage text size."); VerboseLogging = config.Bind<bool>("Debug", "VerboseLogging", false, "Enable verbose logs."); } } }