using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Assets.Scripts.Actors.Enemies;
using Assets.Scripts.Managers;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ShowEnemyHp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ShowEnemyHp")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7eab9528-cad4-481a-af54-e0691c7353a7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 ShowEnemyHpMod
{
internal sealed class EnemyUiContext
{
public GameObject Root { get; }
public GameObject NumericRoot { get; }
public TextMeshProUGUI NumericText { get; }
public GameObject BarRoot { get; }
public Image BarFill { get; }
public RectTransform BarFillRect { get; }
public float BarFullWidth { get; }
public EnemyUiContext(GameObject root, GameObject numericRoot, TextMeshProUGUI numericText, GameObject barRoot, Image barFill, RectTransform barFillRect, float barFullWidth)
{
Root = root;
NumericRoot = numericRoot;
NumericText = numericText;
BarRoot = barRoot;
BarFill = barFill;
BarFillRect = barFillRect;
BarFullWidth = barFullWidth;
}
}
internal enum HpDisplayMode
{
Numeric,
Bar
}
internal readonly struct HpFontInfo
{
public string Key { get; }
public TMP_FontAsset Asset { get; }
public string Description { get; }
public HpFontInfo(string key, TMP_FontAsset asset, string description = "")
{
Key = key;
Asset = asset;
Description = description ?? string.Empty;
}
}
internal static class HpFontCatalog
{
[CompilerGenerated]
private sealed class <GetFontSummaries>d__8 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
{
private int <>1__state;
private string <>2__current;
private int <>l__initialThreadId;
private List<string>.Enumerator <>7__wrap1;
string IEnumerator<string>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <GetFontSummaries>d__8(int <>1__state)
{
this.<>1__state = <>1__state;
<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<>7__wrap1 = default(List<string>.Enumerator);
<>1__state = -2;
}
private bool MoveNext()
{
try
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
EnsureInitialized();
<>7__wrap1 = _orderedKeys.GetEnumerator();
<>1__state = -3;
break;
case 1:
<>1__state = -3;
break;
}
while (<>7__wrap1.MoveNext())
{
string current = <>7__wrap1.Current;
if (_fonts.TryGetValue(current, out var value))
{
<>2__current = value.Key;
<>1__state = 1;
return true;
}
}
<>m__Finally1();
<>7__wrap1 = default(List<string>.Enumerator);
return false;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
((IDisposable)<>7__wrap1).Dispose();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
return this;
}
return new <GetFontSummaries>d__8(0);
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}
public const string DEFAULT_KEY = "Default";
public const string NOTO_KEY = "NotoSansSC";
private const string NOTO_ASSET_NAME = "NotoSansSC-Bold SDF";
private static readonly string[] NotoResourcePaths = new string[2] { "Fonts & Materials/NotoSansSC-Bold SDF", "TextMesh Pro/Resources/Fonts & Materials/NotoSansSC-Bold SDF" };
private static bool _initialized;
private static readonly Dictionary<string, HpFontInfo> _fonts = new Dictionary<string, HpFontInfo>(StringComparer.OrdinalIgnoreCase);
private static readonly List<string> _orderedKeys = new List<string>();
public static void EnsureInitialized()
{
if (!_initialized)
{
_initialized = true;
_fonts.Clear();
_orderedKeys.Clear();
RegisterDefaultFromSettings();
RegisterNotoIfPresent();
if (_fonts.Count == 0)
{
LoggerBridge.Warn("[ShowEnemyHP] No TMP.TMP fonts registered (Default/Noto).");
}
}
}
[IteratorStateMachine(typeof(<GetFontSummaries>d__8))]
public static IEnumerable<string> GetFontSummaries()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <GetFontSummaries>d__8(-2);
}
public static bool TryResolveFont(string raw, out string normalizedName)
{
EnsureInitialized();
if (string.IsNullOrWhiteSpace(raw))
{
normalizedName = "Default";
return false;
}
if (string.Equals(raw, "NotoSansSC", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "NotoSansSC-Bold SDF", StringComparison.OrdinalIgnoreCase))
{
if (_fonts.ContainsKey("NotoSansSC"))
{
normalizedName = "NotoSansSC";
return true;
}
LoggerBridge.Warn("[ShowEnemyHP] Requested NotoSansSC but asset not found; using Default.");
normalizedName = "Default";
return false;
}
normalizedName = "Default";
return string.Equals(raw, "Default", StringComparison.OrdinalIgnoreCase);
}
public static TMP_FontAsset GetFontAsset(string key)
{
EnsureInitialized();
if (string.IsNullOrEmpty(key))
{
key = "Default";
}
if (string.Equals(key, "NotoSansSC", StringComparison.OrdinalIgnoreCase) && _fonts.TryGetValue("NotoSansSC", out var value))
{
return value.Asset;
}
if (_fonts.TryGetValue("Default", out var value2))
{
return value2.Asset;
}
throw new InvalidOperationException("No TextMeshPro font assets are registered.");
}
private static void RegisterDefaultFromSettings()
{
try
{
TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
if ((Object)(object)defaultFontAsset != (Object)null)
{
Register("Default", defaultFontAsset, "TMP.TMP default");
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] TMP.TMP_Settings.defaultFontAsset is null.");
}
}
catch (Exception ex)
{
LoggerBridge.Warn("[ShowEnemyHP] Failed to get TMP.TMP default font: " + ex.Message);
}
}
private static void RegisterNotoIfPresent()
{
TMP_FontAsset val = null;
for (int i = 0; i < NotoResourcePaths.Length; i++)
{
try
{
TMP_FontAsset val2 = Resources.Load<TMP_FontAsset>(NotoResourcePaths[i]);
if ((Object)(object)val2 != (Object)null && string.Equals(((Object)val2).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
{
val = val2;
break;
}
}
catch
{
}
}
if ((Object)(object)val == (Object)null)
{
try
{
Il2CppArrayBase<TMP_FontAsset> val3 = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
for (int j = 0; j < val3.Length; j++)
{
TMP_FontAsset val4 = val3[j];
if ((Object)(object)val4 != (Object)null && string.Equals(((Object)val4).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
{
val = val4;
break;
}
}
}
catch
{
}
}
if ((Object)(object)val == (Object)null)
{
try
{
List<TMP_FontAsset> fallbackFontAssets = TMP_Settings.fallbackFontAssets;
if (fallbackFontAssets != null)
{
for (int k = 0; k < fallbackFontAssets.Count; k++)
{
TMP_FontAsset val5 = fallbackFontAssets[k];
if ((Object)(object)val5 != (Object)null && string.Equals(((Object)val5).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
{
val = val5;
break;
}
}
}
}
catch
{
}
}
if ((Object)(object)val != (Object)null)
{
Register("NotoSansSC", val, "Noto");
}
}
private static void Register(string key, TMP_FontAsset font, string description)
{
if (!((Object)(object)font == (Object)null) && !string.IsNullOrEmpty(key) && !_fonts.ContainsKey(key))
{
_fonts[key] = new HpFontInfo(key, font, description);
_orderedKeys.Add(key);
}
}
}
internal enum HpColorScheme
{
Classic,
Cosmic,
Electric,
Aurora
}
internal readonly struct HpPaletteDefinition
{
public Color Low { get; }
public Color Mid { get; }
public Color High { get; }
public HpPaletteDefinition(Color low, Color mid, Color high)
{
//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_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
Low = low;
Mid = mid;
High = high;
}
public Color Evaluate(float pct)
{
//IL_003a: 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_0046: 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_001f: 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)
pct = Mathf.Clamp01(pct);
if (pct < 0.5f)
{
float num = pct / 0.5f;
return Color.Lerp(Low, Mid, num);
}
float num2 = (pct - 0.5f) / 0.5f;
return Color.Lerp(Mid, High, num2);
}
}
internal static class HpPalette
{
[CompilerGenerated]
private sealed class <GetSchemeSummaries>d__6 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
{
private int <>1__state;
private string <>2__current;
private int <>l__initialThreadId;
private HpColorScheme[] <>7__wrap1;
private int <>7__wrap2;
string IEnumerator<string>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <GetSchemeSummaries>d__6(int <>1__state)
{
this.<>1__state = <>1__state;
<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>7__wrap1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
if (num != 0)
{
if (num != 1)
{
return false;
}
<>1__state = -1;
goto IL_0099;
}
<>1__state = -1;
<>7__wrap1 = ORDERED_SCHEMES;
<>7__wrap2 = 0;
goto IL_00a7;
IL_0099:
<>7__wrap2++;
goto IL_00a7;
IL_00a7:
if (<>7__wrap2 < <>7__wrap1.Length)
{
HpColorScheme hpColorScheme = <>7__wrap1[<>7__wrap2];
if (DESCRIPTIONS.TryGetValue(hpColorScheme, out var value))
{
<>2__current = $"{hpColorScheme} ({value})";
<>1__state = 1;
return true;
}
goto IL_0099;
}
<>7__wrap1 = null;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
return this;
}
return new <GetSchemeSummaries>d__6(0);
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}
private static readonly HpPaletteDefinition CLASSIC = new HpPaletteDefinition(new Color(0.95f, 0.12f, 0.12f), new Color(1f, 0.85f, 0.05f), new Color(0.08f, 0.9f, 0.3f));
private static readonly HpPaletteDefinition COSMIC = new HpPaletteDefinition(new Color(1f, 0.18f, 0.722f), new Color(1f, 0.478f, 0f), new Color(0.431f, 0.184f, 0.961f));
private static readonly HpPaletteDefinition ELECTRIC = new HpPaletteDefinition(new Color(0.039f, 0.141f, 0.388f), new Color(0f, 0.549f, 1f), new Color(1f, 0.831f, 0f));
private static readonly HpPaletteDefinition AURORA = new HpPaletteDefinition(new Color(0.659f, 0.937f, 1f), new Color(0.557f, 0.176f, 0.886f), new Color(0f, 0.902f, 0.463f));
private static readonly HpColorScheme[] ORDERED_SCHEMES = new HpColorScheme[4]
{
HpColorScheme.Classic,
HpColorScheme.Cosmic,
HpColorScheme.Electric,
HpColorScheme.Aurora
};
private static readonly Dictionary<HpColorScheme, string> DESCRIPTIONS = new Dictionary<HpColorScheme, string>
{
{
HpColorScheme.Classic,
"red→yellow→green"
},
{
HpColorScheme.Cosmic,
"pink→orange→purple"
},
{
HpColorScheme.Electric,
"dark blue→blue→yellow"
},
{
HpColorScheme.Aurora,
"ice blue→violet→emerald"
}
};
[IteratorStateMachine(typeof(<GetSchemeSummaries>d__6))]
public static IEnumerable<string> GetSchemeSummaries()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <GetSchemeSummaries>d__6(-2);
}
public static Color FromPercent(float pct)
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
return (ModConfig.ColorScheme switch
{
HpColorScheme.Cosmic => COSMIC,
HpColorScheme.Electric => ELECTRIC,
HpColorScheme.Aurora => AURORA,
_ => CLASSIC,
}).Evaluate(pct);
}
}
internal static class LoggerBridge
{
public static Action<string> Info = delegate
{
};
public static Action<string> Warn = delegate
{
};
public static Action<string> Error = delegate
{
};
}
internal static class ModConfig
{
private const string FILE_NAME = "ShowEnemyHP.cfg";
private const float SCALE_DEFAULT = 0.03f;
private const float SCALE_MIN = 0.005f;
private const float SCALE_MAX = 0.2f;
private const float OFFSET_DEFAULT = -0.25f;
private const float OFFSET_MIN = -5f;
private const float OFFSET_MAX = 5f;
private const HpColorScheme COLOR_SCHEME_DEFAULT = HpColorScheme.Classic;
private const HpDisplayMode DISPLAY_MODE_DEFAULT = HpDisplayMode.Numeric;
private const string FONT_DEFAULT = "Default";
private static float _scale = 0.03f;
private static float _verticalOffset = -0.25f;
private static HpColorScheme _colorScheme = HpColorScheme.Classic;
private static HpDisplayMode _displayMode = HpDisplayMode.Numeric;
private static string _fontName = "Default";
public static float Scale => _scale;
public static float VerticalOffset => _verticalOffset;
public static HpColorScheme ColorScheme => _colorScheme;
public static HpDisplayMode DisplayMode => _displayMode;
public static string FontName => _fontName;
public static void Init()
{
ResetToDefaults();
if (!TryLoadFromDisk())
{
LoggerBridge.Warn("[ShowEnemyHP] Using default config values.");
}
WriteAnnotatedFile();
}
public static bool ReloadFromDisk()
{
if (TryLoadFromDisk())
{
LoggerBridge.Info("[ShowEnemyHP] Config reloaded from disk.");
return true;
}
LoggerBridge.Warn("[ShowEnemyHP] Config reload failed; keeping previous values.");
return false;
}
private static void ResetToDefaults()
{
_scale = 0.03f;
_verticalOffset = -0.25f;
_colorScheme = HpColorScheme.Classic;
_displayMode = HpDisplayMode.Numeric;
_fontName = "Default";
}
private static bool TryLoadFromDisk()
{
ResetToDefaults();
HpFontCatalog.EnsureInitialized();
string path = Path.Combine(PathsBridge.EnsureUserDataDir(), "ShowEnemyHP.cfg");
if (!File.Exists(path))
{
return false;
}
try
{
string[] array = File.ReadAllLines(path);
foreach (string text in array)
{
if (string.IsNullOrWhiteSpace(text))
{
continue;
}
string text2 = text.Trim();
if (text2.StartsWith("#") || text2.StartsWith("//"))
{
continue;
}
int num = text2.IndexOf('=');
if (num <= 0 || num >= text2.Length - 1)
{
continue;
}
string text3 = text2.Substring(0, num).Trim();
string text4 = text2.Substring(num + 1).Trim();
switch (text3.ToLowerInvariant())
{
case "scale":
{
if (TryParseFloat(text4, out var value))
{
_scale = Mathf.Clamp(value, 0.005f, 0.2f);
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] Invalid float for Scale: '" + text4 + "'.");
}
break;
}
case "verticaloffset":
{
if (TryParseFloat(text4, out var value2))
{
_verticalOffset = Mathf.Clamp(value2, -5f, 5f);
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] Invalid float for VerticalOffset: '" + text4 + "'.");
}
break;
}
case "colorscheme":
{
if (TryParseColorScheme(text4, out var scheme))
{
_colorScheme = scheme;
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] Unknown ColorScheme '" + text4 + "'.");
}
break;
}
case "font":
{
if (HpFontCatalog.TryResolveFont(text4, out var normalizedName))
{
_fontName = normalizedName;
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] Unknown Font '" + text4 + "'.");
}
break;
}
case "displaymode":
{
if (TryParseDisplayMode(text4, out var mode))
{
_displayMode = mode;
}
else
{
LoggerBridge.Warn("[ShowEnemyHP] Unknown DisplayMode '" + text4 + "'.");
}
break;
}
}
}
return true;
}
catch (Exception ex)
{
LoggerBridge.Error("[ShowEnemyHP] Failed to read config: " + ex.Message);
ResetToDefaults();
return false;
}
}
private static bool TryParseFloat(string raw, out float value)
{
return float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
}
private static bool TryParseColorScheme(string raw, out HpColorScheme scheme)
{
return Enum.TryParse<HpColorScheme>(raw, ignoreCase: true, out scheme);
}
private static bool TryParseDisplayMode(string raw, out HpDisplayMode mode)
{
return Enum.TryParse<HpDisplayMode>(raw, ignoreCase: true, out mode);
}
private static void WriteAnnotatedFile()
{
try
{
string path = Path.Combine(PathsBridge.EnsureUserDataDir(), "ShowEnemyHP.cfg");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("# Show Enemy HP configuration");
stringBuilder.AppendLine("# Scale adjusts the size of the floating HP canvas.");
stringBuilder.AppendLine("# Range: 0.005 – 0.200 (default 0.03)");
stringBuilder.AppendLine("Scale = " + _scale.ToString(CultureInfo.InvariantCulture));
stringBuilder.AppendLine();
stringBuilder.AppendLine("# VerticalOffset moves the label relative to the enemy head (positive is up).");
stringBuilder.AppendLine("# Range: -5.0 – 5.0 (default -0.25)");
stringBuilder.AppendLine("VerticalOffset = " + _verticalOffset.ToString(CultureInfo.InvariantCulture));
stringBuilder.AppendLine();
stringBuilder.AppendLine("# ColorScheme controls the HP gradient.");
stringBuilder.AppendLine("# Options: " + string.Join(", ", HpPalette.GetSchemeSummaries()));
stringBuilder.AppendLine("# Default: " + HpColorScheme.Classic);
stringBuilder.AppendLine("ColorScheme = " + _colorScheme);
stringBuilder.AppendLine();
stringBuilder.AppendLine("# DisplayMode switches between numeric text and a simple bar.");
stringBuilder.AppendLine("# Options: Numeric, Bar (default Numeric)");
stringBuilder.AppendLine("DisplayMode = " + _displayMode);
stringBuilder.AppendLine();
stringBuilder.AppendLine("# Font selects the TextMeshPro font asset used for HP numbers.");
stringBuilder.AppendLine("# Options: " + string.Join(", ", HpFontCatalog.GetFontSummaries()));
stringBuilder.AppendLine("# Default: Default");
stringBuilder.AppendLine("Font = " + _fontName);
File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
}
catch (Exception ex)
{
LoggerBridge.Warn("[ShowEnemyHP] Failed to write config: " + ex.Message);
}
}
}
internal static class ModCore
{
public static Dictionary<uint, EnemyUiContext> enemyUIContextsById = new Dictionary<uint, EnemyUiContext>();
public static Dictionary<uint, Enemy> enemiesById = new Dictionary<uint, Enemy>();
public static bool ShowUI = true;
public static bool UseManualBillboard = false;
public static void Init()
{
ModConfig.Init();
HpFontCatalog.EnsureInitialized();
LoggerBridge.Info("Show Enemy HP initialized! (F2 to toggle, Ctrl+R to reload config)");
LoggerBridge.Info("Available color schemes: " + string.Join(", ", HpPalette.GetSchemeSummaries()));
LoggerBridge.Info("Available fonts: " + string.Join(", ", HpFontCatalog.GetFontSummaries()));
}
public static void UpdateLoop()
{
if (Input.GetKeyDown((KeyCode)283))
{
SetGlobalVisibility(!ShowUI);
}
HandleHotReload();
}
public static void SetGlobalVisibility(bool visible)
{
ShowUI = visible;
LoggerBridge.Info("Enemy HP UI " + (visible ? "enabled" : "disabled") + " (F2)");
foreach (EnemyUiContext value2 in enemyUIContextsById.Values)
{
if ((Object)(object)value2?.Root != (Object)null)
{
value2.Root.SetActive(visible);
}
}
if (!visible)
{
return;
}
foreach (KeyValuePair<uint, Enemy> item in enemiesById)
{
uint key = item.Key;
Enemy value = item.Value;
if (!((Object)(object)value == (Object)null))
{
EnemyUpdatePatch.UpdateEnemyUI(value, key);
}
}
}
public static void CleanupEnemyUI(uint enemyId)
{
if (enemyUIContextsById.TryGetValue(enemyId, out var value))
{
if ((Object)(object)value?.Root != (Object)null)
{
Object.Destroy((Object)(object)value.Root);
}
enemyUIContextsById.Remove(enemyId);
}
}
public static void ApplyConfigToAllUI()
{
//IL_0041: 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)
TMP_FontAsset fontAsset = HpFontCatalog.GetFontAsset(ModConfig.FontName);
foreach (EnemyUiContext value in enemyUIContextsById.Values)
{
if (value != null)
{
if ((Object)(object)value.Root != (Object)null)
{
value.Root.transform.localScale = Vector3.one * ModConfig.Scale;
}
ApplyDisplayModeToContext(value);
if ((Object)(object)value.NumericText != (Object)null)
{
((TMP_Text)value.NumericText).font = fontAsset;
((TMP_Text)value.NumericText).ForceMeshUpdate(false, false);
}
}
}
foreach (KeyValuePair<uint, Enemy> item in enemiesById)
{
if (!((Object)(object)item.Value == (Object)null))
{
EnemyUpdatePatch.UpdateEnemyUI(item.Value, item.Key);
}
}
}
public static void ApplyDisplayModeToContext(EnemyUiContext context)
{
if (context != null)
{
bool flag = ModConfig.DisplayMode == HpDisplayMode.Numeric;
if ((Object)(object)context.NumericRoot != (Object)null)
{
context.NumericRoot.SetActive(flag);
}
if ((Object)(object)context.BarRoot != (Object)null)
{
context.BarRoot.SetActive(!flag);
}
}
}
private static void HandleHotReload()
{
if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)114) && ModConfig.ReloadFromDisk())
{
ApplyConfigToAllUI();
}
}
}
internal static class EnemyHpUtil
{
public static float GetHealthPercent(Enemy enemy, float currentHp)
{
float hpRatio = enemy.GetHpRatio();
if (hpRatio > 0f && hpRatio <= 1.001f)
{
return Mathf.Clamp01(hpRatio);
}
float num = enemy.maxHp;
if (num <= 1f && (Object)(object)enemy.enemyData != (Object)null)
{
num = enemy.enemyData.hp;
}
if (num <= 0f)
{
num = 1f;
}
return Mathf.Clamp01(currentHp / num);
}
public static string FormatHealthValue(float hp)
{
float num = Mathf.Abs(hp);
if (num < 1000f)
{
return Mathf.RoundToInt(hp).ToString();
}
if (!(num < 10000f))
{
if (!(num < 100000f))
{
if (!(num < 1000000f))
{
if (!(num < 10000000f))
{
if (!(num < 100000000f))
{
return $"{Mathf.RoundToInt(hp / 1000000f)}M";
}
return $"{hp / 1000000f:0.0}M";
}
return $"{hp / 1000000f:0.00}M";
}
return $"{Mathf.RoundToInt(hp / 1000f)}K";
}
return $"{hp / 1000f:0.0}K";
}
return $"{hp / 1000f:0.00}K";
}
}
[HarmonyPatch(typeof(Enemy), "InitEnemy")]
internal static class EnemyInitPatch
{
public static void Postfix(Enemy __instance, uint id)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Expected O, but got Unknown
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_018f: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)__instance == (Object)null)
{
return;
}
ModCore.enemiesById[id] = __instance;
ModCore.CleanupEnemyUI(id);
GameObject val = new GameObject("EnemyHPUI");
val.transform.SetParent(((Component)__instance).transform, false);
val.transform.localScale = Vector3.one * ModConfig.Scale;
Canvas obj = val.AddComponent<Canvas>();
obj.renderMode = (RenderMode)2;
obj.sortingOrder = 100;
((Component)obj).GetComponent<RectTransform>().sizeDelta = new Vector2(250f, 60f);
try
{
LookAtCamera obj2 = val.AddComponent<LookAtCamera>();
obj2.xzOnly = true;
obj2.invert = true;
ModCore.UseManualBillboard = false;
}
catch (Exception ex)
{
ModCore.UseManualBillboard = true;
LoggerBridge.Warn("LookAtCamera not found; using manual billboard. (" + ex.Message + ")");
}
GameObject val2 = new GameObject("HPText");
val2.transform.SetParent(val.transform, false);
TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
((TMP_Text)val3).fontSize = 32f;
((TMP_Text)val3).font = HpFontCatalog.GetFontAsset(ModConfig.FontName);
((Graphic)val3).color = HpPalette.FromPercent(1f);
((TMP_Text)val3).text = "100";
GameObject barRoot;
RectTransform fillRect;
float barFullWidth;
Image barFill = CreateHpBar(val, out barRoot, out fillRect, out barFullWidth);
if (!ModCore.UseManualBillboard)
{
val2.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
if ((Object)(object)barRoot != (Object)null)
{
barRoot.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
}
}
val.transform.position = __instance.GetHeadPosition() + new Vector3(0f, ModConfig.VerticalOffset, 0f);
EnemyUiContext enemyUiContext = new EnemyUiContext(val, val2, val3, barRoot, barFill, fillRect, barFullWidth);
ModCore.enemyUIContextsById[id] = enemyUiContext;
ModCore.ApplyDisplayModeToContext(enemyUiContext);
val.SetActive(ModCore.ShowUI);
if (ModCore.ShowUI)
{
EnemyUpdatePatch.UpdateEnemyUI(__instance, id);
}
}
private static Image CreateHpBar(GameObject parent, out GameObject barRoot, out RectTransform fillRect, out float barFullWidth)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Expected O, but got Unknown
//IL_0031: 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_005b: 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_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Expected O, but got Unknown
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: 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_015e: Unknown result type (might be due to invalid IL or missing references)
barRoot = new GameObject("HPBar");
barRoot.transform.SetParent(parent.transform, false);
RectTransform obj = barRoot.AddComponent<RectTransform>();
obj.anchorMin = new Vector2(0.5f, 0.5f);
obj.anchorMax = new Vector2(0.5f, 0.5f);
obj.pivot = new Vector2(0.5f, 0.5f);
obj.sizeDelta = new Vector2(60f, 8f);
obj.anchoredPosition = Vector2.zero;
Image obj2 = barRoot.AddComponent<Image>();
((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.7f);
((Graphic)obj2).raycastTarget = false;
GameObject val = new GameObject("Fill");
val.transform.SetParent(barRoot.transform, false);
fillRect = val.AddComponent<RectTransform>();
fillRect.anchorMin = new Vector2(0.5f, 0.5f);
fillRect.anchorMax = new Vector2(0.5f, 0.5f);
fillRect.pivot = new Vector2(0f, 0.5f);
barFullWidth = Mathf.Max(0f, 59.6f);
fillRect.sizeDelta = new Vector2(barFullWidth, 7.6f);
fillRect.anchoredPosition = new Vector2(-29.8f, 0f);
Image obj3 = val.AddComponent<Image>();
((Graphic)obj3).color = HpPalette.FromPercent(1f);
((Graphic)obj3).raycastTarget = false;
return obj3;
}
}
[HarmonyPatch(typeof(Enemy), "Damage")]
internal static class EnemyDamagePatch
{
public static void Postfix(Enemy __instance)
{
if (!((Object)(object)__instance == (Object)null))
{
uint id = __instance.id;
if (!ModCore.enemyUIContextsById.ContainsKey(id))
{
EnemyInitPatch.Postfix(__instance, id);
}
if (ModCore.ShowUI)
{
EnemyUpdatePatch.UpdateEnemyUI(__instance, id);
}
}
}
}
[HarmonyPatch(typeof(Enemy), "MyUpdate")]
internal static class EnemyUpdatePatch
{
public static void Postfix(Enemy __instance)
{
if (!((Object)(object)__instance == (Object)null) && ModCore.ShowUI && !(__instance.hp <= 0f))
{
UpdateEnemyUI(__instance, __instance.id);
}
}
public static void UpdateEnemyUI(Enemy enemy, uint id)
{
//IL_0032: 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_004d: 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_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: 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_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_016f: Unknown result type (might be due to invalid IL or missing references)
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
if (!ModCore.enemyUIContextsById.TryGetValue(id, out var value) || value == null)
{
return;
}
GameObject root = value.Root;
if ((Object)(object)root == (Object)null || !ModCore.ShowUI)
{
return;
}
root.transform.localScale = Vector3.one * ModConfig.Scale;
root.transform.position = enemy.GetHeadPosition() + new Vector3(0f, ModConfig.VerticalOffset, 0f);
if (ModCore.UseManualBillboard)
{
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
return;
}
root.transform.LookAt(((Component)main).transform);
root.transform.Rotate(Vector3.up, 180f);
}
float hp = enemy.hp;
if (hp <= 0f)
{
return;
}
float healthPercent = EnemyHpUtil.GetHealthPercent(enemy, hp);
if (ModConfig.DisplayMode == HpDisplayMode.Numeric)
{
TextMeshProUGUI numericText = value.NumericText;
if (!((Object)(object)numericText == (Object)null))
{
TMP_FontAsset fontAsset = HpFontCatalog.GetFontAsset(ModConfig.FontName);
if ((Object)(object)((TMP_Text)numericText).font != (Object)(object)fontAsset)
{
((TMP_Text)numericText).font = fontAsset;
((TMP_Text)numericText).ForceMeshUpdate(false, false);
}
((TMP_Text)numericText).text = EnemyHpUtil.FormatHealthValue(hp);
((Graphic)numericText).color = HpPalette.FromPercent(healthPercent);
}
}
else
{
Image barFill = value.BarFill;
RectTransform barFillRect = value.BarFillRect;
if (!((Object)(object)barFill == (Object)null) && !((Object)(object)barFillRect == (Object)null))
{
Vector2 sizeDelta = barFillRect.sizeDelta;
sizeDelta.x = value.BarFullWidth * Mathf.Clamp01(healthPercent);
barFillRect.sizeDelta = sizeDelta;
((Graphic)barFill).color = HpPalette.FromPercent(healthPercent);
}
}
}
}
[HarmonyPatch(typeof(Enemy), "OnDestroy")]
internal static class EnemyDestroyPatch
{
public static void Prefix(Enemy __instance)
{
if (!((Object)(object)__instance == (Object)null))
{
ModCore.CleanupEnemyUI(__instance.id);
ModCore.enemiesById.Remove(__instance.id);
}
}
}
[HarmonyPatch(typeof(EnemyManager), "RemoveEnemy")]
internal static class EnemyManagerRemovePatch
{
public static void Postfix(EnemyManager __instance, Enemy enemy)
{
if (!((Object)(object)enemy == (Object)null))
{
ModCore.CleanupEnemyUI(enemy.id);
ModCore.enemiesById.Remove(enemy.id);
}
}
}
internal static class PathsBridge
{
public static string UserDataDir => Paths.ConfigPath;
public static string EnsureUserDataDir()
{
string userDataDir = UserDataDir;
if (!Directory.Exists(userDataDir))
{
try
{
Directory.CreateDirectory(userDataDir);
}
catch
{
}
}
return userDataDir;
}
}
[BepInPlugin("com.maskoliver.showenemyhp", "Show Enemy HP", "1.0.0")]
public sealed class BepInEntry : BasePlugin
{
private sealed class UpdateDriver : MonoBehaviour
{
public UpdateDriver(IntPtr ptr)
: base(ptr)
{
}
private void Start()
{
try
{
ModCore.Init();
}
catch (Exception ex)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogError((object)ex);
}
}
}
private void Update()
{
ModCore.UpdateLoop();
}
private void OnDestroy()
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)"UpdateDriver destroyed");
}
}
}
private Harmony _harmony;
internal static ManualLogSource Log;
private static GameObject _host;
public override void Load()
{
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: 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
Log = Logger.CreateLogSource("ShowEnemyHp");
LoggerBridge.Info = delegate(string s)
{
Log.LogInfo((object)s);
};
LoggerBridge.Warn = delegate(string s)
{
Log.LogWarning((object)s);
};
LoggerBridge.Error = delegate(string s)
{
Log.LogError((object)s);
};
ClassInjector.RegisterTypeInIl2Cpp<UpdateDriver>();
_host = new GameObject("ShowEnemyHp_UpdateHost");
Object.DontDestroyOnLoad((Object)(object)_host);
((Object)_host).hideFlags = (HideFlags)61;
_host.AddComponent<UpdateDriver>();
_harmony = new Harmony("com.maskoliver.showenemyhp");
_harmony.PatchAll();
Log.LogInfo((object)"Show Enemy HP (BepInEx) loaded.");
}
}
}