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 BubbleGauge v1.0.1
plugins/BubbleGauge.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BubbleGauge.Logic; 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: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("BubbleGauge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+8afb1ca387b5ba50a88e69d7188edf06c12f9a1e")] [assembly: AssemblyProduct("BubbleGauge")] [assembly: AssemblyTitle("BubbleGauge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BubbleGauge { public enum PulseStyle { Dim, Flash } internal sealed class GaugeSettings { public BarSize BarSize; public float HeightPer100Hp; public float HpPerTick; public bool LiquidAnimation; public float OffsetX; public float OffsetY; public float Scale = 1f; public bool ShowHpNumber = true; public bool ShowTimeNumber = true; public Color BubbleColor; public Color MercuryColor; } internal sealed class GaugeView { private sealed class Gauge { private const float LightTileW = 48f; private const float LightTileH = 96f; private const float DarkTileW = 72f; private const float DarkTileH = 140f; private const float LiquidStrength = 0.5f; private readonly RectTransform _container; private readonly CanvasGroup _group; private readonly Image _flash; private readonly RectTransform _fill; private readonly RawImage _liquid; private readonly RawImage _lightLayer; private readonly RawImage _darkLayer; private readonly List<RectTransform> _ticks = new List<RectTransform>(); private readonly TextMeshProUGUI _number; private readonly float _seed; private int _shownNumber = int.MinValue; public float Height { get; private set; } public Gauge(string name, RectTransform root, TextMeshProUGUI template, float seed) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0246: 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_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) _seed = seed; _container = NewRect(name, (Transform)(object)root); _container.pivot = Vector2.zero; AddImage(_container, Background); _group = ((Component)_container).gameObject.AddComponent<CanvasGroup>(); _group.interactable = false; _group.blocksRaycasts = false; _fill = Stretch(NewRect("Liquid", (Transform)(object)_container)); _liquid = AddRaw(_fill, null, Color.white); _lightLayer = AddRaw(Stretch(NewRect("Swirl", (Transform)(object)_fill)), Liquid.Noise(light: true), new Color(1f, 1f, 1f, 0.5f)); _darkLayer = AddRaw(Stretch(NewRect("Shade", (Transform)(object)_fill)), Liquid.Noise(light: false), new Color(1f, 1f, 1f, 0.5f)); RectTransform val = Stretch(NewRect("Flash", (Transform)(object)_container)); AddImage(val, Color.white); _flash = ((Component)val).GetComponent<Image>(); ((Graphic)_flash).color = new Color(1f, 1f, 1f, 0f); _number = (((Object)(object)template != (Object)null) ? Object.Instantiate<TextMeshProUGUI>(template, (Transform)(object)_container) : ((Component)NewRect("Number", (Transform)(object)_container)).gameObject.AddComponent<TextMeshProUGUI>()); ((Object)_number).name = "Number"; RectTransform rectTransform = ((TMP_Text)_number).rectTransform; ((Transform)rectTransform).localRotation = Quaternion.identity; ((Transform)rectTransform).localScale = Vector3.one; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(1f, 0f); rectTransform.pivot = new Vector2(0.5f, 0f); rectTransform.sizeDelta = new Vector2(-2f, 22f); rectTransform.anchoredPosition = new Vector2(0f, 2f); ((TMP_Text)_number).enableAutoSizing = true; ((TMP_Text)_number).fontSizeMin = 10f; ((TMP_Text)_number).fontSizeMax = 18f; ((TMP_Text)_number).fontStyle = (FontStyles)1; ((TMP_Text)_number).alignment = (TextAlignmentOptions)1026; ((TMP_Text)_number).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)_number).richText = false; ((TMP_Text)_number).faceColor = Color32.op_Implicit(Color.white); ((TMP_Text)_number).outlineWidth = 0f; ((Graphic)_number).color = Color32.op_Implicit(NumberColor); ((Graphic)_number).raycastTarget = false; Material fontSharedMaterial = ((TMP_Text)_number).fontSharedMaterial; Plugin.DebugLog(name + " number: font='" + (((Object)(object)((TMP_Text)_number).font != (Object)null) ? ((Object)((TMP_Text)_number).font).name : "none") + "' material='" + (((Object)(object)fontSharedMaterial != (Object)null) ? ((Object)fontSharedMaterial).name : "none") + "' shader='" + (((Object)(object)fontSharedMaterial != (Object)null && (Object)(object)fontSharedMaterial.shader != (Object)null) ? ((Object)fontSharedMaterial.shader).name : "none") + "' " + $"copiedFromHud={(Object)(object)template != (Object)null}"); } public void SetDim(float alpha) { _group.alpha = alpha; } public void SetLook(Texture2D gradient, bool showNumber) { if ((Object)(object)_liquid.texture != (Object)(object)gradient) { _liquid.texture = (Texture)(object)gradient; } if (((Component)_number).gameObject.activeSelf != showNumber) { ((Component)_number).gameObject.SetActive(showNumber); } } public void SetFlash(float strength) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!Mathf.Approximately(((Graphic)_flash).color.a, strength)) { ((Graphic)_flash).color = new Color(1f, 1f, 1f, strength); } } public void Place(float x, float width) { //IL_000c: 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) _container.anchoredPosition = new Vector2(x, 0f); _container.sizeDelta = new Vector2(width, Height); } public void SetHeight(float height) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Height = height; _container.sizeDelta = new Vector2(_container.sizeDelta.x, height); } public void SetFill(float fraction) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) _fill.anchorMax = new Vector2(1f, fraction); } public void SetTicks(float max, float step, float unitsPerValue) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) int num = GaugeMath.TickCount(max, step); while (_ticks.Count < num) { RectTransform val = NewRect("Tick", (Transform)(object)_container); val.anchorMin = new Vector2(0f, 0f); val.anchorMax = new Vector2(1f, 0f); val.pivot = new Vector2(0.5f, 0.5f); val.sizeDelta = new Vector2(-2f, 1.5f); AddImage(val, TickColor); ((Transform)val).SetSiblingIndex(((TMP_Text)_number).transform.GetSiblingIndex()); _ticks.Add(val); } for (int i = 0; i < _ticks.Count; i++) { bool flag = i < num; if (((Component)_ticks[i]).gameObject.activeSelf != flag) { ((Component)_ticks[i]).gameObject.SetActive(flag); } if (flag) { _ticks[i].anchoredPosition = new Vector2(0f, (float)(i + 1) * step * unitsPerValue); } } } public void SetNumber(int value, Func<float, string> format) { if (value != _shownNumber) { _shownNumber = value; ((TMP_Text)_number).text = ((format != null) ? format(value) : value.ToString()); } } public void AnimateLiquid(float time) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) Rect rect = _fill.rect; _lightLayer.uvRect = new Rect(_seed * 0.13f, _seed * 0.07f - time * 0.1f, ((Rect)(ref rect)).width / 48f, ((Rect)(ref rect)).height / 96f); _darkLayer.uvRect = new Rect(_seed * 0.29f + time * 0.03f, _seed * 0.17f + time * 0.06f, ((Rect)(ref rect)).width / 72f, ((Rect)(ref rect)).height / 140f); } private static void AddImage(RectTransform rt, Color color) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Image obj = ((Component)rt).gameObject.AddComponent<Image>(); ((Graphic)obj).color = color; ((Graphic)obj).raycastTarget = false; } private static RawImage AddRaw(RectTransform rt, Texture2D texture, Color tint) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) RawImage obj = ((Component)rt).gameObject.AddComponent<RawImage>(); obj.texture = (Texture)(object)texture; ((Graphic)obj).color = tint; ((Graphic)obj).raycastTarget = false; return obj; } } private const float FullHeight = 150f; private const float MinHeight = 40f; private const float Gap = 4f; private const float LiftAboveIcon = 6f; private const float MinTickGap = 9f; private const float ResizeSpeed = 300f; private static readonly float[] HpTickSteps = new float[3] { 50f, 100f, 200f }; private static readonly float[] TimeTickSteps = new float[3] { 10f, 30f, 60f }; private static readonly Color Background = new Color(0f, 0f, 0f, 0.55f); private static readonly Color TickColor = new Color(0f, 0f, 0f, 0.45f); private static readonly Color32 NumberColor = new Color32((byte)28, (byte)18, (byte)4, byte.MaxValue); private const float NumberBottom = 2f; private const float NumberHeight = 22f; private const float TopMargin = 24f; private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4]; private const float DimDepth = 0.65f; private const float FlashStrength = 0.7f; private readonly Hud _hud; private readonly RectTransform _root; private readonly CanvasGroup _rootGroup; private readonly Gauge _bubble; private readonly Gauge _mercury; public float HpTickStep { get; private set; } public float TimeTickStep { get; private set; } public bool IsAlive { get { if ((Object)(object)_root != (Object)null) { return (Object)(object)_hud != (Object)null; } return false; } } private GaugeView(Hud hud) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) _hud = hud; Transform parent = ((Transform)hud.m_gpRoot).parent; _root = NewRect("BubbleGauge", parent); _root.pivot = Vector2.zero; _rootGroup = ((Component)_root).gameObject.AddComponent<CanvasGroup>(); _rootGroup.interactable = false; _rootGroup.blocksRaycasts = false; TMP_Text healthText = hud.m_healthText; TextMeshProUGUI template = (TextMeshProUGUI)(object)((healthText is TextMeshProUGUI) ? healthText : null); _bubble = new Gauge("BubbleBar", _root, template, 3f); _mercury = new Gauge("MercuryTube", _root, template, 11f); SetVisible(visible: false); } public static GaugeView TryCreate() { Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_gpRoot == (Object)null || (Object)(object)instance.m_healthBarRoot == (Object)null) { return null; } return new GaugeView(instance); } public void SetVisible(bool visible) { if (((Component)_root).gameObject.activeSelf != visible) { ((Component)_root).gameObject.SetActive(visible); } } public void Show(BarrierReading r, GaugeSettings s) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) bool num = !((Component)_root).gameObject.activeSelf; SetVisible(visible: true); Rect rect = _hud.m_healthBarRoot.rect; float height = ((Rect)(ref rect)).height; float num2 = Mathf.Max(0.1f, s.Scale); Layout(s.OffsetX, s.OffsetY, num2); _bubble.Place(0f, height); _mercury.Place(height + 4f, height); _bubble.SetLook(Liquid.Gradient(s.BubbleColor), s.ShowHpNumber); _mercury.SetLook(Liquid.Gradient(s.MercuryColor), s.ShowTimeNumber); float num3 = GaugeMath.BubbleBarHeight(s.BarSize, r.Max, s.HeightPer100Hp, 150f, 40f); rect = ((RectTransform)((Transform)_root).parent).rect; float num4 = (((Rect)(ref rect)).yMax - ((Transform)_root).localPosition.y - 24f) / num2; num3 = Mathf.Min(num3, Mathf.Max(40f, num4)); float num5 = (num ? num3 : GaugeMath.Approach(_bubble.Height, num3, 300f * Time.deltaTime)); _bubble.SetHeight(num5); _bubble.SetFill(GaugeMath.Fill(r.Remaining, r.Max)); float unitsPerValue = ((r.Max > 0f) ? (num5 / r.Max) : 0f); float num6 = ((s.BarSize == BarSize.Grow) ? s.HpPerTick : GaugeMath.TickStep(HpTickSteps, unitsPerValue, 9f)); _bubble.SetTicks(r.Max, num6, unitsPerValue); HpTickStep = num6; _bubble.SetNumber(Mathf.CeilToInt(r.Remaining), null); _mercury.SetHeight(150f); _mercury.SetFill(GaugeMath.Fill(r.TimeLeft, r.Duration)); float unitsPerValue2 = ((r.Duration > 0f) ? (150f / r.Duration) : 0f); TimeTickStep = GaugeMath.TickStep(TimeTickSteps, unitsPerValue2, 9f); _mercury.SetTicks(r.Duration, TimeTickStep, unitsPerValue2); _mercury.SetNumber(Mathf.CeilToInt(r.TimeLeft), TimeText.Format); float time = (s.LiquidAnimation ? Time.time : 0f); _bubble.AnimateLiquid(time); _mercury.AnimateLiquid(time); } public void ApplyPulse(PulseStyle style, float bothPulse, float tubePulse, float alpha) { if (style == PulseStyle.Dim) { _rootGroup.alpha = alpha * (1f - 0.65f * bothPulse); _mercury.SetDim(1f - 0.65f * tubePulse); _bubble.SetFlash(0f); _mercury.SetFlash(0f); } else { _rootGroup.alpha = alpha; _mercury.SetDim(1f); _bubble.SetFlash(0.7f * bothPulse); _mercury.SetFlash(0.7f * Mathf.Max(bothPulse, tubePulse)); } } private void Layout(float offsetX, float offsetY, float scale) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)_root).parent; _hud.m_healthBarRoot.GetWorldCorners(Corners); float num = float.MinValue; for (int i = 0; i < 4; i++) { num = Mathf.Max(num, parent.InverseTransformPoint(Corners[i]).x); } float num2 = TopOf(_hud.m_gpRoot, parent); if ((Object)(object)_hud.m_gpName != (Object)null) { num2 = Mathf.Max(num2, TopOf(_hud.m_gpName.rectTransform, parent)); } ((Transform)_root).localPosition = new Vector3(num + 4f + offsetX, num2 + 6f + offsetY, 0f); if (!Mathf.Approximately(((Transform)_root).localScale.x, scale)) { ((Transform)_root).localScale = new Vector3(scale, scale, 1f); } } private static float TopOf(RectTransform rt, Transform space) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) rt.GetWorldCorners(Corners); float num = float.MinValue; for (int i = 0; i < 4; i++) { num = Mathf.Max(num, space.InverseTransformPoint(Corners[i]).y); } return num; } internal static RectTransform NewRect(string name, Transform parent) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)parent).gameObject.layer }.transform; ((Transform)val).SetParent(parent, false); Vector2 anchorMin = (val.anchorMax = Vector2.zero); val.anchorMin = anchorMin; return val; } internal static RectTransform Stretch(RectTransform rt) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; Vector2 offsetMin = (rt.offsetMax = Vector2.zero); rt.offsetMin = offsetMin; return rt; } } internal static class Liquid { private const int NoiseW = 64; private const int NoiseH = 128; private static Texture2D _lightNoise; private static Texture2D _darkNoise; private static readonly Dictionary<Color32, Texture2D> Gradients = new Dictionary<Color32, Texture2D>(); public static readonly Color32 DefaultBubble = new Color32(byte.MaxValue, (byte)194, (byte)74, byte.MaxValue); public static readonly Color32 DefaultMercury = new Color32((byte)228, (byte)244, byte.MaxValue, byte.MaxValue); private static readonly Color32[] GoldStops = (Color32[])(object)new Color32[3] { new Color32((byte)107, (byte)58, (byte)6, byte.MaxValue), DefaultBubble, new Color32((byte)138, (byte)76, (byte)10, byte.MaxValue) }; private static readonly Color32[] FrostStops = (Color32[])(object)new Color32[3] { new Color32((byte)93, (byte)127, (byte)154, byte.MaxValue), DefaultMercury, new Color32((byte)110, (byte)144, (byte)171, byte.MaxValue) }; public static Texture2D Gradient(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011f: 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_0151: Unknown result type (might be due to invalid IL or missing references) Color32 val = Color32.op_Implicit(color); val.a = byte.MaxValue; if (Gradients.TryGetValue(val, out var value) && (Object)(object)value != (Object)null) { return value; } Color32[] array; if (((Color32)(ref val)).Equals(DefaultBubble)) { array = GoldStops; } else if (((Color32)(ref val)).Equals(DefaultMercury)) { array = FrostStops; } else { float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(Color32.op_Implicit(val), ref num, ref num2, ref num3); array = (Color32[])(object)new Color32[3] { Color32.op_Implicit(Color.HSVToRGB(num, Mathf.Min(1f, num2 * 1.3f), num3 * 0.42f)), val, Color32.op_Implicit(Color.HSVToRGB(num, Mathf.Min(1f, num2 * 1.25f), num3 * 0.54f)) }; } Texture2D val2 = NewTexture(16, 1, (TextureWrapMode)1); for (int i = 0; i < 16; i++) { float num4 = (float)i / 15f; Color val3 = ((num4 < 0.5f) ? Color.Lerp(Color32.op_Implicit(array[0]), Color32.op_Implicit(array[1]), num4 * 2f) : Color.Lerp(Color32.op_Implicit(array[1]), Color32.op_Implicit(array[2]), (num4 - 0.5f) * 2f)); val2.SetPixel(i, 0, val3); } val2.Apply(); Gradients[val] = val2; return val2; } public static Texture2D Noise(bool light) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) Texture2D val = (light ? _lightNoise : _darkNoise); if ((Object)(object)val != (Object)null) { return val; } Texture2D val2 = NewTexture(64, 128, (TextureWrapMode)0); float seed = (light ? 17.3f : 41.9f); float num = (light ? 1f : 0f); for (int i = 0; i < 128; i++) { for (int j = 0; j < 64; j++) { float num2 = Mathf.Clamp01((TileableNoise(j, i, seed) - 0.42f) * 2.4f); val2.SetPixel(j, i, new Color(num, num, num, num2)); } } val2.Apply(); if (light) { _lightNoise = val2; } else { _darkNoise = val2; } return val2; } private static float TileableNoise(int x, int y, float seed) { float num = (float)x / 64f; float num2 = (float)y / 128f; return Sample(num, num2) * (1f - num) * (1f - num2) + Sample(num - 1f, num2) * num * (1f - num2) + Sample(num, num2 - 1f) * (1f - num) * num2 + Sample(num - 1f, num2 - 1f) * num * num2; float Sample(float sx, float sy) { return Mathf.PerlinNoise(seed + sx * 4f, seed + sy * 8f) * 0.65f + Mathf.PerlinNoise(seed + sx * 9f, seed + sy * 17f) * 0.35f; } } private static Texture2D NewTexture(int w, int h, TextureWrapMode wrap) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown return new Texture2D(w, h, (TextureFormat)4, false) { wrapMode = wrap, filterMode = (FilterMode)1, hideFlags = (HideFlags)32 }; } } public enum MessagePosition { Center, TopLeft } [BepInPlugin("nerdosapiens.BubbleGauge", "BubbleGauge", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "nerdosapiens.BubbleGauge"; public const string PluginName = "BubbleGauge"; public const string PluginVersion = "1.0.1"; private const float EndPulseSeconds = 0.25f; private const float EndFadeSeconds = 0.4f; internal static ManualLogSource Log; private readonly Harmony _harmony = new Harmony("nerdosapiens.BubbleGauge"); private readonly BarrierTracker _tracker = new BarrierTracker(5f); private readonly WarningAnnouncer _announcer = new WarningAnnouncer(); private SE_Shield _tracked; private GaugeView _view; private float _logTimer; private bool _ending; private float _endStarted; private BarrierReading _endReading; private float _warnStarted; private string _lastMessage; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugLogging; private ConfigEntry<float> _offsetX; private ConfigEntry<float> _offsetY; private ConfigEntry<float> _scale; private ConfigEntry<bool> _showHpNumbers; private ConfigEntry<bool> _showTimeNumber; private ConfigEntry<string> _bubbleColor; private ConfigEntry<string> _mercuryColor; private readonly GaugeSettings _settings = new GaugeSettings(); private readonly ColorSetting _bubbleColorCache = new ColorSetting(Color32.op_Implicit(Liquid.DefaultBubble)); private readonly ColorSetting _mercuryColorCache = new ColorSetting(Color32.op_Implicit(Liquid.DefaultMercury)); private ConfigEntry<BarSize> _barSize; private ConfigEntry<float> _heightPer100Hp; private ConfigEntry<float> _hpPerTick; private ConfigEntry<bool> _liquidAnimation; private ConfigEntry<PulseStyle> _pulseStyle; private ConfigEntry<int> _endPulses; private ConfigEntry<float> _warnSeconds; private ConfigEntry<float> _blinkSpeed; private ConfigEntry<WarningMode> _timeWarning; private ConfigEntry<float> _timeWarningSeconds; private ConfigEntry<WarningMode> _hpWarning; private ConfigEntry<float> _hpWarningPercent; private ConfigEntry<MessagePosition> _messagePosition; private ConfigEntry<string> _messageColor; private ConfigEntry<int> _messageSize; internal static ConfigEntry<bool> RefillOnRecast; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)("BubbleGauge 1.0.1 loaded (Valheim " + Version.GetVersionString(false) + ")")); BindConfig(); _tracker.Started += delegate(BarrierReading r) { _announcer.Reset(); DebugLog($"START {Describe(r)} refMax(skill100)={ReferenceMax(_tracked):0} worldLevel={Game.m_worldLevel}"); }; _tracker.Recast += delegate(BarrierReading r) { _announcer.Reset(); DebugLog("RECAST " + Describe(r)); }; _tracker.MaxChanged += delegate(float oldMax, float newMax) { DebugLog($"MAX CHANGED {oldMax:0} -> {newMax:0}"); }; _tracker.WarningStarted += delegate { _warnStarted = Time.time; DebugLog("WARNING " + TimeText.Format(_tracker.Current.TimeLeft) + " left"); }; _tracker.Ended += delegate(EndKind kind, BarrierReading r) { _announcer.Clear(); _ending = true; _endStarted = Time.time; _endReading = r; DebugLog($"END {kind} absorbed={r.Absorbed:0} max={r.Max:0} elapsed={r.Elapsed:0.0}/{r.Duration:0.0}"); }; _announcer.Announce += ShowWarning; _harmony.PatchAll(); } private void BindConfig() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Expected O, but got Unknown //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Expected O, but got Unknown //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Expected O, but got Unknown //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Expected O, but got Unknown //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Expected O, but got Unknown //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Expected O, but got Unknown Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Off: no gauges, no messages, and RefillOnRecast does nothing (pure vanilla)."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Write detailed barrier readings to the BepInEx log (once a second while a barrier is up). Only useful when reporting a bug."); RefillOnRecast = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RefillOnRecast", false, "Off (vanilla): recasting on an active barrier only resets its timer. On: recasting also restores the barrier's HP to the new cast's full amount. Affects only your own barrier, whoever casts it."); _barSize = ((BaseUnityPlugin)this).Config.Bind<BarSize>("Layout", "BarSize", BarSize.Grow, "Grow: the Bubble Bar gets taller with the caster's Blood Magic, like the health bar with better food. Fixed: the Bubble Bar is always full height and a stronger barrier shows more tick marks."); _heightPer100Hp = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "BubbleBarHeightPer100Hp", 38f, new ConfigDescription("Grow mode: how tall the Bubble Bar gets per 100 barrier HP, in HUD units. Default 38 (chosen in play testing): a skill-0 barrier is about half the Mercury Tube, a skill-100 barrier (700 HP) about 1.8x. 128 matches the vanilla health bar exactly. The bar never grows past the top of the screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 200f), Array.Empty<object>())); _hpPerTick = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "HpPerTick", 50f, new ConfigDescription("Grow mode: barrier HP between tick marks on the Bubble Bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>())); _offsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "OffsetX", 0f, new ConfigDescription("Nudge the gauges left (-) or right (+), in HUD units - e.g. to make room for another HUD mod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), Array.Empty<object>())); _offsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "OffsetY", 0f, new ConfigDescription("Nudge the gauges down (-) or up (+), in HUD units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-500f, 500f), Array.Empty<object>())); _scale = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "Scale", 1f, new ConfigDescription("Overall size of both gauges.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); _liquidAnimation = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "LiquidAnimation", true, "Swirl the liquid inside the gauges. Turn off for a still fill."); _showHpNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowHpNumbers", true, "Show the barrier HP number on the Bubble Bar."); _showTimeNumber = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowTimeNumber", true, "Show the time left on the Mercury Tube."); _bubbleColor = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "BubbleColor", "#FFC24A", "Colour of the Bubble Bar's liquid, as a hex code (default: barrier gold)."); _mercuryColor = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "MercuryColor", "#E4F4FF", "Colour of the Mercury Tube's liquid, as a hex code (default: frost blue)."); _pulseStyle = ((BaseUnityPlugin)this).Config.Bind<PulseStyle>("Pulse", "PulseStyle", PulseStyle.Dim, "How the gauges pulse when the barrier ends and when time is running out. Dim: they briefly fade and return. Flash: they briefly flash white."); _endPulses = ((BaseUnityPlugin)this).Config.Bind<int>("Pulse", "EndPulses", 2, new ConfigDescription("Pulses when the barrier breaks or runs out, before the gauges fade away. 0 = just fade.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>())); _warnSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Pulse", "WarnSeconds", 5f, new ConfigDescription("The Mercury Tube pulses when this many seconds are left. 0 = never.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>())); _blinkSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Pulse", "BlinkSpeed", 2f, new ConfigDescription("Pulses per second while time is running out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>())); _timeWarning = ((BaseUnityPlugin)this).Config.Bind<WarningMode>("Messages", "TimeWarning", WarningMode.Off, "On-screen message as the barrier runs out. Once: when TimeWarningSeconds are left. EveryTick: at each tick mark on the Mercury Tube."); _timeWarningSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "TimeWarningSeconds", 10f, new ConfigDescription("For TimeWarning = Once: seconds left when the message appears.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>())); _hpWarning = ((BaseUnityPlugin)this).Config.Bind<WarningMode>("Messages", "HpWarning", WarningMode.Off, "On-screen message as the barrier takes damage. Once: when HP drops to HpWarningPercent. EveryTick: at each tick mark on the Bubble Bar."); _hpWarningPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Messages", "HpWarningPercent", 25f, new ConfigDescription("For HpWarning = Once: percent of the barrier's HP left when the message appears.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 90f), Array.Empty<object>())); _messagePosition = ((BaseUnityPlugin)this).Config.Bind<MessagePosition>("Messages", "MessagePosition", MessagePosition.Center, "Center: the game's large centre-screen text, made smaller and green so it isn't mistaken for a boss or raid alert. TopLeft: the game's small top-left messages."); _messageColor = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "MessageColor", "#8FC93A", "Colour of centre messages, as a hex code (default: Yggdrasil porridge green)."); _messageSize = ((BaseUnityPlugin)this).Config.Bind<int>("Messages", "MessageSize", 80, new ConfigDescription("Size of centre messages, as a percent of the game's own centre text.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(40, 100), Array.Empty<object>())); } private void OnDestroy() { _harmony.UnpatchSelf(); } private void Update() { Player localPlayer = Player.m_localPlayer; SE_Shield val = (((Object)(object)localPlayer != (Object)null) ? FindShield(localPlayer) : null); _tracker.WarnSeconds = _warnSeconds.Value; _announcer.HpMode = _hpWarning.Value; _announcer.TimeMode = _timeWarning.Value; _announcer.HpWarningPercent = _hpWarningPercent.Value; _announcer.TimeWarningSeconds = _timeWarningSeconds.Value; if ((Object)(object)_tracked != (Object)null && val != _tracked) { _tracker.Update(Read(_tracked, active: false)); _tracked = null; } if ((Object)(object)val != (Object)null) { _tracked = val; } if (!Enabled.Value) { if ((Object)(object)val != (Object)null) { _tracker.Update(Read(val, active: true)); } _ending = false; _announcer.Clear(); if (_view != null && _view.IsAlive) { _view.SetVisible(visible: false); } return; } FillSettings(); if ((Object)(object)val != (Object)null) { _ending = false; _tracker.Update(Read(val, active: true)); if (EnsureView()) { _view.Show(_tracker.Current, _settings); float tubePulse = (_tracker.IsWarning ? PulseMath.Blink(Time.time - _warnStarted, _blinkSpeed.Value) : 0f); _view.ApplyPulse(_pulseStyle.Value, 0f, tubePulse, 1f); _announcer.Update(Time.time, _tracker.Current, _view.HpTickStep, _view.TimeTickStep, CanShowWarning()); LogTick(); } return; } if (_ending && EnsureView()) { EndFrame endFrame = PulseMath.End(Time.time - _endStarted, _endPulses.Value, 0.25f, 0.4f); _view.Show(_endReading, _settings); _view.ApplyPulse(_pulseStyle.Value, endFrame.Pulse, 0f, endFrame.Alpha); if (!endFrame.Done) { return; } _ending = false; } if (_view != null && _view.IsAlive) { _view.SetVisible(visible: false); } } private void FillSettings() { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) _settings.BarSize = _barSize.Value; _settings.HeightPer100Hp = _heightPer100Hp.Value; _settings.HpPerTick = _hpPerTick.Value; _settings.LiquidAnimation = _liquidAnimation.Value; _settings.OffsetX = _offsetX.Value; _settings.OffsetY = _offsetY.Value; _settings.Scale = _scale.Value; _settings.ShowHpNumber = _showHpNumbers.Value; _settings.ShowTimeNumber = _showTimeNumber.Value; _settings.BubbleColor = _bubbleColorCache.Get(_bubbleColor.Value); _settings.MercuryColor = _mercuryColorCache.Get(_mercuryColor.Value); } internal static void DebugLog(string message) { if (DebugLogging != null && DebugLogging.Value) { Log.LogInfo((object)message); } } private bool EnsureView() { if (_view == null || !_view.IsAlive) { _view = GaugeView.TryCreate(); } return _view != null; } private bool CanShowWarning() { if (_messagePosition.Value != MessagePosition.Center) { return true; } MessageHud instance = MessageHud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_messageCenterText == (Object)null) { return false; } if (((Graphic)instance.m_messageCenterText).canvasRenderer.GetAlpha() > 0.05f) { return instance.m_messageCenterText.text == _lastMessage; } return true; } private void ShowWarning(string text) { MessageHud instance = MessageHud.instance; if (!((Object)(object)instance == (Object)null)) { if (_messagePosition.Value == MessagePosition.TopLeft) { instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, false); return; } string arg = (_messageColor.Value.StartsWith("#") ? _messageColor.Value : ("#" + _messageColor.Value)); _lastMessage = $"<size={_messageSize.Value}%><color={arg}>{text}</color></size>"; instance.ShowMessage((MessageType)2, _lastMessage, 0, (Sprite)null, false, false); } } private void LogTick() { if (DebugLogging.Value) { _logTimer -= Time.deltaTime; if (!(_logTimer > 0f)) { _logTimer = 1f; DebugLog("tick " + Describe(_tracker.Current)); } } } private static SE_Shield FindShield(Player player) { List<StatusEffect> statusEffects = ((Character)player).GetSEMan().GetStatusEffects(); for (int i = 0; i < statusEffects.Count; i++) { StatusEffect obj = statusEffects[i]; SE_Shield val = (SE_Shield)(object)((obj is SE_Shield) ? obj : null); if (val != null) { return val; } } return null; } private static BarrierReading Read(SE_Shield s, bool active) { return new BarrierReading(s, active, s.m_totalAbsorbDamage, s.m_damage, ((StatusEffect)s).m_time, ((StatusEffect)s).m_ttl); } private static float ReferenceMax(SE_Shield s) { return GaugeMath.ReferenceMax(s.m_absorbDamage, s.m_absorbDamagePerSkillLevel, s.m_absorbDamageWorldLevel, Game.m_worldLevel); } private static string Describe(BarrierReading r) { return $"hp {r.Remaining:0}/{r.Max:0} time {TimeText.Format(r.TimeLeft)} ({r.TimeLeft:0.0}/{r.Duration:0.0}s)"; } } [HarmonyPatch(typeof(SE_Shield), "SetLevel")] internal static class RefillOnRecastPatch { private static void Postfix(SE_Shield __instance) { if (Plugin.RefillOnRecast != null && Plugin.RefillOnRecast.Value && Plugin.Enabled.Value && !((Object)(object)((StatusEffect)__instance).m_character == (Object)null) && !((Object)(object)((StatusEffect)__instance).m_character != (Object)(object)Player.m_localPlayer)) { __instance.m_damage = 0f; } } } internal sealed class ColorSetting { private readonly Color _fallback; private string _text; private Color _color; public ColorSetting(Color fallback) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) _fallback = fallback; _color = fallback; } public Color Get(string text) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (text == _text) { return _color; } _text = text; string text2 = ((text != null && !text.StartsWith("#")) ? ("#" + text.Trim()) : text); Color val = default(Color); _color = (ColorUtility.TryParseHtmlString(text2, ref val) ? val : _fallback); return _color; } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class LogHudLayoutOnSpawn { private static void Postfix(Player __instance) { if (Plugin.DebugLogging.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)Hud.instance == (Object)null)) { LogRect("healthBarRoot", Hud.instance.m_healthBarRoot); LogRect("healthPanel", Hud.instance.m_healthPanel); LogRect("gpRoot", Hud.instance.m_gpRoot); if ((Object)(object)Hud.instance.m_gpName != (Object)null) { LogRect("gpName", Hud.instance.m_gpName.rectTransform); } if ((Object)(object)Hud.instance.m_gpCooldown != (Object)null) { LogRect("gpCooldown", Hud.instance.m_gpCooldown.rectTransform); } } } private static void LogRect(string label, RectTransform rt) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rt == (Object)null) { Plugin.DebugLog("HUD " + label + ": null"); return; } Vector3[] array = (Vector3[])(object)new Vector3[4]; rt.GetWorldCorners(array); object arg = ((Component)rt).gameObject.activeInHierarchy; Transform parent = ((Transform)rt).parent; Plugin.DebugLog($"HUD {label}: active={arg} parent='{((parent != null) ? ((Object)parent).name : null)}' " + $"anchorMin={rt.anchorMin} anchorMax={rt.anchorMax} pivot={rt.pivot} " + $"anchoredPos={rt.anchoredPosition} size={rt.sizeDelta} rotZ={((Transform)rt).localEulerAngles.z:0} " + $"screenBL={array[0]} screenTR={array[2]}"); } } } namespace BubbleGauge.Logic { public enum EndKind { Broke, Expired } public readonly struct BarrierReading { public readonly object Id; public readonly bool Active; public readonly float Max; public readonly float Absorbed; public readonly float Elapsed; public readonly float Duration; public float Remaining => GaugeMath.Remaining(Max, Absorbed); public float TimeLeft => Math.Max(0f, Duration - Elapsed); public BarrierReading(object id, bool active, float max, float absorbed, float elapsed, float duration) { Id = id; Active = active; Max = max; Absorbed = absorbed; Elapsed = elapsed; Duration = duration; } } public sealed class BarrierTracker { private const float RecastJumpSeconds = 0.5f; public float WarnSeconds { get; set; } public bool IsActive { get; private set; } public bool IsWarning { get; private set; } public BarrierReading Current { get; private set; } public event Action<BarrierReading> Started; public event Action<BarrierReading> Recast; public event Action<float, float> MaxChanged; public event Action WarningStarted; public event Action<EndKind, BarrierReading> Ended; public BarrierTracker(float warnSeconds) { WarnSeconds = warnSeconds; } public void Update(BarrierReading reading) { if (!reading.Active) { if (IsActive && reading.Id == Current.Id) { End(reading); } return; } if (!IsActive || reading.Id != Current.Id) { if (IsActive) { End(Current); } Start(reading); return; } BarrierReading current = Current; Current = reading; if (reading.Elapsed < current.Elapsed - 0.5f) { this.Recast?.Invoke(reading); } if (reading.Max != current.Max) { this.MaxChanged?.Invoke(current.Max, reading.Max); } UpdateWarning(); } private void Start(BarrierReading reading) { Current = reading; IsActive = true; IsWarning = false; this.Started?.Invoke(reading); UpdateWarning(); } private void End(BarrierReading final) { IsActive = false; IsWarning = false; Current = final; this.Ended?.Invoke((!(final.Absorbed > final.Max)) ? EndKind.Expired : EndKind.Broke, final); } private void UpdateWarning() { bool flag = GaugeMath.IsWarning(Current.TimeLeft, WarnSeconds); if (flag && !IsWarning) { this.WarningStarted?.Invoke(); } IsWarning = flag; } } public enum BarSize { Grow, Fixed } public static class GaugeMath { public static float TickStep(float[] steps, float unitsPerValue, float minGap) { for (int i = 0; i < steps.Length; i++) { if (steps[i] * unitsPerValue >= minGap) { return steps[i]; } } return steps[^1]; } public static int TickCount(float max, float step) { if (max <= 0f || step <= 0f) { return 0; } int val = (int)Math.Ceiling(max / step - 0.0001f) - 1; return Math.Max(0, val); } public static float BubbleBarHeight(BarSize mode, float max, float heightPer100Hp, float fullHeight, float minHeight) { if (mode == BarSize.Fixed) { return fullHeight; } return Math.Max(minHeight, max * heightPer100Hp / 100f); } public static float Approach(float current, float target, float maxDelta) { if (Math.Abs(target - current) <= maxDelta) { return target; } return current + (float)Math.Sign(target - current) * maxDelta; } public static float Remaining(float max, float absorbed) { return Math.Max(0f, max - absorbed); } public static float Fill(float value, float max) { if (max <= 0f) { return 0f; } return Clamp01(value / max); } public static float ReferenceMax(float baseAbsorb, float perSkillLevel, float perWorldLevel, int worldLevel) { float num = baseAbsorb + perSkillLevel * 100f; if (worldLevel > 0) { num += perWorldLevel * (float)worldLevel; } return num; } public static bool IsWarning(float timeLeft, float warnSeconds) { if (warnSeconds > 0f) { return timeLeft <= warnSeconds; } return false; } private static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } } public readonly struct EndFrame { public readonly float Pulse; public readonly float Alpha; public readonly bool Done; public EndFrame(float pulse, float alpha, bool done) { Pulse = pulse; Alpha = alpha; Done = done; } } public static class PulseMath { public static float Intensity(float phase) { return (1f - (float)Math.Cos(Math.PI * 2.0 * (double)phase)) * 0.5f; } public static float Blink(float time, float pulsesPerSecond) { double num = time * pulsesPerSecond; return Intensity((float)(num - Math.Floor(num))); } public static EndFrame End(float elapsed, int pulses, float pulseSeconds, float fadeSeconds) { float num = (float)Math.Max(0, pulses) * pulseSeconds; if (elapsed < num) { float num2 = elapsed / pulseSeconds; return new EndFrame(Intensity(num2 - (float)Math.Floor(num2)), 1f, done: false); } float num3 = ((fadeSeconds <= 0f) ? 1f : ((elapsed - num) / fadeSeconds)); if (!(num3 >= 1f)) { return new EndFrame(0f, 1f - num3, done: false); } return new EndFrame(0f, 0f, done: true); } } public static class TimeText { public static string Format(float seconds) { int num = ((!(seconds <= 0f)) ? ((int)Math.Ceiling(seconds)) : 0); if (num <= 99) { return num + "s"; } return num / 60 + ":" + (num % 60).ToString("00"); } public static string Spoken(int seconds) { if (seconds < 60 || (seconds <= 99 && seconds % 60 != 0)) { if (seconds != 1) { return seconds + " seconds"; } return "1 second"; } if (seconds % 60 == 0) { if (seconds != 60) { return seconds / 60 + " minutes"; } return "1 minute"; } return seconds / 60 + ":" + (seconds % 60).ToString("00"); } public static string Stack(string text) { if (string.IsNullOrEmpty(text)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(text.Length * 2); for (int i = 0; i < text.Length; i++) { if (i > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(text[i]); } return stringBuilder.ToString(); } } public enum WarningMode { Off, Once, EveryTick } public sealed class WarningAnnouncer { private bool _hasBaseline; private float _lastRemaining; private float _lastTimeLeft; private bool _hpOnceDone; private bool _timeOnceDone; private string _waitingHp; private string _waitingTime; private float _nextAllowed = float.MinValue; public WarningMode HpMode { get; set; } public WarningMode TimeMode { get; set; } public float HpWarningPercent { get; set; } = 25f; public float TimeWarningSeconds { get; set; } = 10f; public float MinGapSeconds { get; set; } = 1.5f; public event Action<string> Announce; public void Reset() { _hasBaseline = false; _hpOnceDone = (_timeOnceDone = false); _waitingHp = (_waitingTime = null); } public void Clear() { Reset(); } public void Update(float now, BarrierReading r, float hpStep, float timeStep, bool canShow) { float remaining = r.Remaining; float timeLeft = r.TimeLeft; if (_hasBaseline) { CheckHp(remaining, hpStep, r.Max); CheckTime(timeLeft, timeStep); } _hasBaseline = true; _lastRemaining = remaining; _lastTimeLeft = timeLeft; if (!canShow || now < _nextAllowed) { return; } bool flag = _waitingHp != null; string text = (flag ? _waitingHp : _waitingTime); if (text != null) { if (flag) { _waitingHp = null; } else { _waitingTime = null; } _nextAllowed = now + MinGapSeconds; this.Announce?.Invoke(text); } } private void CheckHp(float remaining, float step, float max) { if (remaining >= _lastRemaining) { return; } bool flag = false; if (HpMode == WarningMode.Once && !_hpOnceDone) { float num = max * HpWarningPercent / 100f; flag = _lastRemaining > num && remaining <= num; if (flag) { _hpOnceDone = true; } } else if (HpMode == WarningMode.EveryTick) { flag = Level(remaining, step) < Level(_lastRemaining, step); } if (flag) { _waitingHp = $"Barrier down to {(int)Math.Ceiling(remaining)} HP"; } } private void CheckTime(float timeLeft, float step) { if (timeLeft >= _lastTimeLeft) { return; } if (TimeMode == WarningMode.Once && !_timeOnceDone) { if (_lastTimeLeft > TimeWarningSeconds && timeLeft <= TimeWarningSeconds) { _timeOnceDone = true; _waitingTime = "Barrier fades in " + TimeText.Spoken((int)Math.Round(TimeWarningSeconds)); } } else if (TimeMode == WarningMode.EveryTick) { int num = Level(timeLeft, step); if (num < Level(_lastTimeLeft, step) && num > 0) { _waitingTime = "Barrier fades in " + TimeText.Spoken((int)Math.Round((float)num * step)); } } } private static int Level(float value, float step) { if (!(step <= 0f)) { return (int)Math.Ceiling(value / step - 0.0001f); } return 0; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }