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 WiresEnemyHUD v0.9.4
EnemyHudRework.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; 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(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("EnemyHudRework")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+fd41f9d5a164589189f0bb78c651afee0009a375")] [assembly: AssemblyProduct("EnemyHudRework")] [assembly: AssemblyTitle("EnemyHudRework")] [assembly: AssemblyVersion("1.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 EnemyHudRework { [BepInPlugin("kajetan.valheim.enemyhudrework", "Wire's Enemy HUD", "0.9.4")] public sealed class EnemyHudReworkPlugin : BaseUnityPlugin { private sealed class DpsUiState { public GameObject TextObject; public TMP_Text Text; public RectTransform Rect; public float BaseFontSize; public bool EnabledLastFrame; public bool HasLastHealth; public float LastHealth; public Action<float, Character> DamageHandler; public bool Started; public float StartTime; public float EndTime; public float LastHitTime; public int Damage; public int ShownValue = -1; public bool ShownSuffix; public string MeasuredText; public string MeasuredHp; public int MeasuredLayout = -1; public float TextWidth; public float TextHeight; public float HpHeight; public float LeftPad; } private sealed class BubbleUi { public GameObject Root; public RectTransform Rect; public GuiBar Fast; public GuiBar Slow; public Image Frame; public Color FrameOriginal; } private enum IconShape { [Description("Circle")] Circle, [Description("Diamond")] Diamond, [Description("Chevron")] Chevron, [Description("Triangle")] Triangle, [Description("Hexagon")] Hexagon, [Description("Slanted rhombus")] SlantedRhombus, [Description("Upside-down triangle")] UpsideDownTriangle, [Description("Square")] Square, [Description("Upside-down chevron")] UpsideDownChevron, [Description("Sideways triangle")] SidewaysTriangle } private enum IconStyle { [Description("Shapes")] Shapes, [Description("Game icons (not recommended)")] GameIcons } private sealed class NativePalette { public Image HealthFrame; public Image StaggerFrame; public Color HealthFrameOriginal; public Color StaggerFrameOriginal; } internal sealed class EffectUi { public RectTransform Root; public readonly Image[] Icons = (Image[])(object)new Image[4]; public readonly int[] Hashes = new int[4]; public uint Revision = uint.MaxValue; public int Layout = -1; } private sealed class HitPredictionUi { public Image HealthOverlay; public RectTransform HealthBar; public Image StaggerGhost; public Image BubbleOverlay; public RectTransform BubbleBar; public bool BubbleShown; public float BubbleMax; public float BubbleTaken; public RectTransform StaggerBar; public int Version = -1; public float Stagger = -2f; public float Health = -1f; public float MaxHealth = -1f; public bool Alerted; public bool Bubbled; public uint Revision; public ZNetView View; public bool Staggering; public bool Enemy; public bool HealthShown; public bool StaggerShown; public float Fade; } private sealed class PlayerEffectCycle { internal readonly List<int>[] Groups = new List<int>[6]; internal readonly HashSet<int> Seen = new HashSet<int>(); internal readonly int[] Visible = new int[4]; internal ZDO Zdo; internal int PowerIndex; internal int PotionBuffIndex; internal int CooldownIndex; internal bool CyclePower; internal bool CyclePotionBuff; internal bool CycleCooldown; internal bool NeedsCycles { get { if (!CyclePower && !CyclePotionBuff) { return CycleCooldown; } return true; } } internal PlayerEffectCycle() { for (int i = 0; i < Groups.Length; i++) { Groups[i] = new List<int>(); } } internal void Clear() { List<int>[] groups = Groups; for (int i = 0; i < groups.Length; i++) { groups[i].Clear(); } Seen.Clear(); PowerIndex = (PotionBuffIndex = (CooldownIndex = 0)); } } [HarmonyPatch(typeof(Humanoid), "SetupEquipment")] private static class SetupEquipmentRanksPatch { private static void Postfix(Humanoid __instance) { if ((Object)(object)Instance != (Object)null && __instance is Player) { Instance.OnResistancesMayChange((Character)(object)__instance); } } } public enum NameColorMode { Pastel, Normal, Icons, None } public enum BossHealthTextPosition { Tracked, Centered } public enum BossNameColorMode { Pastel, Vanilla, White } private enum HudKind { Enemy, Boss, Player } private sealed class ConfigurationManagerAttributes { public int Order { get; set; } public string Category { get; set; } public Func<bool> ReadOnlyProvider { get; set; } public bool? IsAdvanced { get; set; } public bool ReadOnly { get { if (ReadOnlyProvider != null) { return ReadOnlyProvider(); } return false; } } } private sealed class TrailClip { public RectMask2D Mask; public bool Bubble; public float Shown = -1f; public int Layout = -1; } private sealed class EnemyUiState { public WeaknessUi Weakness; public BubbleUi Bubble; public float BubbleLift; public ZNetView BubbleView; public uint BubbleRevision = uint.MaxValue; public float BubbleMax; public float BubbleDamage; public bool BubbleFoe; public bool BubbleSeen; public bool BubbleTimed; public long BubbleUntil; public float BubbleFlashStart = -1f; public float BubbleFlashShown; public HitPredictionUi Prediction; public bool PredictShowStagger; public float StaggerFlashAlpha; public float CritFlashAlpha; public float StaggerFlashShown; public float CritFlashShown; public float LastStarY = float.NaN; public Character Character; public BaseAI Ai; public GameObject Gui; public object HudRecord; public bool HpDrawn; public int HpA; public int HpB; public int HpLayout; public float HpLift; public string HpName; public int ScaleLayout = -1; public float ScaleHeight; public string UpperSource; public string UpperValue; public bool ClampKnown; public Bounds ClampBounds; public HudKind Kind; public bool IsDebug; public CanvasGroup PlayerOpacityGroup; public bool PlayerFadeGroupAdded; public float PlayerOriginalAlpha = 1f; public float LastAimedAt = -999f; public TrailClip[] Trails; public bool TrailsHidden; public Graphic NameGraphic; public TMP_Text NameText; public RectTransform NameRect; public Vector2 NameOriginalPosition; public string OriginalNameText; public Color NormalNameColor; public GameObject HealthValueObject; public TMP_Text HealthValueText; public RectTransform HealthValueRect; public float HealthValueBaseFontSize; public GameObject AlertedIcon; public GameObject AwareIcon; public Graphic[] IconGraphics; public bool IconsHidden; public RectTransform HealthRect; public Vector2 HealthOriginalPosition; public Vector3 HealthOriginalScale; public float HealthOriginalHeight; public RectTransform HealthFastFill; public RectTransform HealthSlowFill; public RectTransform HealthFriendlyFill; public float HealthFastOriginalHeight; public float HealthSlowOriginalHeight; public float HealthFriendlyOriginalHeight; public GameObject StaggerRoot; public RectTransform StaggerRect; public GuiBar StaggerFast; public GuiBar StaggerBackground; public RectTransform Level2; public TMP_Text LevelNumber; public GameObject LevelSecondStar; public int StarLevel = -1; public GameObject[] OtherStars; public StatusUi Status; public EffectUi Effects; public RectTransform Level3; public Vector2 Level2Original; public Vector2 Level3Original; public bool HasLevel2; public bool HasLevel3; public RectTransform IconRow; public Vector2 IconRowOriginal; public Image RecoveryOverlay; public Animator StaggerAnimator; public float StaggerBegan = -1f; public float StaggerEnd; public float StaggerChecked; public float StaggerSpeed = 1f; public Image HealthBkg; public Image StaggerBkg; public Color HealthBkgOriginal; public Color StaggerBkgOriginal; public int BkgApplied = -1; public GuiBar BossFastBar; public bool BossFastLooked; public GuiBar[] OwnBars; public float HideTimer = 1f; public float LastStaggerPercentage; public bool WasStaggering; public float FlashStarted = -999f; public float FlashUntil = -999f; } private sealed class StaggerSyncState { public float LastSent = -1f; public float NextSendTime; } private struct LookedAway { public Character C; public float At; public bool IconsShown; } [HarmonyPatch(typeof(Character), "SetHealth")] private static class EnemyDeathInstantHide { private static void Postfix(Character __instance, float health) { if (health <= 0f && (Object)(object)__instance != (Object)null && !__instance.IsPlayer()) { Instance?.HideDeadHud(__instance); } } } [HarmonyPatch(typeof(EnemyHud), "ShowHud")] private static class EnemyHudShowHudPatch { private static bool Prefix(Character c, bool isMount) { if (!((Object)(object)c == (Object)null) && !c.IsPlayer()) { return c.GetHealth() > 0f; } return true; } private static void Postfix(EnemyHud __instance, Character c, bool isMount) { if ((Object)(object)Instance != (Object)null) { Instance.SetupHud(__instance, c, isMount); } } } [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] private static class EnemyHudUpdateHudsPatch { [HarmonyPriority(0)] private static void Postfix(EnemyHud __instance, Player player, Sadle sadle, float dt) { if ((Object)(object)Instance != (Object)null) { Instance.UpdateEnemyHuds(player, dt); } } } [HarmonyPatch(typeof(EnemyHud), "LateUpdate")] private static class EnemyHudLateUpdatePatch { [HarmonyPriority(0)] private static void Postfix(EnemyHud __instance) { if ((Object)(object)Instance != (Object)null) { Instance.UpdateDebugHuds(__instance); Instance.ApplyDamageRevealAfterHudUpdate(__instance); } } } [HarmonyPatch(typeof(Character), "UpdateStagger")] private static class CharacterUpdateStaggerPatch { private static void Postfix(Character __instance) { if ((Object)(object)Instance != (Object)null) { Instance.SyncOwnedStagger(__instance); } } } private struct LocalKey : IEquatable<LocalKey> { public ZDOID Id; public int Key; public bool Equals(LocalKey o) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (Id == o.Id) { return Key == o.Key; } return false; } public override bool Equals(object o) { if (o is LocalKey o2) { return Equals(o2); } return false; } public override int GetHashCode() { return (((object)Unsafe.As<ZDOID, ZDOID>(ref Id)/*cast due to .constrained prefix*/).GetHashCode() * 397) ^ Key; } } private sealed class StatusUi { public RectTransform Root; public readonly Image[] Icons = (Image[])(object)new Image[5]; public readonly Image[] Fills = (Image[])(object)new Image[5]; public uint Revision = uint.MaxValue; public int Mask = -1; public int Layout = -1; } [HarmonyPatch(typeof(UITooltip), "UpdateTextElements")] private static class TooltipTextPatch { private static void Postfix() { Instance?.DecorateTooltip(); } } private sealed class HealthSample { public float LastHp; public float SeenAt; public float ShowUntil; public float ParryUntil; public float PendingLocalHitUntil = -999f; public float TrailBeforeShare; public bool TrailSeedPending; public float TrailSeedUntil; } private sealed class HealthOutline { public float StartsAt = -999f; public float EndsAt = -999f; public float ObservedCriticalUntil = -999f; } [HarmonyPatch(typeof(EnemyHud), "LateUpdate")] private static class WatchHealthBeforeHudCreation { private static void Prefix() { Instance?.SampleHealthForVisibility(); } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] private static class ParryVisibility { private static readonly FieldRef<Humanoid, float> BlockTimer = AccessTools.FieldRefAccess<Humanoid, float>("m_blockTimer"); private static readonly Func<Humanoid, ItemData> CurrentBlocker = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetCurrentBlocker", (Type[])null, (Type[])null), (object)null, true); private static void Prefix(Humanoid __instance, out bool __state) { __state = false; if (!((Object)(object)Instance == (Object)null) && Instance._showOnParry.Value && (object)__instance == Player.m_localPlayer) { ItemData val = CurrentBlocker(__instance); float num = BlockTimer.Invoke(__instance); __state = val != null && val.m_shared.m_timedBlockBonus > 1f && num != -1f && num < 0.25f; } } private static void Postfix(Character attacker, bool __result, bool __state) { if (__state && __result && (Object)(object)attacker != (Object)null) { Instance?.OnLocalParry(attacker); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static class RegisterRpcs { private static void Postfix(ZRoutedRpc __instance) { RegisterCriticalRpc(__instance); } } [HarmonyPatch(typeof(EnemyHud), "TestShow")] private static class DamageAdditionalVisibility { private static void Postfix(Character c, ref bool __result) { if (!__result && !((Object)(object)Instance == (Object)null) && !((Object)(object)c == (Object)null)) { if (Instance._healthSamples.Count > 0 && Instance.ForceShowOnDamage(c)) { __result = true; } else if (Instance._farHover != null && Instance._farHover.Value && !c.IsPlayer() && !c.IsBoss() && Instance.LookedAtRecently(c, Instance._lookAwayDelay.Value)) { __result = true; } } } } [HarmonyPatch(typeof(Character), "Damage")] private static class RememberLocalOutgoingDamage { [HarmonyPrefix] private static void Prefix(Character __instance, HitData hit) { Instance?.NoteOutgoingLocalHit(__instance, hit); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] private static class ActualCriticalHit { private struct HitState { internal Vector3 Snapshot; internal bool LocalAttacker; internal bool Active; internal int Types; } private static void Prefix(Character __instance, HitData hit, out HitState __state) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) __state = default(HitState); if (!((Object)(object)Instance == (Object)null)) { float num = BackstabTime.Invoke(__instance); __state = new HitState { Active = true, Snapshot = new Vector3(__instance.GetHealth(), num, __instance.IsStaggering() ? 1f : 0f), LocalAttacker = IsLocalPlayerHit(hit), Types = ((hit != null && hit.GetAttacker() is Player) ? MaskOf(hit.m_damage) : 0) }; } } private static void Postfix(Character __instance, HitState __state) { try { PostfixTimed(__instance, __state); } finally { } } private static void PostfixTimed(Character __instance, HitState __state) { //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) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Vector3 snapshot = __state.Snapshot; if (__state.Active && (Object)(object)Instance != (Object)null && __state.Types != 0 && !__instance.IsPlayer() && __instance.GetHealth() > 0f) { Instance.SendTypeHit(__instance, __state.Types); } if (__state.Active && !((Object)(object)Instance == (Object)null) && IsOrdinaryEnemy(__instance) && !(__instance.GetHealth() >= snapshot.x - 0.01f)) { if (__state.LocalAttacker) { Instance.RecordDamage(__instance, snapshot.x, __instance.GetHealth()); } if ((BackstabTime.Invoke(__instance) != snapshot.y || snapshot.z > 0.5f) && __instance.GetHealth() > 0f) { Instance.MarkCriticalHit(__instance); } } } } private enum IconShow { [Description("Weaknesses and resistances")] Both, [Description("Weaknesses only")] WeaknessesOnly, [Description("Resistances only")] ResistancesOnly, [Description("Off")] Off } private enum WeaponFocus { Off, Highlight, Limited } private enum DamageKind { Blunt, Slash, Pierce, Fire, Frost, Lightning, Poison, Spirit, Pickaxe, Chop } private enum ImmunityShow { [Description("Off")] Off, [Description("Notable only")] Notable, [Description("All")] All } private sealed class WeaknessUi { public RectTransform Root; public Image[] Slots = (Image[])(object)new Image[10]; public Image[] Fills = (Image[])(object)new Image[10]; public int[] Kind = new int[10]; public bool[] Show = new bool[10]; public float[] FlashStart = new float[10] { -1f, -1f, -1f, -1f, -1f, -1f, -1f, -1f, -1f, -1f }; public bool Flashing; public uint RanksRevision = uint.MaxValue; public int Ranks = -1; public bool KindsReady; public bool Dirty = true; public int LastKey = -1; } private const float DpsTimeoutSeconds = 3f; private const float DpsMinimumWindowSeconds = 1f; private const float DpsNormalizeAfterSeconds = 3f; private ConfigEntry<string> _bossDpsMode; private ConfigEntry<float> _dpsFontScale; private readonly Dictionary<EnemyUiState, DpsUiState> _dpsStates = new Dictionary<EnemyUiState, DpsUiState>(); private ConfigEntry<bool> _bubbleWorld; private ConfigEntry<bool> _bubbleHud; private static readonly int BubbleMaxKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.bubble.max"); private static readonly int BubbleDamageKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.bubble.dmg"); private static readonly int BubbleUntilKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.bubble.until"); private static readonly Color BubbleColor = new Color(1f, 0.2941177f, 0.9387633f, 1f); private static readonly Color BubbleTrail = new Color(0.5f, 0.5f, 0.5f, 1f); private static readonly Color BubbleColorFoe = new Color(0.3759732f, 0.659875f, 0.8970588f, 1f); private const float BubblePopLinger = 1f; private static readonly FieldRef<SE_Shield, float> ShieldTotal = AccessTools.FieldRefAccess<SE_Shield, float>("m_totalAbsorbDamage"); private static readonly FieldRef<SE_Shield, float> ShieldDamage = AccessTools.FieldRefAccess<SE_Shield, float>("m_damage"); private static readonly FieldRef<StatusEffect, Character> SeOwner = AccessTools.FieldRefAccess<StatusEffect, Character>("m_character"); private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); private RectTransform _hudBubble; private CanvasGroup _hudBubbleGroup; private GuiBar _hudBubbleFast; private GuiBar _hudBubbleSlow; private RectTransform _gpRoot; private Vector2 _gpBase; private float _gpShift; private float _hudBubbleTotal; private float _hudBubbleDamage; private float _hudBubbleAlpha; private Coroutine _hudBubbleFade; private Coroutine _hudBubblePop; private Image _hudBubbleFrame; private Color _hudBubbleFrameOriginal; private const float HudBubbleGap = 8f; private const float HudBubbleFadeTime = 0.25f; private float _hudBubbleLength = -1f; private static readonly float[] BubbleQuarters = new float[3] { 0.25f, 0.5f, 0.75f }; private readonly List<RectTransform> _bubbleNotches = new List<RectTransform>(); private static readonly string[] DamageNames = new string[10] { "Blunt", "Slash", "Pierce", "Fire", "Frost", "Lightning", "Poison", "Spirit", "Pickaxe", "Chop" }; private ConfigEntry<IconStyle> _iconStyle; private readonly ConfigEntry<IconShape>[] _iconShape = new ConfigEntry<IconShape>[10]; private readonly ConfigEntry<Color>[] _iconColor = new ConfigEntry<Color>[10]; private ConfigEntry<Color> _resistColor; private readonly int[] _shapeOf = new int[10]; private readonly Color[] _tintOf = (Color[])(object)new Color[10]; private Color _resistTint = ResistTint; private Color _immuneTint = ResistTint; private readonly Dictionary<EnemyUiState, NativePalette> _nativePalettes = new Dictionary<EnemyUiState, NativePalette>(); private const string SecGeneral = "01 - General"; private const string SecEnemy = "02 - Nameplates"; private const string SecHud = "03 - Boss Bar"; private const string SecModded = "04 - Custom Bars"; private const string SecVisibility = "05 - Visibility"; private const string SecWeak = "06 - Damage Icons"; private const string SecPredict = "07 - Hit Predictions"; private const string SecIconColors = "08 - Icon Shapes and Colors"; private const string SecTuning = "09 - Fine Tuning"; private const string SecSync = "10 - Multiplayer"; private const string SecDebug = "11 - Debug"; private const string SecPlayer = "02 - Nameplates"; private const string SecBoss = "03 - Boss Bar"; private const string SecStagger = "04 - Custom Bars"; private const string SecFocus = "05 - Visibility"; private const string O1 = "01 - General"; private const string O2 = "02 - Enemy Bars"; private const string O3 = "03 - Weakness Icons"; private const string O4 = "04 - Stagger Bar"; private const string O5 = "05 - Boss Bar"; private const string O6 = "06 - Player Nameplates"; private const string O7 = "07 - Appearance"; private const string O8 = "08 - Damage Icons"; private const string O9 = "09 - Multiplayer"; private const string O10 = "10 - Debug"; private const string O11 = "11 - Hit Predictions"; private const string O12 = "12 - Bubble Bar"; private const string L3 = "03 - Visibility"; private const string L4 = "04 - Focus"; private const string L5 = "05 - Damage Icons"; private const string L6 = "06 - Icon Shapes and Colors"; private const string L7 = "07 - Stagger Bar"; private const string L8 = "08 - Boss Bar"; private const string L9 = "09 - Player Nameplates"; private const string L10 = "10 - Hit Predictions"; private const string L11 = "11 - Fine Tuning"; private const string K2 = "02 - Creature Bars"; private const string K3 = "03 - Modded Bars"; private const string K4 = "04 - Visibility"; private const string K5 = "05 - Focus"; private const string K7 = "07 - Boss Bar"; private const string K8 = "08 - Player Nameplates"; private const string K9 = "09 - Hit Predictions"; private const string K10 = "10 - Icon Shapes and Colors"; private const string K11 = "11 - Fine Tuning"; private const string K12 = "12 - Multiplayer"; private const string K13 = "13 - Debug"; private const string M2 = "02 - In-World Nameplates"; private const string M3 = "03 - HUD"; private const string M5 = "05 - Visibility"; private static readonly string[] HealthTextModes = new string[4] { "Current HP", "Current HP/Max HP", "Percentage", "None" }; private int _order; private const float StarRowHeightDefault = -2f; private static readonly string[][] Moves = new string[41][] { new string[10] { "02 - Nameplates", "Transparent background", "07 - Appearance", "Transparent bar background", "02 - Enemy Bars", "Transparent bar background", "02 - Creature Bars", "Transparent background", "02 - In-World Nameplates", "Transparent background" }, new string[8] { "02 - Nameplates", "Creature HP numbers", "02 - Enemy Bars", "Health display", "02 - Creature Bars", "HP numbers", "02 - In-World Nameplates", "Creature HP numbers" }, new string[8] { "02 - Nameplates", "Double size creature bars", "02 - Enemy Bars", "Double size", "02 - Creature Bars", "Double size", "02 - In-World Nameplates", "Double size creature bars" }, new string[8] { "02 - Nameplates", "Uppercase creature names", "02 - Enemy Bars", "Uppercase names", "02 - Creature Bars", "Uppercase names", "02 - In-World Nameplates", "Uppercase names" }, new string[8] { "02 - Nameplates", "Star count past two stars", "02 - Enemy Bars", "Star count past two stars", "02 - Creature Bars", "Star count past two stars", "02 - In-World Nameplates", "Star count past two stars" }, new string[8] { "02 - Nameplates", "Critical hit flash", "02 - Enemy Bars", "Critical hit flash", "02 - Creature Bars", "Critical hit flash", "02 - In-World Nameplates", "Critical hit flash" }, new string[8] { "02 - Nameplates", "Status effects", "02 - Enemy Bars", "Status effects", "02 - Creature Bars", "Status effects", "02 - In-World Nameplates", "Status effects" }, new string[8] { "02 - Nameplates", "Awareness display", "02 - Enemy Bars", "Awareness display", "02 - Creature Bars", "Awareness display", "02 - In-World Nameplates", "Awareness display" }, new string[10] { "02 - Nameplates", "Player HP numbers", "06 - Player Nameplates", "Health display", "09 - Player Nameplates", "Health display", "08 - Player Nameplates", "HP numbers", "02 - In-World Nameplates", "Player HP numbers" }, new string[10] { "02 - Nameplates", "Double size player bars", "06 - Player Nameplates", "Double size", "09 - Player Nameplates", "Double size", "08 - Player Nameplates", "Double size", "02 - In-World Nameplates", "Double size player bars" }, new string[10] { "03 - Boss Bar", "HP numbers", "05 - Boss Bar", "Health display", "08 - Boss Bar", "Health display", "07 - Boss Bar", "HP numbers", "03 - HUD", "Boss HP numbers" }, new string[10] { "03 - Boss Bar", "HP numbers position", "05 - Boss Bar", "Health text position", "08 - Boss Bar", "Health text position", "07 - Boss Bar", "HP numbers position", "03 - HUD", "Boss HP numbers position" }, new string[10] { "03 - Boss Bar", "DPS", "05 - Boss Bar", "DPS", "08 - Boss Bar", "DPS", "07 - Boss Bar", "DPS", "03 - HUD", "Boss DPS" }, new string[10] { "03 - Boss Bar", "Name color", "05 - Boss Bar", "Name color", "08 - Boss Bar", "Name color", "07 - Boss Bar", "Name color", "03 - HUD", "Boss name color" }, new string[8] { "04 - Custom Bars", "Stagger bar", "04 - Stagger Bar", "Enabled", "07 - Stagger Bar", "Enabled", "03 - Modded Bars", "Stagger bar" }, new string[8] { "04 - Custom Bars", "Stagger recovery overlay", "04 - Stagger Bar", "Recovery overlay", "07 - Stagger Bar", "Recovery overlay", "03 - Modded Bars", "Stagger recovery overlay" }, new string[6] { "04 - Custom Bars", "Bubble bar on nameplates", "12 - Bubble Bar", "Bubble bar on nameplates", "03 - Modded Bars", "Bubble bar on nameplates" }, new string[6] { "04 - Custom Bars", "Bubble bar on your HUD", "12 - Bubble Bar", "Bubble bar on your HUD", "03 - Modded Bars", "Bubble bar on your HUD" }, new string[6] { "05 - Visibility", "Reveal on hit or parry", "03 - Visibility", "Reveal on hit or parry", "04 - Visibility", "Reveal on hit or parry" }, new string[10] { "05 - Visibility", "Keep nameplates on screen", "02 - Enemy Bars", "Keep bars on screen", "03 - Visibility", "Keep bars on screen", "04 - Visibility", "Keep bars on screen", "05 - Visibility", "Keep bars on screen" }, new string[8] { "05 - Visibility", "Creature HP numbers only on hover", "02 - Enemy Bars", "Dynamic HP numbers", "04 - Focus", "Dynamic HP numbers", "05 - Focus", "Creature HP numbers only on focus" }, new string[8] { "05 - Visibility", "Player HP numbers only on hover", "06 - Player Nameplates", "Dynamic HP numbers", "04 - Focus", "Player HP numbers", "05 - Focus", "Player HP numbers only on focus" }, new string[8] { "05 - Visibility", "Damage icons only on hover", "03 - Weakness Icons", "Dynamic mode", "04 - Focus", "Dynamic icons", "05 - Focus", "Damage icons only on focus" }, new string[10] { "05 - Visibility", "Fade creature nameplates unless hovered", "02 - Enemy Bars", "Fade unless looked at", "04 - Focus", "Fade unless looked at", "05 - Focus", "Fade creatures out of focus", "05 - Visibility", "Fade creatures unless hovered" }, new string[10] { "05 - Visibility", "Fade player nameplates unless hovered", "06 - Player Nameplates", "Fade unless aimed at", "04 - Focus", "Fade players unless aimed at", "05 - Focus", "Fade players out of focus", "05 - Visibility", "Fade players unless hovered" }, new string[6] { "06 - Damage Icons", "Nameplate icons", "05 - Damage Icons", "Enemy icons", "06 - Damage Icons", "Creature icons" }, new string[4] { "06 - Damage Icons", "Boss icons", "05 - Damage Icons", "Boss icons" }, new string[4] { "06 - Damage Icons", "Immunities", "05 - Damage Icons", "Immunities" }, new string[6] { "06 - Damage Icons", "Limited mode", "03 - Weakness Icons", "Limited mode", "05 - Damage Icons", "Limited mode" }, new string[4] { "06 - Damage Icons", "Hit response", "05 - Damage Icons", "Hit response" }, new string[6] { "06 - Damage Icons", "Icon style", "08 - Damage Icons", "Icon style", "05 - Damage Icons", "Icon style" }, new string[6] { "09 - Fine Tuning", "Text size", "07 - Appearance", "HP text size", "11 - Fine Tuning", "Text size" }, new string[6] { "09 - Fine Tuning", "Star row height", "02 - Enemy Bars", "Star row height", "11 - Fine Tuning", "Star row height" }, new string[6] { "09 - Fine Tuning", "Stagger bar gap", "04 - Stagger Bar", "Gap below health bar", "11 - Fine Tuning", "Stagger bar gap" }, new string[6] { "09 - Fine Tuning", "Icon size", "03 - Weakness Icons", "Icon size", "11 - Fine Tuning", "Icon size" }, new string[6] { "09 - Fine Tuning", "Icons per row", "03 - Weakness Icons", "Icons per row", "11 - Fine Tuning", "Icons per row" }, new string[6] { "09 - Fine Tuning", "Boss icon height", "03 - Weakness Icons", "Boss icon height", "11 - Fine Tuning", "Boss icon height" }, new string[6] { "09 - Fine Tuning", "Boss icon spacing", "03 - Weakness Icons", "Boss icon spacing", "11 - Fine Tuning", "Boss icon spacing" }, new string[12] { "09 - Fine Tuning", "Hover delay", "03 - Weakness Icons", "Dynamic mode delay", "06 - Player Nameplates", "Delay", "02 - Enemy Bars", "Delay", "11 - Fine Tuning", "Look-away delay", "11 - Fine Tuning", "Focus delay" }, new string[6] { "09 - Fine Tuning", "Limited mode delay", "03 - Weakness Icons", "Limited mode delay", "11 - Fine Tuning", "Limited mode delay" }, new string[6] { "10 - Multiplayer", "Sync info", "09 - Multiplayer", "Sync stagger", "12 - Multiplayer", "Sync info" } }; private static FieldInfo _hoverTimerField; private static readonly FieldRef<GuiBar, float> SlowSmoothValue = AccessTools.FieldRefAccess<GuiBar, float>("m_smoothValue"); private static readonly FieldRef<GuiBar, float> SlowDelayTimer = AccessTools.FieldRefAccess<GuiBar, float>("m_delayTimer"); private static readonly FieldRef<GuiBar, float> SlowBarWidth = AccessTools.FieldRefAccess<GuiBar, float>("m_width"); private ConfigEntry<bool> _otherEffects; private ConfigEntry<bool> _playerEffects; private const int MaxOtherEffects = 4; private static readonly int[] OtherKeys = new int[4] { StringExtensionMethods.GetStableHashCode("wire.enemyhud.se0"), StringExtensionMethods.GetStableHashCode("wire.enemyhud.se1"), StringExtensionMethods.GetStableHashCode("wire.enemyhud.se2"), StringExtensionMethods.GetStableHashCode("wire.enemyhud.se3") }; private static readonly Dictionary<int, Sprite> EffectIcons = new Dictionary<int, Sprite>(); private readonly int[] _otherWork = new int[4]; private readonly int[] _otherPrio = new int[4]; private ConfigEntry<bool> _staggerRecoveryEnabled; private ConfigEntry<bool> _transparentBackground; private static readonly Color TransparentBackground = Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, (byte)123)); private static readonly FieldRef<Character, Animator> CharacterAnimator = AccessTools.FieldRefAccess<Character, Animator>("m_animator"); private static readonly FieldRef<Character, CharacterAnimEvent> CharacterAnimEvents = AccessTools.FieldRefAccess<Character, CharacterAnimEvent>("m_animEvent"); private static readonly FieldRef<CharacterAnimEvent, float> PauseTimer = AccessTools.FieldRefAccess<CharacterAnimEvent, float>("m_pauseTimer"); private static readonly FieldRef<CharacterAnimEvent, float> PauseSpeed = AccessTools.FieldRefAccess<CharacterAnimEvent, float>("m_pauseSpeed"); private static readonly int StaggerTag = ZSyncAnimation.GetHash("stagger"); private const float ProbeStep = 1f / 60f; private const float ProbeLimit = 8f; private static Animator _probe; private const string PredictPrimary = "Primary, secondary while using it"; private const string PredictSecondary = "Secondary, primary while using it"; private ConfigEntry<bool> _hitPredictions; private ConfigEntry<string> _predictionAttack; private static readonly Color StaggerQuiet = new Color(0.5f, 0.5f, 0.5f, 1f); private static readonly Color StaggerLoud = new Color(0.3759732f, 0.659875f, 0.8970588f, 1f); private int _predictionVersion = 1; private int _attackKind; private bool _baseValid; private DamageTypes _baseDamage; private float _baseStaggerMultiplier = 1f; private float _baseBackstab = 1f; private readonly HitData _scratchHit = new HitData(); private ConfigEntry<bool> _predictionHighContrast; private ConfigEntry<string> _predictionRoll; private const string RollTypical = "Typical hit"; private const string RollMinimum = "Weakest possible hit"; private float _chainBonus = 1f; private static readonly FieldRef<Humanoid, Attack> CurrentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack"); private static readonly FieldRef<Attack, int> ChainLevel = AccessTools.FieldRefAccess<Attack, int>("m_currentAttackCainLevel"); private const int PlayerRested = 0; private const int PlayerPower = 1; private const int PlayerPotionBuff = 2; private const int PlayerPotionCooldown = 3; private const int PlayerWeather = 4; private const int PlayerEncumbered = 5; private readonly Dictionary<Player, PlayerEffectCycle> _playerEffectsByOwner = new Dictionary<Player, PlayerEffectCycle>(); private readonly List<Player> _goneEffectPlayers = new List<Player>(); private bool _playerEffectTimerOn; private ConfigEntry<bool> _playerNameplateAimFade; private static readonly RaycastHit[] CrosshairHits = (RaycastHit[])(object)new RaycastHit[64]; private static GameCamera _crosshairCameraOwner; private static Camera _crosshairCamera; private ConfigEntry<string> _preset; private bool _applyingPreset; private static readonly int RanksKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.ranks"); private const int RanksPresent = 1073741824; private readonly HashSet<Character> _ranksPending = new HashSet<Character>(); private readonly List<Character> _ranksWork = new List<Character>(); public const string PluginGuid = "kajetan.valheim.enemyhudrework"; public const string PluginName = "Wire's Enemy HUD"; public const string PluginVersion = "0.9.4"; private const string StaggerZdoKey = "EnemyHudRework_Stagger"; private const float HealthHeightMultiplier = 2f; private const float PlayerLikeHideDelay = 1f; internal static EnemyHudReworkPlugin Instance; private Harmony _harmony; private ConfigEntry<NameColorMode> _nameColorMode; private ConfigEntry<bool> _uppercaseNames; private ConfigEntry<bool> _doubleEnemyHealthBar; private ConfigEntry<string> _enemyHealthDisplayMode; private ConfigEntry<string> _bossHealthDisplayMode; private ConfigEntry<BossHealthTextPosition> _bossHealthTextPosition; private ConfigEntry<BossNameColorMode> _bossNameColorMode; private ConfigEntry<bool> _doublePlayerHealthBar; private ConfigEntry<string> _playerHealthDisplayMode; private ConfigEntry<bool> _debugBossHealthBar; private ConfigEntry<bool> _debugSeeOwnHealthBar; private ConfigEntry<bool> _staggerBarEnabled; private ConfigEntry<float> _staggerVerticalOffset; private ConfigEntry<float> _starRowHeight; private ConfigEntry<bool> _syncStaggerForMultiplayer; private const float SyncInterval = 0.1f; private const float SyncMinimumDelta = 0.01f; private readonly Dictionary<Character, EnemyUiState> _states = new Dictionary<Character, EnemyUiState>(); private readonly Dictionary<int, StaggerSyncState> _syncStates = new Dictionary<int, StaggerSyncState>(); private GameObject _debugBossGui; private EnemyUiState _debugBossState; private string _debugBossNameSource; private GuiBar _debugBossFast; private GuiBar _debugBossSlow; private bool _debugBossBarsLooked; private static readonly FieldInfo EnemyHudHudsField = AccessTools.Field(typeof(EnemyHud), "m_huds"); private static readonly Type HudDataType = AccessTools.Inner(typeof(EnemyHud), "HudData"); private static readonly FieldInfo HudDataGuiField = ((HudDataType != null) ? AccessTools.Field(HudDataType, "m_gui") : null); private static readonly MethodInfo EnemyHudShowHudMethod = AccessTools.Method(typeof(EnemyHud), "ShowHud", (Type[])null, (Type[])null); private static readonly Color StarYellow = new Color(1f, 0.8174443f, 23f / 68f, 1f); private ConfigEntry<bool> _dynamicHp; private ConfigEntry<bool> _starCount; private ConfigEntry<bool> _enemyAimFade; private ConfigEntry<bool> _playerDynamicHp; private ConfigEntry<float> _playerAimDelay; private ConfigEntry<float> _lookAwayDelay; private ConfigEntry<float> _dynamicHpDelay; private Character _looked; private readonly List<LookedAway> _lookedAway = new List<LookedAway>(); private int _layoutVersion = 1; private bool _hadPlayerNameplate; private static float _flashDuration = -1f; private static readonly FieldRef<Character, ZNetView> CharacterView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview"); private readonly List<int> _staleSync = new List<int>(); private static readonly Dictionary<LocalKey, float> LocalFloats = new Dictionary<LocalKey, float>(); private static readonly Dictionary<LocalKey, long> LocalLongs = new Dictionary<LocalKey, long>(); private static readonly Dictionary<LocalKey, int> LocalInts = new Dictionary<LocalKey, int>(); private static uint _localRevision; private const int PruneAt = 256; private static readonly List<LocalKey> Gone = new List<LocalKey>(); private ConfigEntry<bool> _statusEffects; private static readonly int StatusKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.status"); private static readonly int[] StatusHashes = new int[5] { SEMan.s_statusEffectBurning, SEMan.s_statusEffectFrost, SEMan.s_statusEffectLightning, SEMan.s_statusEffectPoison, SEMan.s_statusEffectSpirit }; private static readonly Sprite[] StatusIcon = (Sprite[])(object)new Sprite[5]; private static readonly int[] StatusKind = new int[5] { 3, 4, 5, 6, 7 }; private ConfigEntry<bool> _tooltipIcons; private static readonly string[] TooltipTokens = new string[8] { "$inventory_blunt", "$inventory_slash", "$inventory_pierce", "$inventory_fire", "$inventory_frost", "$inventory_lightning", "$inventory_poison", "$inventory_spirit" }; private static readonly int[] TooltipKinds = new int[8] { 0, 1, 2, 3, 4, 5, 6, 7 }; private const string TooltipRoom = "<space=1.3em>"; private static readonly FieldRef<GameObject> TooltipObject = AccessTools.StaticFieldRefAccess<GameObject>(AccessTools.Field(typeof(UITooltip), "m_tooltip")); private const float DamageShowSeconds = 3f; private static readonly int OldCriticalCounterKey = StringExtensionMethods.GetStableHashCode("wire.enemyhud.critical_id"); private const string CriticalRpc = "WireEnemyHUD_Crit"; private bool _reportedHitReveal; private bool _reportedViewportClamp; private bool _reportedHpFont; private ConfigEntry<bool> _showOnDamage; private ConfigEntry<bool> _showOnParry; private ConfigEntry<bool> _keepNameplatesOnScreen; private ConfigEntry<bool> _farHover; private ConfigEntry<bool> _criticalHitFlash; private ConfigEntry<float> _hpFontScale; private readonly Dictionary<Character, HealthSample> _healthSamples = new Dictionary<Character, HealthSample>(); private readonly Dictionary<Character, float> _lastCriticalHits = new Dictionary<Character, float>(); private readonly Dictionary<EnemyUiState, HealthOutline> _healthOutlines = new Dictionary<EnemyUiState, HealthOutline>(); private static readonly FieldRef<Character, float> BackstabTime = AccessTools.FieldRefAccess<Character, float>("m_backstabTime"); private static Func<object, float> _hoverTimer; private static bool _hoverTimerMade; private readonly List<Character> _pendingHits = new List<Character>(); private readonly List<Character> _missingSamples = new List<Character>(); private static readonly object[] ShowHudArgs = new object[2] { null, false }; private ConfigEntry<IconShow> _enemyIconShow; private ConfigEntry<IconShow> _bossIconShow; private ConfigEntry<bool> _playerIcons; private ConfigEntry<WeaponFocus> _limitedMode; private const float HighlightSize = 1.1f; private ConfigEntry<bool> _dynamicMode; private ConfigEntry<float> _dynamicDelay; private ConfigEntry<float> _limitedDelay; private int _limitedNow; private int _limitedShown; private static readonly FieldRef<Humanoid, ItemDrop> UnarmedWeapon = AccessTools.FieldRefAccess<Humanoid, ItemDrop>("m_unarmedWeapon"); private ConfigEntry<int> _iconsPerRow; private ConfigEntry<float> _iconScale; private ConfigEntry<float> _bossIconHeight; private ConfigEntry<float> _bossIconSpacing; private const int DamageKinds = 10; private const int Immune = -4; private static readonly int[] WeakOrder = new int[3] { 3, 2, 1 }; private static readonly int[] ResistOrder = new int[4] { -1, -2, -3, -4 }; private static readonly int[] EnemyOrder = new int[7] { 3, 2, 1, -1, -2, -3, -4 }; private ConfigEntry<bool> _hitResponse; private const string TypeHitRpc = "WireEnemyHUD_TypeHit"; private readonly List<EnemyUiState> _flashingIcons = new List<EnemyUiState>(); private ConfigEntry<ImmunityShow> _immunityShow; private static readonly string[] DamageAsset = new string[10] { "blunt", "slash", "pierce", "fire", "frost", "lightning", "poison", "spirit", "pickaxe", "chop" }; private static readonly Color[] DamageTint = (Color[])(object)new Color[10] { Color32.op_Implicit(new Color32((byte)232, (byte)232, (byte)232, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)232, (byte)232, (byte)232, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)232, (byte)232, (byte)232, byte.MaxValue)), Color32.op_Implicit(new Color32(byte.MaxValue, (byte)140, (byte)40, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)80, (byte)155, byte.MaxValue, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)165, (byte)225, byte.MaxValue, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)120, (byte)210, (byte)70, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)90, byte.MaxValue, (byte)200, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)80, (byte)68, (byte)98, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)80, (byte)68, (byte)98, byte.MaxValue)) }; private static readonly Color ResistTint = Color32.op_Implicit(new Color32((byte)124, (byte)124, (byte)124, (byte)153)); private const float BossCapHeight = 0.7f; private const float BossIconToCap = 0.8f; private static readonly Sprite[] DamageBack = (Sprite[])(object)new Sprite[10]; private static readonly Sprite[] DamageFill = (Sprite[])(object)new Sprite[10]; private static bool _spritesLoaded; private static readonly Sprite[] GameIcon = (Sprite[])(object)new Sprite[10]; private readonly Dictionary<EnemyUiState, WeaknessUi> _weaknessUi = new Dictionary<EnemyUiState, WeaknessUi>(); private static readonly MethodInfo LoadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }); private bool BossDpsOn => _bossDpsMode.Value != "Off"; private bool BubbleWorldOn => _bubbleWorld.Value; private bool BubbleHudOn => _bubbleHud.Value; private static FieldInfo HoverTimerField { get { if (_hoverTimerField == null && HudDataType != null) { _hoverTimerField = AccessTools.Field(HudDataType, "m_hoverTimer"); } return _hoverTimerField; } } private static int _layoutVersionStatic { get { if (!((Object)(object)Instance != (Object)null)) { return 0; } return Instance._layoutVersion; } } private static bool SyncOn { get { if (!((Object)(object)Instance == (Object)null)) { return Instance._syncStaggerForMultiplayer.Value; } return true; } } private static Func<object, float> HoverTimer { get { if (!_hoverTimerMade) { _hoverTimerMade = true; _hoverTimer = MakeHoverTimer(); } return _hoverTimer; } } private bool LimitedOn { get { if (_limitedMode != null) { return _limitedMode.Value == WeaponFocus.Limited; } return false; } } private bool HighlightOn { get { if (_limitedMode != null) { return _limitedMode.Value == WeaponFocus.Highlight; } return false; } } private void SetupBossDps(EnemyUiState state) { if (state == null || state.Kind != HudKind.Boss || (Object)(object)state.Character == (Object)null || (Object)(object)state.Gui == (Object)null) { return; } DestroyBossDps(state); TMP_Text val = state.NameText; EnemyHud instance = EnemyHud.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_baseHud != (Object)null) { Transform val2 = instance.m_baseHud.transform.Find("Name"); TMP_Text val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<TMP_Text>() : null); if ((Object)(object)val3 != (Object)null) { val = val3; } } if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent == (Object)null) { return; } Transform val4 = (((Object)(object)state.NameText != (Object)null && (Object)(object)state.NameText.transform.parent != (Object)null) ? state.NameText.transform.parent : val.transform.parent); GameObject val5 = Object.Instantiate<GameObject>(((Component)val).gameObject, val4, false); ((Object)val5).name = "DpsValue"; TMP_Text component = val5.GetComponent<TMP_Text>(); Transform transform = val5.transform; RectTransform val6 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)component == (Object)null || (Object)(object)val6 == (Object)null) { Object.Destroy((Object)(object)val5); return; } component.text = ""; component.alignment = (TextAlignmentOptions)516; component.enableAutoSizing = false; component.textWrappingMode = (TextWrappingModes)0; component.overflowMode = (TextOverflowModes)0; ((Graphic)component).raycastTarget = false; DpsUiState dps = new DpsUiState { TextObject = val5, Text = component, Rect = val6, BaseFontSize = component.fontSize, HasLastHealth = true, LastHealth = state.Character.GetHealth() }; dps.DamageHandler = delegate(float damage, Character attacker) { if (dps.EnabledLastFrame) { if (damage > 0.01f) { AddDpsDamage(dps, Mathf.Max(1, Mathf.RoundToInt(damage)), Time.realtimeSinceStartup); } if ((Object)(object)state.Character != (Object)null) { dps.LastHealth = state.Character.GetHealth(); } dps.HasLastHealth = true; } }; Character character = state.Character; character.m_onDamaged = (Action<float, Character>)Delegate.Combine(character.m_onDamaged, dps.DamageHandler); val5.SetActive(false); _dpsStates[state] = dps; } private void DestroyBossDps(EnemyUiState state) { if (state != null && _dpsStates.TryGetValue(state, out var value)) { if ((Object)(object)state.Character != (Object)null && value.DamageHandler != null) { Character character = state.Character; character.m_onDamaged = (Action<float, Character>)Delegate.Remove(character.m_onDamaged, value.DamageHandler); } if ((Object)(object)value.TextObject != (Object)null) { Object.Destroy((Object)(object)value.TextObject); } _dpsStates.Remove(state); } } private void DestroyAllBossDps() { foreach (EnemyUiState item in new List<EnemyUiState>(_dpsStates.Keys)) { DestroyBossDps(item); } } private void UpdateBossDps(EnemyUiState state) { //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) if (state == null || state.Kind != HudKind.Boss || (Object)(object)state.Character == (Object)null || !_dpsStates.TryGetValue(state, out var value) || (Object)(object)value.TextObject == (Object)null) { return; } bool bossDpsOn = BossDpsOn; if (bossDpsOn != value.EnabledLastFrame) { value.EnabledLastFrame = bossDpsOn; value.Started = false; value.Damage = 0; value.LastHealth = state.Character.GetHealth(); value.HasLastHealth = true; } if (!bossDpsOn) { if (value.TextObject.activeSelf) { value.TextObject.SetActive(false); } return; } float health = state.Character.GetHealth(); if (value.HasLastHealth && value.LastHealth - health > 0.01f) { AddDpsDamage(value, Mathf.Max(1, Mathf.RoundToInt(value.LastHealth - health)), Time.realtimeSinceStartup); } value.LastHealth = health; value.HasLastHealth = true; float realtimeSinceStartup = Time.realtimeSinceStartup; if (value.Started && realtimeSinceStartup - value.LastHitTime >= 3f) { value.Started = false; } int num = CurrentDps(value, realtimeSinceStartup); bool flag = _bossDpsMode.Value == "On + DPS"; if (num != value.ShownValue || flag != value.ShownSuffix) { value.Text.text = (flag ? (num + " DPS") : num.ToString()); value.ShownValue = num; value.ShownSuffix = flag; } Color white = Color.white; if (((Graphic)value.Text).color != white) { ((Graphic)value.Text).color = white; } if (!value.TextObject.activeSelf) { value.TextObject.SetActive(true); } LayoutBossDps(state, value); } private static void AddDpsDamage(DpsUiState dps, int damage, float now) { if (damage > 0) { if (dps.Started) { dps.LastHitTime = now; dps.Damage += damage; dps.EndTime = now; } else { dps.Started = true; dps.StartTime = now; dps.EndTime = now; dps.LastHitTime = now; dps.Damage = damage; } } } private static int CurrentDps(DpsUiState dps, float now) { if (!dps.Started) { return 0; } float num = dps.EndTime - dps.StartTime; if (num >= 3f) { dps.StartTime = now - 1f; dps.Damage = ((num > 0f) ? ((int)((float)dps.Damage / num)) : dps.Damage); num = dps.EndTime - dps.StartTime; } if (num < 1f) { num = 1f; } return (int)((float)dps.Damage / num); } private void LayoutBossDps(EnemyUiState state, DpsUiState dps) { //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: 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) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)state.HealthRect == (Object)null) { return; } bool flag = _bossHealthDisplayMode.Value != "None" && (Object)(object)state.HealthValueText != (Object)null; string text = (flag ? state.HealthValueText.text : null); if (dps.MeasuredLayout != _layoutVersion || (object)dps.MeasuredText != dps.Text.text || (object)dps.MeasuredHp != text) { dps.MeasuredLayout = _layoutVersion; dps.MeasuredText = dps.Text.text; dps.MeasuredHp = text; ((Transform)dps.Rect).localScale = Vector3.one; dps.Text.fontSize = dps.BaseFontSize * 0.738f * _dpsFontScale.Value; dps.TextWidth = Mathf.Max(80f, dps.Text.preferredWidth + 12f); dps.TextHeight = Mathf.Max(dps.Text.preferredHeight, dps.Text.fontSize * 1.2f); dps.Rect.sizeDelta = new Vector2(dps.TextWidth, dps.TextHeight); dps.HpHeight = (flag ? Mathf.Max(state.HealthValueText.preferredHeight, state.HealthValueText.fontSize * 1.2f) : dps.TextHeight); dps.LeftPad = TextWidthIn(dps.Text, dps.Rect, state.HealthRect); } float textHeight = dps.TextHeight; Rect rect = state.HealthRect.rect; bool num = _bossHealthTextPosition.Value == BossHealthTextPosition.Tracked; float num2 = ((Rect)(ref rect)).center.x; if (num) { if ((Object)(object)state.HealthFastFill != (Object)null) { Rect rect2 = state.HealthFastFill.rect; Vector3 val = ((Transform)state.HealthFastFill).TransformPoint(new Vector3(((Rect)(ref rect2)).xMax, ((Rect)(ref rect2)).center.y, 0f)); num2 = ((Transform)state.HealthRect).InverseTransformPoint(val).x; } else { float num3 = Mathf.Max(0f, state.Character.GetHealth()); float num4 = Mathf.Max(1f, state.Character.GetMaxHealth()); num2 = Mathf.Lerp(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).xMax, Mathf.Clamp01(num3 / num4)); } num2 = Mathf.Max(num2, ((Rect)(ref rect)).xMin + dps.LeftPad); } float hpHeight = dps.HpHeight; float num5 = (flag ? (((Rect)(ref rect)).yMin - hpHeight * 0.55f - 2f - (hpHeight + textHeight) * 0.5f) : (((Rect)(ref rect)).yMin - textHeight * 0.55f - 2f)); Vector2 val2 = (num ? new Vector2(1f, 0.5f) : new Vector2(0.5f, 0.5f)); if (dps.Rect.pivot != val2) { dps.Rect.pivot = val2; } TextAlignmentOptions val3 = (TextAlignmentOptions)(num ? 516 : 514); if (dps.Text.alignment != val3) { dps.Text.alignment = val3; } ((Transform)dps.Rect).position = ((Transform)state.HealthRect).TransformPoint(new Vector3(num2, num5, 0f)); } private static long WorldMillis() { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0L; } return (long)(ZNet.instance.GetTimeSeconds() * 1000.0); } private void BindBubbleConfig(string section) { _bubbleWorld = Bind(section, "Bubble bar on nameplates", true, "A bar above the health bar of anyone inside a protective bubble (Staff of Protection, fuling shamans), showing how much it can still take. Purple on friendlies, blue on enemies. Other players need Enemy HUD for theirs to show.", null, null); _bubbleHud = Bind(section, "Bubble bar on your HUD", true, "A bar right of your own health bar while you're inside a bubble. The forsaken power moves over to make room.", null, null); if (TakeOrphan("02 - Enemy Bars", "Bubble bars", out var value) && value != null) { value = value.Trim(); _bubbleWorld.Value = value == "Both" || value == "Nameplates"; _bubbleHud.Value = value == "Both" || value == "Hud"; } _bubbleWorld.SettingChanged += delegate { foreach (EnemyUiState value2 in _states.Values) { if (value2 != null) { value2.BubbleSeen = false; } } }; _bubbleHud.SettingChanged += delegate { RefreshHudBubble(); }; } internal void OnShieldChanged(SE_Shield se, bool ended) { Character val = (((Object)(object)se != (Object)null) ? SeOwner.Invoke((StatusEffect)(object)se) : null); if ((Object)(object)val == (Object)null) { return; } bool flag = ended && ShieldTotal.Invoke(se) > 0f && ShieldDamage.Invoke(se) > ShieldTotal.Invoke(se); float num = ((ended && !flag) ? 0f : ShieldTotal.Invoke(se)); float num2 = ((ended && !flag) ? 0f : ShieldDamage.Invoke(se)); long value = (flag ? (WorldMillis() + 1000) : ((ended || ((StatusEffect)se).m_ttl <= 0f) ? 0 : (WorldMillis() + (long)(Mathf.Max(0f, ((StatusEffect)se).m_ttl - ((StatusEffect)se).GetDuration()) * 1000f)))); ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); SetShared(zDO, BubbleMaxKey, num); SetShared(zDO, BubbleDamageKey, num2); SetShared(zDO, BubbleUntilKey, value); } if (ended || num2 == 0f) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Bubble " + (flag ? "popped " : ((num > 0f) ? "on " : "off ")) + val.m_name + (((Object)(object)component != (Object)null && component.IsOwner()) ? "" : " (not owner)"))); } InvalidatePredictions(); if ((object)val == Player.m_localPlayer) { _hudBubbleTotal = num; _hudBubbleDamage = num2; if (flag) { StartHudBubblePop(); } else { StopHudBubblePop(); } RefreshHudBubble(); } } internal void OnCharacterStart(Character c) { if ((Object)(object)c == (Object)null) { return; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } ZDO zDO = component.GetZDO(); DropOldCriticalCounter(zDO); ResyncStatus(c, zDO); DropRanks(zDO); if (c.IsPlayer()) { OnResistancesMayChange(c); } if (zDO.GetFloat(BubbleMaxKey, 0f) <= 0f) { zDO.RemoveFloat(BubbleMaxKey); zDO.RemoveFloat(BubbleDamageKey); zDO.RemoveLong(BubbleUntilKey); return; } SE_Shield val = FindShield(c); if ((Object)(object)val != (Object)null) { OnShieldChanged(val, ended: false); return; } zDO.Set(BubbleMaxKey, 0f); zDO.Set(BubbleDamageKey, 0f); zDO.Set(BubbleUntilKey, 0L); } internal void OnOwnerChanged(ZDO zdo) { if (zdo != null && zdo.IsOwner() && !((Object)(object)ZNetScene.instance == (Object)null) && (zdo.GetPrefab() == PlayerPrefabHash || !(zdo.GetFloat(BubbleMaxKey, 0f) <= 0f) || zdo.GetInt(StatusKey, 0) != 0 || zdo.GetInt(RanksKey, 0) != 0 || zdo.GetInt(OtherKeys[0], 0) != 0 || zdo.GetInt(OtherKeys[1], 0) != 0 || zdo.GetInt(OtherKeys[2], 0) != 0 || zdo.GetInt(OtherKeys[3], 0) != 0)) { ZNetView val = ZNetScene.instance.FindInstance(zdo); Character val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Character>() : null); if ((Object)(object)val2 != (Object)null) { OnCharacterStart(val2); } } } private static SE_Shield FindShield(Character c) { SEMan val = (((Object)(object)c != (Object)null) ? c.GetSEMan() : null); if (val == null) { return null; } foreach (StatusEffect statusEffect in val.GetStatusEffects()) { SE_Shield val2 = (SE_Shield)(object)((statusEffect is SE_Shield) ? statusEffect : null); if (val2 != null) { return val2; } } return null; } internal void OnLocalSpawned(Player p) { SE_Shield val = FindShield((Character)(object)p); if ((Object)(object)val != (Object)null) { OnShieldChanged(val, ended: false); return; } StopHudBubblePop(); _hudBubbleTotal = 0f; _hudBubbleDamage = 0f; RefreshHudBubble(); } private static bool HasBubble(ZDO zdo) { float num = ((zdo != null) ? GetShared(zdo, BubbleMaxKey) : 0f); if (num <= 0f || GetShared(zdo, BubbleDamageKey) > num) { return false; } long shared = GetShared(zdo, BubbleUntilKey, 0L); if (shared > 0) { return WorldMillis() <= shared; } return true; } private Vector2 NameBase(EnemyUiState state) { //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) //IL_0011: 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) return state.NameOriginalPosition + Vector2.up * state.BubbleLift; } private void UpdateWorldBubble(EnemyUiState state, bool isEnemy) { //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) if (state == null || state.Kind == HudKind.Boss || (Object)(object)state.Character == (Object)null || (Object)(object)state.HealthRect == (Object)null || (Object)(object)state.Gui == (Object)null || !state.Gui.activeInHierarchy) { return; } if (!BubbleWorldOn) { if (state.Bubble != null && state.Bubble.Root.activeSelf) { EndWorldBubbleFlash(state); state.Bubble.Root.SetActive(false); } state.BubbleLift = 0f; state.BubbleSeen = false; return; } if ((Object)(object)state.BubbleView == (Object)null) { state.BubbleView = ((Component)state.Character).GetComponent<ZNetView>(); } ZDO val = (((Object)(object)state.BubbleView != (Object)null && state.BubbleView.IsValid()) ? state.BubbleView.GetZDO() : null); if (val == null) { return; } if (state.BubbleFlashStart >= 0f) { UpdateWorldBubbleFlash(state); } uint num = SharedRevision(val); if (state.BubbleSeen && num == state.BubbleRevision && isEnemy == state.BubbleFoe && (!state.BubbleTimed || WorldMillis() <= state.BubbleUntil)) { return; } state.BubbleRevision = num; float num2 = (BubbleWorldOn ? GetShared(val, BubbleMaxKey) : 0f); long num3 = ((num2 > 0f) ? GetShared(val, BubbleUntilKey, 0L) : 0); if (num3 > 0 && WorldMillis() > num3) { num2 = 0f; } state.BubbleTimed = num3 > 0 && num2 > 0f; state.BubbleUntil = num3; float num4 = ((num2 > 0f) ? GetShared(val, BubbleDamageKey) : 0f); if (state.BubbleSeen && num2 == state.BubbleMax && num4 == state.BubbleDamage && isEnemy == state.BubbleFoe) { return; } bool flag = state.BubbleSeen && num2 > 0f && num4 > num2 && state.BubbleMax > 0f && state.BubbleDamage <= state.BubbleMax; state.BubbleSeen = true; state.BubbleFoe = isEnemy; state.BubbleMax = num2; state.BubbleDamage = num4; if (num2 <= 0f) { EndWorldBubbleFlash(state); if (state.Bubble != null && state.Bubble.Root.activeSelf) { state.Bubble.Root.SetActive(false); } state.BubbleLift = 0f; return; } float value = Mathf.Clamp01((num2 - num4) / num2); if (state.Bubble != null) { BubbleUi bubble = state.Bubble; SetBarColor(bubble.Fast, isEnemy ? BubbleColorFoe : BubbleColor); Vector2 val2 = state.HealthOriginalPosition + Vector2.up * _staggerVerticalOffset.Value; if (bubble.Rect.anchoredPosition != val2) { bubble.Rect.anchoredPosition = val2; } if (!bubble.Root.activeSelf) { bubble.Root.SetActive(true); } bubble.Fast.SetValue(value); if ((Object)(object)bubble.Slow != (Object)null) { bubble.Slow.SetValue(value); } state.BubbleLift = _staggerVerticalOffset.Value; if (flag) { state.BubbleFlashStart = Time.time; UpdateWorldBubbleFlash(state); } } } private void UpdateWorldBubbleFlash(EnemyUiState state) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) BubbleUi bubble = state.Bubble; float playerStaggerFlashDuration = GetPlayerStaggerFlashDuration(); float num = VanillaFlash((playerStaggerFlashDuration > 0f) ? ((Time.time - state.BubbleFlashStart) / playerStaggerFlashDuration) : 1f); if (bubble != null) { BlendFrame(bubble.Frame, bubble.FrameOriginal, num, ref state.BubbleFlashShown); } if (num <= 0f) { state.BubbleFlashStart = -1f; } } private void EndWorldBubbleFlash(EnemyUiState state) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (!(state.BubbleFlashStart < 0f)) { state.BubbleFlashStart = -1f; if (state.Bubble != null) { BlendFrame(state.Bubble.Frame, state.Bubble.FrameOriginal, 0f, ref state.BubbleFlashShown); } } } private BubbleUi CreateWorldBubble(EnemyUiState state) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) if (state == null || (Object)(object)state.HealthRect == (Object)null) { return null; } GameObject val = Object.Instantiate<GameObject>(((Component)state.HealthRect).gameObject, ((Transform)state.HealthRect).parent, false); ((Object)val).name = "WireBubble"; TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)((Component)componentsInChildren[i]).gameObject); } BubbleUi bubbleUi = new BubbleUi { Root = val, Rect = (RectTransform)val.transform }; ((Transform)bubbleUi.Rect).localScale = state.HealthOriginalScale; Transform val2 = val.transform.Find("health_fast"); Transform val3 = val.transform.Find("health_slow"); Transform val4 = val.transform.Find("health_fast_friendly"); if ((Object)(object)val4 != (Object)null) { ((Component)val4).gameObject.SetActive(false); } bubbleUi.Fast = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<GuiBar>() : null); bubbleUi.Slow = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<GuiBar>() : null); if ((Object)(object)bubbleUi.Fast == (Object)null) { Object.Destroy((Object)(object)val); return null; } if (!((Component)bubbleUi.Fast).gameObject.activeSelf) { ((Component)bubbleUi.Fast).gameObject.SetActive(true); } bubbleUi.Fast.SetColor(BubbleColor); if ((Object)(object)bubbleUi.Slow != (Object)null) { bubbleUi.Slow.SetColor(BubbleTrail); } bubbleUi.Frame = Child(bubbleUi.Rect, "darken"); Capture(bubbleUi.Frame, out bubbleUi.FrameOriginal); val.SetActive(false); return bubbleUi; } private static void SetBarColor(GuiBar bar, Color color) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) Image val = (((Object)(object)bar != (Object)null && (Object)(object)bar.m_bar != (Object)null) ? ((Component)bar.m_bar).GetComponent<Image>() : null); if ((Object)(object)val != (Object)null) { ((Graphic)val).color = color; } } private static void DestroyWorldBubble(EnemyUiState state) { if ((Object)(object)state?.Bubble?.Root != (Object)null) { Object.Destroy((Object)(object)state.Bubble.Root); } if (state != null) { state.Bubble = null; state.BubbleLift = 0f; state.BubbleFlashStart = -1f; } } internal void BuildHudBubble(Hud hud) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //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) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: 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_0156: 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) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hud == (Object)null || (Object)(object)hud.m_healthBarRoot == (Object)null || (Object)(object)hud.m_healthPanel == (Object)null || (Object)(object)hud.m_eitrBarFast == (Object)null) { return; } RectTransform healthBarRoot = hud.m_healthBarRoot; Transform parent = ((Component)hud.m_eitrBarFast).transform.parent; if ((Object)(object)parent == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(((Component)parent).gameObject, ((Transform)healthBarRoot).parent, false); ((Object)val).name = "HealthBubble"; TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)((Component)componentsInChildren[i]).gameObject); } RectTransform val2 = (RectTransform)val.transform; Rect rect = healthBarRoot.rect; float width = ((Rect)(ref rect)).width; rect = healthBarRoot.rect; float height = ((Rect)(ref rect)).height; rect = ((RectTransform)parent).rect; float num = ((Rect)(ref rect)).height; if (num <= 0f) { num = height * 0.5f; } val2.anchorMin = healthBarRoot.anchorMin; val2.anchorMax = healthBarRoot.anchorMax; val2.pivot = healthBarRoot.pivot; ((Transform)val2).localRotation = ((Transform)healthBarRoot).localRotation; ((Transform)val2).localScale = ((Transform)healthBarRoot).localScale; val2.sizeDelta = new Vector2(width, num); Vector2 val3 = -Vector2.op_Implicit(((Transform)healthBarRoot).localRotation * Vector3.up); float num2 = height * 0.5f + 8f + num * 0.5f; val2.anchoredPosition = healthBarRoot.anchoredPosition + val3 * num2; _bubbleNotches.Clear(); GuiBar[] componentsInChildren2 = val.GetComponentsInChildren<GuiBar>(true); foreach (GuiBar val4 in componentsInChildren2) { if (((Object)val4).name.ToLowerInvariant().Contains("slow")) { _hudBubbleSlow = val4; } else { _hudBubbleFast = val4; } PrepareBubbleFill(val4); } _hudBubbleLength = -1f; _hudBubble = val2; OnHealthBarSize(width); _hudBubbleFrame = Child(val2, "darken"); Capture(_hudBubbleFrame, out _hudBubbleFrameOriginal); _hudBubbleGroup = val.AddComponent<CanvasGroup>(); _hudBubbleGroup.alpha = 0f; _hudBubbleGroup.interactable = false; _hudBubbleGroup.blocksRaycasts = false; _hudBubble = val2; _hudBubbleAlpha = 0f; val.SetActive(false); _gpRoot = hud.m_gpRoot; _gpBase = (((Object)(object)_gpRoot != (Object)null) ? _gpRoot.anchoredPosition : Vector2.zero); _gpShift = 8f + num; _hudBubbleTotal = 0f; _hudBubbleDamage = 0f; } internal void OnHealthBarSize(float size) { if (!((Object)(object)_hudBubble == (Object)null) && !(size <= 0f) && size != _hudBubbleLength) { _hudBubbleLength = size; _hudBubble.SetSizeWithCurrentAnchors((Axis)0, size); FitBubbleFill(_hudBubbleFast); FitBubbleFill(_hudBubbleSlow); } } private void PrepareBubbleFill(GuiBar bar) { //IL_0059: 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) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: 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_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) Image val = (((Object)(object)bar != (Object)null && (Object)(object)bar.m_bar != (Object)null) ? ((Component)bar.m_bar).GetComponent<Image>() : null); if ((Object)(object)val == (Object)null || (Object)(object)val.sprite == (Object)null || (Object)(object)val.sprite.texture == (Object)null) { return; } Sprite sprite = val.sprite; Rect textureRect = sprite.textureRect; if (!(((Rect)(ref textureRect)).width < 4f)) { Sprite val2 = Sprite.Create(sprite.texture, new Rect(((Rect)(ref textureRect)).x + 1f, ((Rect)(ref textureRect)).y, ((Rect)(ref textureRect)).width - 2f, ((Rect)(ref textureRect)).height), new Vector2(0.5f, 0.5f), sprite.pixelsPerUnit, 0u, (SpriteMeshType)0); ((Object)val2).hideFlags = (HideFlags)61; val.sprite = val2; Texture texture = (Texture)(object)sprite.texture; Rect uvRect = default(Rect); ((Rect)(ref uvRect))..ctor((((Rect)(ref textureRect)).x + 0.5f) / (float)texture.width, ((Rect)(ref textureRect)).y / (float)texture.height, 0.0001f, ((Rect)(ref textureRect)).height / (float)texture.height); if ((Object)(object)((Component)bar.m_bar).GetComponent<Mask>() == (Object)null) { ((Component)bar.m_bar).gameObject.AddComponent<Mask>().showMaskGraphic = true; } Rect rect = bar.m_bar.rect; float num; if (!(((Rect)(ref rect)).height > 0f)) { num = 1f; } else { rect = bar.m_bar.rect; num = ((Rect)(ref rect)).height / ((Rect)(ref textureRect)).height; } float num2 = num; for (int i = 0; i < BubbleQuarters.Length; i++) { GameObject val3 = new GameObject("HealthBubbleNotch", new Type[2] { typeof(RectTransform), typeof(RawImage) }); RectTransform val4 = (RectTransform)val3.transform; ((Transform)val4).SetParent((Transform)(object)bar.m_bar, false); val4.anchorMin = new Vector2(0f, 0f); val4.anchorMax = new Vector2(0f, 1f); val4.pivot = new Vector2(0.5f, 0.5f); val4.sizeDelta = new Vector2(num2 * 2f, 0f); RawImage component = val3.GetComponent<RawImage>(); component.texture = texture; component.uvRect = uvRect; ((Graphic)component).material = ((Graphic)val).material; ((Graphic)component).color = ((Graphic)val).color; ((Graphic)component).raycastTarget = false; _bubbleNotches.Add(val4); } } } private void FitBubbleFill(GuiBar bar) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bar == (Object)null || (Object)(object)bar.m_bar == (Object)null || _hudBubbleLength <= 0f) { return; } bar.SetWidth(_hudBubbleLength); bar.m_bar.SetSizeWithCurrentAnchors((Axis)0, _hudBubbleLength * bar.GetSmoothValue()); Vector2 val2 = default(Vector2); for (int i = 0; i < _bubbleNotches.Count; i++) { RectTransform val = _bubbleNotches[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Transform)val).parent != (Object)(object)bar.m_bar)) { ((Vector2)(ref val2))..ctor(_hudBubbleLength * BubbleQuarters[i % BubbleQuarters.Length], 0f); if (val.anchoredPosition != val2) { val.anchoredPosition = val2; } } } } private void RefreshHudBubble() { if ((Object)(object)_hudBubble == (Object)null) { return; } int num; if (BubbleHudOn) { num = ((_hudBubbleTotal > 0f) ? 1 : 0); if (num != 0) { float value = Mathf.Clamp01((_hudBubbleTotal - _hudBubbleDamage) / _hudBubbleTotal); if (!((Component)_hudBubble).gameObject.activeSelf) { ((Component)_hudBubble).gameObject.SetActive(true); } FitBubbleFill(_hudBubbleFast); FitBubbleFill(_hudBubbleSlow); if ((Object)(object)_hudBubbleFast != (Object)null) { _hudBubbleFast.SetValue(value); } if ((Object)(object)_hudBubbleSlow != (Object)null) { _hudBubbleSlow.SetValue(value); } } } else { num = 0; } float num2 = ((num != 0) ? 1f : 0f); if (num != 0) { SetGpShifted(shifted: true); } if (_hudBubbleAlpha != num2) { if (_hudBubbleFade != null) { ((MonoBehaviour)this).StopCoroutine(_hudBubbleFade); } _hudBubbleFade = ((MonoBehaviour)this).StartCoroutine(FadeHudBubble(num2)); } } private IEnumerator FadeHudBubble(float target) { while ((Object)(object)_hudBubble != (Object)null && _hudBubbleAlpha != target) { _hudBubbleAlpha = Mathf.MoveTowards(_hudBubbleAlpha, target, Time.unscaledDeltaTime / 0.25f); ApplyHudBubbleAlpha(); yield return null; } if ((Object)(object)_hudBubble != (Object)null && target <= 0f) { ((Component)_hudBubble).gameObject.SetActive(false); SetGpShifted(shifted: false); } _hudBubbleFade = null; } private void StartHudBubblePop() { StopHudBubblePop(); if ((Object)(object)_hudBubble != (Object)null && BubbleHudOn) { _hudBubblePop = ((MonoBehaviour)this).StartCoroutine(HudBubblePop()); return; } _hudBubbleTotal = 0f; _hudBubbleDamage = 0f; } private void StopHudBubblePop() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (_hudBubblePop != null) { ((MonoBehaviour)this).StopCoroutine(_hudBubblePop); } _hudBubblePop = null; if ((Object)(object)_hudBubbleFrame != (Object)null && ((Graphic)_hudBubbleFrame).color != _hudBubbleFrameOriginal) { ((Graphic)_hudBubbleFrame).color = _hudBubbleFrameOriginal; } } private IEnumerator HudBubblePop() { float start = Time.time; float duration = GetPlayerStaggerFlashDuration(); while (Time.time - start < 1f) { float t = ((duration > 0f) ? ((Time.time - start) / duration) : 1f); if ((Object)(object)_hudBubbleFrame != (Object)null) { Color val = Color.Lerp(_hudBubbleFrameOriginal, Color.white, VanillaFlash(t)); if (((Graphic)_hudBubbleFrame).color != val) { ((Graphic)_hudBubbleFrame).color = val; } } yield return null; } if ((Object)(object)_hudBubbleFrame != (Object)null) { ((Graphic)_hudBubbleFrame).color = _hudBubbleFrameOriginal; } _hudBubblePop = null; _hudBubbleTotal = 0f; _hudBubbleDamage = 0f; RefreshHudBubble(); } private void ApplyHudBubbleAlpha() { float alpha = _hudBubbleAlpha * _hudBubbleAlpha * (3f - 2f * _hudBubbleAlpha); if ((Object)(object)_hudBubbleGroup != (Object)null) { _hudBubbleGroup.alpha = alpha; } } private void SetGpShifted(bool shifted) { //IL_0010: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_004e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_gpRoot == (Object)null)) { Vector2 val = _gpBase + (shifted ? (Vector2.right * _gpShift) : Vector2.zero); if (_gpRoot.anchoredPosition != val) { _gpRoot.anchoredPosition = val; } } } private void BindColorSections() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) _order = 1000; _resistColor = Bind<Color>("08 - Icon Shapes and Colors", "Resistance color", ResistTint, "Color of every resistance icon.", null, null, Array.Empty<string>()); _resistColor.SettingChanged += delegate { RefreshIconLook(); }; for (int num = 0; num < 10; num++) { _iconShape[num] = Bind("08 - Icon Shapes and Colors", DamageNames[num] + " icon", (IconShape)num, "Shape of the " + DamageNames[num].ToLowerInvariant() + " icon.", null, null); _iconColor[num] = Bind<Color>("08 - Icon Shapes and Colors", DamageNames[num] + " color", DamageTint[num], "Color of the " + DamageNames[num].ToLowerInvariant() + " icon when the creature is weak to it.", null, null, Array.Empty<string>()); _iconShape[num].SettingChanged += delegate { RefreshIconLook(); }; _iconColor[num].SettingChanged += delegate { RefreshIconLook(); }; } RefreshIconLook(); } private void RefreshIconLook() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) //IL_005f: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 10; i++) { int num = ((_iconShape[i] != null) ? ((int)_iconShape[i].Value) : i); _shapeOf[i] = ((num >= 0 && num < 10) ? num : i); _tintOf[i] = ((_iconColor[i] != null) ? _iconColor[i].Value : DamageTint[i]); } _resistTint = ((_resistColor != null) ? _resistColor.Value : ResistTint); _immuneTint = new Color(_resistTint.r * 0.55f, _resistTint.g * 0.55f, _resistTint.b * 0.55f, _resistTint.a); MarkAllWeaknessDirty(); } private void ResetIconLook() { //IL_0020: 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) for (int i = 0; i < 10; i++) { _iconShape[i].Value = (IconShape)i; _iconColor[i].Value = DamageTint[i]; } _resistColor.Value = ResistTint; } private static Image Child(RectTransform parent, string name) { Transform val = (((Object)(object)parent != (Object)null) ? ((Transform)parent).Find(name) : null); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponent<Image>(); } private static void Capture(Image image, out Color original) { //IL_0016: 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_0012: 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) original = (Color)(((Object)(object)image != (Object)null) ? ((Graphic)image).color : default(Color)); } private void CaptureNativePalette(EnemyUiState state) { if (state != null) { NativePalette nativePalette = new NativePalette(); nativePalette.HealthFrame = Child(state.HealthRect, "darken"); nativePalette.StaggerFrame = Child(state.StaggerRect, "darken"); Capture(nativePalette.HealthFrame, out nativePalette.HealthFrameOriginal); Capture(nativePalette.StaggerFrame, out nativePalette.StaggerFrameOriginal); _nativePalettes[state] = nativePalette; } } private void ApplyFrameFlash(EnemyUiState state) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_nativePalettes.TryGetValue(state, out var value)) { BlendFrame(value.HealthFrame, value.HealthFrameOriginal, state.CritFlashAlpha, ref state.CritFlashShown); BlendFrame(value.StaggerFrame, value.StaggerFrameOriginal, state.StaggerFlashAlpha, ref state.StaggerFlashShown); } } private static void BlendFrame(Image frame, Color rest, float alpha, ref float shown) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0042: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)frame == (Object)null) && (!(alpha <= 0f) || !(shown <= 0f))) { Color val = ((alpha > 0f) ? Color.Lerp(rest, Color.white, alpha) : rest); if (((Graphic)frame).color != val) { ((Graphic)frame).color = val; } shown = alpha; } } private void ForgetNativePalette(EnemyUiState state) { if (state != null) { _nativePalettes.Remove(state); } } private Color BarBackground(Color original) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!_transparentBackground.Value) { return original; } return TransparentBackground; } private void MoveKey(string fromSection, string fromKey, string toSection, string toKey) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown Dictionary<ConfigDefinition, string> dictionary = Orphans(); if (dictionary == null) { return; } ConfigDefinition key = new ConfigDefinition(fromSection, fromKey); if (dictionary.TryGetValue(key, out var value)) { dictionary.Remove(key); ConfigDefinition key2 = new ConfigDefinition(toSection, toKey); if (!dictionary.ContainsKey(key2)) { dictionary[key2] = value; } } } private void MigrateLayout() { DropSection("08 - Bar Colors"); DropSection("09 - Text Colors"); DropSection("Colors"); DropSection("08 - Colors"); DropSection("10 - Icon Colors"); DropSection("08 - Icon Colors"); MoveSection("13 - Hit Predictions", "07 - Hit Predictions"); MoveSection("14 - Bubble Bar", "12 - Bubble Bar"); string[][] moves = Moves; foreach (string[] array in moves) { for (int num = array.Length - 2; num >= 2; num -= 2) { MoveKey(array[num], array[num + 1], array[0], array[1]); } } MoveSection("09 - Hit Predictions", "07 - Hit Predictions"); MoveSection("10 - Hit Predictions", "07 - Hit Predictions"); MoveSection("11 - Hit Predictions", "07 - Hit Predictions"); MoveSection("10 - Icon Shapes and Colors", "08 - Icon Shapes and Colors"); MoveSection("06 - Icon Shapes and Colors", "08 - Icon Shapes and Colors"); MoveSection("08 - Damage Icons", "08 - Icon Shapes and Colors"); MoveSection("13 - Debug", "11 - Debug"); MoveSection("10 - Debug", "11 - Debug"); } private void BindConfig() { MigrateLayout(); TakeOrphan("Features", "NameAwarenessColors", out var value); TakeOrphan("Features", "UppercaseEnemyNames", out var value2); _order = 1000; _preset = Bind("01 - General", "Preset", "Author's Choice", "Author's Choice applies my preferred settings, Vanilla a vanilla-style starting point. Picking either one overwrites the settings below.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Author's Choice", "Vanilla" }), null, "General", "Preset"); ((BaseUnityPlugin)this).Config.SettingChanged += OnPresetSettingChanged; ((BaseUnityPlugin)this).Config.SettingChanged += delegate { MarkAllWeaknessDirty(); InvalidatePredictions(); _layoutVersion++; }; _order = 1000; _transparentBackground = Bind("02 - Nameplates", "Transparent background", true, "Make the gray background behind every bar the mod touches (health, stagger, bubble) see-through, like your own HUD bars.", null, null, "Bars", "Transparent bar background"); _enemyHealthDisplayMode = Bind("02 - Nameplates", "Creature HP numbers", "Current HP", "HP numbers on creature bars: current HP, current/max HP, percentage, or None.", (AcceptableValueBase)(object)new AcceptableValueList<string>(HealthTextModes), null, "Enemy Health Bar", "Health display"); _playerHealthDisplayMode = Bind("02 - Nameplates", "Player HP numbers", "Current HP/Max HP", "HP numbers on other players' bars: current HP, current/max HP, percentage, or None.", (AcceptableValueBase)(object)new AcceptableValueList<string>(HealthTextModes), null, "Player Health Bar", "Health display"); _doubleEnemyHealthBar = Bind("02 - Nameplates", "Double size creature bars", true, "Doubles the thickness of creature health bars while keeping their width.", null, null, "Enemy Health Bar", "Double size"); _doublePlayerHealthBar = Bind("02 - Nameplates", "Double size player bars", true, "Doubles the thickness of other players' health bars while keeping their width.", null, null, "Player Health Bar", "Double size"); _uppercaseNames = Bind("02 - Nameplates", "Uppercase creature names", false, "Show creature names in uppercase.", null, null, "Enemy Health Bar", "Uppercase names"); _starCount = Bind("02 - Nameplates", "Star count past two stars", true, "Creatures with more than two stars (from level mods) show one star and the count next to it, instead of the game's nothing. Also keeps other mods from switching the stars around. Off is the game's own stars.", null, null); _starCount.SettingChanged += delegate { foreach (EnemyUiState value13 in _states.Values) { if (value13 != null && (Object)(object)value13.Character != (Object)null) { ApplyLevelStars(value13, value13.Character); } } }; _criticalHitFlash = Bind("02 - Nameplates", "Critical hit flash", true, "A creature bar's frame flashes white, like the game's own flashes, when a backstab or a hit on a staggered creature lands. Never on the killing blow.", null, null, "Enemy Health Bar", "Critical hit flash"); _statusEffects = Bind("02 - Nameplates", "Status effects", true, "Icons left of a creature's or player's health bar while they're burning, frozen, shocked, poisoned or hit by spirit: the same icons as those damage types. With Icon style on Game icons, the game's own status icons instead. Other players need Enemy HUD for theirs to show.", null, null); _otherEffects = Bind("02 - Nameplates", "Other status effects", true, "The game's icons, right of the health bar, for other effects that change the creature or player itself: its resistances, speed, damage, stagger or armor (wet, tarred, harpooned, resistance meads...). Effects that don't (cold, rested...) aren't shown, except wet, which makes fire burn faster. Players have their own list, see Player nameplate effects.", null, null); _playerEffects = Bind("02 - Nameplates", "Player nameplate effects", true, "Up to four icons right of other players' health bars, in this exact order: Rested; active Forsaken powers; active potion buffs (Ratatosk, Lightfoot, etc.); potion cooldowns (healing, stamina, eitr, etc.); Wet/Cold/Freezing; Encumbered/Tarred/Harpooned. No other effects. Forsaken powers, potion buffs and cooldowns cycle independently every 0.5 seconds when needed. Other players need Enemy HUD to share their effects.", null, null); _nameColorMode = Bind("02 - Nameplates", "Awareness display", NameColorMode.Pastel, "How a creature shows that it noticed you. Pastel: soft name colors. Normal: stronger Valheim name colors. Icons: the vanilla awareness icons. None: nothing.", null, null, "Enemy Health Bar", "Awareness display"); if (value != null) { _nameColorMode.Value = ((value == "Normal") ? NameColorMode.Normal : ((!(value == "Pastel")) ? NameColorMode.Icons : NameColorMode.Pastel)); } if (value2 != null) { _uppercaseNames.Value = string.Equals(value2, "true", StringComparison.OrdinalIgnoreCase); } _order = 1000; _bossHealthDisplayMode = Bind("03 - Boss Bar", "HP numbers", "Current HP/Max HP", "HP numbers on boss bars: current HP, current/max HP, percentage, or None.", (AcceptableValueBase)(object)new AcceptableValueList<string>(HealthTextModes), null, "Boss Health Bar", "Health display"); _bossHealthTextPosition = Bind("03 - Boss Bar", "HP numbers position", BossHealthTextPosition.Tracked, "Tracked follows the right edge of the boss health fill, never past the bar's left end. Centered keeps the HP numbers and DPS in the middle of the bar.", null, () => _bossHealthDisplayMode != null && _bossHealthDisplayMode.Value == "None" && _bossDpsMode != null && _bossDpsMode.Value == "Off", "Boss Health Bar", "Health counter position"); _bossDpsMode = Bind("03 - Boss Bar", "DPS", "On + DPS", "Damage per second under the boss HP numbers, from all damage the boss takes. On shows the number (230), On + DPS adds the label (230 DPS).", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "On", "On + DPS", "Off" }), null, "DPS Meter", "Boss DPS"); _bossNameColorMode = Bind("03 - Boss Bar", "Name color", BossNameColorMode.White, "Boss name color: Pastel is soft pink, Vanilla is Valheim red, White is white.", null, null, "Boss Health Bar", "Boss name color"); _order = 1000; Func<bool> readOnly = () => _staggerBarEnabled != null && !_staggerBarEnabled.Value; _staggerBarEnabled = Bind("04 - Custom Bars", "Stagger bar", true, "A stagger meter under creature and player health bars, styled after your own. It fills as they take stagger damage and flashes when they stagger.", null, null, "Stagger Bar", "Enabled"); _staggerRecoveryEnabled = Bind("04 - Custom Bars", "Stagger recovery overlay", true, "During a stagger, a yellow layer recedes across the stagger bar over the creature's real stagger time (the window in which your hits deal double damage).", null, readOnly, "Stagger Bar", "Recovery overlay"); BindBubbleConfig("04 - Custom Bars"); _order = 1000; string value3; bool flag = TakeOrphan("02 - Enemy Bars", "Show on damage", out value3); string value4; bool flag2 = TakeOrphan("02 - Enemy Bars", "S