Decompiled source of PreciseUI v0.3.4

PreciseUI.dll

Decompiled a day ago
using System;
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.Text;
using System.Text.RegularExpressions;
using Agents;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using ChainedPuzzles;
using GTFO.API.Utilities;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using Localization;
using Microsoft.CodeAnalysis;
using Player;
using TMPro;
using TenCC.Utils;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("PreciseUI")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PreciseUI")]
[assembly: AssemblyTitle("PreciseUI")]
[assembly: AssemblyVersion("1.0.0.0")]
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;
		}
	}
}
namespace Andocas.PreciseUI
{
	internal static class BioscanPatches
	{
		private static CP_Bioscan_Hud? callerBioscanHud;

		[HarmonyPatch(typeof(CP_Bioscan_Core), "OnSyncStateChange")]
		[HarmonyPostfix]
		private static void CP_Bioscan_Core_OnSyncStateChange_Postfix(CP_Bioscan_Core __instance)
		{
			CP_Bioscan_Graphics val = ((Il2CppObjectBase)__instance.m_graphics).TryCast<CP_Bioscan_Graphics>();
			if (Config.ShowPlayerScaling.Value && val != null && !((TMP_Text)val.m_textMeshRenderer).text.EndsWith("]</size>"))
			{
				CP_PlayerScanner component = ((Component)__instance).GetComponent<CP_PlayerScanner>();
				float[] source = ((!component.m_reqItemsEnabled) ? Il2CppArrayBase<float>.op_Implicit((Il2CppArrayBase<float>)(object)component.m_scanSpeeds) : new float[4]
				{
					((Il2CppArrayBase<float>)(object)component.m_scanSpeeds)[0],
					((Il2CppArrayBase<float>)(object)component.m_scanSpeeds)[0],
					((Il2CppArrayBase<float>)(object)component.m_scanSpeeds)[0],
					((Il2CppArrayBase<float>)(object)component.m_scanSpeeds)[0]
				});
				float exitScanMultiplier = 1f;
				if (component.IsExitScan && WardenObjectiveManager.Current.m_activeWardenObjectives.ContainsKey((LG_LayerType)0))
				{
					WardenObjectiveDataBlock val2 = WardenObjectiveManager.Current.m_activeWardenObjectives[(LG_LayerType)0];
					exitScanMultiplier = val2.ChainedPuzzleAtExitScanSpeedMultiplier;
				}
				string[] value = source.Select((float scanSpeed) => (scanSpeed <= 0f) ? "-" : $"{1f / (scanSpeed * exitScanMultiplier):F1}s").ToArray();
				TextMeshPro textMeshRenderer = val.m_textMeshRenderer;
				((TMP_Text)textMeshRenderer).text = ((TMP_Text)textMeshRenderer).text + "\n<size=70%>[" + string.Join("|", value) + "]</size>";
			}
		}

		[HarmonyPatch(typeof(CP_Bioscan_Hud), "SetVisible")]
		[HarmonyAfter(new string[] { "dev.AuriRex.gtfo.TheArchive" })]
		[HarmonyPostfix]
		private static void CP_Bioscan_Hud_SetVisible_Postfix(CP_Bioscan_Hud __instance, int puzzleIndex, bool visible)
		{
			if (Config.ShowChainedPuzzleProgress.Value && visible)
			{
				int? chainedPuzzleLength = GetChainedPuzzleLength(__instance);
				if (chainedPuzzleLength.HasValue && chainedPuzzleLength > 1)
				{
					__instance.m_atText = $" <#fff>({puzzleIndex + 1}/{chainedPuzzleLength})</color> {Text.Get(1085u)} ";
					__instance.m_enterSecurityScanText = $"{Text.Get(1088u)} <#fff>({puzzleIndex + 1}/{chainedPuzzleLength})</color>";
				}
			}
		}

