Decompiled source of BetterInfoUI v0.0.1

plugins/BetterInfoUI.dll

Decompiled a day ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using BetterInfoUI.Common;
using BetterInfoUI.Configuration;
using BetterInfoUI.Framework;
using BetterInfoUI.Hooks;
using BetterInfoUI.Models;
using BetterInfoUI.Services.Game;
using BetterInfoUI.Services.Infrastructure;
using BetterInfoUI.Services.UI;
using BetterInfoUI.Trackers;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("NastyaLove")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: AssemblyInformationalVersion("0.0.1+13fd3dc0b4ff98bf2ed41fb8481b7da91173fa83")]
[assembly: AssemblyProduct("BetterInfoUI")]
[assembly: AssemblyTitle("BetterInfoUI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BetterInfoUI
{
	internal static class Core
	{
		private static bool _hasInitialized;

		public static FrameMonitor FrameMonitor;

		public static FramePerformanceData LastFramePerformance;

		public static volatile bool IsFrameMonitoringEnabled = true;

		public static bool IsDebug { get; private set; }

		public static ManualLogSource Logger => Plugin.Logger;

		public static ConfigService Config { get; private set; }

		public static void Initialize()
		{
			if (!_hasInitialized)
			{
				IsDebug = typeof(Plugin).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()?.Configuration == "Debug";
				LoggerService.Create().Initialize(splitLogMode: true);
				GameContext.Initialize();
				Config = new ConfigService().Initialize();
				GameContext.RegisterSingleton(Config);
				GameContext.RegisterSingleton(new CharacterService());
				GameContext.RegisterSingleton(new FontService());
				GameContext.RegisterSingleton(new AfflictionService());
				GameContext.RegisterSingleton(new StaminaUIService(GameContext.Get<ConfigService>(), GameContext.Get<FontService>()));
				GameContext.RegisterSingleton(new AfflictionUIService(GameContext.Get<ConfigService>(), GameContext.Get<FontService>()));
				GameContext.RegisterSingleton(new RunLifecycleService(GameContext.Get<ConfigService>(), GameContext.Get<AfflictionUIService>()).Initialize());
				StaminaBarUpdatePatch.Initialize();
				StaminaBarStartPatch.Initialize();
				BarAfflictionUpdatePatch.Initialize();
				StaminaBarChangeBarPatch.Initialize();
				TaskRunner.Initialize();
				GameFrame.Initialize();
				if (IsFrameMonitoringEnabled)
				{
					FrameMonitor = new FrameMonitor(75L, 150L);
					LoggerService.Info($"Frame monitoring enabled: LagThreshold={75}ms, CriticalLag={150}ms", "Core");
				}
				LoggerService.Info("Core initialized! Version: v" + Plugin.Version, "Core", printToConsole: true);
				_hasInitialized = true;
			}
		}
	}
	public class GameFrame : MonoBehaviour
	{
		public delegate void GameFrameUpdateEventHandler();

		private static GameFrame _instance;

		public static event GameFrameUpdateEventHandler OnUpdate;

		public static event GameFrameUpdateEventHandler OnLateUpdate;

		public void Awake()
		{
			LoggerService.Debug("Awake called", "GameFrame", printToConsole: true);
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		public void Update()
		{
			try
			{
				if (Core.IsFrameMonitoringEnabled)
				{
					Core.FrameMonitor.StartFrame();
				}
				GameFrame.OnUpdate?.Invoke();
			}
			catch (Exception arg)
			{
				LoggerService.Error($"Error dispatching OnUpdate event:\n{arg}", "GameFrame", printToConsole: true);
			}
		}

		public void LateUpdate()
		{
			try
			{
				GameFrame.OnLateUpdate?.Invoke();
				if (Core.IsFrameMonitoringEnabled)
				{
					Core.LastFramePerformance = Core.FrameMonitor.EndFrame();
					if (Core.LastFramePerformance.Type == FrameType.CriticalLag)
					{
						LoggerService.Warning($"Critical lag detected: {Core.LastFramePerformance.DelayMs}ms delay " + $"(frame time: {Core.LastFramePerformance.FrameTime}ms)", "TaskRunner");
					}
				}
			}
			catch (Exception arg)
			{
				LoggerService.Error($"Error dispatching OnLateUpdate event:\n{arg}", "GameFrame", printToConsole: true);
			}
		}

		public static void Initialize()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			LoggerService.Debug("Creating GameFrame instance...", "GameFrame");
			_instance = new GameObject("BetterInfoUI_GameFrame").AddComponent<GameFrame>();
			LoggerService.Info($"GameFrame instance created: {(Object)(object)_instance != (Object)null}", "GameFrame");
			if (!((Object)(object)_instance == (Object)null))
			{
				LoggerService.Info("GameFrame instance name: " + ((Object)((Component)_instance).gameObject).name, "GameFrame");
				LoggerService.Info($"GameFrame instance type: {((object)_instance).GetType()}", "GameFrame");
			}
		}

		public static void DestroyInstance()
		{
			Core.Logger.LogInfo((object)"[GameFrame]: Destroying instance...");
			if (GameFrame.OnUpdate != null)
			{
				Delegate[] invocationList = GameFrame.OnUpdate.GetInvocationList();
				int num = invocationList.Length;
				Delegate[] array = invocationList;
				for (int i = 0; i < array.Length; i++)
				{
					OnUpdate -= (GameFrameUpdateEventHandler)array[i];
				}
				Core.Logger.LogDebug((object)string.Format("[{0}]: OnUpdate subscribers removed: {1}", "GameFrame", num));
				GameFrame.OnUpdate = null;
			}
			if (GameFrame.OnLateUpdate != null)
			{
				Delegate[] invocationList2 = GameFrame.OnLateUpdate.GetInvocationList();
				int num2 = invocationList2.Length;
				Delegate[] array = invocationList2;
				for (int i = 0; i < array.Length; i++)
				{
					OnLateUpdate -= (GameFrameUpdateEventHandler)array[i];
				}
				Core.Logger.LogInfo((object)string.Format("[{0}]: OnLateUpdate subscribers removed: {1}", "GameFrame", num2));
				GameFrame.OnLateUpdate = null;
			}
			if ((Object)(object)_instance == (Object)null)
			{
				Core.Logger.LogInfo((object)"[GameFrame]: Instance is null, nothing to destroy");
				return;
			}
			Core.Logger.LogInfo((object)("[GameFrame]: Destroying instance: " + ((Object)((Component)_instance).gameObject).name));
			Object.Destroy((Object)(object)((Component)_instance).gameObject);
			_instance = null;
			Core.Logger.LogInfo((object)"[GameFrame]: Instance destroyed");
		}
	}
	[BepInPlugin("BetterInfoUI", "BetterInfoUI", "0.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("BetterInfoUI");

		internal static ManualLogSource Logger;

		public const string Id = "BetterInfoUI";

		internal static Plugin Instance { get; private set; }

		public static string Name => "BetterInfoUI";

		public static string Version => "0.0.1";

		public void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin BetterInfoUI is loaded!");
			Core.Initialize();
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
		}

		public void OnDestroy()
		{
			if (GameContext.TryGet<RunLifecycleService>(out var service))
			{
				service.Dispose();
			}
			if (GameContext.TryGet<StaminaUIService>(out var service2))
			{
				service2.Cleanup();
			}
			if (GameContext.TryGet<AfflictionUIService>(out var service3))
			{
				service3.Cleanup();
			}
		}
	}
}
namespace BetterInfoUI.Utils
{
	public class TimeUtility
	{
		public static string GetNumWithTextSuffix(long num, string oneSfx, string twoSfx, string fiveSfx, string sep = " ")
		{
			string text2 = new string(num.ToString().Reverse().ToArray());
			if (text2.Length > 1 && text2[1] == '1')
			{
				return Result(num, fiveSfx);
			}
			switch (text2[0])
			{
			case '1':
				return Result(num, oneSfx);
			case '2':
			case '3':
			case '4':
				return Result(num, twoSfx);
			default:
				return Result(num, fiveSfx);
			}
			string Result(long number, string text)
			{
				return $"{number}{sep}{text}";
			}
		}
	}
}
namespace BetterInfoUI.Trackers
{
	public sealed class AfflictionTextTracker : MonoBehaviour
	{
		private STATUSTYPE _statusType;

		private BarAffliction _affliction;

		private RectTransform _textRect;

		private float _verticalOffset;

		public void Initialize(BarAffliction affliction, float verticalOffset)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			_affliction = affliction;
			_verticalOffset = verticalOffset;
			_textRect = ((Component)this).GetComponent<RectTransform>();
			_statusType = affliction.afflictionType;
		}

		public void UpdateAffliction(BarAffliction affliction)
		{
			//IL_0013: 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)
			if (!((Object)(object)affliction == (Object)null))
			{
				_affliction = affliction;
				_statusType = affliction.afflictionType;
			}
		}

		public void UpdateWidth(float width)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_textRect != (Object)null)
			{
				_textRect.sizeDelta = new Vector2(width, _textRect.sizeDelta.y);
			}
		}

		private void LateUpdate()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_affliction) || !Object.op_Implicit((Object)(object)_textRect))
			{
				return;
			}
			RectTransform component = ((Component)_affliction).GetComponent<RectTransform>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			Vector3 position = ((Transform)component).position;
			((Transform)_textRect).position = new Vector3(position.x, position.y + _verticalOffset, ((Transform)_textRect).position.z);
			float num = 0f;
			if (Object.op_Implicit((Object)(object)Character.observedCharacter))
			{
				num = Character.observedCharacter.refs.afflictions.GetCurrentStatus(_statusType) * 100f;
			}
			TextMeshProUGUI val = default(TextMeshProUGUI);
			if (num > 0f && ((Component)this).TryGetComponent<TextMeshProUGUI>(ref val))
			{
				ConfigService config = Core.Config;
				string text = ((config != null && (config.Current?.ShowPercentageSign).GetValueOrDefault()) ? $"{num:F0}%" : $"{num:F0}");
				if (((TMP_Text)val).text != text)
				{
					((TMP_Text)val).text = text;
				}
			}
			bool flag = num > 0f;
			if (((Component)this).gameObject.activeSelf != flag)
			{
				((Component)this).gameObject.SetActive(flag);
			}
		}
	}
}
namespace BetterInfoUI.Services.UI
{
	public sealed class AfflictionUIService
	{
		[CompilerGenerated]
		private ConfigService <configService>P;

