Decompiled source of PreciseUI v0.2.4

PreciseUI.dll

Decompiled 3 months ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Player;
using TMPro;
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 PreciseUI
{
	internal static class PUI_Patches
	{
		private static float _prefix_stamina;

		private static float sprint_stamrate;

		private static float sprint_stamrate_update_time;

		[HarmonyPatch(typeof(PUI_LocalPlayerStatus), "UpdateHealth")]
		[HarmonyPostfix]
		private static void UpdateHealth_Postfix(PUI_LocalPlayerStatus __instance, float health)
		{
			string text = health.ToString("p" + ConfigManager.localplayerstatus_health_precision.Value);
			((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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_0229: 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 = new LocalPlayerAgent(((Il2CppObjectBase)PlayerManager.GetLocalPlayerAgent()).Pointer);
			float num = 0f;
			if (ConfigManager.localplayerstatus_infection_rate_enable.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;
				}
				if (((PlayerAgent)val).Interaction.m_bestSelectedInteract != null && ((Il2CppObjectBase)((PlayerAgent)val).Interaction.m_bestSelectedInteract).ObjectClass == Il2CppClassPointerStore.GetNativeClassPointer(typeof(Interact_DisinfectionStation)))
				{
					Interact_DisinfectionStation val3 = new Interact_DisinfectionStation(((Il2CppObjectBase)((PlayerAgent)val).Interaction.m_bestSelectedInteract).Pointer);
					if (val3.m_station.m_localDisinfectionActive)
					{
						num -= val3.m_station.m_disinfectSpeed;
					}
				}
			}
			string text = infection.ToString("p" + ConfigManager.localplayerstatus_infection_precision.Value);
			if (ConfigManager.localplayerstatus_infection_rate_enable.Value && num != 0f)
			{
				string text2 = num.ToString("p" + ConfigManager.localplayerstatus_infection_rate_precision.Value);
				if (num >= 0f)
				{
					text2 = "+" + text2;
				}
				text = text + " <size=80%>" + text2 + "/s</size>";
			}
			((TMP_Text)playerStatus.m_infectionText).text = text;
			if (0f < infection && infection <= 0.1f && ConfigManager.localplayerstatus_infection_sub10_enable.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)
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent();
			PUI_LocalPlayerStatus playerStatus = GuiManager.PlayerLayer.m_playerStatus;
			string value = Regex.Match(((TMP_Text)playerStatus.m_pulseText).text, "<sprite name=\"heartbeat\" color=#[A-Z0-9]{8}>").Value;
			float num = (__instance.Stamina - _prefix_stamina) / Time.deltaTime;
			if (Time.time - sprint_stamrate_update_time > Time.deltaTime * 5f)
			{
				sprint_stamrate = 0f;
			}
			float value2 = num + sprint_stamrate;
			float value3 = __instance.MoveSpeedModifier * __instance.PlayerData.runMoveSpeed;
			string value4 = ConfigManager.localplayerstatus_staminabar_format.Value;
			value4 = value4.Replace("{heart}", value);
			value4 = value4.Replace("{vanilla_bpm}", playerStatus.m_randomizedBPM);
			value4 = Utils.DoCustomInterpolation(value4, "true_bpm", playerStatus.m_currentBPM, 'N');
			value4 = Utils.DoCustomInterpolation(value4, "stamina", __instance.Stamina, 'P');
			value4 = Utils.DoCustomInterpolation(value4, "stamina_rate", value2, 'P', "", "/s", always_sign: true);
			value4 = Utils.DoCustomInterpolation(value4, "sprint_percent", __instance.MoveSpeedModifier, 'P');
			value4 = Utils.DoCustomInterpolation(value4, "sprint_speed", value3, 'N', "", "m/s");
			string string_to_interpolate = value4;
			Vector3 horizontalVelocity = localPlayerAgent.Locomotion.HorizontalVelocity;
			value4 = Utils.DoCustomInterpolation(string_to_interpolate, "current_speed", ((Vector3)(ref horizontalVelocity)).magnitude, 'N', "", "m/s");
			((TMP_Text)playerStatus.m_pulseText).text = value4;
			((TMP_Text)playerStatus.m_pulseText).enableAutoSizing = false;
		}

		[HarmonyPatch(typeof(PUI_Inventory), "SetSlotAmmo")]
		[HarmonyPostfix]
		private static void SetSlotAmmo(PUI_Inventory __instance, InventorySlot slot, float inPackRel)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigManager.inventory_precision.Value != 0)
			{
				string text = inPackRel.ToString("p" + ConfigManager.inventory_precision.Value);
				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")]
		[HarmonyPostfix]
		private static void SetPageMapInventoryHeader(PUI_Inventory __instance)
		{
			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)
			{
				string playerName = val.PlayerName;
				string text = Math.Clamp(((Dam_SyncedDamageBase)val.Damage).GetHealthRel(), 0f, 1f).ToString("p" + ConfigManager.mapteamstatus_health_precision.Value);
				float infection = val.Damage.Infection;
				string value = infection.ToString("p" + ConfigManager.mapteamstatus_infection_precision.Value);
				if (infection <= 0f)
				{
					((TMP_Text)__instance.m_headerTxt).text = playerName + " <color=orange>(" + text + ")</color>";
				}
				else if (infection <= 0.1f && ConfigManager.mapteamstatus_infection_sub10_enable.Value)
				{
					((TMP_Text)__instance.m_headerTxt).text = $"{playerName} <color=orange>({text})</color><color=#33FFBB><size=70%>({value})</size></color>";
				}
				else
				{
					((TMP_Text)__instance.m_headerTxt).text = $"{playerName} <color=orange>({text})</color><color=#00FFA8>({value})</color>";
				}
			}
		}
	}
	internal static class Utils
	{
		private static ManualLogSource logger = Logger.CreateLogSource("PreciseUI");

		public static void LogInfo(string text)
		{
			logger.LogInfo((object)text);
		}

		public static void LogInfo(object o)
		{
			LogInfo(o.ToString() ?? "No string representation for object");
		}

		public static void LogError(string text)
		{
			logger.LogError((object)text);
		}

		public static void LogError(object o)
		{
			LogError(o.ToString() ?? "No string representation for object");
		}

		public static void SendChatMessage(string text)
		{
			LogInfo("SendChatMessage: " + text);
			string text2 = "<color=yellow>";
			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);
		}

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

		public static string DoCustomInterpolation(string string_to_interpolate, string key, float value, char format_specifier, string prefix = "", string suffix = "", bool always_sign = false)
		{
			string prefix2 = prefix;
			string suffix2 = suffix;
			return Regex.Replace(string_to_interpolate, "{" + key + ":([0-9]+)}", ReplaceOneInstance);
			string ReplaceOneInstance(Match match)
			{
				string text = "";
				if (always_sign && value >= 0f)
				{
					text = "+";
				}
				return prefix2 + text + value.ToString(format_specifier + match.Groups[1].Value) + suffix2;
			}
		}
	}
	[BepInPlugin("PreciseUI", "PreciseUI", "0.2.4")]
	internal class Plugin : BasePlugin
	{
		private class UnityPlugin : 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("PreciseUI is loading...");
			Harmony val = new Harmony("PreciseUI");
			val.PatchAll(typeof(PUI_Patches));
			((BasePlugin)this).AddComponent<UnityPlugin>();
			_ = ConfigManager.localplayerstatus_health_precision;
			Utils.LogInfo("PreciseUI is loaded");
		}
	}
	internal static class ConfigManager
	{
		public static readonly ConfigEntry<int> localplayerstatus_health_precision;

		public static readonly ConfigEntry<int> localplayerstatus_infection_precision;

		public static readonly ConfigEntry<bool> localplayerstatus_infection_sub10_enable;

		public static readonly ConfigEntry<bool> localplayerstatus_infection_rate_enable;

		public static readonly ConfigEntry<int> localplayerstatus_infection_rate_precision;

		public static readonly ConfigEntry<string> localplayerstatus_staminabar_format;

		public static readonly ConfigEntry<int> mapteamstatus_health_precision;

		public static readonly ConfigEntry<int> mapteamstatus_infection_precision;

		public static readonly ConfigEntry<bool> mapteamstatus_infection_sub10_enable;

		public static readonly ConfigEntry<int> inventory_precision;

		static ConfigManager()
		{
			//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);
			localplayerstatus_health_precision = val.Bind<int>("Player status bar", "Health precision", 2, "Number of digits after the decimal place");
			localplayerstatus_infection_precision = val.Bind<int>("Player status bar", "Infection precision", 2, "Number of digits after the decimal place");
			localplayerstatus_infection_sub10_enable = val.Bind<bool>("Player status bar", "Show infection below 10%", true, "Show infection percentage even when infection is less than 10%");
			localplayerstatus_infection_rate_enable = val.Bind<bool>("Player status bar", "Show rate of infection", true, "Show the rate at which infection is increasing/decreasing");
			localplayerstatus_infection_rate_precision = val.Bind<int>("Player status bar", "Infection rate precision", 2, "Number of digits after the decimal place (assuming \"Show rate of infection\" is enabled)");
			localplayerstatus_staminabar_format = 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#");
			mapteamstatus_health_precision = val.Bind<int>("Team status (map view)", "Health precision", 2, "Number of digits after the decimal place");
			mapteamstatus_infection_precision = val.Bind<int>("Team status (map view)", "Infection precision", 2, "Number of digits after the decimal place");
			mapteamstatus_infection_sub10_enable = val.Bind<bool>("Team status (map view)", "Show infection below 10%", true, "Show infection percentage even when infection is less than 10%");
			inventory_precision = val.Bind<int>("Inventory", "Precision", 0, "Number of digits after the decimal place for your and your teammates' ammo/tool");
		}
	}
}