		private static int? GetChainedPuzzleLength(CP_Bioscan_Hud targetBioscanHud)
		{
			foreach (ChainedPuzzleInstance componentsInChild in ((Component)((Component)targetBioscanHud).transform.parent).GetComponentsInChildren<ChainedPuzzleInstance>())
			{
				foreach (iChainedPuzzleCore item in (Il2CppArrayBase<iChainedPuzzleCore>)(object)componentsInChild.m_chainedPuzzleCores)
				{
					CP_Bioscan_Core val = ((Il2CppObjectBase)item).TryCast<CP_Bioscan_Core>();
					if (val != null)
					{
						if (((Il2CppObjectBase)val.m_hud).Pointer == ((Il2CppObjectBase)targetBioscanHud).Pointer)
						{
							return ((Il2CppArrayBase<iChainedPuzzleCore>)(object)componentsInChild.m_chainedPuzzleCores).Length;
						}
						continue;
					}
					CP_Cluster_Core val2 = ((Il2CppObjectBase)item).TryCast<CP_Cluster_Core>();
					if (val2 != null)
					{
						CP_Cluster_Hud val3 = ((Il2CppObjectBase)val2.m_hud).TryCast<CP_Cluster_Hud>();
						if (val3 != null && ((Il2CppObjectBase)val3.m_hud).Pointer == ((Il2CppObjectBase)targetBioscanHud).Pointer)
						{
							return ((Il2CppArrayBase<iChainedPuzzleCore>)(object)componentsInChild.m_chainedPuzzleCores).Length;
						}
					}
				}
			}
			return null;
		}

		[HarmonyPatch(typeof(CP_Bioscan_Hud), "Update")]
		[HarmonyPrefix]
		private static void CP_Bioscan_Hud_Update_Prefix_InstanceMarker(CP_Bioscan_Hud __instance)
		{
			callerBioscanHud = __instance;
		}

		[HarmonyPatch(typeof(CP_Bioscan_Hud), "Update")]
		[HarmonyPostfix]
		private static void CP_Bioscan_Hud_Update_Postfix_InstanceMarker()
		{
			callerBioscanHud = null;
		}

		[HarmonyPatch(typeof(InteractionGuiLayer), "SetMessage")]
		[HarmonyPatch(new Type[]
		{
			typeof(CharBuffer),
			typeof(ePUIMessageStyle),
			typeof(int)
		})]
		[HarmonyPrefix]
		private static void InteractionGuiLayer_SetMessage_Prefix(ref CharBuffer buffer)
		{
			if (callerBioscanHud == null || ((buffer.Length < 2 || ((Il2CppArrayBase<char>)(object)buffer.CharArray)[buffer.Length - 1] != '%') && (buffer.Length < 3 || ((Il2CppArrayBase<char>)(object)buffer.CharArray)[buffer.Length - 2] != '%' || ((Il2CppArrayBase<char>)(object)buffer.CharArray)[buffer.Length - 1] != ' ')))
			{
				return;
			}
			CP_PlayerScanner playerScanner = GetPlayerScanner(callerBioscanHud);
			if (playerScanner == null)
			{
				Utils.LogError("Couldn't find CP_PlayerScanner component for " + ((Object)callerBioscanHud).name);
				return;
			}
			float num = CP_PlayerScanner_MasterScan_Emulator(playerScanner);
			if (num > 0f)
			{
				float num2 = (1f - callerBioscanHud.m_progressSmooth) / num;
				if (Config.HudTimerFormat.Value == HudTimerFormats.MinutesAndSeconds || (Config.HudTimerFormat.Value == HudTimerFormats.Auto && num2 >= 60f))
				{
					float num3 = Mathf.Ceil(num2);
					int value = Mathf.FloorToInt(num3 / 60f);
					int value2 = Mathf.FloorToInt(num3 % 60f);
					buffer.Set($"{((Object)buffer).ToString().TrimEnd()} ({value:D2}:{value2:D2})");
				}
				if (Config.HudTimerFormat.Value == HudTimerFormats.Seconds || (Config.HudTimerFormat.Value == HudTimerFormats.Auto && num2 < 60f))
				{
					buffer.Set($"{((Object)buffer).ToString().TrimEnd()} ({num2:F1}<space=0.1em><size=70%>S</size>)");
				}
			}
		}