		[CompilerGenerated]
		private FontService <fontService>P;

		private const TextAlignmentOptions AfflictionAlignment = 514;

		private readonly Dictionary<STATUSTYPE, TextMeshProUGUI> _texts;

		private Canvas _canvas;

		public AfflictionUIService(ConfigService configService, FontService fontService)
		{
			<configService>P = configService;
			<fontService>P = fontService;
			_texts = new Dictionary<STATUSTYPE, TextMeshProUGUI>();
			base..ctor();
		}

		public void HandleAfflictionUpdate(BarAffliction affliction, StaminaBar staminaBar)
		{
			//IL_0104: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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)
			try
			{
				if (!AfflictionService.TryGetModel(affliction, out var model) || (!model.IsActive && !_texts.ContainsKey(model.Type)))
				{
					return;
				}
				TextMeshProUGUI val = EnsureText(model.Type, affliction);
				if (!((Object)(object)val == (Object)null))
				{
					PluginConfig current = <configService>P.Current;
					((TMP_Text)val).fontSize = current.AfflictionFontSize;
					((TMP_Text)val).outlineWidth = current.TextOutlineThickness;
					((TMP_Text)val).text = (current.ShowPercentageSign ? $"{model.ValuePercent:F0}%" : $"{model.ValuePercent:F0}");
					((Graphic)val).color = ResolveAfflictionColor(model.Type, affliction);
					bool flag = model.ValuePercent > 0f;
					if (((Component)val).gameObject.activeSelf != flag)
					{
						((Component)val).gameObject.SetActive(flag);
					}
				}
			}
			catch (Exception ex)
			{
				LoggerService.Error($"Error updating affliction text for {affliction?.afflictionType}: {ex.Message}", "AfflictionUIService", <configService>P.Current.VerboseLogging);
			}
		}

		public void HandleBarChanged(StaminaBar staminaBar)
		{
			//IL_0038: 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_0056: 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)
			try
			{
				if ((Object)(object)staminaBar == (Object)null || (Object)(object)Character.observedCharacter == (Object)null)
				{
					return;
				}
				foreach (STATUSTYPE value2 in Enum.GetValues(typeof(STATUSTYPE)))
				{
					if (AfflictionService.TryGetValue(value2, out var value) && !(value <= 0f) && !_texts.ContainsKey(value2) && AfflictionService.TryFindAffliction(staminaBar, value2, out var affliction))
					{
						HandleAfflictionUpdate(affliction, staminaBar);
					}
				}
			}
			catch (Exception ex)
			{
				LoggerService.Error("Error processing StaminaBar.ChangeBar: " + ex.Message, "AfflictionUIService", <configService>P.Current.VerboseLogging);
			}
		}

		public void HandleBarStart(StaminaBar staminaBar)
		{
			if (staminaBar?.afflictions != null && staminaBar.afflictions.Length != 0 && _texts.Count != 0)
			{
				LoggerService.Info("StaminaBar.Start detected, resetting affliction UI cache", "AfflictionUIService", <configService>P.Current.VerboseLogging);
				Cleanup();
			}
		}

