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");
}
}
}