		private static CP_PlayerScanner? GetPlayerScanner(CP_Bioscan_Hud bioscanHud)
		{
			CP_PlayerScanner result = null;
			CP_Cluster_Core component = ((Component)bioscanHud).GetComponent<CP_Cluster_Core>();
			CP_Cluster_Hud component2 = ((Component)bioscanHud).GetComponent<CP_Cluster_Hud>();
			if (component == null || component2 == null)
			{
				result = ((Component)bioscanHud).GetComponent<CP_PlayerScanner>();
			}
			else
			{
				int num = -1;
				int num2 = -1;
				for (int i = 0; i < component2.m_clusterSize; i++)
				{
					if (((Il2CppArrayBase<bool>)(object)component2.m_localPlayerInScan)[i] && ((Il2CppArrayBase<bool>)(object)component2.m_visible)[i])
					{
						num = i;
						num2 = i;
						break;
					}
					if (((Il2CppArrayBase<bool>)(object)component2.m_visible)[i])
					{
						num2 = i;
					}
				}
				int num3 = ((num >= 0) ? num : ((num2 < 0) ? (-1) : num2));
				if (num3 >= 0)
				{
					CP_Bioscan_Core val = ((Il2CppObjectBase)((Il2CppArrayBase<iChainedPuzzleCore>)(object)component.m_childCores)[num3]).TryCast<CP_Bioscan_Core>();
					if (val == null)
					{
						Utils.LogError($"Cluster '{((Object)component).name}' child {num3} is not a CP_Bioscan_Core!");
					}
					else
					{
						result = ((Component)val).GetComponent<CP_PlayerScanner>();
					}
				}
			}
			return result;
		}

		private static float CP_PlayerScanner_MasterScan_Emulator(CP_PlayerScanner playerScanner)
		{
			//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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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)
			List<PlayerAgent> list = new List<PlayerAgent>();
			float num = 0f;
			List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
			int count = playerAgentsInLevel.Count;
			Vector3 position = ((Component)playerScanner).transform.position;
			list.Clear();
			for (int i = 0; i < count; i++)
			{
				if ((Object)(object)playerAgentsInLevel[i] != (Object)null && ((Agent)playerAgentsInLevel[i]).Alive)
				{
					Vector3 val = position - ((Agent)playerAgentsInLevel[i]).Position;
					if (((Vector3)(ref val)).sqrMagnitude < playerScanner.m_scanRadiusSqr)
					{
						list.Add(playerAgentsInLevel[i]);
					}
				}
			}
			int count2 = list.Count;
			bool flag = (Extensions.RequireAllPlayers(playerScanner.m_playerRequirement) ? (count2 == count) : ((!Extensions.RequireSoloPlayer(playerScanner.m_playerRequirement)) ? (count2 > 0) : (count2 == 1)));
			if (count2 > 0 && count2 - 1 < ((Il2CppArrayBase<float>)(object)playerScanner.m_scanSpeeds).Length)
			{
				num = ((Il2CppArrayBase<float>)(object)playerScanner.m_scanSpeeds)[count2 - 1];
			}
			if (flag && playerScanner.m_reqItemsEnabled)
			{
				num = ((Il2CppArrayBase<float>)(object)playerScanner.m_scanSpeeds)[0];
			}
			if (flag)
			{
				if (playerScanner.IsExitScan && WardenObjectiveManager.Current.m_activeWardenObjectives.ContainsKey((LG_LayerType)0))
				{
					WardenObjectiveDataBlock val2 = WardenObjectiveManager.Current.m_activeWardenObjectives[(LG_LayerType)0];
					num *= val2.ChainedPuzzleAtExitScanSpeedMultiplier;
				}
				if (list != null && list.Count > 0)
				{
					float num2 = 1f;
					for (int j = 0; j < list.Count; j++)
					{
						num2 += AgentModifierManager.ApplyModifier((Agent)(object)list[j], (AgentModifier)157, 1f) - 1f;
					}
					num *= Mathf.Max(num2, 0.1f);
				}
			}
			return num;
		}
	}
	internal static class Config
	{
		internal static readonly ConfigEntry<int> LocalplayerstatusHealthPrecision;