		public void Cleanup()
		{
			if (_texts.Count > 0)
			{
				LoggerService.Info($"Cleaning up {_texts.Count} affliction text elements", "AfflictionUIService", <configService>P.Current.VerboseLogging);
			}
			foreach (TextMeshProUGUI item in _texts.Values.Where((TextMeshProUGUI text) => (Object)(object)text != (Object)null && (Object)(object)((Component)text).gameObject != (Object)null))
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			_texts.Clear();
			if ((Object)(object)_canvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvas).gameObject);
			}
			_canvas = null;
		}

		private TextMeshProUGUI EnsureText(STATUSTYPE type, BarAffliction affliction)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if (_texts.TryGetValue(type, out var value) && (Object)(object)value != (Object)null)
			{
				UpdateTracker(value, affliction);
				return value;
			}
			<fontService>P.EnsureResolved();
			EnsureCanvas();
			GameObject val = new GameObject($"{type}Text");
			val.transform.SetParent(((Component)_canvas).transform, false);
			value = val.AddComponent<TextMeshProUGUI>();
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.sizeDelta = new Vector2(GetWidth(affliction), 24f);
			SetupTextStyle(value);
			((Graphic)value).raycastTarget = false;
			val.AddComponent<AfflictionTextTracker>().Initialize(affliction, -22f);
			_texts[type] = value;
			LoggerService.Info($"Created affliction text element for {type}", "AfflictionUIService", <configService>P.Current.VerboseLogging);
			return value;
		}

		private void UpdateTracker(TextMeshProUGUI text, BarAffliction affliction)
		{
			AfflictionTextTracker component = ((Component)text).GetComponent<AfflictionTextTracker>();
			if (!((Object)(object)component == (Object)null))
			{
				component.UpdateWidth(GetWidth(affliction));
				component.UpdateAffliction(affliction);
			}
		}

		private void EnsureCanvas()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_canvas != (Object)null))
			{
				GameObject val = new GameObject("AfflictionTextCanvas");
				Object.DontDestroyOnLoad((Object)(object)val);
				_canvas = val.AddComponent<Canvas>();
				_canvas.renderMode = (RenderMode)0;
				_canvas.sortingOrder = 100;
				CanvasScaler obj = val.AddComponent<CanvasScaler>();
				obj.uiScaleMode = (ScaleMode)1;
				obj.referenceResolution = Constants.Ui.Affliction.CanvasReferenceResolution;
				obj.screenMatchMode = (ScreenMatchMode)0;
				val.AddComponent<GraphicRaycaster>();
				val.layer = LayerMask.NameToLayer("UI");
				LoggerService.Info("Created top-level affliction canvas", "AfflictionUIService", <configService>P.Current.VerboseLogging);
			}
		}

		private void SetupTextStyle(TextMeshProUGUI text)
		{
			//IL_001a: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)text).alignment = (TextAlignmentOptions)514;
			((TMP_Text)text).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)text).overflowMode = (TextOverflowModes)0;
			((Graphic)text).color = Color.white;
			((TMP_Text)text).enableAutoSizing = false;
			((TMP_Text)text).fontStyle = (FontStyles)1;
			((TMP_Text)text).faceColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
			if ((Object)(object)<fontService>P.Font != (Object)null)
			{
				((TMP_Text)text).font = <fontService>P.Font;
			}
			if ((Object)(object)<fontService>P.Material != (Object)null)
			{
				((Graphic)text).material = <fontService>P.Material;
			}
			Shadow obj = ((Component)text).gameObject.AddComponent<Shadow>();
			obj.effectColor = new Color(0f, 0f, 0f, 0.95f);
			obj.effectDistance = new Vector2(2f, -2f);
		}

		private static float GetWidth(BarAffliction affliction)
		{
			if (!((Object)(object)affliction == (Object)null))
			{
				return Mathf.Max(40f, affliction.width * 0.8f);
			}
			return 40f;
		}

		private static Color ResolveAfflictionColor(STATUSTYPE type, BarAffliction affliction)
		{
			//IL_0005: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (Colors.AfflictionByStatus.TryGetValue(type, out var value))
			{
				return value;
			}
			Image componentInChildren = ((Component)affliction).GetComponentInChildren<Image>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return Color.white;
			}
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(((Graphic)componentInChildren).color, ref num, ref num2, ref num3);
			return Color.HSVToRGB(num, Mathf.Clamp01(num2 * 1.2f), Mathf.Clamp01(num3 * 1.3f));
		}
	}
	public sealed class FontService
	{
		private bool _hasResolved;

		public TMP_FontAsset Font { get; private set; }

		public Material Material { get; private set; }

		public void EnsureResolved()
		{
			if (_hasResolved)
			{
				return;
			}
			_hasResolved = true;
			TextMeshProUGUI[] array = Resources.FindObjectsOfTypeAll<TextMeshProUGUI>();
			foreach (TextMeshProUGUI val in array)
			{
				if (!(((Object)val).name != "InteractNameText") || !(((Object)val).name != "InteractPromptText") || !(((Object)val).name != "ItemPromptMain"))
				{
					Font = ((TMP_Text)val).font;
					Material = ((Graphic)val).material;
					return;
				}
			}
			TMP_FontAsset[] array2 = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			if (array2.Length != 0)
			{
				Font = array2[0];
			}
		}
	}
	public sealed class StaminaUIService
	{
		[CompilerGenerated]
		private ConfigService <configService>P;

		[CompilerGenerated]
		private FontService <fontService>P;

		private const TextAlignmentOptions StaminaAlignment = 516;

		private const TextAlignmentOptions ExtraAlignment = 513;

		private TextMeshProUGUI _extraStaminaText;

		private TextMeshProUGUI _staminaText;

		private GameObject _container;

		public StaminaUIService(ConfigService configService, FontService fontService)
		{
			<configService>P = configService;
			<fontService>P = fontService;
			base..ctor();
		}

		public void Update(StaminaBar staminaBar)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (!CharacterService.TryGetStaminaModel(staminaBar, out var model))
			{
				return;
			}
			EnsureElements(staminaBar);
			if ((Object)(object)_staminaText == (Object)null)
			{
				return;
			}
			PluginConfig current = <configService>P.Current;
			ApplyTextStyle(_staminaText, current.StaminaFontSize, current.TextOutlineThickness);
			((TMP_Text)_staminaText).text = FormatValue(model.CurrentPercent, current.ShowPercentageSign);
			((Graphic)_staminaText).color = ResolveStaminaColor(staminaBar, in model);
			if ((Object)(object)_extraStaminaText != (Object)null)
			{
				ApplyTextStyle(_extraStaminaText, current.StaminaFontSize, current.TextOutlineThickness);
				if (model.ExtraPercent > 0f)
				{
					((Component)_extraStaminaText).gameObject.SetActive(true);
					((TMP_Text)_extraStaminaText).text = (current.ShowPercentageSign ? $"  +{model.ExtraPercent:F0}%" : $"  +{model.ExtraPercent:F0}");
				}
				else if (((Component)_extraStaminaText).gameObject.activeSelf)
				{
					((Component)_extraStaminaText).gameObject.SetActive(false);
				}
			}
			if ((Object)(object)_container != (Object)null && _container.activeSelf != model.IsVisible)
			{
				_container.SetActive(model.IsVisible);
			}
		}

		public void HandleBarStart(StaminaBar staminaBar)
		{
			if (!((Object)(object)_container == (Object)null))
			{
				if ((Object)(object)staminaBar == (Object)null)
				{
					Cleanup();
				}
				else if ((Object)(object)_container.transform.parent != (Object)(object)((Component)staminaBar).transform)
				{
					Cleanup();
				}
			}
		}

		private void EnsureElements(StaminaBar staminaBar)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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)
			if (!((Object)(object)_container != (Object)null) || !((Object)(object)_container.transform.parent == (Object)(object)((Component)staminaBar).transform))
			{
				Cleanup();
				<fontService>P.EnsureResolved();
				_container = new GameObject("StaminaTextContainer");
				_container.transform.SetParent(((Component)staminaBar).transform, false);
				_staminaText = CreateTextElement(_container.transform, "StaminaText", Constants.Ui.Stamina.MainTextSize, Constants.Ui.Stamina.MainTextAnchoredPosition, new Vector2(1f, 0f), (TextAlignmentOptions)516);
				_extraStaminaText = CreateTextElement(_container.transform, "ExtraStaminaText", Constants.Ui.Stamina.ExtraTextSize, Constants.Ui.Stamina.ExtraTextAnchoredPosition, new Vector2(0f, 0f), (TextAlignmentOptions)513);
				((Graphic)_extraStaminaText).color = Colors.ExtraStamina;
			}
		}

		private TextMeshProUGUI CreateTextElement(Transform parent, string name, Vector2 size, Vector2 anchoredPosition, Vector2 pivot, TextAlignmentOptions alignment)
		{
			//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_0013: 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_003f: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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);
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = pivot;
			component.sizeDelta = size;
			component.anchoredPosition = anchoredPosition;
			SetupBaseTextStyle(val2, alignment);
			return val2;
		}

		private void SetupBaseTextStyle(TextMeshProUGUI text, TextAlignmentOptions alignment)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)text).alignment = alignment;
			((TMP_Text)text).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)text).overflowMode = (TextOverflowModes)0;
			((Graphic)text).color = Color.white;
			((TMP_Text)text).enableAutoSizing = false;
			((TMP_Text)text).fontStyle = (FontStyles)1;
			((TMP_Text)text).faceColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
			if ((Object)(object)<fontService>P.Font != (Object)null)
			{
				((TMP_Text)text).font = <fontService>P.Font;
			}
			if ((Object)(object)<fontService>P.Material != (Object)null)
			{
				((Graphic)text).material = <fontService>P.Material;
			}
			Shadow obj = ((Component)text).gameObject.AddComponent<Shadow>();
			obj.effectColor = new Color(0f, 0f, 0f, 0.95f);
			obj.effectDistance = new Vector2(2f, -2f);
		}

		private static void ApplyTextStyle(TextMeshProUGUI text, int fontSize, float outlineThickness)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)text).fontSize = fontSize;
			((TMP_Text)text).outlineWidth = outlineThickness;
			((TMP_Text)text).outlineColor = Color32.op_Implicit(Color.black);
		}

		private static string FormatValue(float value, bool withPercent)
		{
			if (!withPercent)
			{
				return $"{value:F0}";
			}
			return $"{value:F0}%";
		}

		private static Color ResolveStaminaColor(StaminaBar staminaBar, in StaminaModel model)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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_0093: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			Image component = ((Component)staminaBar.staminaBar).GetComponent<Image>();
			if ((Object)(object)component != (Object)null)
			{
				float num = default(float);
				float num2 = default(float);
				float num3 = default(float);
				Color.RGBToHSV(((Graphic)component).color, ref num, ref num2, ref num3);
				return Color.HSVToRGB(num, Mathf.Clamp01(num2 * 1.2f), Mathf.Clamp01(num3 * 1.3f));
			}
			float num4 = ((model.MaxPercent <= 0f) ? 0f : (model.CurrentPercent / model.MaxPercent));
			if (!(num4 < 0.25f))
			{
				if (num4 < 0.5f)
				{
					return Colors.MediumStamina;
				}
				return Colors.HighStamina;
			}
			return Colors.LowStamina;
		}

		public void Cleanup()
		{
			if ((Object)(object)_container != (Object)null)
			{
				Object.Destroy((Object)(object)_container);
			}
			_container = null;
			_staminaText = null;
			_extraStaminaText = null;
		}
	}
}
namespace BetterInfoUI.Services.Infrastructure
{
	public class LoggerService
	{
		private static readonly string PluginPath = Directory.GetCurrentDirectory();

		private static readonly string BepInExConfigsPath = Path.Combine(PluginPath, "BepInEx", "config");

		private static readonly string ConfigsPath = Path.Combine(BepInExConfigsPath, "BetterInfoUI".ToLower());

		private const string DefaultLogType = "BetterInfoUI";

		private static readonly string LogsPath = Path.Combine(ConfigsPath, "Logs");

		private static readonly string ArchiveLogsPath = Path.Combine(LogsPath, "ArchiveLogs");

		private const int MaxLogFiles = 5;

		private bool _isInitialized;

		private bool _splitLogMode;

		private readonly Dictionary<string, string> _registeredLogTypes = new Dictionary<string, string>();

		private readonly List<LogItem> _logBuffer = new List<LogItem>();

		private static LoggerService _instance;

		private static string CurrentTime => GetFormatTime();

		public LoggerService()
		{
			_instance = this;
			RegisterLogType("Info", "BetterInfoUI");
			RegisterLogType("Warning", "DEBUG");
			RegisterLogType("Debug", "DEBUG");
			RegisterLogType("Error", "BetterInfoUI");
			RegisterLogType("Fatal", "BetterInfoUI");
		}

		public static LoggerService Create()
		{
			return _instance ?? new LoggerService();
		}

		public LoggerService Initialize(bool splitLogMode = false)
		{
			if (_isInitialized)
			{
				Core.Logger.LogError((object)"LoggerService is already initialized. Skipping initialization...");
				throw new InvalidOperationException("LoggerService is already initialized.");
			}
			_splitLogMode = splitLogMode;
			try
			{
				if (!Directory.Exists(LogsPath))
				{
					Directory.CreateDirectory(ArchiveLogsPath);
				}
				else
				{
					ArchiveOldLogsFiles();
				}
				InitializeLogFiles();
				TaskRunner.AddUniqueTask((Action)WriteLogBuffer, "LoggerService::WriteBuffer", 0, 5f, highPriority: true, loop: true, runNow: false);
				_isInitialized = true;
				return this;
			}
			catch (Exception ex)
			{
				Core.Logger.LogError((object)("Failed to initialize LoggerService: " + ex.Message + "\n" + ex.StackTrace));
				throw new InvalidOperationException("LoggerService initialization failed.", ex);
			}
		}

		public static void Info(string content, string moduleName = null, bool printToConsole = false)
		{
			_instance.Log("Info", content, moduleName, printToConsole);
		}

		public static void Warning(string content, string moduleName = null, bool printToConsole = false)
		{
			_instance.Log("Warning", content, moduleName, printToConsole);
		}

		public static void Debug(string content, string moduleName = null, bool printToConsole = false)
		{
			_instance.Log("Debug", content, moduleName, printToConsole);
		}

