Decompiled source of StaminaStatsTactikot v1.3.7

plugins/StaminaStatsTactikot.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("pixx; TactiKot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Numerical stamina and affliction values for current PEAK builds.")]
[assembly: AssemblyFileVersion("1.3.7.0")]
[assembly: AssemblyInformationalVersion("1.3.7+7441897f07d7f8fb69e84e4656e79cea9531f0de")]
[assembly: AssemblyProduct("StaminaStatsTactikot")]
[assembly: AssemblyTitle("StaminaStatsTactikot")]
[assembly: AssemblyVersion("1.3.7.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 StaminaStatsTactikot
{
	internal static class HudController
	{
		private static StaminaHudView attachedView;

		internal static void AttachTo(StaminaBar bar)
		{
			if ((Object)(object)bar == (Object)null)
			{
				return;
			}
			GUIManager instance = GUIManager.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.bar == (Object)null) && !((Object)(object)instance.bar != (Object)(object)bar))
			{
				StaminaHudView staminaHudView = ((Component)bar).GetComponent<StaminaHudView>();
				if ((Object)(object)staminaHudView == (Object)null)
				{
					staminaHudView = ((Component)bar).gameObject.AddComponent<StaminaHudView>();
				}
				staminaHudView.Initialize(bar);
				attachedView = staminaHudView;
			}
		}

		internal static void Cleanup()
		{
			if ((Object)(object)attachedView != (Object)null)
			{
				Object.Destroy((Object)(object)attachedView);
				attachedView = null;
			}
		}
	}
	internal static class HudLabelLayout
	{
		private const float LaneGapRatio = 0.1f;

		internal static float CalculateLaneStep(float renderedTextHeight)
		{
			if (renderedTextHeight < 0f || float.IsNaN(renderedTextHeight) || float.IsInfinity(renderedTextHeight))
			{
				throw new ArgumentOutOfRangeException("renderedTextHeight");
			}
			return renderedTextHeight + renderedTextHeight * 0.1f;
		}
	}
	internal static class HudLabelRowLayout
	{
		internal static void AssignLanes(float[] centers, float[] widths, float[] rowYs, int count, int laneCount, float gap, float rowTolerance, int[] lanes, int[] orderWorkspace, float[] laneRightWorkspace)
		{
			if (centers == null)
			{
				throw new ArgumentNullException("centers");
			}
			if (widths == null)
			{
				throw new ArgumentNullException("widths");
			}
			if (rowYs == null)
			{
				throw new ArgumentNullException("rowYs");
			}
			if (lanes == null)
			{
				throw new ArgumentNullException("lanes");
			}
			if (orderWorkspace == null)
			{
				throw new ArgumentNullException("orderWorkspace");
			}
			if (laneRightWorkspace == null)
			{
				throw new ArgumentNullException("laneRightWorkspace");
			}
			if (count < 0 || count > centers.Length || count > widths.Length || count > rowYs.Length || count > lanes.Length || count > orderWorkspace.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (laneCount <= 0 || laneCount > laneRightWorkspace.Length)
			{
				throw new ArgumentOutOfRangeException("laneCount");
			}
			if (gap < 0f || float.IsNaN(gap) || float.IsInfinity(gap))
			{
				throw new ArgumentOutOfRangeException("gap");
			}
			if (rowTolerance < 0f || float.IsNaN(rowTolerance) || float.IsInfinity(rowTolerance))
			{
				throw new ArgumentOutOfRangeException("rowTolerance");
			}
			for (int i = 0; i < count; i++)
			{
				orderWorkspace[i] = i;
			}
			for (int j = 1; j < count; j++)
			{
				int num = orderWorkspace[j];
				int num2 = j - 1;
				while (num2 >= 0 && (rowYs[orderWorkspace[num2]] > rowYs[num] || (rowYs[orderWorkspace[num2]] == rowYs[num] && centers[orderWorkspace[num2]] > centers[num])))
				{
					orderWorkspace[num2 + 1] = orderWorkspace[num2];
					num2--;
				}
				orderWorkspace[num2 + 1] = num;
			}
			int num3 = 0;
			while (num3 < count)
			{
				float num4 = rowYs[orderWorkspace[num3]];
				int k;
				for (k = num3 + 1; k < count && Math.Abs(rowYs[orderWorkspace[k]] - num4) <= rowTolerance; k++)
				{
				}
				for (int l = num3 + 1; l < k; l++)
				{
					int num5 = orderWorkspace[l];
					int num6 = l - 1;
					while (num6 >= num3 && centers[orderWorkspace[num6]] > centers[num5])
					{
						orderWorkspace[num6 + 1] = orderWorkspace[num6];
						num6--;
					}
					orderWorkspace[num6 + 1] = num5;
				}
				for (int m = 0; m < laneCount; m++)
				{
					laneRightWorkspace[m] = float.NegativeInfinity;
				}
				for (int n = num3; n < k; n++)
				{
					int num7 = orderWorkspace[n];
					float num8 = Math.Max(0f, widths[num7]) * 0.5f;
					float num9 = centers[num7] - num8;
					float num10 = centers[num7] + num8;
					int num11 = -1;
					for (int num12 = 0; num12 < laneCount; num12++)
					{
						if (num9 >= laneRightWorkspace[num12] + gap)
						{
							num11 = num12;
							break;
						}
					}
					if (num11 < 0)
					{
						num11 = 0;
						for (int num13 = 1; num13 < laneCount; num13++)
						{
							if (laneRightWorkspace[num13] < laneRightWorkspace[num11])
							{
								num11 = num13;
							}
						}
					}
					lanes[num7] = num11;
					laneRightWorkspace[num11] = num10;
				}
				num3 = k;
			}
		}
	}
	internal static class HudTextColor
	{
		internal static float GetWhiteBlend(float red, float green, float blue, bool highContrast)
		{
			float num = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
			float num2 = (highContrast ? 0.75f : 0.55f);
			if (num >= num2)
			{
				return 0f;
			}
			return (num2 - num) / (1f - num);
		}
	}
	internal enum HudValueKind
	{
		Hidden,
		Number
	}
	internal readonly struct HudValuePresentation
	{
		internal HudValueKind Kind { get; }

		internal int Value { get; }

		internal bool IsVisible => Kind != HudValueKind.Hidden;

		private HudValuePresentation(HudValueKind kind, int value)
		{
			Kind = kind;
			Value = value;
		}

		internal static HudValuePresentation Create(int roundedValue, bool enabled, bool sourceVisible, bool showZero)
		{
			if (!enabled || !sourceVisible)
			{
				return new HudValuePresentation(HudValueKind.Hidden, 0);
			}
			if (roundedValue == 0 && !showZero)
			{
				return new HudValuePresentation(HudValueKind.Hidden, 0);
			}
			return new HudValuePresentation(HudValueKind.Number, roundedValue);
		}
	}
	internal static class MinimaxRounding
	{
		internal enum RoundingProtection : byte
		{
			ForceUp,
			None,
			ForceDown
		}

		internal readonly struct Remainder
		{
			internal int Index { get; }

			internal double Fraction { get; }

			internal RoundingProtection Protection { get; }

			internal Remainder(int index, double fraction, RoundingProtection protection)
			{
				Index = index;
				Fraction = fraction;
				Protection = protection;
			}
		}

		private sealed class ExactRemainderComparer : IComparer<Remainder>
		{
			internal static readonly ExactRemainderComparer Instance = new ExactRemainderComparer();

			public int Compare(Remainder left, Remainder right)
			{
				int num = right.Fraction.CompareTo(left.Fraction);
				if (num == 0)
				{
					return left.Index.CompareTo(right.Index);
				}
				return num;
			}
		}

		private sealed class StableRemainderComparer : IComparer<Remainder>
		{
			internal static readonly StableRemainderComparer Instance = new StableRemainderComparer();

			public int Compare(Remainder left, Remainder right)
			{
				if (left.Protection != right.Protection)
				{
					return left.Protection.CompareTo(right.Protection);
				}
				int num = right.Fraction.CompareTo(left.Fraction);
				if (num == 0)
				{
					return left.Index.CompareTo(right.Index);
				}
				return num;
			}
		}

		internal static int[] Round(IReadOnlyList<double> values)
		{
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			int count = values.Count;
			int[] result = new int[count];
			if (count == 0)
			{
				return result;
			}
			double[] array = new double[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = values[i];
			}
			Remainder[] workspace = new Remainder[count];
			RoundInto(array, count, result, workspace, null, null, null, 0.0, stable: false);
			return result;
		}

		internal static void RoundInto(double[] values, int count, int[] result, Remainder[] workspace, int[] previousRoundedValues, double[] previousExactValues, bool[] hasPreviousValues, double directionTolerance, bool stable)
		{
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			if (result == null)
			{
				throw new ArgumentNullException("result");
			}
			if (workspace == null)
			{
				throw new ArgumentNullException("workspace");
			}
			if (count < 0 || count > values.Length || count > result.Length || count > workspace.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (previousRoundedValues != null && previousRoundedValues.Length < count)
			{
				throw new ArgumentException("Previous values buffer is too small.", "previousRoundedValues");
			}
			if (previousExactValues != null && previousExactValues.Length < count)
			{
				throw new ArgumentException("Previous exact values buffer is too small.", "previousExactValues");
			}
			if (hasPreviousValues != null && hasPreviousValues.Length < count)
			{
				throw new ArgumentException("Previous-value flags buffer is too small.", "hasPreviousValues");
			}
			if (directionTolerance < 0.0 || double.IsNaN(directionTolerance))
			{
				throw new ArgumentOutOfRangeException("directionTolerance");
			}
			if (count == 0)
			{
				return;
			}
			double num = 0.0;
			long num2 = 0L;
			int num3 = 0;
			for (int i = 0; i < count; i++)
			{
				double num4 = values[i];
				if (double.IsNaN(num4) || double.IsInfinity(num4))
				{
					throw new ArgumentOutOfRangeException("values", "Values must be finite.");
				}
				double num5 = Math.Floor(num4);
				if (num5 < -2147483648.0 || num5 > 2147483647.0)
				{
					throw new ArgumentOutOfRangeException("values", "Values are outside the supported range.");
				}
				int num6 = (result[i] = (int)num5);
				num2 += num6;
				num += num4;
				if (num4 != num5)
				{
					RoundingProtection protection = ResolveProtection(num4, num6, i, previousRoundedValues, previousExactValues, hasPreviousValues, directionTolerance);
					workspace[num3++] = new Remainder(i, num4 - num5, protection);
				}
			}
			long num7 = checked((long)Math.Round(num, MidpointRounding.AwayFromZero)) - num2;
			if (num7 < 0 || num7 > num3)
			{
				throw new InvalidOperationException("The rounded total cannot be reached.");
			}
			IComparer<Remainder> comparer;
			if (!stable)
			{
				IComparer<Remainder> instance = ExactRemainderComparer.Instance;
				comparer = instance;
			}
			else
			{
				IComparer<Remainder> instance = StableRemainderComparer.Instance;
				comparer = instance;
			}
			IComparer<Remainder> comparer2 = comparer;
			for (int j = 1; j < num3; j++)
			{
				Remainder remainder = workspace[j];
				int num8 = j - 1;
				while (num8 >= 0 && comparer2.Compare(workspace[num8], remainder) > 0)
				{
					workspace[num8 + 1] = workspace[num8];
					num8--;
				}
				workspace[num8 + 1] = remainder;
			}
			for (int k = 0; k < num7; k++)
			{
				result[workspace[k].Index]++;
			}
		}

		private static RoundingProtection ResolveProtection(double value, int floorValue, int index, int[] previousRoundedValues, double[] previousExactValues, bool[] hasPreviousValues, double tolerance)
		{
			if (previousRoundedValues == null || previousExactValues == null || hasPreviousValues == null || !hasPreviousValues[index])
			{
				return RoundingProtection.None;
			}
			int num = previousRoundedValues[index];
			double num2 = previousExactValues[index];
			bool num3 = (double)num > num2 + tolerance;
			bool flag = (double)num < num2 - tolerance;
			if (num3 && value >= num2 - tolerance && floorValue < num)
			{
				return RoundingProtection.ForceUp;
			}
			if (flag && value <= num2 + tolerance && floorValue + 1 > num)
			{
				return RoundingProtection.ForceDown;
			}
			return RoundingProtection.None;
		}
	}
	internal enum RoundingMode
	{
		Balanced,
		Standard,
		Truncate
	}
	internal static class NumericRounding
	{
		private const double IntegerSnapTolerancePerScale = 1E-05;

		internal static int GetScale(int decimalPlaces)
		{
			return decimalPlaces switch
			{
				0 => 1, 
				1 => 10, 
				2 => 100, 
				_ => throw new ArgumentOutOfRangeException("decimalPlaces"), 
			};
		}

		internal static double Scale(double value, int decimalPlaces)
		{
			int scale = GetScale(decimalPlaces);
			double num = value * (double)scale;
			double num2 = Math.Round(num, MidpointRounding.AwayFromZero);
			if (!(Math.Abs(num - num2) <= 1E-05 * (double)scale))
			{
				return num;
			}
			return num2;
		}

		internal static int RoundIndependent(double value, int decimalPlaces, RoundingMode mode)
		{
			double num = Scale(value, decimalPlaces);
			double num2 = ((mode == RoundingMode.Truncate) ? Math.Floor(num) : Math.Round(num, MidpointRounding.AwayFromZero));
			if (num2 < -2147483648.0 || num2 > 2147483647.0)
			{
				throw new ArgumentOutOfRangeException("value");
			}
			return (int)num2;
		}
	}
	[BepInPlugin("com.tactikot.StaminaStatsTactikot", "StaminaStatsTactikot", "1.3.7")]
	[BepInProcess("PEAK.exe")]
	[BepInIncompatibility("com.StaminaStats")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(StaminaBar), "Start")]
		private static class StaminaBarStartPatch
		{
			private static void Postfix(StaminaBar __instance)
			{
				try
				{
					HudController.AttachTo(__instance);
				}
				catch (Exception exception)
				{
					ReportOnce("StaminaBar.Start patch failed", exception);
				}
			}
		}

		[HarmonyPatch(typeof(GUIManager), "Start")]
		private static class GUIManagerStartPatch
		{
			private static void Postfix(GUIManager __instance)
			{
				try
				{
					HudController.AttachTo(__instance.bar);
				}
				catch (Exception exception)
				{
					ReportOnce("GUIManager.Start patch failed", exception);
				}
			}
		}

		public const string PluginGuid = "com.tactikot.StaminaStatsTactikot";

		public const string PluginName = "StaminaStatsTactikot";

		public const string PluginVersion = "1.3.7";

		private const int MaximumReportedErrors = 64;

		private static readonly HashSet<string> ReportedErrors = new HashSet<string>();

		private Harmony harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> ShowStamina { get; private set; }

		internal static ConfigEntry<bool> ShowBonusStamina { get; private set; }

		internal static ConfigEntry<bool> ShowAfflictions { get; private set; }

		internal static ConfigEntry<bool> ShowPercentageSign { get; private set; }

		internal static ConfigEntry<bool> ShowZeroValues { get; private set; }

		internal static ConfigEntry<RoundingMode> RoundingMode { get; private set; }

		internal static ConfigEntry<int> DecimalPlaces { get; private set; }

		internal static ConfigEntry<int> StaminaFontSize { get; private set; }

		internal static ConfigEntry<int> AfflictionFontSize { get; private set; }

		internal static ConfigEntry<float> TextOutlineThickness { get; private set; }

		internal static ConfigEntry<float> HudOpacity { get; private set; }

		internal static ConfigEntry<float> StaminaVerticalOffset { get; private set; }

		internal static ConfigEntry<float> AfflictionVerticalOffset { get; private set; }

		internal static ConfigEntry<int> MaxAfflictionLabelLanes { get; private set; }

		internal static ConfigEntry<float> AfflictionLaneTransitionDuration { get; private set; }

		internal static ConfigEntry<bool> HighContrastMode { get; private set; }

		internal static ConfigEntry<bool> FollowGameColorblindMode { get; private set; }

		internal static ConfigEntry<int> ValueRefreshRate { get; private set; }

		internal static ConfigEntry<bool> VerboseLogging { get; private set; }

		internal static int DisplaySettingsVersion { get; private set; }

		private void Awake()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			bool hadCurrentConfig = File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath);
			BindConfig();
			MigrateLegacyConfig(hadCurrentConfig);
			harmony = new Harmony("com.tactikot.StaminaStatsTactikot");
			harmony.PatchAll();
			Log.LogInfo((object)"StaminaStatsTactikot v1.3.7 loaded.");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			HudController.Cleanup();
		}

		private void BindConfig()
		{
			Enabled = BindDisplay("Enabled", defaultValue: true, "Enable the numeric HUD overlay.");
			ShowStamina = BindDisplay("ShowStamina", defaultValue: true, "Show current stamina.");
			ShowBonusStamina = BindDisplay("ShowBonusStamina", defaultValue: true, "Show bonus stamina independently of the main value.");
			ShowAfflictions = BindDisplay("ShowAfflictions", defaultValue: true, "Show values for visible affliction segments.");
			ShowPercentageSign = BindDisplay("ShowPercentageSign", defaultValue: false, "Show a percentage sign (%) after values.");
			ShowZeroValues = BindDisplay("ShowZeroValues", defaultValue: false, "Show values that round to zero.");
			RoundingMode = BindDisplay("RoundingMode", StaminaStatsTactikot.RoundingMode.Balanced, "Value rounding: Balanced preserves the combined total and favors stable rounding; Standard rounds independently; Truncate discards extra digits.");
			DecimalPlaces = BindDisplay("DecimalPlaces", 0, "Number of digits shown after the decimal point.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2));
			StaminaFontSize = BindDisplay("StaminaFontSize", 18, "Font size for stamina values.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 36));
			AfflictionFontSize = BindDisplay("AfflictionFontSize", 16, "Font size for affliction values.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 32));
			TextOutlineThickness = BindDisplay("TextOutlineThickness", 0.1f, "Text outline thickness. Set to 0 to disable the outline.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.3f));
			HudOpacity = BindDisplay("HudOpacity", 1f, "Opacity of numeric labels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 1f));
			StaminaVerticalOffset = BindDisplay("StaminaVerticalOffset", 25f, "Vertical offset of stamina labels relative to the native bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-80f, 120f));
			AfflictionVerticalOffset = BindDisplay("AfflictionVerticalOffset", -15f, "Vertical offset of affliction labels relative to their native segments.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-100f, 100f));
			MaxAfflictionLabelLanes = BindDisplay("MaxAfflictionLabelLanes", 3, "Maximum label rows used to prevent overlapping affliction values.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3));
			AfflictionLaneTransitionDuration = BindDisplay("AfflictionLaneTransitionDuration", 0.12f, "Seconds used to smoothly move labels between rows. Set to 0 for instant movement.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f));
			HighContrastMode = BindDisplay("HighContrastMode", defaultValue: false, "Lighten numeric labels further and use a stronger outline.");
			FollowGameColorblindMode = BindDisplay("FollowGameColorblindMode", defaultValue: true, "Use PEAK's HUD hues, including color-blind adjustments; dark shades are brightened for readability.");
			ValueRefreshRate = BindDisplay("ValueRefreshRate", 20, "Numeric value refreshes per second. Position and visibility still update every frame.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 60));
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Enable detailed diagnostic logging.");
		}

		private ConfigEntry<T> BindDisplay<T>(string key, T defaultValue, string description)
		{
			return BindDisplay(key, defaultValue, description, null);
		}

		private ConfigEntry<T> BindDisplay<T>(string key, T defaultValue, string description, AcceptableValueBase acceptableValues)
		{
			//IL_001a: 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_0020: Expected O, but got Unknown
			ConfigDescription val = ((acceptableValues == null) ? new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()) : new ConfigDescription(description, acceptableValues, Array.Empty<object>()));
			ConfigEntry<T> obj = ((BaseUnityPlugin)this).Config.Bind<T>("Display", key, defaultValue, val);
			obj.SettingChanged += OnDisplaySettingChanged;
			return obj;
		}

		private static void OnDisplaySettingChanged(object sender, EventArgs eventArgs)
		{
			DisplaySettingsVersion++;
		}

		private void MigrateLegacyConfig(bool hadCurrentConfig)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (hadCurrentConfig)
			{
				return;
			}
			string text = Path.Combine(Paths.ConfigPath, "com.StaminaStats.cfg");
			if (!File.Exists(text))
			{
				return;
			}
			try
			{
				ConfigFile val = new ConfigFile(text, false);
				ShowPercentageSign.Value = val.Bind<bool>("Display", "ShowPercentageSign", ShowPercentageSign.Value, (ConfigDescription)null).Value;
				StaminaFontSize.Value = val.Bind<int>("Display", "StaminaFontSize", StaminaFontSize.Value, (ConfigDescription)null).Value;
				AfflictionFontSize.Value = val.Bind<int>("Display", "AfflictionFontSize", AfflictionFontSize.Value, (ConfigDescription)null).Value;
				TextOutlineThickness.Value = val.Bind<float>("Display", "TextOutlineThickness", TextOutlineThickness.Value, (ConfigDescription)null).Value;
				VerboseLogging.Value = val.Bind<bool>("Debug", "VerboseLogging", VerboseLogging.Value, (ConfigDescription)null).Value;
				((BaseUnityPlugin)this).Config.Save();
				Log.LogInfo((object)"Migrated settings from com.StaminaStats.cfg.");
			}
			catch (Exception exception)
			{
				ReportOnce("Legacy config migration failed", exception);
			}
		}

		internal static void ReportOnce(string operation, Exception exception)
		{
			string item = operation + ":" + exception.GetType().FullName;
			if (ReportedErrors.Count >= 64 || !ReportedErrors.Add(item))
			{
				return;
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogError((object)(operation + ": " + exception.Message));
			}
			if (VerboseLogging != null && VerboseLogging.Value)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)exception);
				}
			}
		}
	}
	internal sealed class StaminaHudView : MonoBehaviour
	{
		private struct PresentationState
		{
			private HudValueKind kind;

			private int value;

			private bool percentage;

			private int decimalPlaces;

			private bool initialized;

			internal bool Matches(HudValuePresentation presentation, bool showPercentage, int displayedDecimalPlaces)
			{
				if (initialized && kind == presentation.Kind && value == presentation.Value && percentage == showPercentage)
				{
					return decimalPlaces == displayedDecimalPlaces;
				}
				return false;
			}

			internal void Set(HudValuePresentation presentation, bool showPercentage, int displayedDecimalPlaces)
			{
				kind = presentation.Kind;
				value = presentation.Value;
				percentage = showPercentage;
				decimalPlaces = displayedDecimalPlaces;
				initialized = true;
			}
		}

		private const int MaximumAfflictions = 32;

		private const float LabelGap = 1f;

		private const float LabelRowTolerance = 1f;

		private const float RoundingChangeTolerance = 0.0001f;

		private static readonly Color FallbackMainColor = new Color(0.72f, 1f, 0.72f);

		private static readonly Color FallbackBonusColor = new Color(1f, 0.92f, 0.25f);

		private readonly BarAffliction[] afflictions = (BarAffliction[])(object)new BarAffliction[32];

		private readonly RectTransform[] afflictionRects = (RectTransform[])(object)new RectTransform[32];

		private readonly Image[] afflictionImages = (Image[])(object)new Image[32];

		private readonly TextMeshProUGUI[] afflictionTexts = (TextMeshProUGUI[])(object)new TextMeshProUGUI[32];

		private readonly PresentationState[] afflictionPresentationStates = new PresentationState[32];

		private readonly int[] roundedAfflictionValues = new int[32];

		private readonly int[] previousRoundedByItem = new int[33];

		private readonly double[] roundingValues = new double[33];

		private readonly int[] roundingResults = new int[33];

		private readonly int[] previousRoundingResults = new int[33];

		private readonly double[] previousExactRoundingValues = new double[33];

		private readonly bool[] hasPreviousRoundingValues = new bool[33];

		private readonly int[] roundingItemIndexes = new int[33];

		private readonly MinimaxRounding.Remainder[] roundingWorkspace = new MinimaxRounding.Remainder[33];

		private readonly double[] previousExactByItem = new double[33];

		private readonly bool[] hasPreviousExactByItem = new bool[33];

		private readonly int[] activeAfflictionIndexes = new int[32];

		private readonly float[] labelCenters = new float[32];

		private readonly float[] labelWidths = new float[32];

		private readonly float[] labelBaseY = new float[32];

		private readonly int[] labelLanes = new int[32];

		private readonly int[] labelOrderWorkspace = new int[32];

		private readonly float[] laneRightWorkspace = new float[3];

		private readonly float[] currentLaneOffsets = new float[32];

		private readonly float[] laneOffsetVelocities = new float[32];

		private readonly bool[] laneOffsetInitialized = new bool[32];

		private StaminaBar bar;

		private Character previousObservedCharacter;

		private RectTransform overlayRoot;

		private TextMeshProUGUI staminaText;

		private TextMeshProUGUI bonusText;

		private Image staminaImage;

		private Image bonusImage;

		private Material sharedTextMaterial;

		private int afflictionCount;

		private int appliedSettingsVersion = -1;

		private RoundingMode appliedRoundingMode;

		private int appliedDecimalPlaces = -1;

		private float nextValueRefreshTime;

		private int previousActiveLabelCount = -1;

		private int previousLabelLaneCount;

		private bool initialized;

		private bool faulted;

		private PresentationState staminaPresentationState;

		private PresentationState bonusPresentationState;

		internal void Initialize(StaminaBar sourceBar)
		{
			if ((Object)(object)sourceBar == (Object)null)
			{
				return;
			}
			if (initialized)
			{
				int num = afflictionCount;
				CacheAfflictions();
				if (afflictionCount != num)
				{
					ApplySettings(force: true);
				}
				return;
			}
			bar = sourceBar;
			BuildView();
			CacheAfflictions();
			ApplySettings(force: true);
			initialized = true;
			nextValueRefreshTime = 0f;
			if (Plugin.VerboseLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"Attached numeric HUD to {((Object)bar).name} with {afflictionCount} affliction slots.");
			}
		}

		private void LateUpdate()
		{
			if (!initialized || faulted)
			{
				return;
			}
			try
			{
				Character observedCharacter = Character.observedCharacter;
				if (previousObservedCharacter != observedCharacter)
				{
					previousObservedCharacter = observedCharacter;
					ResetRoundingHistory();
					for (int i = 0; i < afflictionCount; i++)
					{
						ResetLaneTransition(i);
					}
					nextValueRefreshTime = 0f;
				}
				bool flag = ShouldBeVisible(observedCharacter);
				SetOverlayVisible(flag);
				if (flag)
				{
					ApplySettings(force: false);
					if (Time.unscaledTime >= nextValueRefreshTime)
					{
						UpdateValues(observedCharacter);
						nextValueRefreshTime = Time.unscaledTime + 1f / (float)Plugin.ValueRefreshRate.Value;
					}
					LayoutAfflictionLabels();
				}
			}
			catch (Exception exception)
			{
				faulted = true;
				SetOverlayVisible(visible: false);
				Plugin.ReportOnce("Stamina HUD disabled after an update failure", exception);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)sharedTextMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)sharedTextMaterial);
				sharedTextMaterial = null;
			}
			if ((Object)(object)overlayRoot != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)overlayRoot).gameObject);
				overlayRoot = null;
			}
		}

		private void BuildView()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Expected O, but got Unknown
			GameObject val = new GameObject("StaminaStatsTactikot", new Type[2]
			{
				typeof(RectTransform),
				typeof(Canvas)
			});
			val.layer = ((Component)bar).gameObject.layer;
			overlayRoot = (RectTransform)val.transform;
			((Transform)overlayRoot).SetParent(((Component)bar).transform, false);
			overlayRoot.anchorMin = Vector2.zero;
			overlayRoot.anchorMax = Vector2.one;
			overlayRoot.offsetMin = Vector2.zero;
			overlayRoot.offsetMax = Vector2.zero;
			((Transform)overlayRoot).localScale = Vector3.one;
			Canvas component = val.GetComponent<Canvas>();
			Canvas componentInParent = ((Component)bar).GetComponentInParent<Canvas>();
			component.overrideSorting = true;
			if ((Object)(object)componentInParent != (Object)null)
			{
				component.sortingLayerID = componentInParent.sortingLayerID;
				component.sortingOrder = componentInParent.sortingOrder + 1;
			}
			TMP_FontAsset val2 = (((Object)(object)bar.moraleBoostText != (Object)null) ? ((TMP_Text)bar.moraleBoostText).font : TMP_Settings.defaultFontAsset);
			Material val3 = (((Object)(object)bar.moraleBoostText != (Object)null) ? ((TMP_Text)bar.moraleBoostText).fontSharedMaterial : (((Object)(object)val2 != (Object)null) ? ((TMP_Asset)val2).material : null));
			if ((Object)(object)val3 != (Object)null)
			{
				sharedTextMaterial = new Material(val3)
				{
					name = "StaminaStatsTactikot Shared Text Material"
				};
			}
			staminaText = CreateText("StaminaValue", val2, (TextAlignmentOptions)1028);
			ConfigureRect(((TMP_Text)staminaText).rectTransform, new Vector2(1f, 0f), new Vector2(0f, 0.5f));
			bonusText = CreateText("BonusStaminaValue", val2, (TextAlignmentOptions)1025);
			ConfigureRect(((TMP_Text)bonusText).rectTransform, new Vector2(0f, 0f), new Vector2(0f, 0.5f));
			staminaImage = (((Object)(object)bar.staminaBar != (Object)null) ? ((Component)bar.staminaBar).GetComponent<Image>() : null);
			bonusImage = (((Object)(object)bar.extraBar != (Object)null) ? ((Component)bar.extraBar).GetComponent<Image>() : null);
		}

		private void CacheAfflictions()
		{
			int num = ((bar.afflictions != null) ? Mathf.Min(bar.afflictions.Length, 32) : 0);
			for (int i = 0; i < num; i++)
			{
				CacheAffliction(bar.afflictions[i]);
			}
			if ((Object)(object)bar.petrifyAffliction != (Object)null && !ContainsAffliction(bar.petrifyAffliction))
			{
				CacheAffliction(bar.petrifyAffliction);
			}
			BarAffliction[] array = bar.afflictions;
			if (((array != null) ? array.Length : 0) + (((Object)(object)bar.petrifyAffliction != (Object)null && !ContainsAffliction(bar.petrifyAffliction)) ? 1 : 0) > 32)
			{
				Plugin.Log.LogWarning((object)$"Only the first {32} affliction segments can be displayed.");
			}
		}

		private void CacheAffliction(BarAffliction affliction)
		{
			//IL_00c2: 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)
			if (!((Object)(object)affliction == (Object)null) && afflictionCount < 32 && !ContainsAffliction(affliction))
			{
				int num = afflictionCount++;
				afflictions[num] = affliction;
				afflictionRects[num] = (((Object)(object)affliction.rtf != (Object)null) ? affliction.rtf : ((Component)affliction).GetComponent<RectTransform>());
				afflictionImages[num] = (((Object)(object)affliction.icon != (Object)null) ? affliction.icon : ((Component)affliction).GetComponentInChildren<Image>());
				TextMeshProUGUI val = CreateText(((object)Unsafe.As<STATUSTYPE, STATUSTYPE>(ref affliction.afflictionType)/*cast due to .constrained prefix*/).ToString() + "Value", ((TMP_Text)staminaText).font, (TextAlignmentOptions)514);
				ConfigureRect(((TMP_Text)val).rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f));
				((Component)val).gameObject.SetActive(false);
				afflictionTexts[num] = val;
			}
		}

		private bool ContainsAffliction(BarAffliction candidate)
		{
			for (int i = 0; i < afflictionCount; i++)
			{
				if ((Object)(object)afflictions[i] == (Object)(object)candidate)
				{
					return true;
				}
			}
			return false;
		}

		private TextMeshProUGUI CreateText(string name, TMP_FontAsset font, TextAlignmentOptions alignment)
		{
			//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_0061: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)bar).gameObject.layer
			};
			val.transform.SetParent((Transform)(object)overlayRoot, false);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).font = font;
			((TMP_Text)obj).fontSharedMaterial = sharedTextMaterial;
			((TMP_Text)obj).alignment = alignment;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)0;
			((TMP_Text)obj).enableAutoSizing = false;
			((TMP_Text)obj).fontStyle = (FontStyles)1;
			((TMP_Text)obj).text = string.Empty;
			return obj;
		}

		private static void ConfigureRect(RectTransform rect, Vector2 pivot, Vector2 anchor)
		{
			//IL_0001: 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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchor;
			rect.anchorMax = anchor;
			rect.pivot = pivot;
			rect.sizeDelta = new Vector2(40f, 24f);
		}

		private bool ShouldBeVisible(Character observed)
		{
			if (!Plugin.Enabled.Value || (Object)(object)bar == (Object)null || !((Component)bar).gameObject.activeInHierarchy || (Object)(object)observed == (Object)null || (Object)(object)observed.data == (Object)null || observed.data.dead || observed.data.fullyPassedOut)
			{
				return false;
			}
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.bar != (Object)(object)bar)
			{
				return false;
			}
			if ((Object)(object)instance.hudCanvas != (Object)null && !((Behaviour)instance.hudCanvas).enabled)
			{
				return false;
			}
			if (!((Object)(object)instance.staminaCanvasGroup == (Object)null))
			{
				return instance.staminaCanvasGroup.alpha > 0.01f;
			}
			return true;
		}

		private void ApplySettings(bool force)
		{
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			if (!force && appliedSettingsVersion == Plugin.DisplaySettingsVersion)
			{
				return;
			}
			appliedSettingsVersion = Plugin.DisplaySettingsVersion;
			RoundingMode value = Plugin.RoundingMode.Value;
			int value2 = Plugin.DecimalPlaces.Value;
			if (value != appliedRoundingMode || value2 != appliedDecimalPlaces)
			{
				ResetRoundingHistory();
				appliedRoundingMode = value;
				appliedDecimalPlaces = value2;
			}
			((TMP_Text)staminaText).fontSize = Plugin.StaminaFontSize.Value;
			((TMP_Text)bonusText).fontSize = Plugin.StaminaFontSize.Value;
			for (int i = 0; i < afflictionCount; i++)
			{
				((TMP_Text)afflictionTexts[i]).fontSize = Plugin.AfflictionFontSize.Value;
			}
			float num = (Plugin.HighContrastMode.Value ? Mathf.Max(0.16f, Plugin.TextOutlineThickness.Value) : Plugin.TextOutlineThickness.Value);
			if ((Object)(object)sharedTextMaterial != (Object)null)
			{
				ShaderUtilities.GetShaderPropertyIDs();
				if (sharedTextMaterial.HasProperty(ShaderUtilities.ID_OutlineWidth))
				{
					sharedTextMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, num);
				}
				if (sharedTextMaterial.HasProperty(ShaderUtilities.ID_OutlineColor))
				{
					sharedTextMaterial.SetColor(ShaderUtilities.ID_OutlineColor, Color.black);
				}
				if (sharedTextMaterial.HasProperty(ShaderUtilities.ID_FaceColor))
				{
					sharedTextMaterial.SetColor(ShaderUtilities.ID_FaceColor, Color.white);
				}
			}
			UpdateMeshPadding();
			UpdateStaminaLayout();
			UpdateColors();
			nextValueRefreshTime = 0f;
		}

		private void UpdateMeshPadding()
		{
			((TMP_Text)staminaText).UpdateMeshPadding();
			((TMP_Text)bonusText).UpdateMeshPadding();
			for (int i = 0; i < afflictionCount; i++)
			{
				((TMP_Text)afflictionTexts[i]).UpdateMeshPadding();
			}
		}

		private void UpdateStaminaLayout()
		{
			float height = (float)Plugin.StaminaFontSize.Value + 8f;
			SetRect(((TMP_Text)staminaText).rectTransform, 20f, Plugin.StaminaVerticalOffset.Value, 72f, height);
			SetRect(((TMP_Text)bonusText).rectTransform, 26f, Plugin.StaminaVerticalOffset.Value, 72f, height);
		}

		private void UpdateValues(Character observed)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			int value = Plugin.DecimalPlaces.Value;
			int scale = NumericRounding.GetScale(value);
			RoundingMode value2 = Plugin.RoundingMode.Value;
			float value3 = Mathf.Max(0f, observed.data.currentStamina * 100f);
			float num = Mathf.Max(0f, observed.data.extraStamina * 100f);
			bool flag = observed.refs != null && (Object)(object)observed.refs.afflictions != (Object)null;
			int count = 0;
			if (Plugin.ShowStamina.Value)
			{
				AddRoundingValue(ref count, 0, value3, value);
			}
			else
			{
				ClearRoundingItem(0);
			}
			for (int i = 0; i < afflictionCount; i++)
			{
				BarAffliction val = afflictions[i];
				bool num2 = Plugin.ShowAfflictions.Value && flag && (Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy;
				float value4 = 0f;
				if (num2)
				{
					value4 = (val.isPetrify ? Mathf.Max(0f, (float)observed.data.petrifyAmount) : Mathf.Max(0f, observed.refs.afflictions.GetCurrentStatus(val.afflictionType) * 100f));
				}
				if (num2)
				{
					AddRoundingValue(ref count, i + 1, value4, value);
					continue;
				}
				roundedAfflictionValues[i] = 0;
				ClearRoundingItem(i + 1);
			}
			if (count > 0)
			{
				if (value2 == RoundingMode.Balanced)
				{
					MinimaxRounding.RoundInto(roundingValues, count, roundingResults, roundingWorkspace, previousRoundingResults, previousExactRoundingValues, hasPreviousRoundingValues, 0.0001f * (float)scale, stable: true);
				}
				else
				{
					for (int j = 0; j < count; j++)
					{
						roundingResults[j] = ((value2 == RoundingMode.Truncate) ? ((int)Math.Floor(roundingValues[j])) : ((int)Math.Round(roundingValues[j], MidpointRounding.AwayFromZero)));
					}
				}
				for (int k = 0; k < count; k++)
				{
					int num3 = roundingItemIndexes[k];
					int num4 = roundingResults[k];
					previousRoundedByItem[num3] = num4;
					if (num3 > 0)
					{
						roundedAfflictionValues[num3 - 1] = num4;
					}
				}
			}
			HudValuePresentation presentation = HudValuePresentation.Create(Plugin.ShowStamina.Value ? previousRoundedByItem[0] : 0, Plugin.ShowStamina.Value, sourceVisible: true, Plugin.ShowZeroValues.Value);
			ApplyPresentation(staminaText, presentation, bonus: false, value, ref staminaPresentationState);
			RoundingMode mode = ((value2 == RoundingMode.Balanced) ? RoundingMode.Standard : value2);
			HudValuePresentation presentation2 = HudValuePresentation.Create(NumericRounding.RoundIndependent(num, value, mode), Plugin.ShowBonusStamina.Value, sourceVisible: true, Plugin.ShowZeroValues.Value);
			ApplyPresentation(bonusText, presentation2, bonus: true, value, ref bonusPresentationState);
			for (int l = 0; l < afflictionCount; l++)
			{
				BarAffliction val2 = afflictions[l];
				bool sourceVisible = flag && (Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy;
				HudValuePresentation presentation3 = HudValuePresentation.Create(roundedAfflictionValues[l], Plugin.ShowAfflictions.Value, sourceVisible, Plugin.ShowZeroValues.Value);
				ApplyPresentation(afflictionTexts[l], presentation3, bonus: false, value, ref afflictionPresentationStates[l]);
			}
			UpdateColors();
		}

		private void AddRoundingValue(ref int count, int itemIndex, float value, int decimalPlaces)
		{
			double num = NumericRounding.Scale(value, decimalPlaces);
			roundingValues[count] = num;
			roundingItemIndexes[count] = itemIndex;
			previousRoundingResults[count] = previousRoundedByItem[itemIndex];
			previousExactRoundingValues[count] = previousExactByItem[itemIndex];
			hasPreviousRoundingValues[count] = hasPreviousExactByItem[itemIndex];
			previousExactByItem[itemIndex] = num;
			hasPreviousExactByItem[itemIndex] = true;
			count++;
		}

		private void ClearRoundingItem(int itemIndex)
		{
			previousRoundedByItem[itemIndex] = 0;
			previousExactByItem[itemIndex] = 0.0;
			hasPreviousExactByItem[itemIndex] = false;
		}

		private void ResetRoundingHistory()
		{
			Array.Clear(previousRoundedByItem, 0, previousRoundedByItem.Length);
			Array.Clear(previousExactByItem, 0, previousExactByItem.Length);
			Array.Clear(hasPreviousExactByItem, 0, hasPreviousExactByItem.Length);
		}

		private static void ApplyPresentation(TextMeshProUGUI text, HudValuePresentation presentation, bool bonus, int decimalPlaces, ref PresentationState state)
		{
			bool isVisible = presentation.IsVisible;
			if (((Component)text).gameObject.activeSelf != isVisible)
			{
				((Component)text).gameObject.SetActive(isVisible);
			}
			bool value = Plugin.ShowPercentageSign.Value;
			if (!isVisible)
			{
				state.Set(presentation, value, decimalPlaces);
			}
			else if (!state.Matches(presentation, value, decimalPlaces))
			{
				float num = (float)presentation.Value / (float)NumericRounding.GetScale(decimalPlaces);
				switch (decimalPlaces)
				{
				case 0:
					((TMP_Text)text).SetText((!value) ? (bonus ? "+{0:0}" : "{0:0}") : (bonus ? "+{0:0}%" : "{0:0}%"), num);
					break;
				case 1:
					((TMP_Text)text).SetText((!value) ? (bonus ? "+{0:0.0}" : "{0:0.0}") : (bonus ? "+{0:0.0}%" : "{0:0.0}%"), num);
					break;
				case 2:
					((TMP_Text)text).SetText((!value) ? (bonus ? "+{0:0.00}" : "{0:0.00}") : (bonus ? "+{0:0.00}%" : "{0:0.00}%"), num);
					break;
				default:
					throw new ArgumentOutOfRangeException("decimalPlaces");
				}
				state.Set(presentation, value, decimalPlaces);
			}
		}

		private void UpdateColors()
		{
			//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_0028: 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)
			//IL_004d: 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)
			((Graphic)staminaText).color = ResolveTextColor(staminaImage, FallbackMainColor, 0);
			((Graphic)bonusText).color = ResolveTextColor(bonusImage, FallbackBonusColor, 1);
			for (int i = 0; i < afflictionCount; i++)
			{
				((Graphic)afflictionTexts[i]).color = ResolveTextColor(afflictionImages[i], CreateFallbackStatusColor(i), i + 2);
			}
		}

		private static Color ResolveTextColor(Image image, Color fallback, int index)
		{
			//IL_000d: 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_0012: 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)
			//IL_0065: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Color val = (((Object)(object)image != (Object)null) ? ((Graphic)image).color : fallback);
			if (!Plugin.FollowGameColorblindMode.Value)
			{
				float num2 = default(float);
				float num3 = default(float);
				float num = default(float);
				Color.RGBToHSV(val, ref num, ref num2, ref num3);
				num = Mathf.Repeat(num + (float)index * 0.011f, 1f);
				val = Color.HSVToRGB(num, Mathf.Clamp01(num2 * 1.15f), Mathf.Max(0.8f, num3));
			}
			float whiteBlend = HudTextColor.GetWhiteBlend(val.r, val.g, val.b, Plugin.HighContrastMode.Value);
			val = Color.Lerp(val, Color.white, whiteBlend);
			val.a = Plugin.HudOpacity.Value;
			return val;
		}

		private static Color CreateFallbackStatusColor(int index)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return Color.HSVToRGB(Mathf.Repeat((float)index * 0.618034f, 1f), 0.68f, 1f);
		}

		private void LayoutAfflictionLabels()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_00d6: 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_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int value = Plugin.MaxAfflictionLabelLanes.Value;
			bool flag = value != previousLabelLaneCount;
			for (int i = 0; i < afflictionCount; i++)
			{
				TextMeshProUGUI val = afflictionTexts[i];
				RectTransform val2 = afflictionRects[i];
				BarAffliction val3 = afflictions[i];
				if ((Object)(object)val3 == (Object)null || !((Component)val3).gameObject.activeInHierarchy)
				{
					ResetLaneTransition(i);
					if (((Component)val).gameObject.activeSelf)
					{
						((Component)val).gameObject.SetActive(false);
					}
					continue;
				}
				if (!((Component)val).gameObject.activeSelf || (Object)(object)val2 == (Object)null)
				{
					ResetLaneTransition(i);
					continue;
				}
				Rect rect = val2.rect;
				Vector3 val4 = ((Transform)val2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center));
				Vector3 val5 = ((Transform)overlayRoot).InverseTransformPoint(val4);
				float num2 = Mathf.Max(1f, ((TMP_Text)val).preferredWidth);
				flag |= activeAfflictionIndexes[num] != i || labelCenters[num] != val5.x || labelBaseY[num] != val5.y || labelWidths[num] != num2;
				activeAfflictionIndexes[num] = i;
				labelCenters[num] = val5.x;
				labelBaseY[num] = val5.y;
				labelWidths[num] = num2;
				num++;
			}
			flag |= num != previousActiveLabelCount;
			previousActiveLabelCount = num;
			previousLabelLaneCount = value;
			if (num == 0)
			{
				return;
			}
			if (flag)
			{
				HudLabelRowLayout.AssignLanes(labelCenters, labelWidths, labelBaseY, num, value, 1f, 1f, labelLanes, labelOrderWorkspace, laneRightWorkspace);
			}
			float height = (float)Plugin.AfflictionFontSize.Value + 8f;
			float num3 = HudLabelLayout.CalculateLaneStep(Plugin.AfflictionFontSize.Value);
			float num4 = ((Plugin.AfflictionVerticalOffset.Value <= 0f) ? (-1f) : 1f);
			float value2 = Plugin.AfflictionLaneTransitionDuration.Value;
			for (int j = 0; j < num; j++)
			{
				int num5 = activeAfflictionIndexes[j];
				float num6 = num4 * (float)labelLanes[j] * num3;
				if (!laneOffsetInitialized[num5] || value2 <= 0f)
				{
					currentLaneOffsets[num5] = num6;
					laneOffsetVelocities[num5] = 0f;
					laneOffsetInitialized[num5] = true;
				}
				else
				{
					currentLaneOffsets[num5] = Mathf.SmoothDamp(currentLaneOffsets[num5], num6, ref laneOffsetVelocities[num5], value2, float.PositiveInfinity, Time.unscaledDeltaTime);
					if (Mathf.Abs(currentLaneOffsets[num5] - num6) <= 0.01f)
					{
						currentLaneOffsets[num5] = num6;
						laneOffsetVelocities[num5] = 0f;
					}
				}
				float y = labelBaseY[j] + Plugin.AfflictionVerticalOffset.Value + currentLaneOffsets[num5];
				SetRect(((TMP_Text)afflictionTexts[num5]).rectTransform, labelCenters[j], y, Mathf.Clamp(labelWidths[j] + 8f, 32f, 88f), height);
			}
		}

		private void ResetLaneTransition(int index)
		{
			currentLaneOffsets[index] = 0f;
			laneOffsetVelocities[index] = 0f;
			laneOffsetInitialized[index] = false;
		}

		private static void SetRect(RectTransform rect, float x, float y, float width, float height)
		{
			//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_0007: 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_001b: 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_0042: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			Vector2 anchoredPosition = rect.anchoredPosition;
			if (Mathf.Abs(anchoredPosition.x - x) > 0.01f || Mathf.Abs(anchoredPosition.y - y) > 0.01f)
			{
				rect.anchoredPosition = new Vector2(x, y);
			}
			Vector2 sizeDelta = rect.sizeDelta;
			if (Mathf.Abs(sizeDelta.x - width) > 0.01f || Mathf.Abs(sizeDelta.y - height) > 0.01f)
			{
				rect.sizeDelta = new Vector2(width, height);
			}
		}

		private void SetOverlayVisible(bool visible)
		{
			if ((Object)(object)overlayRoot != (Object)null && ((Component)overlayRoot).gameObject.activeSelf != visible)
			{
				((Component)overlayRoot).gameObject.SetActive(visible);
			}
		}
	}
}