using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using CellMenu; using EWC.API; using EWC.API.Accuracy; using EWC.CustomWeapon; using EWC.CustomWeapon.Properties; using EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components; using EWC.CustomWeapon.Structs; using Enemies; using GTFO.API; using GTFO.API.Utilities; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using LevelGeneration; using Localization; using Microsoft.CodeAnalysis; using Player; using SNetwork; using StatDisplay.Attributes; using StatDisplay.Config; using StatDisplay.Config.Archive; using StatDisplay.Config.Vanilla; using StatDisplay.Data; using StatDisplay.Dependencies; using StatDisplay.Handler; using StatDisplay.Networking; using StatDisplay.Networking.Events; using StatDisplay.Utils.Extensions; using TMPro; using TheArchive; using TheArchive.Core; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Settings; using TheArchive.Core.Localization; using TheArchive.Core.Models; using TheArchive.Interfaces; using TheArchive.Utilities; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("StatDisplay")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+bb549e3583571ce3f7196f9b4ac06eb92714f259")] [assembly: AssemblyProduct("StatDisplay")] [assembly: AssemblyTitle("StatDisplay")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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 StatDisplay { internal static class DinoLogger { private static ManualLogSource logger = Logger.CreateLogSource("StatDisplay"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (logger != null) { logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.StatDisplay", "StatDisplay", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] internal sealed class EntryPoint : BasePlugin { public const string MODNAME = "StatDisplay"; private IEnumerable _cleanupCallbacks; private IEnumerable _enterCallbacks; private IEnumerable _buildStartCallbacks; private Type[] _allTypes; 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 _allTypes = GetTypesSafe(); Harmony val = new Harmony("StatDisplay"); Type[] allTypes = _allTypes; foreach (Type type in allTypes) { if (((MemberInfo)type).GetCustomAttribute() != null) { val.PatchAll(type); } } CacheFrequentCallbacks(); InvokeCallbacks(); LevelAPI.OnLevelCleanup += RunFrequentCallback(_cleanupCallbacks); LevelAPI.OnEnterLevel += RunFrequentCallback(_enterCallbacks); LevelAPI.OnBuildStart += RunFrequentCallback(_buildStartCallbacks); AssetAPI.OnStartupAssetsLoaded += InvokeCallbacks; ((BasePlugin)this).Log.LogMessage((object)"Loaded StatDisplay"); } private Type[] GetTypesSafe() { IEnumerable source; try { source = ((object)this).GetType().Assembly.DefinedTypes; } catch (ReflectionTypeLoadException ex) { source = ex.Types.Where((Type type) => type != null); } if (!ArchiveWrapper.HasArchive) { return source.Where((Type type) => !type.FullName.Contains("Archive")).ToArray(); } return source.ToArray(); } private static Action RunFrequentCallback(IEnumerable callbacks) { return delegate { foreach (MethodInfo callback in callbacks) { callback.Invoke(null, null); } }; } private void CacheFrequentCallbacks() { IEnumerable source = from method in ((IEnumerable)_allTypes).SelectMany((Func>)AccessTools.GetDeclaredMethods) where method.IsStatic select method; _cleanupCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute() != null); _enterCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute() != null); _buildStartCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute() != null); } private void InvokeCallbacks() where T : Attribute { foreach (MethodInfo item in from method in ((IEnumerable)_allTypes).SelectMany((Func>)AccessTools.GetDeclaredMethods) where method.GetCustomAttribute() != null where method.IsStatic select method) { item.Invoke(null, null); } } } public static class StatManager { private static readonly Dictionary _remotePlayers = new Dictionary(); private static bool _masterHasMod = false; public static IReadOnlyCollection RemotePlayers => _remotePlayers.Values; public static bool MasterHasMod { get { return _masterHasMod; } private set { if (_masterHasMod == value) { return; } _masterHasMod = value; foreach (SNet_Slot item in (Il2CppArrayBase)(object)SNet.Slots.PlayerSlots) { if ((Object)(object)item.player != (Object)null && item.player.IsBot) { StatHandler.AddPlayer(item.player, MasterHasMod, onlyIfExists: true); } } } } public static bool PlayerHasMod(SNet_Player player) { if (player.IsLocal || (MasterHasMod && player.IsBot)) { return true; } return _remotePlayers.ContainsKey(player.Lookup); } [InvokeOnLoad] private static void Init() { SNet_Events.OnMasterChanged += Action.op_Implicit((Action)OnMasterSet); } internal static void AddPlayer(SNet_Player player, bool hasMod) { if (hasMod) { if (player.IsMaster) { MasterHasMod = true; } if (!player.IsLocal && !player.IsBot) { _remotePlayers.TryAdd(player.Lookup, player); } } StatHandler.AddPlayer(player, hasMod); } internal static void RemovePlayer(SNet_Player player) { StatHandler.RemovePlayer(player); _remotePlayers.Remove(player.Lookup); } internal static void OnMasterSet() { MasterHasMod = SNet.IsMaster || _remotePlayers.ContainsKey(SNet.Master.Lookup); StatHandler.OnMasterSet(); } internal static void Clear() { _masterHasMod = false; _remotePlayers.Clear(); StatHandler.Clear(); } } } namespace StatDisplay.Utils.Extensions { internal static class SlotExtensions { public static AccSlotType ToAccSlotType(this AmmoType ammo) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 if ((int)ammo != 0) { if ((int)ammo == 1) { return AccSlotType.Secondary; } return AccSlotType.All; } return AccSlotType.Primary; } public static DamSlotType ToDamSlotType(this AmmoType slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected I4, but got Unknown return (int)slot switch { 0 => DamSlotType.Primary, 1 => DamSlotType.Secondary, 2 => DamSlotType.Tool, 4 => DamSlotType.Melee, _ => DamSlotType.All, }; } public static AccSlotType ToAccSlotType(this InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)slot != 1) { if ((int)slot == 2) { return AccSlotType.Secondary; } return AccSlotType.All; } return AccSlotType.Primary; } public static DamSlotType ToDamSlotType(this InventorySlot slot) { //IL_0000: 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) //IL_0014: Expected I4, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 switch (slot - 1) { default: if ((int)slot == 10) { return DamSlotType.Melee; } return DamSlotType.All; case 0: return DamSlotType.Primary; case 1: return DamSlotType.Secondary; case 2: return DamSlotType.Tool; } } public static InventorySlot ToInventorySlot(this AmmoType ammo) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected I4, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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) //IL_0025: 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_0029: Unknown result type (might be due to invalid IL or missing references) return (InventorySlot)((int)ammo switch { 0 => 1, 1 => 2, 2 => 3, 3 => 4, _ => 0, }); } public static AmmoType ToAmmoType(this InventorySlot slot) { //IL_0000: 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) //IL_0018: Expected I4, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) return (AmmoType)((slot - 1) switch { 0 => 0, 1 => 1, 2 => 2, 3 => 3, _ => 4, }); } } internal static class StringExtensions { public static T ToEnum(this string? value, T defaultValue) where T : struct { if (string.IsNullOrEmpty(value)) { return defaultValue; } if (!Enum.TryParse(value.Replace(" ", null), ignoreCase: true, out var result)) { return defaultValue; } return result; } public static bool TryToEnum(this string? value, out T enumValue) where T : struct { enumValue = default(T); if (string.IsNullOrEmpty(value)) { return false; } return Enum.TryParse(value.Replace(" ", null), ignoreCase: true, out enumValue); } } } namespace StatDisplay.Patches { [HarmonyPatch] internal static class AccuracyPatches { private struct CacheInfo { public AccSlotType slot; public ulong lookup; public readonly bool[,] history; public readonly short[,] delta; public readonly bool Valid => slot != AccSlotType.All; public CacheInfo() { slot = AccSlotType.All; lookup = 0uL; history = new bool[2, 2]; delta = new short[3, 3]; } } private static CacheInfo _cache = new CacheInfo(); private static void CacheInc(AccShotType shotType, AccStatType statType, bool checkHistory = false) { if (checkHistory) { if (_cache.history[(uint)shotType, (uint)statType]) { return; } _cache.history[(uint)shotType, (uint)statType] = true; } _cache.delta[(uint)shotType, (uint)statType]++; } private static void ResetHistory(AccShotType shotType) { for (int i = 0; i < 2; i++) { _cache.history[(uint)shotType, i] = false; } } [HarmonyPatch(typeof(ShotgunSynced), "Fire")] [HarmonyPatch(typeof(BulletWeaponSynced), "Fire")] [HarmonyPatch(typeof(Shotgun), "Fire")] [HarmonyPatch(typeof(BulletWeapon), "Fire")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_Fire(BulletWeapon __instance) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)((Item)__instance).Owner == (Object)null)) { SNet_Player owner = ((Item)__instance).Owner.Owner; if ((owner.IsLocal || (SNet.IsMaster && owner.IsBot)) && !EWCWrapper.HasCWC(__instance)) { _cache.slot = ((ItemEquippable)__instance).AmmoType.ToAccSlotType(); _cache.lookup = owner.Lookup; CacheInc(AccShotType.Group, AccStatType.Fired); } } } [HarmonyPatch(typeof(ShotgunSynced), "Fire")] [HarmonyPatch(typeof(BulletWeaponSynced), "Fire")] [HarmonyPatch(typeof(Shotgun), "Fire")] [HarmonyPatch(typeof(BulletWeapon), "Fire")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_Fire() { if (!_cache.Valid) { return; } StatHandler.AddAccuracyDelta(_cache.lookup, _cache.slot, _cache.delta); _cache.slot = AccSlotType.All; _cache.lookup = 0uL; short[,] delta = _cache.delta; for (int i = 0; i < delta.GetLength(0); i++) { for (int j = 0; j < delta.GetLength(1); j++) { delta[i, j] = 0; } } bool[,] history = _cache.history; for (int k = 0; k < history.GetLength(0); k++) { for (int l = 0; l < history.GetLength(1); l++) { history[k, l] = false; } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_CastWeaponRay(WeaponHitData weaponRayData) { if (_cache.Valid && !(weaponRayData.maxRayDist < 100f)) { ResetHistory(AccShotType.Shot); CacheInc(AccShotType.Shot, AccStatType.Fired); CacheInc(AccShotType.Full, AccStatType.Fired); } } [HarmonyPatch(typeof(Dam_EnemyDamageLimb), "BulletDamage")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_EnemyDamage(Dam_EnemyDamageLimb __instance) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 if (_cache.Valid) { CacheInc(AccShotType.Shot, AccStatType.Hit, checkHistory: true); CacheInc(AccShotType.Group, AccStatType.Hit, checkHistory: true); CacheInc(AccShotType.Full, AccStatType.Hit); if ((int)__instance.m_type == 1) { CacheInc(AccShotType.Shot, AccStatType.Crit, checkHistory: true); CacheInc(AccShotType.Group, AccStatType.Crit, checkHistory: true); CacheInc(AccShotType.Full, AccStatType.Crit); } } } } [HarmonyPatch] internal static class DamagePatches { private struct CacheInfo { public DamSlotType slot; public ulong lookup; public readonly bool Valid { get { if (slot != DamSlotType.All) { return lookup != 0; } return false; } } public CacheInfo() { slot = DamSlotType.All; lookup = 0uL; } } private static CacheInfo _cache = new CacheInfo(); private static readonly Random _random = new Random(); [InvokeOnLoad] private static void Init() { EWCWrapper.AddProjectileHitCallback(delegate(ulong lookup, DamSlotType slot) { _cache.slot = slot; _cache.lookup = lookup; }); } private static void ClearCache() { _cache.slot = DamSlotType.All; _cache.lookup = 0uL; } [HarmonyPatch(typeof(ShotgunSynced), "Fire")] [HarmonyPatch(typeof(BulletWeaponSynced), "Fire")] [HarmonyPatch(typeof(Shotgun), "Fire")] [HarmonyPatch(typeof(BulletWeapon), "Fire")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_Fire(BulletWeapon __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) _cache.slot = ((ItemEquippable)__instance).AmmoType.ToDamSlotType(); _cache.lookup = ((Item)__instance).Owner.Owner.Lookup; } [HarmonyPatch(typeof(ShotgunSynced), "Fire")] [HarmonyPatch(typeof(BulletWeaponSynced), "Fire")] [HarmonyPatch(typeof(Shotgun), "Fire")] [HarmonyPatch(typeof(BulletWeapon), "Fire")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_Fire() { ClearCache(); } [HarmonyPatch(typeof(MeleeWeaponFirstPerson), "DoAttackDamage")] [HarmonyPatch(typeof(MeleeWeaponThirdPerson), "DoAttackDamage")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_Attack(ItemEquippable __instance) { _cache.slot = DamSlotType.Melee; _cache.lookup = ((Item)__instance).Owner.Owner.Lookup; } [HarmonyPatch(typeof(MeleeWeaponFirstPerson), "DoAttackDamage")] [HarmonyPatch(typeof(MeleeWeaponThirdPerson), "DoAttackDamage")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_Attack() { ClearCache(); } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireMaster")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_SentryFire(SentryGunInstance_Firing_Bullets __instance) { CacheTool(__instance.m_core.Owner); } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireMaster")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_SentryFire() { ClearCache(); } [HarmonyPatch(typeof(MineDeployerInstance_Detonate_Explosive), "DoExplode")] [HarmonyWrapSafe] [HarmonyPrefix] public static void ExplodePrefix(MineDeployerInstance_Detonate_Explosive __instance) { CacheTool(__instance.m_core.Owner); } [HarmonyPatch(typeof(MineDeployerInstance_Detonate_Explosive), "DoExplode")] [HarmonyWrapSafe] [HarmonyPostfix] public static void ExplodePostfix() { ClearCache(); } private static void CacheTool(PlayerAgent? owner) { _cache.slot = DamSlotType.Tool; _cache.lookup = ((owner != null) ? owner.Owner.Lookup : 0); } [HarmonyPatch(typeof(Dam_EnemyDamageBase), "BulletDamage")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_BulletDamage(Dam_EnemyDamageBase __instance, int limbID, float dam, ref uint gearCategoryId) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (_cache.Valid && limbID != -1) { gearCategoryId = (uint)(_cache.slot + 1); if (!StatManager.MasterHasMod) { DamStatType stat = (((int)((Il2CppArrayBase)(object)__instance.DamageLimbs)[limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(_cache.lookup, _cache.slot, stat, Math.Min(dam, ((Dam_SyncedDamageBase)__instance).Health)); } } } [HarmonyPatch(typeof(Dam_EnemyDamageLimb), "MeleeDamage")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_MeleeDamage(Dam_EnemyDamageLimb __instance, Agent sourceAgent, float dam) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (!StatManager.MasterHasMod) { DamStatType stat = (((int)__instance.m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(((Il2CppObjectBase)sourceAgent).Cast().Owner.Lookup, DamSlotType.Melee, stat, Math.Min(dam, ((Dam_SyncedDamageBase)__instance.m_base).Health)); } } [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveBulletDamage")] [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveMeleeDamage")] [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveExplosionDamage")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_RecvEnemyDamage(Dam_EnemyDamageBase __instance, ref float __state) { __state = ((Dam_SyncedDamageBase)__instance).Health; } [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveBulletDamage")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_RecvBulletDamage(Dam_EnemyDamageBase __instance, pBulletDamageData data, float __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0061: Invalid comparison between Unknown and I4 //IL_00bd: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (CacheDamageHost(__instance, data.limbID, __state) || _cache.slot == DamSlotType.Tool) { return; } DamSlotType damSlotType; if (data.gearCategoryId != 0) { damSlotType = (DamSlotType)(data.gearCategoryId - 1); if ((int)damSlotType >= 4) { return; } } else { Agent val = default(Agent); if (!((pAgent)(ref data.source)).TryGet(ref val)) { return; } PlayerInventorySynced val2 = ((Il2CppObjectBase)((Il2CppObjectBase)val).Cast().Inventory).Cast(); if ((int)val2.m_equipStatus == 1) { AmmoType ammoType = val2.m_queuedEquipItem.AmmoType; DamSlotType damSlotType2 = (((int)ammoType != 0) ? (((int)ammoType == 1) ? DamSlotType.Secondary : ((PlayerInventoryBase)val2).WieldedSlot.ToDamSlotType()) : DamSlotType.Primary); damSlotType = damSlotType2; } else { damSlotType = ((PlayerInventoryBase)val2).WieldedSlot.ToDamSlotType(); } if (damSlotType != DamSlotType.Primary && damSlotType != DamSlotType.Secondary) { damSlotType = ((!(_random.NextSingle() < 0.5f)) ? DamSlotType.Secondary : DamSlotType.Primary); } } CacheDamage(__instance, data.limbID, __state, data.source, damSlotType); } [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveMeleeDamage")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_RecvMeleeDamage(Dam_EnemyDamageBase __instance, pFullDamageData data, float __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!CacheDamageHost(__instance, data.limbID, __state)) { CacheDamage(__instance, data.limbID, __state, data.source, DamSlotType.Melee); } } [HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveExplosionDamage")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_RecvExplosionDamage(Dam_EnemyDamageBase __instance, pExplosionDamageData data, float __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) CacheDamageHost(__instance, data.limbID, __state); } private static bool CacheDamageHost(Dam_EnemyDamageBase damBase, byte limbID, float prevHealth) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 if (!_cache.Valid) { return false; } DamStatType stat = (((int)((Il2CppArrayBase)(object)damBase.DamageLimbs)[(int)limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(_cache.lookup, _cache.slot, stat, prevHealth - ((Dam_SyncedDamageBase)damBase).Health); return true; } private static void CacheDamage(Dam_EnemyDamageBase damBase, byte limbID, float prevHealth, pAgent agent, DamSlotType slot) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 Agent val = default(Agent); if (((pAgent)(ref agent)).TryGet(ref val)) { ulong lookup = ((Il2CppObjectBase)val).Cast().Owner.Lookup; DamStatType stat = (((int)((Il2CppArrayBase)(object)damBase.DamageLimbs)[(int)limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(lookup, slot, stat, prevHealth - ((Dam_SyncedDamageBase)damBase).Health); } } } [HarmonyPatch] internal static class NetworkPatches { [HarmonyPatch(typeof(PlayerSync), "OnSpawn")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_PlayerSpawn(PlayerSync __instance) { ModSyncManager.OnAddPlayer(__instance.Replicator.OwningPlayer); } [HarmonyPatch(typeof(PlayerAgent), "OnDespawn")] [HarmonyWrapSafe] [HarmonyPrefix] private static void Pre_PlayerDespawn(PlayerAgent __instance) { ModSyncManager.OnRemovePlayer(__instance.Owner); } [HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_LeaveHub() { StatManager.Clear(); } } [HarmonyPatch] internal static class PagePatches { private static bool _isReady = false; private static readonly CM_PageSuccess_PrisonerEvaluation[] _failReports = (CM_PageSuccess_PrisonerEvaluation[])(object)new CM_PageSuccess_PrisonerEvaluation[4]; private static IntPtr _lastFailPage = IntPtr.Zero; [InvokeOnAssetLoad] private static void OnAssetLoad() { _isReady = true; } [HarmonyPatch(typeof(CM_PageExpeditionSuccess), "Setup")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_PageSetup(CM_PageExpeditionSuccess __instance) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_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_0071: Unknown result type (might be due to invalid IL or missing references) foreach (CM_PageSuccess_PrisonerEvaluation item in (Il2CppArrayBase)(object)__instance.m_playerReports) { Vector2 sizeDelta = ((TMP_Text)item.m_gear).rectTransform.sizeDelta; sizeDelta.y += 20f; ((TMP_Text)item.m_gear).rectTransform.sizeDelta = sizeDelta; sizeDelta = ((TMP_Text)item.m_eval).rectTransform.sizeDelta; sizeDelta.y += 20f; ((TMP_Text)item.m_eval).rectTransform.sizeDelta = sizeDelta; } } [HarmonyPatch(typeof(CM_PageExpeditionSuccess), "OnEnable")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_PageEnable(CM_PageExpeditionSuccess __instance) { if (!_isReady || SNet.Slots.PlayerSlots == null) { return; } for (int i = 0; i < ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots).Length; i++) { SNet_Slot val = ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots)[i]; if (val != null && !((Object)(object)val.player == (Object)null) && val.player.IsInSlot) { SNet_Player player = val.player; PopulateReport(__instance, ((Il2CppArrayBase)(object)__instance.m_playerReports)[i], player, isFail: false, i); } } } [HarmonyPatch(typeof(CM_PageExpeditionFail), "OnEnable")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_PageEnable(CM_PageExpeditionFail __instance) { if (!_isReady || SNet.Slots.PlayerSlots == null) { return; } CreateFailReports(__instance); CM_PageExpeditionSuccess pageExpeditionSuccess = MainMenuGuiLayer.Current.PageExpeditionSuccess; Random.InitState(Builder.SessionSeedRandom.Seed); for (int i = 0; i < ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots).Length; i++) { SNet_Slot val = ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots)[i]; CM_PageSuccess_PrisonerEvaluation val2 = _failReports[i]; val2.SetVisible(false); if (val != null && !((Object)(object)val.player == (Object)null) && val.player.IsInSlot) { SNet_Player player = val.player; PopulateReport(pageExpeditionSuccess, val2, player, isFail: true, i); } } } private static void CreateFailReports(CM_PageExpeditionFail __instance) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) if (_lastFailPage == ((Il2CppObjectBase)__instance).Pointer) { return; } _lastFailPage = ((Il2CppObjectBase)__instance).Pointer; Transform transform = ((Component)((CM_PageBase)__instance).m_staticContentHolder).transform; if (Object.op_Implicit((Object)(object)transform.FindChild("StatDisplay_Align0"))) { return; } __instance.m_artifactInfo_text.transform.localPosition = new Vector3(0f, 2000f, 0f); ((Transform)((CM_PageBase)__instance).m_staticContentHolder).localPosition = new Vector3(0f, 100f, 0f); ((Component)__instance.m_ArtifactInventoryDisplay).transform.localPosition = new Vector3(0f, 100f, 0f); Transform[] array = (Transform[])(object)new Transform[4]; for (int i = 0; i < array.Length; i++) { GameObject val = new GameObject("StatDisplay_Align" + i); Object.DontDestroyOnLoad((Object)(object)val); array[i] = val.transform; array[i].SetParent(transform, false); array[i].localPosition = new Vector3((float)(-900 + 600 * i), -250f, 0f); } GameObject playerReportPrefab = MainMenuGuiLayer.Current.PageExpeditionSuccess.m_playerReportPrefab; for (int j = 0; j < _failReports.Length; j++) { Transform val2 = ((Component)__instance).transform; if (j < array.Length) { val2 = array[j]; } _failReports[j] = GOUtil.SpawnChildAndGetComp(playerReportPrefab, val2); ((Component)_failReports[j]).transform.localPosition = Vector3.zero; ((Component)_failReports[j]).transform.localRotation = Quaternion.identity; ((Component)_failReports[j]).transform.localScale = new Vector3(0.8f, 0.8f, 0.8f); Vector2 sizeDelta = ((TMP_Text)_failReports[j].m_gear).rectTransform.sizeDelta; sizeDelta.y += 20f; ((TMP_Text)_failReports[j].m_gear).rectTransform.sizeDelta = sizeDelta; _failReports[j].SetVisible(false); } } private static void PopulateReport(CM_PageExpeditionSuccess successPage, CM_PageSuccess_PrisonerEvaluation report, SNet_Player player, bool isFail, int index) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) StatData data; bool flag = StatHandler.TryGetData(player, out data); PlayerBackpack val = default(PlayerBackpack); if ((!flag && !isFail) || !PlayerBackpackManager.TryGetBackpack(player, ref val)) { return; } if (isFail) { report.SetColor(new Color(1f, 1f, 1f, 0.65f)); ((TMP_Text)report.m_name).text = "" + (player.IsBot ? ("[" + Text.Get(1372u) + "]") : "") + player.GetName() + ""; ((TMP_Text)report.m_gearHeader).text = "" + Text.Get(888u) + ""; } bool flag2 = false; bool flag3 = false; StringBuilder stringBuilder = new StringBuilder(""); string text = default(string); if (successPage.TryGetArchetypeName(val, (InventorySlot)1, ref text)) { flag2 = true; stringBuilder.AppendLine(text + GetDamageText(data, DamSlotType.Primary)); } string text2 = default(string); if (successPage.TryGetArchetypeName(val, (InventorySlot)2, ref text2)) { flag3 = true; stringBuilder.AppendLine(text2 + GetDamageText(data, DamSlotType.Secondary)); } string text3 = default(string); if (successPage.TryGetArchetypeName(val, (InventorySlot)3, ref text3)) { stringBuilder.AppendLine(text3 + GetDamageText(data, DamSlotType.Tool)); } if (successPage.TryGetArchetypeName(val, (InventorySlot)10, ref text3)) { stringBuilder.AppendLine(text3 + GetDamageText(data, DamSlotType.Melee)); } if (flag) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2); handler.AppendLiteral("Total: "); handler.AppendFormatted(GetDamageText(data, DamSlotType.All, includePipe: false)); stringBuilder3.Append(ref handler); } stringBuilder.Append(""); ((TMP_Text)report.m_gear).text = stringBuilder.ToString(); stringBuilder.Clear(); if (isFail) { ((TMP_Text)report.m_evalHeader).text = "" + Text.Get(898u) + ""; } else { PlayerAgent obj = ((Il2CppObjectBase)player.PlayerAgent).Cast(); float healthRel = ((Dam_SyncedDamageBase)obj.Damage).GetHealthRel(); string text4 = ((healthRel > 0.7f) ? Text.Get(889u) : ((healthRel > 0.5f) ? Text.Get(890u) : ((healthRel > 0.3f) ? Text.Get(891u) : ((!(healthRel > 0f)) ? ("" + Text.Get(893u) + "") : ("" + Text.Get(892u) + ""))))); float infection = obj.Damage.Infection; string text5 = ((infection > 0.85f) ? Text.Get(894u) : ((infection > 0.5f) ? Text.Get(895u) : ((!(infection > 0f)) ? Text.Get(897u) : Text.Get(896u)))); stringBuilder.AppendLine(Text.Format(899u, (Object[])(object)new Object[1] { Object.op_Implicit("" + text4 + "") })); stringBuilder.AppendLine(Text.Format(900u, (Object[])(object)new Object[1] { Object.op_Implicit("" + text5 + "") })); } if (flag && data.HasMod) { if (flag2) { stringBuilder.AppendLine(text + ": " + data.StatText.GetEndscreenText((InventorySlot)1) + ""); } if (flag3) { stringBuilder.AppendLine(text2 + ": " + data.StatText.GetEndscreenText((InventorySlot)2) + ""); } StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2); handler.AppendLiteral("Total: "); handler.AppendFormatted(data.StatText.GetEndscreenText((InventorySlot)4)); handler.AppendLiteral(""); stringBuilder4.Append(ref handler); } else { stringBuilder.Append("Missing StatDisplay"); } ((TMP_Text)report.m_eval).text = stringBuilder.ToString(); if (isFail) { CoroutineManager.BlinkIn(((Component)report).gameObject, 3f + (float)index * 0.2f); } } private static string GetDamageText(StatData? data, DamSlotType slot, bool includePipe = true) { if (data == null) { return ""; } string text = (includePipe ? " | " : ""); if (!data.HasMod && !StatManager.MasterHasMod) { return text + "N/A"; } return text + $"<#a0a0a0>{(ulong)data.GetDamage(slot, DamStatType.Any)} (<#ffff00>{(ulong)data.GetDamage(slot, DamStatType.Crit)})"; } } [HarmonyPatch] internal static class WOPatches { [HarmonyPatch(typeof(WardenObjectiveManager), "OnWinConditionSolved")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_WinSolved() { StatHandler.ForceSync(); } } } namespace StatDisplay.Networking { internal static class ModSyncManager { private static readonly NotifySync _notifySync = new NotifySync(); [InvokeOnLoad] private static void Init() { _notifySync.Setup(); } internal static void OnAddPlayer(SNet_Player player) { if (player.IsLocal) { StatManager.AddPlayer(player, hasMod: true); } else if (!player.IsBot) { _notifySync.Send(packetData: true, player, (SNet_ChannelType)0); StatManager.AddPlayer(player, hasMod: false); } else { StatManager.AddPlayer(player, StatManager.MasterHasMod); } } internal static void ReceiveNotify(SNet_Player player) { StatManager.AddPlayer(player, hasMod: true); } internal static void OnRemovePlayer(SNet_Player player) { StatManager.RemovePlayer(player); } } internal static class StatSyncManager { private static readonly AccDeltaSync _accSync = new AccDeltaSync(); private static readonly DamDeltaSync _damSync = new DamDeltaSync(); [InvokeOnLoad] private static void Init() { _accSync.Setup(); _damSync.Setup(); } internal static void BroadcastAccData(ulong lookup, StatData data) { if (!data.PopRemoteAccDelta(out short[,,] deltaAccuracy)) { return; } foreach (SNet_Player remotePlayer in StatManager.RemotePlayers) { _accSync.Send(pAccDeltaData.Create(lookup, deltaAccuracy), remotePlayer, (SNet_ChannelType)4); } } internal static void BroadcastDamData(ulong lookup, StatData data) { if (!data.PopRemoteDamDelta(out float[,] deltaDamage)) { return; } foreach (SNet_Player remotePlayer in StatManager.RemotePlayers) { _damSync.Send(pDamDeltaData.Create(lookup, deltaDamage), remotePlayer, (SNet_ChannelType)4); } } internal static void ReceiveData(pAccDeltaData data) { StatHandler.AddRemoteAccDelta(data.Lookup, data.DeltaData); } internal static void ReceiveData(pDamDeltaData data) { StatHandler.AddRemoteDamDelta(data.Lookup, data.DeltaData); } } public abstract class SyncedEvent where T : struct { public delegate void ReceiveHandler(T packet); private bool _isSetup; public abstract string GUID { get; } public bool IsSetup => _isSetup; public string EventName { get; private set; } = string.Empty; public void Setup() { if (!_isSetup) { EventName = "STAT" + GUID; NetworkAPI.RegisterEvent(EventName, (Action)ReceiveClient_Callback); _isSetup = true; } } public void Send(T packetData, SNet_Player? target = null, SNet_ChannelType priority = (SNet_ChannelType)4) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target != (Object)null) { NetworkAPI.InvokeEvent(EventName, packetData, target, priority); } else { NetworkAPI.InvokeEvent(EventName, packetData, priority); } ReceiveLocal_Callback(packetData); } private void ReceiveLocal_Callback(T packet) { ReceiveLocal(packet); } private void ReceiveClient_Callback(ulong sender, T packet) { Receive(sender, packet); } protected virtual void ReceiveLocal(T packet) { } protected virtual void Receive(ulong sender, T packet) { Receive(packet); } protected virtual void Receive(T packet) { } } } namespace StatDisplay.Networking.Events { internal sealed class AccDeltaSync : SyncedEvent { public override string GUID => "ACCSYN"; protected override void Receive(ulong lookup, pAccDeltaData packet) { StatSyncManager.ReceiveData(packet); } } internal sealed class DamDeltaSync : SyncedEvent { public override string GUID => "DAMSYN"; protected override void Receive(ulong lookup, pDamDeltaData packet) { StatSyncManager.ReceiveData(packet); } } internal struct pAccDeltaData { private const int Count = 18; public ulong Lookup; private unsafe fixed short _deltaData[18]; public unsafe short[,,] DeltaData { get { short[,,] array = new short[2, 3, 3]; int num = 0; for (int i = 0; i < array.GetLength(0); i++) { for (int j = 0; j < array.GetLength(1); j++) { for (int k = 0; k < array.GetLength(2); k++) { array[i, j, k] = _deltaData[num++]; } } } return array; } set { int num = 0; for (int i = 0; i < value.GetLength(0); i++) { for (int j = 0; j < value.GetLength(1); j++) { for (int k = 0; k < value.GetLength(2); k++) { _deltaData[num++] = value[i, j, k]; } } } } } public static pAccDeltaData Create(ulong lookup, short[,,] deltaData) { return new pAccDeltaData { Lookup = lookup, DeltaData = deltaData }; } } internal struct pDamDeltaData { private const int Count = 8; public ulong Lookup; private unsafe fixed float _deltaData[8]; public unsafe float[,] DeltaData { get { float[,] array = new float[4, 2]; int num = 0; for (int i = 0; i < array.GetLength(0); i++) { for (int j = 0; j < array.GetLength(1); j++) { array[i, j] = _deltaData[num++]; } } return array; } set { int num = 0; for (int i = 0; i < value.GetLength(0); i++) { for (int j = 0; j < value.GetLength(1); j++) { _deltaData[num++] = value[i, j]; } } } } public static pDamDeltaData Create(ulong lookup, float[,] deltaData) { return new pDamDeltaData { Lookup = lookup, DeltaData = deltaData }; } } internal sealed class NotifySync : SyncedEvent { public override string GUID => "NOTIF"; protected override void Receive(ulong lookup, bool _) { SNet_Player player = default(SNet_Player); if (SNet.TryGetPlayer(lookup, ref player)) { ModSyncManager.ReceiveNotify(player); } } } } namespace StatDisplay.Handler { public sealed class StatHandler : MonoBehaviour { private static readonly (float x, float y) BaseOffset = (x: -70f, y: -62f); private static readonly float TextMargin = 35f; private const float SyncInterval = 2f; private static RectTransform? s_baseRectTransform; private static TextMeshPro? s_baseTextMesh; private readonly List _textMeshList = new List(); private readonly List _meshGOList = new List(); private readonly Dictionary _data = new Dictionary(); private float _nextSyncTime; private bool _visible = true; [HideFromIl2Cpp] public static StatHandler Instance { get; private set; } = null; public StatHandler(IntPtr ptr) : base(ptr) { } [InvokeOnAssetLoad] private static void Init() { //IL_000a: 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_0015: Expected O, but got Unknown ClassInjector.RegisterTypeInIl2Cpp(); GameObject val = new GameObject("StatHandler"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent(); } [InvokeOnBuildStart] private static void ResetValues() { foreach (StatData value in Instance._data.Values) { value.Reset(); } } private void Awake() { Instance = this; EnsureMeshCapacity(4); Configuration.Config.OnChanged += ResetMeshOffsets; } [HideFromIl2Cpp] public static bool TryGetData(SNet_Player player, [MaybeNullWhen(false)] out StatData data) { return Instance._data.TryGetValue(player.Lookup, out data); } [HideFromIl2Cpp] public static void AddAccuracyDelta(ulong lookup, AccSlotType slot, short[,] delta) { if (Instance._data.TryGetValue(lookup, out StatData value)) { value.AddAccuracy(slot, delta); } } [HideFromIl2Cpp] public static void AddDamage(ulong lookup, DamSlotType slot, DamStatType stat, float value) { if (Instance._data.TryGetValue(lookup, out StatData value2)) { value2.AddDamage(slot, stat, value); } } public static void AddRemoteAccDelta(ulong lookup, short[,,] deltaAccuracy) { if (Instance._data.TryGetValue(lookup, out StatData value)) { value.AddRemoteAccDelta(deltaAccuracy); } } public static void AddRemoteDamDelta(ulong lookup, float[,] deltaDamage) { if (Instance._data.TryGetValue(lookup, out StatData value)) { value.AddRemoteDamDelta(deltaDamage); } } internal static void AddPlayer(SNet_Player player, bool hasMod = true, bool onlyIfExists = false) { Instance.AddPlayer_Internal(player, hasMod, onlyIfExists); } private void AddPlayer_Internal(SNet_Player player, bool hasMod = true, bool onlyIfExists = false) { if (_data.TryGetValue(player.Lookup, out StatData value)) { if (value.HasMod == hasMod) { return; } value.HasMod = hasMod; } else { if (onlyIfExists) { return; } _data.Add(player.Lookup, new StatData(player, hasMod)); } RefreshMeshList(); } internal static void RemovePlayer(SNet_Player player) { Instance.RemovePlayer_Internal(player); } private void RemovePlayer_Internal(SNet_Player player) { if (_data.Remove(player.Lookup, out StatData value) && (value.HasMod || StatManager.MasterHasMod)) { RefreshMeshList(); } } internal static void Clear() { Instance.Clear_Internal(); } private void Clear_Internal() { _data.Clear(); for (int i = 0; i < 4; i++) { _meshGOList[i].SetActive(false); } for (int j = 4; j < _meshGOList.Count; j++) { Object.Destroy((Object)(object)_meshGOList[j]); } if (_meshGOList.Count > 4) { _textMeshList.RemoveRange(4, _meshGOList.Count - 4); _meshGOList.RemoveRange(4, _meshGOList.Count - 4); } } internal static void OnMasterSet() { Instance.RefreshMeshList(); } internal static void SetVisible(bool visible) { Instance.SetVisible_Internal(visible); } private void SetVisible_Internal(bool visible) { if (visible == _visible) { return; } _visible = visible; if (visible) { RefreshMeshList(); return; } foreach (GameObject meshGO in _meshGOList) { meshGO.SetActive(false); } } private void RefreshMeshList() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { return; } foreach (GameObject meshGO in _meshGOList) { meshGO.SetActive(false); } int num = 0; Il2CppReferenceArray playerSlots = SNet.Slots.PlayerSlots; for (int i = 0; i < ((Il2CppArrayBase)(object)playerSlots).Count; i++) { if ((Object)(object)((Il2CppArrayBase)(object)playerSlots)[i].player == (Object)null) { continue; } ulong lookup = ((Il2CppArrayBase)(object)playerSlots)[i].player.Lookup; if (_data.TryGetValue(lookup, out StatData value)) { if (value.HasMod || StatManager.MasterHasMod) { EnsureMeshCapacity(num + 1); value.StatText.TextMesh = _textMeshList[num]; _meshGOList[num].transform.localPosition = GetMeshPos(num); _meshGOList[num].SetActive(true); num++; } else { value.StatText.TextMesh = null; } } } } internal static void ForceSync() { Instance.DoSync(); } private void DoSync() { _nextSyncTime = Clock.Time + 2f; foreach (var (lookup, data) in _data) { StatSyncManager.BroadcastAccData(lookup, data); StatSyncManager.BroadcastDamData(lookup, data); } } private void Update() { if (_data.Count == 0) { return; } foreach (StatData value in _data.Values) { value.StatText.UpdateMesh(); } if (!(Clock.Time < _nextSyncTime)) { DoSync(); } } private void EnsureMeshCapacity(int num) { //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) num -= _textMeshList.Count; if (num <= 0) { return; } if ((Object)(object)s_baseRectTransform == (Object)null) { PUI_Inventory inventory = GuiManager.Current.m_playerLayer.Inventory; s_baseTextMesh = inventory.m_inventorySlots[(InventorySlot)10].m_slim_archetypeName; foreach (RectTransform componentsInChild in ((Component)inventory.m_iconDisplay).GetComponentsInChildren(true)) { if (((Object)componentsInChild).name == "Background Fade") { s_baseRectTransform = componentsInChild; break; } } if ((Object)(object)s_baseRectTransform == (Object)null) { return; } } for (int i = 0; i < num; i++) { GameObject obj = Object.Instantiate(((Component)s_baseRectTransform).gameObject, ((Transform)s_baseRectTransform).parent); RectTransform component = obj.GetComponent(); obj.gameObject.SetActive(true); foreach (Transform componentsInChild2 in obj.GetComponentsInChildren(true)) { if (((Object)componentsInChild2).name == "TimerShowObject") { ((Component)componentsInChild2).gameObject.active = false; } } GameObject val = new GameObject("StatDisplay" + _textMeshList.Count) { layer = 5, hideFlags = (HideFlags)61 }; val.transform.SetParent(((Component)component).transform, false); TextMeshPro val2 = Object.Instantiate(s_baseTextMesh, val.transform); ((TMP_Text)val2).m_width = ((TMP_Text)val2).m_width * 2f; ((Component)val2).GetComponent().anchoredPosition = new Vector2(-5f, 9f); ((TMP_Text)val2).SetText("-", true); ((TMP_Text)val2).ForceMeshUpdate(false, false); _textMeshList.Add(val2); _meshGOList.Add(((Component)val2.transform.parent.parent).gameObject); List meshGOList = _meshGOList; meshGOList[meshGOList.Count - 1].SetActive(false); } } private static Vector3 GetMeshPos(int i) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) return new Vector3(BaseOffset.x + Configuration.Config.Offset.X, BaseOffset.y + Configuration.Config.Offset.Y - (float)i * TextMargin, 0f); } private void ResetMeshOffsets() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _textMeshList.Count; i++) { _meshGOList[i].transform.localPosition = new Vector3(BaseOffset.x + Configuration.Config.Offset.X, BaseOffset.y + Configuration.Config.Offset.Y - (float)i * TextMargin, 0f); } } } } namespace StatDisplay.Dependencies { internal static class ArchiveWrapper { public const string PLUGIN_GUID = "dev.AuriRex.gtfo.TheArchive"; public static readonly bool HasArchive; static ArchiveWrapper() { HasArchive = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("dev.AuriRex.gtfo.TheArchive"); } } internal static class EWCWrapper { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static AccuracyCallback <>9__6_0; internal void b__6_0(WeaponAccuracy accuracy, WeaponDelta delta) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0035: 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_0058: Unknown result type (might be due to invalid IL or missing references) if ((int)accuracy.Slot != 3) { <>c__DisplayClass6_0 <>c__DisplayClass6_ = default(<>c__DisplayClass6_0); <>c__DisplayClass6_.deltaArr = new short[3, 3]; g__Copy|6_1(AccShotType.Shot, delta.Shots, ref <>c__DisplayClass6_); g__Copy|6_1(AccShotType.Group, delta.Groups, ref <>c__DisplayClass6_); g__Copy|6_1(AccShotType.Full, delta.FullShots, ref <>c__DisplayClass6_); StatHandler.AddAccuracyDelta(accuracy.ParentStats.Owner.Owner.Lookup, accuracy.Slot.ToAccSlotType(), <>c__DisplayClass6_.deltaArr); } } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct <>c__DisplayClass6_0 { public short[,] deltaArr; } public const string PLUGIN_GUID = "Dinorush.ExtraWeaponCustomization"; public static readonly bool HasEWC; private static float _healthState; static EWCWrapper() { HasEWC = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("Dinorush.ExtraWeaponCustomization"); if (HasEWC) { AddCallbacks_Unsafe(); } } public static bool HasCWC(BulletWeapon weapon) { if (HasEWC) { return HasCWC_Unsafe(weapon); } return false; } public static void AddProjectileHitCallback(Action hitCallback) { if (HasEWC) { AddHitCallback_Unsafe(hitCallback); } } [MethodImpl(MethodImplOptions.NoInlining)] private static bool HasCWC_Unsafe(BulletWeapon weapon) { return (Object)(object)((Component)weapon).GetComponent() != (Object)null; } [MethodImpl(MethodImplOptions.NoInlining)] private static void AddCallbacks_Unsafe() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown object obj = <>c.<>9__6_0; if (obj == null) { AccuracyCallback val = delegate(WeaponAccuracy accuracy, WeaponDelta delta) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0035: 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_0058: Unknown result type (might be due to invalid IL or missing references) short[,] deltaArr; if ((int)accuracy.Slot != 3) { deltaArr = new short[3, 3]; Copy(AccShotType.Shot, delta.Shots); Copy(AccShotType.Group, delta.Groups); Copy(AccShotType.Full, delta.FullShots); StatHandler.AddAccuracyDelta(accuracy.ParentStats.Owner.Owner.Lookup, accuracy.Slot.ToAccSlotType(), deltaArr); } void Copy(AccShotType shotType, StatDelta stat) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) deltaArr[(uint)shotType, 0] = (short)stat.Hits; deltaArr[(uint)shotType, 1] = (short)stat.Crits; deltaArr[(uint)shotType, 2] = (short)stat.Count; } }; <>c.<>9__6_0 = val; obj = (object)val; } AccuracyAPI.OnAccuracyUpdate += (AccuracyCallback)obj; DamageAPI.PreLocalDOTDamage += new DamageCallback(RecordDamageLocal); DamageAPI.PreLocalExplosiveDamage += new DamageCallback(RecordDamageLocal); DamageAPI.PreLocalShrapnelDamage += new DamageCallback(RecordDamageLocal); DamageAPI.PreDOTDamage += new DamageCallback(RecordPreDamageHost); DamageAPI.PreExplosiveDamage += new DamageCallback(RecordPreDamageHost); DamageAPI.PreShrapnelDamage += new DamageCallback(RecordPreDamageHost); DamageAPI.PostDOTDamage += new DamageCallback(RecordPostDamageHost); DamageAPI.PostExplosiveDamage += new DamageCallback(RecordPostDamageHost); DamageAPI.PostShrapnelDamage += new DamageCallback(RecordPostDamageHost); } private static void RecordDamageLocal(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!StatManager.MasterHasMod) { DamStatType stat = (((int)limb.m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(source.Owner.Lookup, cwc.slot.ToDamSlotType(), stat, Math.Min(damage, ((Dam_SyncedDamageBase)limb.m_base).Health)); } } private static void RecordPreDamageHost(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc) { _healthState = ((Dam_SyncedDamageBase)limb.m_base).Health; } private static void RecordPostDamageHost(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) DamStatType stat = (((int)limb.m_type == 1) ? DamStatType.Crit : DamStatType.Any); StatHandler.AddDamage(source.Owner.Lookup, cwc.slot.ToDamSlotType(), stat, _healthState - ((Dam_SyncedDamageBase)limb.m_base).Health); } [MethodImpl(MethodImplOptions.NoInlining)] private static void AddHitCallback_Unsafe(Action hitCallback) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown ProjectileAPI.OnProjectileHit += (ProjectileHitCallback)delegate(EWCProjectileComponentBase comp, IDamageable? damageable) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) CustomWeaponComponent cWC = ((WeaponPropertyBase)comp.Settings).CWC; if (!((Object)(object)cWC.Owner.Player == (Object)null)) { hitCallback(cWC.Owner.Player.Owner.Lookup, cWC.Weapon.AmmoType.ToDamSlotType()); } }; } [CompilerGenerated] internal static void g__Copy|6_1(AccShotType shotType, StatDelta stat, ref <>c__DisplayClass6_0 P_2) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) P_2.deltaArr[(uint)shotType, 0] = (short)stat.Hits; P_2.deltaArr[(uint)shotType, 1] = (short)stat.Crits; P_2.deltaArr[(uint)shotType, 2] = (short)stat.Count; } } } namespace StatDisplay.Data { public enum AccSlotType : byte { Primary = 0, Main = 0, Secondary = 1, Special = 1, Count = 2, All = 3 } public enum AccShotType : byte { Shot, Group, Full, Count } public enum AccStatType : byte { Hit, Crit, Fired, Count } public enum DamSlotType : byte { Primary = 0, Main = 0, Secondary = 1, Special = 1, Tool = 2, Class = 2, Melee = 3, Count = 4, All = 5 } public enum DamStatType : byte { Any, Crit, Count } public sealed class StatData { public abstract class Retriever { protected readonly StatData _data; public abstract long Value { get; } public Retriever(StatData data) { _data = data; } } private class AccuracyRetriever : Retriever { private readonly int _slotType; private readonly int _shotType; private readonly int _statType; private readonly bool _isAllSlot; public override long Value { get { if (_isAllSlot) { return _data._accData[0, _shotType, _statType] + _data._accData[1, _shotType, _statType]; } return _data._accData[_slotType, _shotType, _statType]; } } public AccuracyRetriever(StatData data, AccSlotType slotType, AccShotType shotType, AccStatType statType) : base(data) { _slotType = (int)slotType; _shotType = (int)shotType; _statType = (int)statType; _isAllSlot = slotType == AccSlotType.All; } } private class DamageRetriever : Retriever { private readonly int _slotType; private readonly int _statType; private readonly bool _isAllSlot; public override long Value { get { double num = 0.0; if (_isAllSlot) { for (int i = 0; i < 4; i++) { num += _data._damData[i, _statType]; } } else { num += _data._damData[_slotType, _statType]; } return (long)num; } } public DamageRetriever(StatData data, DamSlotType slotType, DamStatType statType) : base(data) { _slotType = (int)slotType; _statType = (int)statType; _isAllSlot = slotType == DamSlotType.All; } } private readonly long[,,] _accData; private readonly short[,,] _deltaAccData; private readonly double[,] _damData; private readonly float[,] _deltaDamData; private bool _needsAccSync; private bool _needsDamSync; public readonly StatParser StatText; public bool HasMod { get { return StatText.HasMod; } set { StatText.HasMod = value; } } public StatData(SNet_Player player, bool hasMod) { _accData = new long[2, 3, 3]; _deltaAccData = new short[2, 3, 3]; _damData = new double[4, 2]; _deltaDamData = new float[4, 2]; StatText = new StatParser(this, player, hasMod); } public long GetAccuracy(AccSlotType slot, AccShotType shot, AccStatType stat) { if (slot == AccSlotType.All) { return _accData[0, (uint)shot, (uint)stat] + _accData[1, (uint)shot, (uint)stat]; } return _accData[(uint)slot, (uint)shot, (uint)stat]; } public double GetDamage(DamSlotType slot, DamStatType stat) { double num = 0.0; if (slot == DamSlotType.All) { for (int i = 0; i < 4; i++) { num += _damData[i, (uint)stat]; } } else { num += _damData[(uint)slot, (uint)stat]; } return num; } public void Reset() { for (int i = 0; i < _accData.GetLength(0); i++) { for (int j = 0; j < _accData.GetLength(1); j++) { for (int k = 0; k < _accData.GetLength(2); k++) { _accData[i, j, k] = 0L; _deltaAccData[i, j, k] = 0; } } } _needsAccSync = false; _needsDamSync = false; StatText.Update(updateMesh: true); } public void AddAccuracy(AccSlotType slot, short[,] deltaData) { _needsAccSync = true; for (int i = 0; i < deltaData.GetLength(0); i++) { for (int j = 0; j < deltaData.GetLength(1); j++) { short num = deltaData[i, j]; _accData[(uint)slot, i, j] += num; _deltaAccData[(uint)slot, i, j] += num; } } StatText.Update(); } public void AddDamage(DamSlotType slot, DamStatType stat, float value) { _needsDamSync = true; _damData[(uint)slot, (uint)stat] += value; _deltaDamData[(uint)slot, (uint)stat] += value; if (stat != DamStatType.Any) { _damData[(uint)slot, 0] += value; _deltaDamData[(uint)slot, 0] += value; } StatText.Update(); } public bool PopRemoteAccDelta([MaybeNullWhen(false)] out short[,,] deltaAccuracy) { if (!_needsAccSync) { deltaAccuracy = null; return false; } deltaAccuracy = (short[,,])_deltaAccData.Clone(); for (int i = 0; i < _deltaAccData.GetLength(0); i++) { for (int j = 0; j < _deltaAccData.GetLength(1); j++) { for (int k = 0; k < _deltaAccData.GetLength(2); k++) { _deltaAccData[i, j, k] = 0; } } } _needsAccSync = false; return true; } public bool PopRemoteDamDelta([MaybeNullWhen(false)] out float[,] deltaDamage) { if (!_needsDamSync) { deltaDamage = null; return false; } deltaDamage = (float[,])_deltaDamData.Clone(); for (int i = 0; i < _deltaDamData.GetLength(0); i++) { for (int j = 0; j < _deltaDamData.GetLength(1); j++) { _deltaDamData[i, j] = 0f; } } _needsDamSync = false; return true; } public void AddRemoteAccDelta(short[,,] deltaData) { for (int i = 0; i < deltaData.GetLength(0); i++) { for (int j = 0; j < deltaData.GetLength(1); j++) { for (int k = 0; k < deltaData.GetLength(2); k++) { _accData[i, j, k] += deltaData[i, j, k]; } } } StatText.Update(updateMesh: true); } public void AddRemoteDamDelta(float[,] deltaDamage) { for (int i = 0; i < deltaDamage.GetLength(0); i++) { for (int j = 0; j < deltaDamage.GetLength(1); j++) { _damData[i, j] += deltaDamage[i, j]; } } StatText.Update(updateMesh: true); } public static Retriever CreateAccuracyRetriever(StatData data, AccSlotType slotType, AccShotType shotType, AccStatType statType) { return new AccuracyRetriever(data, slotType, shotType, statType); } public static Retriever CreateDamageRetriever(StatData data, DamSlotType slotType, DamStatType statType) { return new DamageRetriever(data, slotType, statType); } } public sealed class StatParser { private class Token { private static readonly string[] s_accSlotTypes = Array.ConvertAll(Enum.GetValues()[..2], (AccSlotType slot) => slot.ToString()); private static readonly string[] s_accShotTypes = Array.ConvertAll(Enum.GetValues()[..3], (AccShotType shot) => shot.ToString()); private static readonly string[] s_accStatTypes = Array.ConvertAll(Enum.GetValues()[..3], (AccStatType stat) => stat.ToString()); private static readonly string[] s_damSlotTypes = Array.ConvertAll(Enum.GetValues()[..4], (DamSlotType slot) => slot.ToString()); private static readonly string[] s_damStatTypes = Array.ConvertAll(Enum.GetValues()[..2], (DamStatType stat) => stat.ToString()); private readonly StatData.Retriever _base; private readonly StatData.Retriever? _divider; public Token(StatData data, string text, InventorySlot slot) { //IL_001e: 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) int num = text.IndexOf('/'); if (num != -1) { _divider = ParseRetriever(data, text.Substring(num + 1), slot); text = text.Substring(0, num); } else { _divider = null; } _base = ParseRetriever(data, text, slot); } public string GetString() { long value = _base.Value; if (_divider != null) { long value2 = _divider.Value; if (value2 == 0L) { if (value <= 0) { return "-"; } return "100%"; } return ((double)value / (double)value2).ToString("P0"); } return value.ToString(); } private static StatData.Retriever ParseRetriever(StatData data, string text, InventorySlot slot) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!text.Contains("Damage", StringComparison.CurrentCultureIgnoreCase)) { return ParseAccuracyRetriever(data, text, slot); } return ParseDamageRetriever(data, text, slot); } private static StatData.Retriever ParseDamageRetriever(StatData data, string text, InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) DamSlotType value; if ((int)slot != 0) { value = slot.ToDamSlotType(); } else if (!TryGetEnumInText(text, s_damSlotTypes, out value)) { value = DamSlotType.All; } if (!TryGetEnumInText(text, s_damStatTypes, out var value2)) { value2 = DamStatType.Any; } return StatData.CreateDamageRetriever(data, value, value2); } private static StatData.Retriever ParseAccuracyRetriever(StatData data, string text, InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) AccSlotType value; if ((int)slot != 0) { value = slot.ToAccSlotType(); } else if (!TryGetEnumInText(text, s_accSlotTypes, out value)) { value = AccSlotType.All; } if (!TryGetEnumInText(text, s_accShotTypes, out var value2)) { value2 = AccShotType.Shot; } if (!TryGetEnumInText(text, s_accStatTypes, out var value3)) { value3 = AccStatType.Hit; } return StatData.CreateAccuracyRetriever(data, value, value2, value3); } private static bool TryGetEnumInText(string text, string[] enumStrings, out T value) where T : struct { int num = -1; int length = 0; foreach (string text2 in enumStrings) { if ((num = text.IndexOf(text2)) > -1) { length = text2.Length; break; } } if (num != -1) { if (text.Substring(num, length).TryToEnum(out value)) { return true; } DinoLogger.Error($"Unable to parse {typeof(T).Name} in token \"{text}\""); return false; } value = default(T); return false; } } private const char StartToken = '{'; private const char EndToken = '}'; private const string NoModFormat = "N/A (<#a0a0a0>{Damage})"; public static readonly string[] SlotNames = new string[5] { "RED", "GRE", "BLU", "PUR", "EX" }; private List _strings; private List _tokens; private StringBuilder _builder; private readonly StatData _parent; private readonly int _characterSlot; private readonly string _characterName; private string _text = ""; private TextMeshPro? _textMesh; private bool _hasMod; private bool _needsUpdate; private bool _meshNeedsUpdate; private string FormattedSlotName { get { //IL_0075: Unknown result type (might be due to invalid IL or missing references) string value = ((!Configuration.Config.FormatFullName) ? ((_characterSlot < 4) ? SlotNames[_characterSlot] : (SlotNames[^1] + (_characterSlot - 3))) : _characterName); return $"<#{ColorUtility.ToHtmlStringRGB(((Il2CppArrayBase)(object)PlayerManager.Current.m_playerColors)[_characterSlot])}>{value}: "; } } public string Text { get { if (_needsUpdate) { UpdateInner(); } return _text; } } public TextMeshPro? TextMesh { get { return _textMesh; } set { _textMesh = value; Update(updateMesh: true); } } public bool HasMod { get { return _hasMod; } set { _hasMod = value; SetFormat(); } } public StatParser(StatData data, SNet_Player player, bool hasMod) { _characterSlot = player.CharacterIndex; _characterName = player.NickName; _parent = data; _hasMod = hasMod; SetFormat(); Configuration.Config.OnChanged += SetFormat; } public void UpdateMesh() { if (_meshNeedsUpdate) { _meshNeedsUpdate = false; TextMeshPro? textMesh = TextMesh; if (textMesh != null) { ((TMP_Text)textMesh).SetText(Text, true); } } } public void Update(bool updateMesh = false) { _needsUpdate = true; _meshNeedsUpdate = !updateMesh; if (updateMesh) { TextMeshPro? textMesh = TextMesh; if (textMesh != null) { ((TMP_Text)textMesh).SetText(Text, true); } } } public string GetEndscreenText(InventorySlot slot) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) ExtractFormatInfo(Configuration.Config.EndScreenFormat, out StringBuilder builder, out List strings, out List tokens, slot); return CreateText(builder, strings, tokens); } [MemberNotNull(new string[] { "_builder", "_strings", "_tokens" })] private void SetFormat() { ExtractFormatInfo(_hasMod ? Configuration.Config.Format : "N/A (<#a0a0a0>{Damage})", out _builder, out _strings, out _tokens, (InventorySlot)0); Update(); } private void ExtractFormatInfo(string format, out StringBuilder builder, out List strings, out List tokens, InventorySlot restrictSlot = (InventorySlot)0) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) strings = new List(); tokens = new List(); int num = 0; int num2 = format.IndexOf('{'); while (num2 != -1) { int num3 = format.IndexOf('}', num2 + 1); if (num3 == -1) { break; } List obj = strings; int num4 = num; obj.Add(format.Substring(num4, num2 - num4)); List obj2 = tokens; StatData parent = _parent; num4 = num2 + 1; obj2.Add(new Token(parent, format.Substring(num4, num3 - num4), restrictSlot)); num = num3 + 1; num2 = format.IndexOf('{', num3 + 1); } strings.Add(format.Substring(num)); builder = new StringBuilder(6 + _strings.Sum((string segment) => segment.Length) + _tokens.Count * 11); } private void UpdateInner() { _builder.Append(FormattedSlotName); _text = CreateText(_builder, _strings, _tokens); _needsUpdate = false; } private static string CreateText(StringBuilder builder, List strings, List tokens) { for (int i = 0; i < strings.Count; i++) { builder.Append(strings[i]); if (i < tokens.Count) { builder.Append(tokens[i].GetString()); } } string result = builder.ToString(); builder.Clear(); return result; } } } namespace StatDisplay.Config { public static class Configuration { public static readonly IConfiguration Config; [InvokeOnLoad] private static void Init() { } static Configuration() { if (ArchiveWrapper.HasArchive) { Config = LoadArchive(); } else { Config = new VanillaConfiguration(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static IConfiguration LoadArchive() { StatDisplayModule.Load(); return StatDisplayFeature.Instance; } } public interface IConfiguration { static readonly string[] DefaultColors = new string[5] { "#a0a0a0", "#ffff00", "#ff3080", "#a0a0a0", "#ffff00" }; const PresetConfigSetting DefaultFormat = PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage; const PresetConfigSetting DefaultEndScreenFormat = PresetConfigSetting.Hit | PresetConfigSetting.Crit; string Format { get; } bool FormatFullName { get; } Vector2 Offset { get; } string EndScreenFormat { get; } event Action? OnChanged; static string BuildPresetFormat(PresetConfigSetting settings) { return BuildPresetFormat(settings, DefaultColors); } static string BuildPresetFormat(PresetConfigSetting settings, string[] colors) { StringBuilder sb = new StringBuilder(); if (settings.HasFlag(PresetConfigSetting.Hit)) { StringBuilder stringBuilder = sb; StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder); handler.AppendLiteral("<"); handler.AppendFormatted(colors[0]); handler.AppendLiteral(">{Hit/Fired}"); stringBuilder2.Append(ref handler); } if (settings.HasFlag(PresetConfigSetting.Crit)) { AppendPrefixNotFirst('/', "<" + colors[1] + ">{Crit/Hit}"); } if (settings.HasFlag(PresetConfigSetting.Pierce)) { AppendPrefixNotFirst('/', "<" + colors[2] + ">{FullHit/Hit}"); } if (settings.HasFlag(PresetConfigSetting.Damage)) { AppendPrefixNotFirst(' ', "(<" + colors[3] + ">{Damage}"); if (settings.HasFlag(PresetConfigSetting.CritDamage)) { StringBuilder stringBuilder = sb; StringBuilder stringBuilder3 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(23, 1, stringBuilder); handler.AppendLiteral("/<"); handler.AppendFormatted(colors[4]); handler.AppendLiteral(">{DamageCrit}"); stringBuilder3.Append(ref handler); } sb.Append(')'); } else if (settings.HasFlag(PresetConfigSetting.CritDamage)) { AppendPrefixNotFirst(' ', "(<" + colors[4] + ">{DamageCrit})"); } return sb.ToString(); internal void AppendPrefixNotFirst(char prefix, string value) { if (sb.Length == 0) { sb.Append(value); } else { sb.Append(prefix); sb.Append(value); } } } static Vector2 ParseOffset(string offset) { if (!offset.Contains(',')) { return Vector2.Zero; } string[] array = offset.Split(','); if (!float.TryParse(array[0].Trim(), out var result) || !float.TryParse(array[1].Trim(), out var result2)) { return Vector2.Zero; } return new Vector2(result, result2); } } [Flags] public enum PresetConfigSetting { None = 0, Hit = 1, Crit = 2, Pierce = 4, Damage = 8, CritDamage = 0x10 } } namespace StatDisplay.Config.Vanilla { public class VanillaConfiguration : IConfiguration { private readonly ConfigEntry _format; private readonly ConfigEntry _overrideFormat; private readonly ConfigEntry _formatFullName; private readonly ConfigEntry _customFormat; private readonly ConfigEntry _offset; private readonly string[] _colors = (string[])IConfiguration.DefaultColors.Clone(); private readonly ConfigEntry _hitColor; private readonly ConfigEntry _critColor; private readonly ConfigEntry _pierceColor; private readonly ConfigEntry _damageColor; private readonly ConfigEntry _critDamageColor; private readonly ConfigEntry _endScreenFormat; private readonly ConfigEntry _endScreenOverrideFormat; private readonly ConfigEntry _endScreenCustomFormat; private readonly ConfigFile _configFile; public string Format { get; private set; } public bool FormatFullName => _formatFullName.Value; public Vector2 Offset { get; private set; } public string EndScreenFormat { get; private set; } public event Action? OnChanged; public VanillaConfiguration() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Expected O, but got Unknown _configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "StatDisplay.cfg"), true); string text = "Display Settings"; string text2 = "Preset display settings to use.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- CritDamage: Crit Damage Dealt"; _format = _configFile.Bind(text, "Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage, text2); text2 = "Show full player names instead of shorthand names."; _formatFullName = _configFile.Bind(text, "Display Full Names", false, text2); text2 = "x, y offset of stat data."; _offset = _configFile.Bind(text, "Display Offset", "0, 0", text2); text2 = "Preset display format to use for the mission end screen. Displayed for each weapon."; _endScreenFormat = _configFile.Bind(text, "End Screen Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit, text2); text = "Preset Colors"; text2 = "Color for Hit preset setting."; _hitColor = _configFile.Bind(text, "Hit Color", _colors[0], text2); text2 = "Color for Crit preset setting."; _critColor = _configFile.Bind(text, "Crit Color", _colors[1], text2); text2 = "Color for Pierce preset setting."; _pierceColor = _configFile.Bind(text, "Pierce Color", _colors[2], text2); text2 = "Color for Damage preset setting."; _damageColor = _configFile.Bind(text, "Damage Color", _colors[3], text2); text2 = "Color for CritDamage preset setting."; _critDamageColor = _configFile.Bind(text, "CritDamage Color", _colors[4], text2); text = "Custom Settings"; text2 = "Whether to use the custom format instead of the preset format."; _overrideFormat = _configFile.Bind(text, "Use Custom Format", false, text2); text2 = "Custom display format to use. Use variables by placing them in \"{}\", e.g. {PrimaryShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of slot type, shot type, and stat type. They may occur in any order and are case-insensitive.\nAccepted slots (Unspecified = All):\n- All\n- Primary = Main\n- Secondary = Special\n- Melee (Damage Only)\n- Tool = Class (Damage Only)\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits."; _customFormat = _configFile.Bind(text, "Custom Format", IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage), text2); text2 = "Whether to use the custom format for the mission end screen instead of the preset format."; _endScreenOverrideFormat = _configFile.Bind(text, "End Screen Use Custom Format", false, text2); text2 = "Custom display format to use for the mission end screen. Follows the same rules as Custom Format, but slot cannot be specified."; _endScreenCustomFormat = _configFile.Bind(text, "End Screen Custom Format", IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit), text2); CacheValues(); LiveEdit.CreateListener(Paths.ConfigPath, "StatDisplay.cfg", false).FileChanged += new LiveEditEventHandler(OnFileChanged); } private void OnFileChanged(LiveEditEventArgs _) { _configFile.Reload(); CacheValues(); this.OnChanged?.Invoke(); } [MemberNotNull(new string[] { "Format", "EndScreenFormat" })] private void CacheValues() { _colors[0] = _hitColor.Value; _colors[1] = _critColor.Value; _colors[2] = _pierceColor.Value; _colors[3] = _damageColor.Value; _colors[4] = _critDamageColor.Value; Offset = IConfiguration.ParseOffset(_offset.Value); Format = (_overrideFormat.Value ? _customFormat.Value : IConfiguration.BuildPresetFormat(_format.Value, _colors)); EndScreenFormat = (_endScreenOverrideFormat.Value ? _endScreenCustomFormat.Value : IConfiguration.BuildPresetFormat(_endScreenFormat.Value, _colors)); } } } namespace StatDisplay.Config.Archive { [EnableFeatureByDefault] public class StatDisplayFeature : Feature, IConfiguration { public class StatDisplaySettings { [FSDisplayName("Display Format")] [FSDescription("Preset display settings to use.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- Damage: All Damage Dealt\n- CritDamage: Crit Damage Dealt")] public List Format { get; set; } = new List(); [FSDisplayName("Display Full Names")] [FSDescription("Show full player names instead of shorthand names.")] public bool FormatFullName { get; set; } [FSDisplayName("Display Offset")] [FSDescription("x, y offset of stat data.")] public string Offset { get; set; } = "0, 0"; [FSDisplayName("End Screen Display Format")] [FSDescription("Preset display format to use for the mission end screen. Displayed for each weapon.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- Damage: All Damage Dealt\n- CritDamage: Crit Damage Dealt")] public List EndScreenFormat { get; set; } = new List(); [FSHeader("Preset Colors", true)] [FSDisplayName("Hit Color")] [FSDescription("Color for Hit preset setting.")] public SColor HitColor { get; set; } = new SColor(0.63f, 0.63f, 0.63f, (float?)null); [FSDisplayName("Crit Color")] [FSDescription("Color for Crit preset setting.")] public SColor CritColor { get; set; } = new SColor(1f, 1f, 0f, (float?)null); [FSDisplayName("Pierce Color")] [FSDescription("Color for Pierce preset setting.")] public SColor PierceColor { get; set; } = new SColor(1f, 0.2f, 0.5f, (float?)null); [FSDisplayName("Damage Color")] [FSDescription("Color for Damage preset setting.")] public SColor DamageColor { get; set; } = new SColor(0.63f, 0.63f, 0.63f, (float?)null); [FSDisplayName("Crit Damage Color")] [FSDescription("Color for Crit Damage preset setting.")] public SColor CritDamageColor { get; set; } = new SColor(1f, 1f, 0f, (float?)null); [FSHeader("Custom", true)] [FSDisplayName("Use Custom Format")] [FSDescription("Whether to use the custom format instead of the preset format.")] public bool OverrideFormat { get; set; } [FSDisplayName("Custom Format")] [FSMaxLength(255)] [FSDescription("Custom display format to use. Use variables by placing them in \"{}\", e.g. {PrimaryShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of slot type, shot type, and stat type. They may occur in any order and are case-insensitive.\nAccepted slots (Unspecified = All):\n- All\n- Primary = Main\n- Secondary = Special\n- Melee (Damage Only)\n- Tool = Class (Damage Only)\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits.")] public string CustomFormat { get; set; } = IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage); [FSDisplayName("End Screen Use Custom Format")] [FSDescription("Whether to use the custom format for the mission end screen instead of the preset format.")] public bool EndScreenOverrideFormat { get; set; } [FSDisplayName("End Screen Custom Format")] [FSMaxLength(255)] [FSDescription("Custom display format to use for the mission end screen. Use variables by placing them in \"{}\", e.g. {ShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of shot type and stat type. They may occur in any order and are case-insensitive.\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits.")] public string EndScreenCustomFormat { get; set; } = IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit); [FSHide] public bool IsFirstTime { get; set; } = true; } private readonly string[] _colors = new string[5]; public static StatDisplayFeature Instance { get; private set; } public override string Name => "Stat Display"; public override string Description => "Displays in-game accuracy and damage."; public override FeatureGroup Group => FeatureGroups.Hud; public override bool SkipInitialOnEnable => true; public string Format { get; private set; } = string.Empty; public bool FormatFullName => Settings.FormatFullName; public Vector2 Offset { get; private set; } public string EndScreenFormat { get; private set; } = string.Empty; [FeatureConfig] public static StatDisplaySettings Settings { get; set; } public event Action? OnChanged; public override void Init() { Instance = this; if (Settings.IsFirstTime) { Settings.Format = new List { PresetConfigSetting.Hit, PresetConfigSetting.Crit, PresetConfigSetting.Damage }; Settings.EndScreenFormat = new List { PresetConfigSetting.Hit, PresetConfigSetting.Crit }; Settings.IsFirstTime = false; } CacheValues(); } public override void OnFeatureSettingChanged(FeatureSetting setting) { CacheValues(); this.OnChanged?.Invoke(); } private void CacheValues() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) _colors[0] = SColorExtensions.ToHexString(Settings.HitColor); _colors[1] = SColorExtensions.ToHexString(Settings.CritColor); _colors[2] = SColorExtensions.ToHexString(Settings.PierceColor); _colors[3] = SColorExtensions.ToHexString(Settings.DamageColor); _colors[4] = SColorExtensions.ToHexString(Settings.CritDamageColor); Offset = IConfiguration.ParseOffset(Settings.Offset); Format = (Settings.OverrideFormat ? Settings.CustomFormat : IConfiguration.BuildPresetFormat((PresetConfigSetting)Settings.Format.Sum((PresetConfigSetting x) => (int)x), _colors)); EndScreenFormat = (Settings.EndScreenOverrideFormat ? Settings.EndScreenCustomFormat : IConfiguration.BuildPresetFormat((PresetConfigSetting)Settings.EndScreenFormat.Sum((PresetConfigSetting x) => (int)x), _colors)); } public override void OnEnable() { CacheValues(); this.OnChanged?.Invoke(); StatHandler.SetVisible(visible: true); } public override void OnDisable() { StatHandler.SetVisible(visible: false); } } internal sealed class StatDisplayModule : IArchiveModule { public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } public void Init() { } [MethodImpl(MethodImplOptions.NoInlining)] public static void Load() { ArchiveMod.RegisterArchiveModule(typeof(StatDisplayModule)); } } } namespace StatDisplay.Attributes { [AttributeUsage(AttributeTargets.Method)] internal sealed class InvokeOnAssetLoadAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class InvokeOnBuildStartAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class InvokeOnCleanupAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class InvokeOnEnterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class InvokeOnLoadAttribute : Attribute { } }