		public static void Error(string content, string moduleName = null, bool printToConsole = false)
		{
			_instance.Log("Error", content, moduleName, printToConsole);
		}

		public static void Fatal(string content, string moduleName = null, bool printToConsole = false)
		{
			_instance.Log("Fatal", content, moduleName, printToConsole);
		}

		public static Action<string, bool> CreateCustomTypeLogger(string type)
		{
			return CreateCustomTypeLogger(type, null);
		}

		public static Action<string, bool> CreateCustomTypeLogger(string type, string moduleName)
		{
			if (string.IsNullOrWhiteSpace(type))
			{
				throw new ArgumentException("Log type cannot be null or empty.", "type");
			}
			if (!_instance._registeredLogTypes.ContainsKey(type))
			{
				throw new InvalidOperationException("Log type '" + type + "' is not registered. Use RegisterLogType to register it first.");
			}
			return delegate(string content, bool printToConsole)
			{
				Custom(type, content, moduleName, printToConsole);
			};
		}

		public static void Custom(string type, string content, string moduleName = null, bool printToConsole = false)
		{
			if (string.IsNullOrWhiteSpace(type))
			{
				Core.Logger.LogError((object)"Log type cannot be null or empty.");
			}
			else if (!_instance._registeredLogTypes.ContainsKey(type))
			{
				Core.Logger.LogError((object)("Log type '" + type + "' is not registered. Use RegisterLogType to register it first."));
			}
			else
			{
				_instance.Log(type, content, moduleName, printToConsole);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void Log(string type, string content, string moduleName = null, bool printToConsole = false)
		{
			if (!_isInitialized)
			{
				Core.Logger.LogError((object)"LoggerService is not initialized. Cannot log messages.");
				return;
			}
			LogItem logItem = new LogItem(content, type, moduleName ?? string.Empty);
			_logBuffer.Add(logItem);
			if (printToConsole)
			{
				LogToConsole(type, logItem.GetLine());
			}
			if (_logBuffer.Count >= 50)
			{
				WriteLogBuffer();
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public LoggerService RegisterLogType(string name, string category)
		{
			if (_registeredLogTypes.ContainsKey(name))
			{
				Core.Logger.LogError((object)("Log type '" + name + "' is already registered. Skipping registration..."));
				return this;
			}
			_registeredLogTypes[name] = (_splitLogMode ? category : "BetterInfoUI");
			return this;
		}

		private void InitializeLogFiles()
		{
			foreach (string item in _registeredLogTypes.Values.Distinct())
			{
				if (!File.Exists(GetLogFilePath(item)))
				{
					InitializeLogFile(item);
				}
			}
		}

		private void WriteLogBuffer()
		{
			if (_logBuffer.Count == 0)
			{
				return;
			}
			Dictionary<string, List<LogItem>> dictionary = (from log in _logBuffer
				group log by (!_registeredLogTypes.ContainsKey(log.Type)) ? "BetterInfoUI" : _registeredLogTypes[log.Type]).ToDictionary((IGrouping<string, LogItem> group) => group.Key, (IGrouping<string, LogItem> group) => group.ToList());
			_logBuffer.Clear();
			foreach (string key in dictionary.Keys)
			{
				string logFilePath = GetLogFilePath(key);
				if (!File.Exists(logFilePath))
				{
					InitializeLogFile(key);
				}
				IEnumerable<string> contents = dictionary[key].Select((LogItem log) => log.GetLine());
				File.AppendAllLines(logFilePath, contents);
			}
		}

		public static void Shutdown()
		{
			if (!_instance._isInitialized)
			{
				return;
			}
			try
			{
				if (_instance._logBuffer.Count > 0)
				{
					_instance.WriteLogBuffer();
				}
				ManualLogSource logger = Core.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)"LoggerService shutdown completed successfully.");
				}
				_instance._isInitialized = false;
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Core.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)("LoggerService shutdown error: " + ex.Message + "\n" + ex.StackTrace));
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static string GetLogFilePath(string category)
		{
			string path = category + ".log";
			return Path.Combine(LogsPath, path);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void InitializeLogFile(string category)
		{
			string text = CurrentTime + " [BetterInfoUI] [LoggerService]";
			text += Environment.NewLine;
			text += "-----------------------------------------------";
			text = text + Environment.NewLine + Environment.NewLine;
			File.WriteAllText(GetLogFilePath(category), text);
		}

		private static string GetFormatTime()
		{
			DateTime dateTime = DateTime.Now.ToLocalTime();
			string text = $"{GetTwoDigit(dateTime.Day)}.{GetTwoDigit(dateTime.Month)}.{dateTime.Year}";
			string text2 = GetTwoDigit(dateTime.Hour) + ":" + GetTwoDigit(dateTime.Minute) + ":" + GetTwoDigit(dateTime.Second);
			return "[" + text + "|" + text2 + "]";
			static string GetTwoDigit(int value)
			{
				return value.ToString("D2");
			}
		}

		private static void ArchiveOldLogsFiles()
		{
			if (!Directory.Exists(ArchiveLogsPath))
			{
				Directory.CreateDirectory(ArchiveLogsPath);
			}
			string text = Path.Combine(ArchiveLogsPath, DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss"));
			string[] files = Directory.GetFiles(LogsPath, "*.log");
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			string[] array = files;
			foreach (string obj in array)
			{
				string fileName = Path.GetFileName(obj);
				string destFileName = Path.Combine(text, fileName);
				File.Move(obj, destFileName);
			}
		}

		private static void DeleteOldLogFiles()
		{
			string[] files = Directory.GetFiles(LogsPath, "*.log");
			if (files.Length <= 5)
			{
				return;
			}
			foreach (FileInfo item in (from file in files
				select new FileInfo(file) into fileInfo
				orderby fileInfo.LastWriteTime descending
				select fileInfo).Skip(5).ToList())
			{
				try
				{
					item.Delete();
				}
				catch (Exception ex)
				{
					Core.Logger.LogError((object)("Failed to delete old log file '" + item.FullName + "': " + ex.Message));
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void LogToConsole(string type, string content)
		{
			switch (char.ToUpper(type[0]) + type.Substring(1, type.Length - 1).ToLower())
			{
			case "Warning":
				Core.Logger.LogWarning((object)content);
				break;
			case "Debug":
				Core.Logger.LogDebug((object)content);
				break;
			case "Error":
				Core.Logger.LogError((object)content);
				break;
			case "Fatal":
				Core.Logger.LogFatal((object)content);
				break;
			default:
				Core.Logger.LogInfo((object)content);
				break;
			}
		}
	}
}
namespace BetterInfoUI.Services.Game
{
	public sealed class AfflictionService
	{
		public static bool TryGetModel(BarAffliction affliction, out AfflictionModel model)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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)
			model = default(AfflictionModel);
			if ((Object)(object)affliction == (Object)null || (Object)(object)Character.observedCharacter == (Object)null)
			{
				return false;
			}
			STATUSTYPE afflictionType = affliction.afflictionType;
			float currentStatus = Character.observedCharacter.refs.afflictions.GetCurrentStatus(afflictionType);
			model = new AfflictionModel(afflictionType, currentStatus, affliction.width, ((Component)affliction).gameObject.activeSelf);
			return true;
		}

		public static bool TryGetValue(STATUSTYPE type, out float value)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			value = 0f;
			if ((Object)(object)Character.observedCharacter == (Object)null)
			{
				return false;
			}
			value = Character.observedCharacter.refs.afflictions.GetCurrentStatus(type);
			return true;
		}

		public static bool TryFindAffliction(StaminaBar staminaBar, STATUSTYPE type, out BarAffliction affliction)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			affliction = null;
			if ((Object)(object)staminaBar == (Object)null || staminaBar.afflictions == null)
			{
				return false;
			}
			BarAffliction[] afflictions = staminaBar.afflictions;
			foreach (BarAffliction val in afflictions)
			{
				if (val.afflictionType == type)
				{
					affliction = val;
					return true;
				}
			}
			return false;
		}
	}
	public sealed class CharacterService
	{
		private const int DeadHeightThreshold = 2000;

		public static bool TryGetStaminaModel(StaminaBar staminaBar, out StaminaModel model)
		{
			model = default(StaminaModel);
			if ((Object)(object)Character.observedCharacter == (Object)null || (Object)(object)staminaBar == (Object)null || (Object)(object)staminaBar.staminaBar == (Object)null)
			{
				return false;
			}
			Character observedCharacter = Character.observedCharacter;
			CharacterData data = observedCharacter.data;
			bool isVisible = ((Component)staminaBar.staminaBar).gameObject.activeSelf && !data.fullyPassedOut && !IsDead(observedCharacter);
			model = new StaminaModel(data.currentStamina, data.extraStamina, observedCharacter.GetMaxStamina(), isVisible);
			return true;
		}

		private static bool IsDead(Character character)
		{
			return Mathf.FloorToInt(character.refs.stats.heightInMeters) > 2000;
		}
	}
	public sealed class RunLifecycleService
	{
		[CompilerGenerated]
		private ConfigService <configService>P;

		[CompilerGenerated]
		private AfflictionUIService <afflictionUiService>P;

		private bool _isSubscribed;

		public RunLifecycleService(ConfigService configService, AfflictionUIService afflictionUiService)
		{
			<configService>P = configService;
			<afflictionUiService>P = afflictionUiService;
			base..ctor();
		}

		public RunLifecycleService Initialize()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (_isSubscribed)
			{
				return this;
			}
			Application.logMessageReceived += new LogCallback(OnLogMessageReceived);
			_isSubscribed = true;
			LoggerService.Info("Subscribed to Application.logMessageReceived", "RunLifecycleService", <configService>P.Current.VerboseLogging);
			return this;
		}

		public void Dispose()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (_isSubscribed)
			{
				Application.logMessageReceived -= new LogCallback(OnLogMessageReceived);
				_isSubscribed = false;
				LoggerService.Info("Unsubscribed from Application.logMessageReceived", "RunLifecycleService", <configService>P.Current.VerboseLogging);
			}
		}