		internal static readonly ConfigEntry<int> LocalplayerstatusInfectionPrecision;

		internal static readonly ConfigEntry<bool> LocalplayerstatusInfectionSub10Enable;

		internal static readonly ConfigEntry<bool> LocalplayerstatusInfectionRateEnable;

		internal static readonly ConfigEntry<int> LocalplayerstatusInfectionRatePrecision;

		internal static readonly ConfigEntry<string> LocalplayerstatusStaminabarFormat;

		internal static readonly ConfigEntry<int> MapteamstatusHealthPrecision;

		internal static readonly ConfigEntry<int> MapteamstatusInfectionPrecision;

		internal static readonly ConfigEntry<bool> MapteamstatusInfectionSub10Enable;

		internal static readonly ConfigEntry<int> InventoryPrecision;

		internal static readonly ConfigEntry<bool> ShowPlayerScaling;

		internal static readonly ConfigEntry<bool> ShowChainedPuzzleProgress;

		internal static readonly ConfigEntry<HudTimerFormats> HudTimerFormat;

		static Config()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "PreciseUI.cfg"), true);
			LocalplayerstatusHealthPrecision = val.Bind<int>("Player status bar", "Health precision", 2, "Number of digits after the decimal place");
			LocalplayerstatusInfectionPrecision = val.Bind<int>("Player status bar", "Infection precision", 2, "Number of digits after the decimal place");
			LocalplayerstatusInfectionSub10Enable = val.Bind<bool>("Player status bar", "Show infection below 10%", true, "Show infection percentage even when infection is less than 10%");
			LocalplayerstatusInfectionRateEnable = val.Bind<bool>("Player status bar", "Show rate of infection", true, "Show the rate at which infection is increasing/decreasing");
			LocalplayerstatusInfectionRatePrecision = val.Bind<int>("Player status bar", "Infection rate precision", 2, "Number of digits after the decimal place (assuming \"Show rate of infection\" is enabled)");
			LocalplayerstatusStaminabarFormat = val.Bind<string>("Player status bar", "Stamina bar format", "BPM:{heart}{true_bpm:2} | STAM: {stamina:2} <size=80%>{stamina_rate:2}</size> | SPD: {current_speed:2}", "#\r\nWhatever you type here will be displayed between the health bar and the infection bar. Keywords surrounded by curly brackets, e.g. `{vanilla_bpm}`, will be replaced by the appropriate values.\r\n#\r\nMany keywords require rounding. These keywords must be followed by a colon and the desired number of decimal places, e.g. `{stamina:2}`.\r\n#\r\nRecognized keywords:\r\n- {heart}: Heart icon that changes color as pulse increases\r\n- {vanilla_bpm}: Pulse with some random variation, as displayed in vanilla\r\n- {true_bpm:N}: Pulse without any random variation\r\n- {stamina:N}: GTFO's internal stamina measurement, as a percentage from 0% to 100%\r\n- {stamina_rate:N}: The rate at which stamina is being consumed/regenerated per second\r\n- {sprint_speed:N}: Sprinting speed at your current stamina, in meters per second\r\n- {sprint_percent:N}: Sprinting speed at your current stamina, as a percentage of max sprinting speed\r\n- {current_speed:N}: The speed at which you're currently moving, in meters per second\r\n#\r\nYou can type whatever you want. Don't overthink it. For example, `My current pulse is {true_bpm:0} beats per minute` is perfectly valid.\r\n#\r\nFor a vanilla look, set to `PULSE:{heart}{vanilla_bpm}`.\r\n#");
			MapteamstatusHealthPrecision = val.Bind<int>("Team status (map view)", "Health precision", 2, "Number of digits after the decimal place");
			MapteamstatusInfectionPrecision = val.Bind<int>("Team status (map view)", "Infection precision", 2, "Number of digits after the decimal place");
			MapteamstatusInfectionSub10Enable = val.Bind<bool>("Team status (map view)", "Show infection below 10%", true, "Show infection percentage even when infection is less than 10%");
			InventoryPrecision = val.Bind<int>("Inventory", "Precision", 0, "Number of digits after the decimal place for your and your teammates' ammo/tool");
			ShowPlayerScaling = val.Bind<bool>("Bioscans", "Show player scaling", true, "Indicate how long each bioscan would take to complete with 1, 2, 3, or 4 players");
			ShowChainedPuzzleProgress = val.Bind<bool>("Bioscans", "Show chained puzzle progress", true, "Display how many sets of bioscans are in the current alarm");
			HudTimerFormat = val.Bind<HudTimerFormats>("Bioscans", "Timer format", HudTimerFormats.Auto, "\r\nDisplay time until completion of current bioscan\r\n- Seconds: Show seconds remaining, e.g. 68.5s\r\n- MinutesAndSeconds: Show MM:SS, e.g. 01:08\r\n- Auto: Switch between Seconds and MinutesAndSeconds automatically depending on the duration\r\n- None: Do not display timer\r\n");
			if (((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("dev.gtfomodding.gtfo-api"))
			{
				Utils.LogInfo("Creating LiveEdit Listener.");
				SetupLiveEditListener(val);
			}
			else
			{
				Utils.LogInfo("GTFO-API is not loaded. Proceeding without LiveEdit.");
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void SetupLiveEditListener(ConfigFile configFile)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			ConfigFile configFile2 = configFile;
			LiveEditListener val = LiveEdit.CreateListener(Path.GetDirectoryName(configFile2.ConfigFilePath), Path.GetFileName(configFile2.ConfigFilePath), false);
			val.FileChanged += (LiveEditEventHandler)delegate
			{
				configFile2.Reload();
			};
		}
	}
	internal enum HudTimerFormats
	{
		Auto,
		Seconds,
		MinutesAndSeconds,
		None
	}
	internal enum PuzzleProgressFormats
	{
		Roman,
		Arabic,
		None
	}
	internal static class FormatUtils
	{
		private static NumberFormatInfo? _numberFormat;

		internal static NumberFormatInfo NumberFormat
		{
			get
			{
				if (_numberFormat == null)
				{
					_numberFormat = new NumberFormatInfo
					{
						NumberDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator,
						NumberGroupSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator,
						NumberGroupSizes = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes,
						PercentPositivePattern = 1,
						PercentNegativePattern = 1,
						PercentDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.PercentDecimalSeparator,
						PercentGroupSeparator = CultureInfo.CurrentCulture.NumberFormat.PercentGroupSeparator,
						PercentGroupSizes = CultureInfo.CurrentCulture.NumberFormat.PercentGroupSizes
					};
				}
				return _numberFormat;
			}
		}
	}
	internal static class PluginInfo
	{
		public const string GUID = "Andocas.PreciseUI";

		public const string NAME = "PreciseUI";

		public const string VERSION = "0.3.3";
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Andocas.PreciseUI", "PreciseUI", "0.3.3")]
	internal class Plugin : BasePlugin
	{
		private class UnityComponent : MonoBehaviour
		{
			private void LateUpdate()
			{
				PUI_Patches.LateUpdateInfectionText();
			}
		}

		public override void Load()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			Utils.LogInfo("Andocas.PreciseUI is loading...");
			Harmony val = new Harmony("Andocas.PreciseUI");
			val.PatchAll(typeof(PUI_Patches));
			val.PatchAll(typeof(BioscanPatches));
			((BasePlugin)this).AddComponent<UnityComponent>();
			_ = Config.LocalplayerstatusHealthPrecision;
			Utils.LogInfo("Andocas.PreciseUI is loaded");
		}
	}
	internal static class PUI_Patches
	{
		private static float _prefix_stamina = 0f;

		private static float sprint_stamrate = 0f;

		private static float sprint_stamrate_update_time = 0f;

		private static readonly Regex bpmSpriteRegex = new Regex("<sprite name=\"heartbeat\" color=#[A-Z0-9]{8}>", RegexOptions.Compiled);

		private static readonly Regex staminabarFormatRegex = new Regex("{(heart|vanilla_bpm|true_bpm|stamina|stamina_rate|sprint_percent|sprint_speed|current_speed)(?::([0-9]+))?}", RegexOptions.Compiled);

		[HarmonyPatch(typeof(PUI_LocalPlayerStatus), "UpdateHealth")]
		[HarmonyPostfix]
		private static void UpdateHealth_Postfix(PUI_LocalPlayerStatus __instance, float health)
		{
			string text = health.ToString("p" + Config.LocalplayerstatusHealthPrecision.Value, FormatUtils.NumberFormat);
			((TMP_Text)__instance.m_healthText).text = text;
			((TMP_Text)__instance.m_healthText).enableWordWrapping = false;
			((TMP_Text)__instance.m_healthText).enableAutoSizing = true;
			((TMP_Text)__instance.m_healthText).fontSizeMin = 0f;
			((TMP_Text)__instance.m_healthText).fontSizeMax = 22f;
		}

		internal static void LateUpdateInfectionText()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0051: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			if ((int)GameStateManager.CurrentStateName != 10)
			{
				return;
			}
			PUI_LocalPlayerStatus playerStatus = GuiManager.PlayerLayer.m_playerStatus;
			float infection = playerStatus.m_infection;
			LocalPlayerAgent val = ((Il2CppObjectBase)PlayerManager.GetLocalPlayerAgent()).Cast<LocalPlayerAgent>();
			float num = 0f;
			if (Config.LocalplayerstatusInfectionRateEnable.Value)
			{
				EV_TargetData effectVolumeTargetData = ((PlayerAgent)val).EffectVolumeTargetData;
				EV_ModificationData val2 = default(EV_ModificationData);
				float num2 = 1f;
				Enumerator<EffectVolume> enumerator = EffectVolumeManager.volumes.GetEnumerator();
				while (enumerator.MoveNext())
				{
					EffectVolume current = enumerator.Current;
					current.Update(ref effectVolumeTargetData, ref val2, num2);
				}
				num += val2.infection;
				if (infection > 0.85f)
				{
					num -= 0.01f;
				}
				IInteractable bestSelectedInteract = ((PlayerAgent)val).Interaction.m_bestSelectedInteract;
				Interact_DisinfectionStation val3 = ((bestSelectedInteract != null) ? ((Il2CppObjectBase)bestSelectedInteract).TryCast<Interact_DisinfectionStation>() : null);
				if (val3 != null && val3.m_station.m_localDisinfectionActive)
				{
					num -= val3.m_station.m_disinfectSpeed;
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(infection.ToString("P" + Config.LocalplayerstatusInfectionPrecision.Value, FormatUtils.NumberFormat));
			if (Config.LocalplayerstatusInfectionRateEnable.Value && num != 0f)
			{
				stringBuilder.Append(" <size=80%>");
				if (num >= 0f)
				{
					stringBuilder.Append('+');
				}
				stringBuilder.Append(num.ToString("P" + Config.LocalplayerstatusInfectionRatePrecision.Value, FormatUtils.NumberFormat));
				stringBuilder.Append("/s</size>");
			}
			((TMP_Text)playerStatus.m_infectionText).text = stringBuilder.ToString();
			if (0f < infection && infection <= 0.1f && Config.LocalplayerstatusInfectionSub10Enable.Value)
			{
				((Behaviour)playerStatus.m_infectionText).enabled = true;
				((Graphic)playerStatus.m_infectionText).color = playerStatus.m_infectionLow;
				((TMP_Text)playerStatus.m_infectionText).fontSize = 14f;
			}
			else if (0.1f <= infection)
			{
				((TMP_Text)playerStatus.m_infectionText).fontSize = 18f;
			}
		}

		[HarmonyPatch(typeof(PlayerStamina), "UseRunStamina")]
		[HarmonyPrefix]
		private static void UpdateSprintStamratePart1(PlayerStamina __instance)
		{
			_prefix_stamina = __instance.Stamina;
		}

		[HarmonyPatch(typeof(PlayerStamina), "UseRunStamina")]
		[HarmonyPostfix]
		private static void UpdateSprintStamratePart2(PlayerStamina __instance, float deltaTime)
		{
			sprint_stamrate = (__instance.Stamina - _prefix_stamina) / deltaTime;
			sprint_stamrate_update_time = Time.time;
		}

		[HarmonyPatch(typeof(PlayerStamina), "LateUpdate")]
		[HarmonyPrefix]
		private static void UpdateStaminaTextPart1(PlayerStamina __instance)
		{
			_prefix_stamina = __instance.Stamina;
		}

		[HarmonyPatch(typeof(PlayerStamina), "LateUpdate")]
		[HarmonyPostfix]
		private static void UpdateStaminaTextPart2(PlayerStamina __instance)
		{
			PlayerStamina __instance2 = __instance;
			PlayerAgent lpa = PlayerManager.GetLocalPlayerAgent();
			PUI_LocalPlayerStatus pui_lps = GuiManager.PlayerLayer.m_playerStatus;
			string bpm_sprite = bpmSpriteRegex.Match(((TMP_Text)pui_lps.m_pulseText).text).Value;
			float num = (__instance2.Stamina - _prefix_stamina) / Time.deltaTime;
			if (Time.time - sprint_stamrate_update_time > Time.deltaTime * 5f)
			{
				sprint_stamrate = 0f;
			}
			float stamrate = num + sprint_stamrate;
			float sprint_speed = __instance2.MoveSpeedModifier * __instance2.PlayerData.runMoveSpeed;
			((TMP_Text)pui_lps.m_pulseText).enableAutoSizing = false;
			((TMP_Text)pui_lps.m_pulseText).text = staminabarFormatRegex.Replace(Config.LocalplayerstatusStaminabarFormat.Value, delegate(Match match)
			{
				//IL_027d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0282: Unknown result type (might be due to invalid IL or missing references)
				string value = match.Groups[1].Value;
				string value2 = match.Groups[2].Value;
				switch (value)
				{
				case "heart":
					return bpm_sprite;
				case "vanilla_bpm":
					return pui_lps.m_randomizedBPM;
				case "true_bpm":
					return pui_lps.m_currentBPM.ToString("N" + value2, FormatUtils.NumberFormat);
				case "stamina":
					return __instance2.Stamina.ToString("P" + value2, FormatUtils.NumberFormat);
				case "stamina_rate":
					return ((stamrate >= 0f) ? "+" : string.Empty) + stamrate.ToString("P" + value2, FormatUtils.NumberFormat) + "/s";
				case "sprint_percent":
					return __instance2.MoveSpeedModifier.ToString("P" + value2, FormatUtils.NumberFormat);
				case "sprint_speed":
					return sprint_speed.ToString("N" + value2, FormatUtils.NumberFormat) + "m/s";
				case "current_speed":
				{
					Vector3 horizontalVelocity = lpa.Locomotion.HorizontalVelocity;
					return ((Vector3)(ref horizontalVelocity)).magnitude.ToString("N" + value2, FormatUtils.NumberFormat) + "m/s";
				}
				default:
					Utils.LogError("UpdateStaminaTextPart2: Unknown key `" + value + "`");
					return "";
				}
			});
		}

		[HarmonyPatch(typeof(PUI_Inventory), "SetSlotAmmo")]
		[HarmonyPostfix]
		private static void SetSlotAmmo(PUI_Inventory __instance, InventorySlot slot, float inPackRel)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (Config.InventoryPrecision.Value != 0)
			{
				string text = inPackRel.ToString("p" + Config.InventoryPrecision.Value, FormatUtils.NumberFormat);
				if (__instance.m_inventorySlots.ContainsKey(slot))
				{
					((TMP_Text)__instance.m_inventorySlots[slot].m_selected_ammoTotal).text = text;
					((TMP_Text)__instance.m_inventorySlots[slot].m_slim_ammoTotal).text = text;
				}
			}
		}

		[HarmonyPatch(typeof(PUI_Inventory), "SetHeader")]
		[HarmonyPrefix]
		private static void SetPageMapInventoryHeader(PUI_Inventory __instance, ref string header)
		{
			PlayerAgent val = null;
			Enumerator<PlayerAgent> enumerator = PlayerManager.PlayerAgentsInLevel.GetEnumerator();
			while (enumerator.MoveNext())
			{
				PlayerAgent current = enumerator.Current;
				if ((Object)(object)current.Owner == (Object)(object)__instance.m_owner)
				{
					val = current;
					break;
				}
			}
			if (val == null)
			{
				Utils.LogWarning($"SetPageMapInventoryHeader: No PlayerAgent found for PUI_Inventory owner {__instance.m_owner}");
				return;
			}
			string playerName = val.PlayerName;
			string text = Math.Clamp(((Dam_SyncedDamageBase)val.Damage).GetHealthRel(), 0f, 1f).ToString("p" + Config.MapteamstatusHealthPrecision.Value, FormatUtils.NumberFormat);
			float infection = val.Damage.Infection;
			string value = infection.ToString("p" + Config.MapteamstatusInfectionPrecision.Value, FormatUtils.NumberFormat);
			if (infection <= 0f)
			{
				header = playerName + " <color=orange>(" + text + ")</color>";
			}
			else if (infection <= 0.1f && Config.MapteamstatusInfectionSub10Enable.Value)
			{
				header = $"{playerName} <color=orange>({text})</color><color=#33FFBB><size=70%>({value})</size></color>";
			}
			else
			{
				header = $"{playerName} <color=orange>({text})</color><color=#00FFA8>({value})</color>";
			}
		}
	}
	internal static class Utils
	{
		private static readonly ManualLogSource logger = Logger.CreateLogSource("Andocas.PreciseUI");

		public static void LogInfo(object? o)
		{
			logger.LogInfo((object)GetMessage(o));
		}

		public static void LogWarning(object? o)
		{
			logger.LogWarning((object)GetMessage(o));
		}

		public static void LogError(object? o)
		{
			logger.LogError((object)GetMessage(o));
		}

		private static string GetMessage(object? o)
		{
			if (o == null)
			{
				return "No string representation for <null>";
			}
			return o.ToString() ?? $"No string representation for <{o.GetType()}>";
		}

		public static void SendLocalMessage(string text)
		{
			LogInfo("SendLocalMessage: " + text);
			GuiManager.PlayerLayer.m_gameEventLog.AddLogItem(text, (eGameEventChatLogType)2);
			CM_PageLoadout.Current.m_gameEventLog.AddLogItem(text, (eGameEventChatLogType)2);
		}

		public static void SendChatMessage(string text)
		{
			LogInfo("SendChatMessage: " + text);
			string text2 = "<#ff0>";
			int num = 50 - text2.Length;
			while (text.Length > num)
			{
				PlayerChatManager.WantToSentTextMessage(PlayerManager.GetLocalPlayerAgent(), text2 + text.Substring(0, num).Trim(), (PlayerAgent)null);
				text = text.Substring(num).Trim();
			}
			PlayerChatManager.WantToSentTextMessage(PlayerManager.GetLocalPlayerAgent(), text2 + text, (PlayerAgent)null);
		}
	}
}