		private void OnLogMessageReceived(string logString, string stackTrace, LogType logType)
		{
			if (string.IsNullOrEmpty(logString) || !logString.Contains("RUN STARTED", StringComparison.Ordinal))
			{
				return;
			}
			try
			{
				LoggerService.Info("RUN STARTED detected, resetting affliction UI state", "RunLifecycleService", <configService>P.Current.VerboseLogging);
				<afflictionUiService>P.Cleanup();
			}
			catch (Exception ex)
			{
				LoggerService.Error("Failed to reset affliction UI: " + ex.Message, "RunLifecycleService", printToConsole: true);
			}
		}
	}
}
namespace BetterInfoUI.Models
{
	public readonly struct AfflictionModel
	{
		public STATUSTYPE Type { get; }

		public float Value { get; }

		public float Width { get; }

		public bool IsActive { get; }

		public float ValuePercent => Value * 100f;

		public AfflictionModel(STATUSTYPE type, float value, float width, bool isActive)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Type = type;
			Value = value;
			Width = width;
			IsActive = isActive;
		}
	}
	public readonly struct StaminaModel
	{
		public float Current { get; }

		public float Extra { get; }

		public float Max { get; }

		public bool IsVisible { get; }

		public float CurrentPercent => Current * 100f;

		public float ExtraPercent => Extra * 100f;

		public float MaxPercent => Max * 100f;

		public StaminaModel(float current, float extra, float max, bool isVisible)
		{
			Current = current;
			Extra = extra;
			Max = max;
			IsVisible = isVisible;
		}
	}
}
namespace BetterInfoUI.Hooks
{
	[HarmonyPatch(typeof(BarAffliction), "UpdateAffliction")]
	public static class BarAfflictionUpdatePatch
	{
		private static AfflictionUIService _afflictionUiService;

		public static void Initialize()
		{
			_afflictionUiService = GameContext.Get<AfflictionUIService>();
		}

		[HarmonyPostfix]
		public static void Postfix(BarAffliction __instance, StaminaBar bar)
		{
			_afflictionUiService?.HandleAfflictionUpdate(__instance, bar);
		}
	}
	[HarmonyPatch(typeof(StaminaBar), "ChangeBar")]
	public static class StaminaBarChangeBarPatch
	{
		private static AfflictionUIService _afflictionUiService;

		public static void Initialize()
		{
			_afflictionUiService = GameContext.Get<AfflictionUIService>();
		}

		[HarmonyPostfix]
		public static void Postfix(StaminaBar __instance)
		{
			_afflictionUiService?.HandleBarChanged(__instance);
		}
	}
	[HarmonyPatch(typeof(StaminaBar), "Start")]
	public static class StaminaBarStartPatch
	{
		private static StaminaUIService _staminaUiService;

		private static AfflictionUIService _afflictionUiService;

		public static void Initialize()
		{
			_staminaUiService = GameContext.Get<StaminaUIService>();
			_afflictionUiService = GameContext.Get<AfflictionUIService>();
		}

		[HarmonyPostfix]
		public static void Postfix(StaminaBar __instance)
		{
			_staminaUiService?.HandleBarStart(__instance);
			_afflictionUiService?.HandleBarStart(__instance);
		}
	}
	[HarmonyPatch(typeof(StaminaBar), "Update")]
	public static class StaminaBarUpdatePatch
	{
		private static StaminaUIService _staminaUiService;

		public static void Initialize()
		{
			_staminaUiService = GameContext.Get<StaminaUIService>();
		}

		[HarmonyPostfix]
		public static void Postfix(StaminaBar __instance)
		{
			_staminaUiService?.Update(__instance);
		}
	}
}
namespace BetterInfoUI.Framework
{
	public static class GameContext
	{
		private static readonly Dictionary<Type, object> Services = new Dictionary<Type, object>();

		private static bool _isInitialized;

		public static void Initialize()
		{
			if (!_isInitialized)
			{
				_isInitialized = true;
			}
		}

		public static void RegisterSingleton<TService>(TService instance) where TService : class
		{
			Services[typeof(TService)] = instance ?? throw new ArgumentNullException("instance");
		}

		public static TService Get<TService>() where TService : class
		{
			if (Services.TryGetValue(typeof(TService), out var value))
			{
				return (TService)value;
			}
			throw new InvalidOperationException("Service not registered: " + typeof(TService).Name);
		}

		public static bool TryGet<TService>(out TService service) where TService : class
		{
			if (Services.TryGetValue(typeof(TService), out var value))
			{
				service = (TService)value;
				return true;
			}
			service = null;
			return false;
		}

		public static void Clear()
		{
			Services.Clear();
			_isInitialized = false;
		}
	}
	public static class TaskRunner
	{
		public struct TaskRunnerStats
		{
			public int ActiveTasks { get; set; }

			public int HighPriorityQueueSize { get; set; }

			public int NormalPriorityQueueSize { get; set; }

			public int PooledTasksAvailable { get; set; }

			public string CurrentFrameLevel { get; set; }

			public string FrameCounterDetails { get; set; }

			public int ResetCount { get; set; }

			public double CycleProgress { get; set; }

			public bool IsInitialized { get; set; }

			public bool IsServerMonitoringEnabled { get; set; }

			public FramePerformanceData LastFramePerformance { get; set; }

			public ServerPerformanceStats ServerPerformanceStats { get; set; }

			public int MaxHighPriorityBatchSize { get; set; }

			public int MaxNormalPriorityBatchSize { get; set; }

			public int CurrentHighPriorityBatchCount { get; set; }

			public int CurrentNormalPriorityBatchCount { get; set; }
		}

		private static readonly ObjectPool<TaskItem> TaskPool;

		private static readonly ConcurrentDictionary<string, TaskItem> NamedTasks;

		private static readonly PriorityTaskQueue HighPriorityQueue;

		private static readonly PriorityTaskQueue NormalPriorityQueue;

		private static long _cachedTimeMs;

		private static readonly Timer TimeCacheUpdater;

		private static readonly ThreadLocal<List<TaskItem>> HighPriorityBatch;

		private static readonly ThreadLocal<List<TaskItem>> NormalPriorityBatch;

		private static volatile bool _isInitialized;

		private static int _processingLock;

		public static int ActiveTaskCount => NamedTasks.Count;

		static TaskRunner()
		{
			TaskPool = new ObjectPool<TaskItem>(200);
			NamedTasks = new ConcurrentDictionary<string, TaskItem>();
			HighPriorityQueue = new PriorityTaskQueue();
			NormalPriorityQueue = new PriorityTaskQueue();
			HighPriorityBatch = new ThreadLocal<List<TaskItem>>(() => new List<TaskItem>(64));
			NormalPriorityBatch = new ThreadLocal<List<TaskItem>>(() => new List<TaskItem>(128));
			TimeCacheUpdater = new Timer(UpdateTimeCache, null, 0, 16);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void UpdateTimeCache(object _)
		{
			Interlocked.Exchange(ref _cachedTimeMs, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
		}

		public static void Initialize()
		{
			if (!_isInitialized)
			{
				GameFrame.OnUpdate += ProcessAllTasks;
				_isInitialized = true;
				LoggerService.Info("Initialized successfully", "TaskRunner", printToConsole: true);
			}
		}

		public static void Shutdown()
		{
			if (_isInitialized)
			{
				_isInitialized = false;
				GameFrame.OnUpdate -= ProcessAllTasks;
				ClearAllTasks();
				TimeCacheUpdater?.Dispose();
				HighPriorityBatch.Dispose();
				NormalPriorityBatch.Dispose();
				LoggerService.Info("Shutdown completed", "TaskRunner", printToConsole: true);
			}
		}

		public static bool HasTask(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return NamedTasks.ContainsKey(name);
			}
			return false;
		}

		public static TaskRunnerStats GetDetailedStats()
		{
			TaskRunnerStats result = default(TaskRunnerStats);
			result.ActiveTasks = NamedTasks.Count;
			result.HighPriorityQueueSize = HighPriorityQueue.Count;
			result.NormalPriorityQueueSize = NormalPriorityQueue.Count;
			result.PooledTasksAvailable = TaskPool.AvailableCount;
			result.IsInitialized = _isInitialized;
			result.IsServerMonitoringEnabled = Core.IsFrameMonitoringEnabled;
			result.LastFramePerformance = Core.LastFramePerformance;
			result.ServerPerformanceStats = (Core.IsFrameMonitoringEnabled ? Core.FrameMonitor.GetPerformanceStats() : default(ServerPerformanceStats));
			result.MaxHighPriorityBatchSize = HighPriorityBatch.Value?.Capacity ?? 0;
			result.MaxNormalPriorityBatchSize = NormalPriorityBatch.Value?.Capacity ?? 0;
			result.CurrentHighPriorityBatchCount = HighPriorityBatch.Value?.Count ?? 0;
			result.CurrentNormalPriorityBatchCount = NormalPriorityBatch.Value?.Count ?? 0;
			return result;
		}

		private static string GetTaskRunnerBlock()
		{
			TaskRunnerStats detailedStats = GetDetailedStats();
			string text = (detailedStats.IsInitialized ? "True" : "False");
			string text2 = detailedStats.ActiveTasks.ToString();
			string text3 = detailedStats.HighPriorityQueueSize.ToString();
			string text4 = detailedStats.NormalPriorityQueueSize.ToString();
			string text5 = detailedStats.PooledTasksAvailable.ToString();
			string text6 = detailedStats.MaxHighPriorityBatchSize.ToString();
			string text7 = detailedStats.MaxNormalPriorityBatchSize.ToString();
			int currentHighPriorityBatchCount = detailedStats.CurrentHighPriorityBatchCount;
			int currentNormalPriorityBatchCount = detailedStats.CurrentNormalPriorityBatchCount;
			return "[TaskRunner| Init: " + text + "]:\n- Tasks: " + text2 + " | HPQueue: " + text3 + ", NQueue: " + text4 + " | Pool: " + text5 + "\n" + $"- HPBatch Size: {text6} ({currentHighPriorityBatchCount}) | NBatch Size: {text7} ({currentNormalPriorityBatchCount})";
		}

		public static string PrintHpTasksList()
		{
			List<string> tasksInfo = HighPriorityQueue.GetTasksInfo();
			return string.Join("\n", tasksInfo);
		}

		public static string PrintNTasksList()
		{
			List<string> tasksInfo = NormalPriorityQueue.GetTasksInfo();
			return string.Join("\n", tasksInfo);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static Action CreateParameterizedAction<T>(Action<T> action, T parameter)
		{
			return delegate
			{
				action(parameter);
			};
		}

		public static void AddTask(Action action, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false, string name = null)
		{
			AddTaskToSystem(CreateAndConfigureTask(action, frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: false);
		}

		public static void AddTask(Func<Task> asyncAction, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false, string name = null)
		{
			AddTaskToSystem(CreateAndConfigureTask(asyncAction, frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: false);
		}

		public static void AddTask<T>(Action<T> action, T parameter, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false, string name = null)
		{
			AddTaskToSystem(CreateAndConfigureTask(CreateParameterizedAction(action, parameter), frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: false);
		}

		public static void AddUniqueTask(Action action, string name, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false)
		{
			ValidateUniqueTaskName(name);
			AddTaskToSystem(CreateAndConfigureTask(action, frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: true);
		}

		public static void AddUniqueTask(Func<Task> asyncAction, string name, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false)
		{
			ValidateUniqueTaskName(name);
			AddTaskToSystem(CreateAndConfigureTask(asyncAction, frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: true);
		}

		public static void AddUniqueTask<T>(Action<T> action, T parameter, string name, int frameDelay = 0, float timeDelay = 0f, bool highPriority = false, bool loop = false, bool runNow = false)
		{
			ValidateUniqueTaskName(name);
			AddTaskToSystem(CreateAndConfigureTask(CreateParameterizedAction(action, parameter), frameDelay, timeDelay, highPriority, loop, name), runNow, uniqueCheck: true);
		}

		public static bool RemoveTask(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (!NamedTasks.TryRemove(name, out var value))
			{
				return false;
			}
			CleanupTask(value);
			return true;
		}

		public static void ClearAllTasks()
		{
			NamedTasks.Clear();
			ClearQueue(HighPriorityQueue);
			ClearQueue(NormalPriorityQueue);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void ProcessAllTasks()
		{
			if (Interlocked.CompareExchange(ref _processingLock, 1, 0) != 0)
			{
				return;
			}
			try
			{
				long cachedTimeMs = _cachedTimeMs;
				ProcessTaskQueue(HighPriorityQueue, HighPriorityBatch.Value, cachedTimeMs);
				ProcessTaskQueue(NormalPriorityQueue, NormalPriorityBatch.Value, cachedTimeMs);
			}
			finally
			{
				Interlocked.Exchange(ref _processingLock, 0);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void ProcessTaskQueue(PriorityTaskQueue queue, List<TaskItem> batch, long currentTime)
		{
			CollectReadyTasks(queue, batch, currentTime);
			if (batch.Count != 0)
			{
				ExecuteTaskBatch(batch);
				batch.Clear();
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void CollectReadyTasks(PriorityTaskQueue queue, List<TaskItem> batch, long currentTime)
		{
			int num = 0;
			int num2 = 0;
			TaskItem task;
			while (num < 100 && num2 < 50 && queue.TryPeek(out task))
			{
				if (!queue.TryDequeue(out task))
				{
					continue;
				}
				if (task.IsRemoved)
				{
					CleanupTask(task);
					num2++;
					continue;
				}
				if (!task.IsReadyToExecute(currentTime))
				{
					queue.Enqueue(task);
					break;
				}
				batch.Add(task);
				num++;
			}
		}

		private static void ExecuteTaskBatch(IEnumerable<TaskItem> batch)
		{
			foreach (TaskItem item in batch.Where((TaskItem task) => !task.IsRemoved))
			{
				try
				{
					item.Execute();
					HandleTaskCompletion(item);
				}
				catch (Exception ex)
				{
					LoggerService.Error("Task execution failed: " + ex.Message, "TaskRunner");
					CleanupTask(item);
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void HandleTaskCompletion(TaskItem task)
		{
			if (task.IsRemoved || !task.ShouldLoop)
			{
				CleanupTask(task);
				return;
			}
			task.PrepareForNextLoop(_cachedTimeMs);
			(task.IsHighPriority ? HighPriorityQueue : NormalPriorityQueue).Enqueue(task);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void ValidateUniqueTaskName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentException("Unique tasks must have a name", "name");
			}
		}

		private static TaskItem CreateAndConfigureTask(Action action, int frameDelay, float timeDelay, bool highPriority, bool loop, string name)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			TaskItem taskItem = TaskPool.Get();
			taskItem.Initialize(action, frameDelay, timeDelay, highPriority, loop, name, _cachedTimeMs);
			return taskItem;
		}

		private static TaskItem CreateAndConfigureTask(Func<Task> asyncAction, int frameDelay, float timeDelay, bool highPriority, bool loop, string name)
		{
			if (asyncAction == null)
			{
				throw new ArgumentNullException("asyncAction");
			}
			TaskItem taskItem = TaskPool.Get();
			taskItem.Initialize(asyncAction, frameDelay, timeDelay, highPriority, loop, name, _cachedTimeMs);
			return taskItem;
		}

		private static void AddTaskToSystem(TaskItem taskItem, bool runNow, bool uniqueCheck)
		{
			if (uniqueCheck && HasTask(taskItem.Name))
			{
				RemoveTask(taskItem.Name);
			}
			if (runNow)
			{
				ExecuteTaskImmediately(taskItem);
			}
			RegisterNamedTask(taskItem);
			(taskItem.IsHighPriority ? HighPriorityQueue : NormalPriorityQueue).Enqueue(taskItem);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void ExecuteTaskImmediately(TaskItem taskItem)
		{
			try
			{
				taskItem.Execute();
			}
			catch (Exception ex)
			{
				LoggerService.Error("Immediate task execution failed: " + ex.Message, "TaskRunner");
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void RegisterNamedTask(TaskItem taskItem)
		{
			if (!string.IsNullOrEmpty(taskItem.Name))
			{
				NamedTasks.TryAdd(taskItem.Name, taskItem);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static void CleanupTask(TaskItem task)
		{
			if (!string.IsNullOrEmpty(task.Name))
			{
				NamedTasks.TryRemove(task.Name, out var _);
			}
			(task.IsHighPriority ? HighPriorityQueue : NormalPriorityQueue)?.Remove(task);
			TaskPool.Return(task.Reset());
		}

		private static void ClearQueue(PriorityTaskQueue queue)
		{
			TaskItem task;
			while (queue.TryDequeue(out task))
			{
				CleanupTask(task);
			}
		}
	}
}
namespace BetterInfoUI.Configuration
{
	public sealed class ConfigService
	{
		private static readonly string PluginPath = Directory.GetCurrentDirectory();

		private static readonly string BepInExConfigsPath = Path.Combine(PluginPath, "BepInEx", "config");

		private static readonly string ConfigsPath = Path.Combine(BepInExConfigsPath, "BetterInfoUI".ToLowerInvariant());

		private static readonly string ConfigFilePath = Path.Combine(ConfigsPath, "config.json");

		public PluginConfig Current { get; private set; } = PluginConfig.CreateDefault();


		public ConfigService Initialize()
		{
			if (!Directory.Exists(ConfigsPath))
			{
				Directory.CreateDirectory(ConfigsPath);
			}
			if (!File.Exists(ConfigFilePath))
			{
				Current = PluginConfig.CreateDefault();
				Save();
				return this;
			}
			try
			{
				PluginConfig pluginConfig = JsonConvert.DeserializeObject<PluginConfig>(File.ReadAllText(ConfigFilePath));
				Current = pluginConfig ?? PluginConfig.CreateDefault();
				Normalize(Current);
			}
			catch
			{
				Current = PluginConfig.CreateDefault();
				Save();
			}
			return this;
		}

		private void Save()
		{
			Normalize(Current);
			string contents = JsonConvert.SerializeObject((object)Current, (Formatting)1);
			File.WriteAllText(ConfigFilePath, contents);
		}

		private static void Normalize(PluginConfig config)
		{
			config.StaminaFontSize = Clamp(config.StaminaFontSize, 10, 32);
			config.AfflictionFontSize = Clamp(config.AfflictionFontSize, 10, 32);
			float textOutlineThickness = config.TextOutlineThickness;
			float textOutlineThickness2 = ((textOutlineThickness < 0.1f) ? 0.1f : ((!(textOutlineThickness > 0.3f)) ? config.TextOutlineThickness : 0.3f));
			config.TextOutlineThickness = textOutlineThickness2;
			if (config.ConfigVersion <= 0)
			{
				config.ConfigVersion = 1;
			}
		}

		private static int Clamp(int value, int min, int max)
		{
			if (value < min)
			{
				return min;
			}
			if (value <= max)
			{
				return value;
			}
			return max;
		}
	}
	public sealed class PluginConfig
	{
		public int ConfigVersion { get; set; } = 1;


		public bool ShowPercentageSign { get; set; }

		public int StaminaFontSize { get; set; } = 18;


		public int AfflictionFontSize { get; set; } = 18;


		public float TextOutlineThickness { get; set; } = 0.1f;


		public bool VerboseLogging { get; set; }

		public static PluginConfig CreateDefault()
		{
			return new PluginConfig();
		}
	}
}
namespace BetterInfoUI.Common
{
	public class CircularBuffer<T>
	{
		private readonly T[] _buffer = new T[capacity];

		private int _head;

		public int Count { get; private set; }

		public int Capacity => _buffer.Length;

		public T this[int index]
		{
			get
			{
				if (index < 0 || index >= Count)
				{
					throw new IndexOutOfRangeException();
				}
				int num = (_head - Count + index + _buffer.Length) % _buffer.Length;
				return _buffer[num];
			}
		}

		public CircularBuffer(int capacity)
		{
		}

		public void Add(T item)
		{
			_buffer[_head] = item;
			_head = (_head + 1) % _buffer.Length;
			if (Count < _buffer.Length)
			{
				Count++;
			}
		}

		public void Clear()
		{
			_head = 0;
			Count = 0;
			Array.Clear(_buffer, 0, _buffer.Length);
		}
	}
	public static class Colors
	{
		public static readonly Color LowStamina = new Color(1f, 0.3f, 0.3f);

		public static readonly Color MediumStamina = new Color(1f, 0.92f, 0.016f);

		public static readonly Color HighStamina = new Color(0.5f, 1f, 0.5f);

		public static readonly Color ExtraStamina = new Color(1f, 0.92f, 0.016f);

		public static readonly IReadOnlyDictionary<STATUSTYPE, Color> AfflictionByStatus = new Dictionary<STATUSTYPE, Color>
		{
			[(STATUSTYPE)0] = new Color(1f, 0.3f, 0.3f),
			[(STATUSTYPE)1] = new Color(0.9f, 0.6f, 0.1f),
			[(STATUSTYPE)2] = new Color(0.2f, 0.6f, 0.9f),
			[(STATUSTYPE)3] = new Color(0.6f, 0.1f, 0.6f),
			[(STATUSTYPE)4] = new Color(0.9f, 0.4f, 0.4f),
			[(STATUSTYPE)5] = new Color(0.5f, 0.1f, 0.5f),
			[(STATUSTYPE)6] = new Color(1f, 0.4f, 0.8f),
			[(STATUSTYPE)7] = new Color(0.6f, 0.4f, 0.2f),
			[(STATUSTYPE)8] = new Color(1f, 0.3f, 0.1f)
		};
	}
	public static class Constants
	{
		public static class Config
		{
			public const string FileName = "config.json";

			public const int Version = 1;

			public const int MinFontSize = 10;

			public const int MaxFontSize = 32;

			public const float MinOutlineThickness = 0.1f;

			public const float MaxOutlineThickness = 0.3f;
		}

		public static class Ui
		{
			public static class Stamina
			{
				public const string ContainerName = "StaminaTextContainer";

				public const string MainTextName = "StaminaText";

				public const string ExtraTextName = "ExtraStaminaText";

				public static readonly Vector2 MainTextSize = new Vector2(60f, 30f);

				public static readonly Vector2 ExtraTextSize = new Vector2(60f, 30f);

				public static readonly Vector2 MainTextAnchoredPosition = new Vector2(20f, 22f);

				public static readonly Vector2 ExtraTextAnchoredPosition = new Vector2(24f, 22f);
			}

			public static class Affliction
			{
				public const string CanvasName = "AfflictionTextCanvas";

				public static readonly Vector2 CanvasReferenceResolution = new Vector2(1920f, 1080f);

				public const float MinTextWidth = 40f;

				public const float TextWidthFactor = 0.8f;

				public const float VerticalOffset = -22f;

				public const float TextHeight = 24f;
			}
		}
	}
	public struct FrameMonitor
	{
		private readonly long _minExpectedFrameTimeMs;

		private long _lastFrameTimeMs;

		private long _frameStartTimeMs;

		private long _totalDelayMs;

		private int _delayedFrameCount;

		private long _maxDelayMs;

		private long _recentDelaySum;

		private int _recentFrameCount;

		private readonly long _lagThresholdMs;

		private readonly long _criticalLagThresholdMs;

		private readonly CircularBuffer<long> _frameTimings;

		public FrameMonitor(long lagThresholdMs = 75L, long criticalLagThresholdMs = 150L, float minExpectedFPS = 20f)
		{
			_minExpectedFrameTimeMs = (long)(1000.0 / (double)minExpectedFPS);
			_lagThresholdMs = lagThresholdMs;
			_criticalLagThresholdMs = criticalLagThresholdMs;
			_frameTimings = new CircularBuffer<long>(100);
			_lastFrameTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
			_frameStartTimeMs = _lastFrameTimeMs;
			_totalDelayMs = 0L;
			_delayedFrameCount = 0;
			_maxDelayMs = 0L;
			_recentDelaySum = 0L;
			_recentFrameCount = 0;
		}

		public void StartFrame()
		{
			_frameStartTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
		}

		public FramePerformanceData EndFrame()
		{
			long num = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
			long num2 = num - _frameStartTimeMs;
			long num3 = num - _lastFrameTimeMs;
			long minExpectedFrameTimeMs = _minExpectedFrameTimeMs;
			long num4 = num3 - minExpectedFrameTimeMs;
			FrameType frameType = FrameType.Normal;
			if (num4 > _criticalLagThresholdMs)
			{
				frameType = FrameType.CriticalLag;
			}
			else if (num4 > _lagThresholdMs)
			{
				frameType = FrameType.Lag;
			}
			UpdateStatistics(num4, frameType != FrameType.Normal);
			_frameTimings.Add(num2);
			FramePerformanceData result = new FramePerformanceData(num2, num3, minExpectedFrameTimeMs, Math.Max(0L, num4), frameType, CalculateAverageFrameTime(), CalculateRecentAverageDelay());
			_lastFrameTimeMs = num;
			return result;
		}

		private void UpdateStatistics(long delayDifference, bool isDelayed)
		{
			if (isDelayed)
			{
				_delayedFrameCount++;
				_totalDelayMs += delayDifference;
				_maxDelayMs = Math.Max(_maxDelayMs, delayDifference);
			}
			_recentDelaySum += Math.Max(0L, delayDifference);
			_recentFrameCount++;
			if (_recentFrameCount > 60)
			{
				_recentDelaySum /= 2L;
				_recentFrameCount = 30;
			}
		}

		private readonly double CalculateAverageFrameTime()
		{
			if (_frameTimings.Count == 0)
			{
				return _minExpectedFrameTimeMs;
			}
			long num = 0L;
			for (int i = 0; i < _frameTimings.Count; i++)
			{
				num += _frameTimings[i];
			}
			return (double)num / (double)_frameTimings.Count;
		}

		private readonly double CalculateRecentAverageDelay()
		{
			if (_recentFrameCount <= 0)
			{
				return 0.0;
			}
			return (double)_recentDelaySum / (double)_recentFrameCount;
		}

		public readonly ServerPerformanceStats GetPerformanceStats()
		{
			ServerPerformanceStats result = default(ServerPerformanceStats);
			result.TotalDelayMs = _totalDelayMs;
			result.DelayedFrameCount = _delayedFrameCount;
			result.MaxDelayMs = _maxDelayMs;
			result.AverageDelayMs = ((_delayedFrameCount > 0) ? ((double)_totalDelayMs / (double)_delayedFrameCount) : 0.0);
			result.RecentAverageDelayMs = CalculateRecentAverageDelay();
			result.AverageFrameTimeMs = CalculateAverageFrameTime();
			result.MinExpectedFrameTimeMs = _minExpectedFrameTimeMs;
			result.LagThresholdMs = _lagThresholdMs;
			result.CriticalLagThresholdMs = _criticalLagThresholdMs;
			result.FrameTimingsCount = _frameTimings.Count;
			return result;
		}
	}
	public readonly struct FramePerformanceData
	{
		public long FrameTime { get; }

		public long TimeSinceLastFrame { get; }

		public long MinExpectedFrameTime { get; }

		public long DelayMs { get; }

		public FrameType Type { get; }

		public double AverageFrameTime { get; }

		public double RecentAverageDelay { get; }

		public FramePerformanceData(long frameTime, long timeSinceLastFrame, long minExpectedFrameTime, long delayMs, FrameType type, double averageFrameTime, double recentAverageDelay)
		{
			FrameTime = frameTime;
			TimeSinceLastFrame = timeSinceLastFrame;
			MinExpectedFrameTime = minExpectedFrameTime;
			DelayMs = delayMs;
			Type = type;
			AverageFrameTime = averageFrameTime;
			RecentAverageDelay = recentAverageDelay;
		}
	}
	public enum FrameType
	{
		Normal,
		Lag,
		CriticalLag
	}
	public struct ServerPerformanceStats
	{
		public long TotalDelayMs { get; set; }

		public int DelayedFrameCount { get; set; }

		public long MaxDelayMs { get; set; }

		public double AverageDelayMs { get; set; }

		public double RecentAverageDelayMs { get; set; }

		public double AverageFrameTimeMs { get; set; }

		public long MinExpectedFrameTimeMs { get; set; }

		public long LagThresholdMs { get; set; }

		public long CriticalLagThresholdMs { get; set; }

		public int FrameTimingsCount { get; set; }

		public override string ToString()
		{
			return $"Delays: {DelayedFrameCount} frames, Avg: {AverageDelayMs:F2}ms, " + $"Recent: {RecentAverageDelayMs:F2}ms, Max: {MaxDelayMs}ms, " + $"Frame Time: {AverageFrameTimeMs:F2}ms (MinExpectedFrameTime: {MinExpectedFrameTimeMs}ms)";
		}
	}
	public class LogItem
	{
		private string Content { get; }

		public string Type { get; }

		private string ModuleName { get; }

		private DateTime Timestamp { get; }

		public LogItem(string content, string type, string moduleName)
		{
			Content = content;
			Type = type;
			ModuleName = moduleName;
			Timestamp = DateTime.Now;
			base..ctor();
		}

		public string GetLine()
		{
			string text = Timestamp.ToString("dd.MM.yyyy|HH:mm:ss", CultureInfo.InvariantCulture);
			string text2 = (string.Empty.Equals(ModuleName) ? "" : ("[" + ModuleName + "]: "));
			return "[" + text + "][" + Type + "]" + text2 + Content;
		}
	}
	internal sealed class TaskItem
	{
		private Action _syncAction;

		private Func<Task> _asyncAction;

		private TaskExecutionMode _executionMode;

		private float _originalTimeDelay;

		private int _originalFrameDelay;

		private volatile bool _isRemoved;

		public string Name { get; private set; }

		public bool IsHighPriority { get; private set; }

		public bool ShouldLoop { get; private set; }

		public long Priority { get; private set; }

		public bool IsRemoved => _isRemoved;

		public void Initialize(Action action, int frameDelay, float timeDelay, bool highPriority, bool loop, string name, long currentTime)
		{
			_syncAction = action;
			_asyncAction = null;
			InitializeCommon(frameDelay, timeDelay, highPriority, loop, name, currentTime);
		}

		public void Initialize(Func<Task> asyncAction, int frameDelay, float timeDelay, bool highPriority, bool loop, string name, long currentTime)
		{
			_syncAction = null;
			_asyncAction = asyncAction;
			InitializeCommon(frameDelay, timeDelay, highPriority, loop, name, currentTime);
		}

		private void InitializeCommon(int frameDelay, float timeDelay, bool highPriority, bool loop, string name, long currentTime)
		{
			if (frameDelay <= 0 && timeDelay <= 0f)
			{
				throw new ArgumentException("Either frameDelay or timeDelay must be greater than 0");
			}
			_executionMode = TaskExecutionMode.Time;
			IsHighPriority = highPriority;
			ShouldLoop = loop;
			Name = name;
			_isRemoved = false;
			_originalFrameDelay = frameDelay;
			_originalTimeDelay = timeDelay;
			CalculateExecutionTime(currentTime);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void CalculateExecutionTime(long currentTime)
		{
			if (_executionMode == TaskExecutionMode.Time)
			{
				Priority = currentTime + (long)(_originalTimeDelay * 1000f);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public bool IsReadyToExecute(long currentTime)
		{
			if (_isRemoved)
			{
				return false;
			}
			if (_executionMode == TaskExecutionMode.Time)
			{
				return currentTime >= Priority;
			}
			return false;
		}

		public async void Execute()
		{
			try
			{
				if (!_isRemoved)
				{
					if (_syncAction != null)
					{
						_syncAction();
					}
					else if (_asyncAction != null)
					{
						await _asyncAction().ConfigureAwait(continueOnCapturedContext: false);
					}
				}
			}
			catch (Exception ex)
			{
				LoggerService.Error("Task '" + (Name ?? "unnamed") + "' execution failed: " + ex.Message, "TaskItem");
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void PrepareForNextLoop(long currentTime)
		{
			if (ShouldLoop)
			{
				CalculateExecutionTime(currentTime);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void MarkAsRemoved()
		{
			_isRemoved = true;
		}

		public TaskItem Reset()
		{
			_syncAction = null;
			_asyncAction = null;
			_executionMode = TaskExecutionMode.Time;
			Priority = 0L;
			_originalTimeDelay = 0f;
			_originalFrameDelay = 0;
			Name = null;
			IsHighPriority = false;
			ShouldLoop = false;
			_isRemoved = false;
			return this;
		}
	}
	internal sealed class ObjectPool<T> where T : class, new()
	{
		[CompilerGenerated]
		private int <maxPoolSize>P;

		[CompilerGenerated]
		private Action<T> <resetAction>P;

		private readonly ConcurrentQueue<T> _objects;

		private int _currentCount;

		public int AvailableCount => _currentCount;

		public ObjectPool(int maxPoolSize = 100, Action<T> resetAction = null)
		{
			<maxPoolSize>P = maxPoolSize;
			<resetAction>P = resetAction;
			_objects = new ConcurrentQueue<T>();
			base..ctor();
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public T Get()
		{
			if (!_objects.TryDequeue(out var result))
			{
				return new T();
			}
			Interlocked.Decrement(ref _currentCount);
			return result;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void Return(T item)
		{
			if (item != null && _currentCount < <maxPoolSize>P)
			{
				<resetAction>P?.Invoke(item);
				_objects.Enqueue(item);
				Interlocked.Increment(ref _currentCount);
			}
		}
	}
	internal sealed class PriorityTaskQueue
	{
		private readonly SortedDictionary<long, Queue<TaskItem>> _priorityQueues = new SortedDictionary<long, Queue<TaskItem>>();

		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		public int Count
		{
			get
			{
				_lock.EnterReadLock();
				try
				{
					return _priorityQueues.Count;
				}
				finally
				{
					_lock.ExitReadLock();
				}
			}
		}

		public void Remove(TaskItem task)
		{
			_lock.EnterWriteLock();
			try
			{
				if (_priorityQueues.TryGetValue(task.Priority, out var value) && value.Contains(task))
				{
					if (!task.IsRemoved)
					{
						task.MarkAsRemoved();
					}
					value = new Queue<TaskItem>(value.Where((TaskItem t) => !t.IsRemoved));
					_priorityQueues[task.Priority] = value;
					if (value.Count == 0)
					{
						_priorityQueues.Remove(task.Priority);
					}
				}
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public List<string> GetTasksInfo()
		{
			_lock.EnterReadLock();
			try
			{
				return _priorityQueues.SelectMany((KeyValuePair<long, Queue<TaskItem>> pair) => pair.Value.Select((TaskItem task) => $"{task.Name}: {!task.IsRemoved}")).ToList();
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}

		public void Enqueue(TaskItem task)
		{
			long priority = task.Priority;
			_lock.EnterWriteLock();
			try
			{
				if (!_priorityQueues.TryGetValue(priority, out var value))
				{
					value = new Queue<TaskItem>();
					_priorityQueues[priority] = value;
				}
				value.Enqueue(task);
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public bool TryDequeue(out TaskItem task)
		{
			_lock.EnterWriteLock();
			try
			{
				if (_priorityQueues.Count == 0)
				{
					task = null;
					return false;
				}
				_priorityQueues.First().Deconstruct(out var key, out var value);
				long key2 = key;
				Queue<TaskItem> queue = value;
				task = queue.Dequeue();
				if (queue.Count == 0)
				{
					_priorityQueues.Remove(key2);
				}
				return true;
			}
			finally
			{
				_lock.ExitWriteLock();
			}
		}

		public bool TryPeek(out TaskItem task)
		{
			_lock.EnterReadLock();
			try
			{
				if (_priorityQueues.Count == 0)
				{
					task = null;
					return false;
				}
				Queue<TaskItem> value = _priorityQueues.First().Value;
				task = value.Peek();
				return true;
			}
			finally
			{
				_lock.ExitReadLock();
			}
		}
	}
	internal enum TaskExecutionMode
	{
		Time
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	internal sealed class IsExternalInit
	{
	}
}