Decompiled source of StatDisplay v1.1.0

StatDisplay.dll

Decompiled 2 days ago
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<MethodInfo> _cleanupCallbacks;

		private IEnumerable<MethodInfo> _enterCallbacks;

		private IEnumerable<MethodInfo> _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<HarmonyPatch>() != null)
				{
					val.PatchAll(type);
				}
			}
			CacheFrequentCallbacks();
			InvokeCallbacks<InvokeOnLoadAttribute>();
			LevelAPI.OnLevelCleanup += RunFrequentCallback(_cleanupCallbacks);
			LevelAPI.OnEnterLevel += RunFrequentCallback(_enterCallbacks);
			LevelAPI.OnBuildStart += RunFrequentCallback(_buildStartCallbacks);
			AssetAPI.OnStartupAssetsLoaded += InvokeCallbacks<InvokeOnAssetLoadAttribute>;
			((BasePlugin)this).Log.LogMessage((object)"Loaded StatDisplay");
		}

		private Type[] GetTypesSafe()
		{
			IEnumerable<Type> 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<MethodInfo> callbacks)
		{
			return delegate
			{
				foreach (MethodInfo callback in callbacks)
				{
					callback.Invoke(null, null);
				}
			};
		}

		private void CacheFrequentCallbacks()
		{
			IEnumerable<MethodInfo> source = from method in ((IEnumerable<Type>)_allTypes).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods)
				where method.IsStatic
				select method;
			_cleanupCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnCleanupAttribute>() != null);
			_enterCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnEnterAttribute>() != null);
			_buildStartCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnBuildStartAttribute>() != null);
		}

		private void InvokeCallbacks<T>() where T : Attribute
		{
			foreach (MethodInfo item in from method in ((IEnumerable<Type>)_allTypes).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods)
				where method.GetCustomAttribute<T>() != null
				where method.IsStatic
				select method)
			{
				item.Invoke(null, null);
			}
		}
	}
	public static class StatManager
	{
		private static readonly Dictionary<ulong, SNet_Player> _remotePlayers = new Dictionary<ulong, SNet_Player>();

		private static bool _masterHasMod = false;

		public static IReadOnlyCollection<SNet_Player> RemotePlayers => _remotePlayers.Values;

		public static bool MasterHasMod
		{
			get
			{
				return _masterHasMod;
			}
			private set
			{
				if (_masterHasMod == value)
				{
					return;
				}
				_masterHasMod = value;
				foreach (SNet_Slot item in (Il2CppArrayBase<SNet_Slot>)(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<T>(this string? value, T defaultValue) where T : struct
		{
			if (string.IsNullOrEmpty(value))
			{
				return defaultValue;
			}
			if (!Enum.TryParse<T>(value.Replace(" ", null), ignoreCase: true, out var result))
			{
				return defaultValue;
			}
			return result;
		}

		public static bool TryToEnum<T>(this string? value, out T enumValue) where T : struct
		{
			enumValue = default(T);
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			return Enum.TryParse<T>(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<Dam_EnemyDamageLimb>)(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<PlayerAgent>().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<PlayerAgent>().Inventory).Cast<PlayerInventorySynced>();
				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<Dam_EnemyDamageLimb>)(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<PlayerAgent>().Owner.Lookup;
				DamStatType stat = (((int)((Il2CppArrayBase<Dam_EnemyDamageLimb>)(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<CM_PageSuccess_PrisonerEvaluation>)(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<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Length; i++)
			{
				SNet_Slot val = ((Il2CppArrayBase<SNet_Slot>)(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<CM_PageSuccess_PrisonerEvaluation>)(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<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Length; i++)
			{
				SNet_Slot val = ((Il2CppArrayBase<SNet_Slot>)(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<CM_PageSuccess_PrisonerEvaluation>(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 = "<color=red>" + (player.IsBot ? ("[" + Text.Get(1372u) + "]") : "") + player.GetName() + "</color>";
				((TMP_Text)report.m_gearHeader).text = "<size=150%><nobr>" + Text.Get(888u) + "</nobr></size>";
			}
			bool flag2 = false;
			bool flag3 = false;
			StringBuilder stringBuilder = new StringBuilder("<color=white>");
			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("</color>");
			((TMP_Text)report.m_gear).text = stringBuilder.ToString();
			stringBuilder.Clear();
			if (isFail)
			{
				((TMP_Text)report.m_evalHeader).text = "<size=150%><nobr>" + Text.Get(898u) + "</nobr></size>";
			}
			else
			{
				PlayerAgent obj = ((Il2CppObjectBase)player.PlayerAgent).Cast<PlayerAgent>();
				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)) ? ("<color=red>" + Text.Get(893u) + "</color>") : ("<color=red>" + Text.Get(892u) + "</color>")))));
				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("<color=white>" + text4 + "</color>") }));
				stringBuilder.AppendLine(Text.Format(900u, (Object[])(object)new Object[1] { Object.op_Implicit("<color=white>" + text5 + "</color>") }));
			}
			if (flag && data.HasMod)
			{
				if (flag2)
				{
					stringBuilder.AppendLine(text + "<color=white>: " + data.StatText.GetEndscreenText((InventorySlot)1) + "</color>");
				}
				if (flag3)
				{
					stringBuilder.AppendLine(text2 + "<color=white>: " + data.StatText.GetEndscreenText((InventorySlot)2) + "</color>");
				}
				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("</color>");
				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)}</color> (<#ffff00>{(ulong)data.GetDamage(slot, DamStatType.Crit)}</color>)";
		}
	}
	[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<T> 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<T>(EventName, (Action<ulong, T>)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<T>(EventName, packetData, target, priority);
			}
			else
			{
				NetworkAPI.InvokeEvent<T>(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<pAccDeltaData>
	{
		public override string GUID => "ACCSYN";

		protected override void Receive(ulong lookup, pAccDeltaData packet)
		{
			StatSyncManager.ReceiveData(packet);
		}
	}
	internal sealed class DamDeltaSync : SyncedEvent<pDamDeltaData>
	{
		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<bool>
	{
		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<TextMeshPro> _textMeshList = new List<TextMeshPro>();

		private readonly List<GameObject> _meshGOList = new List<GameObject>();

		private readonly Dictionary<ulong, StatData> _data = new Dictionary<ulong, StatData>();

		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<StatHandler>();
			GameObject val = new GameObject("StatHandler");
			Object.DontDestroyOnLoad((Object)val);
			Instance = val.AddComponent<StatHandler>();
		}

		[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<SNet_Slot> playerSlots = SNet.Slots.PlayerSlots;
			for (int i = 0; i < ((Il2CppArrayBase<SNet_Slot>)(object)playerSlots).Count; i++)
			{
				if ((Object)(object)((Il2CppArrayBase<SNet_Slot>)(object)playerSlots)[i].player == (Object)null)
				{
					continue;
				}
				ulong lookup = ((Il2CppArrayBase<SNet_Slot>)(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<RectTransform>(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<GameObject>(((Component)s_baseRectTransform).gameObject, ((Transform)s_baseRectTransform).parent);
				RectTransform component = obj.GetComponent<RectTransform>();
				obj.gameObject.SetActive(true);
				foreach (Transform componentsInChild2 in obj.GetComponentsInChildren<Transform>(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<TextMeshPro>(s_baseTextMesh, val.transform);
				((TMP_Text)val2).m_width = ((TMP_Text)val2).m_width * 2f;
				((Component)val2).GetComponent<RectTransform>().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<GameObject> 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<BasePlugin>)(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 <AddCallbacks_Unsafe>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];
					<AddCallbacks_Unsafe>g__Copy|6_1(AccShotType.Shot, delta.Shots, ref <>c__DisplayClass6_);
					<AddCallbacks_Unsafe>g__Copy|6_1(AccShotType.Group, delta.Groups, ref <>c__DisplayClass6_);
					<AddCallbacks_Unsafe>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<BasePlugin>)(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<ulong, DamSlotType> hitCallback)
		{
			if (HasEWC)
			{
				AddHitCallback_Unsafe(hitCallback);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool HasCWC_Unsafe(BulletWeapon weapon)
		{
			return (Object)(object)((Component)weapon).GetComponent<CustomWeaponComponent>() != (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<ulong, DamSlotType> 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 <AddCallbacks_Unsafe>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<AccSlotType>()[..2], (AccSlotType slot) => slot.ToString());

			private static readonly string[] s_accShotTypes = Array.ConvertAll(Enum.GetValues<AccShotType>()[..3], (AccShotType shot) => shot.ToString());

			private static readonly string[] s_accStatTypes = Array.ConvertAll(Enum.GetValues<AccStatType>()[..3], (AccStatType stat) => stat.ToString());

			private static readonly string[] s_damSlotTypes = Array.ConvertAll(Enum.GetValues<DamSlotType>()[..4], (DamSlotType slot) => slot.ToString());

			private static readonly string[] s_damStatTypes = Array.ConvertAll(Enum.GetValues<DamStatType>()[..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<DamSlotType>(text, s_damSlotTypes, out value))
				{
					value = DamSlotType.All;
				}
				if (!TryGetEnumInText<DamStatType>(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<AccSlotType>(text, s_accSlotTypes, out value))
				{
					value = AccSlotType.All;
				}
				if (!TryGetEnumInText<AccShotType>(text, s_accShotTypes, out var value2))
				{
					value2 = AccShotType.Shot;
				}
				if (!TryGetEnumInText<AccStatType>(text, s_accStatTypes, out var value3))
				{
					value3 = AccStatType.Hit;
				}
				return StatData.CreateAccuracyRetriever(data, value, value2, value3);
			}

			private static bool TryGetEnumInText<T>(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<T>(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}</color>)";

		public static readonly string[] SlotNames = new string[5] { "RED", "GRE", "BLU", "PUR", "EX" };

		private List<string> _strings;

		private List<Token> _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<Color>)(object)PlayerManager.Current.m_playerColors)[_characterSlot])}>{value}</color>: ";
			}
		}

		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<string> strings, out List<Token> 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}</color>)", out _builder, out _strings, out _tokens, (InventorySlot)0);
			Update();
		}

		private void ExtractFormatInfo(string format, out StringBuilder builder, out List<string> strings, out List<Token> tokens, InventorySlot restrictSlot = (InventorySlot)0)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			strings = new List<string>();
			tokens = new List<Token>();
			int num = 0;
			int num2 = format.IndexOf('{');
			while (num2 != -1)
			{
				int num3 = format.IndexOf('}', num2 + 1);
				if (num3 == -1)
				{
					break;
				}
				List<string> obj = strings;
				int num4 = num;
				obj.Add(format.Substring(num4, num2 - num4));
				List<Token> 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<string> strings, List<Token> 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}</color>");
				stringBuilder2.Append(ref handler);
			}
			if (settings.HasFlag(PresetConfigSetting.Crit))
			{
				AppendPrefixNotFirst('/', "<" + colors[1] + ">{Crit/Hit}</color>");
			}
			if (settings.HasFlag(PresetConfigSetting.Pierce))
			{
				AppendPrefixNotFirst('/', "<" + colors[2] + ">{FullHit/Hit}</color>");
			}
			if (settings.HasFlag(PresetConfigSetting.Damage))
			{
				AppendPrefixNotFirst(' ', "(<" + colors[3] + ">{Damage}</color>");
				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}</color>");
					stringBuilder3.Append(ref handler);
				}
				sb.Append(')');
			}
			else if (settings.HasFlag(PresetConfigSetting.CritDamage))
			{
				AppendPrefixNotFirst(' ', "(<" + colors[4] + ">{DamageCrit}</color>)");
			}
			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<PresetConfigSetting> _format;

		private readonly ConfigEntry<bool> _overrideFormat;

		private readonly ConfigEntry<bool> _formatFullName;

		private readonly ConfigEntry<string> _customFormat;

		private readonly ConfigEntry<string> _offset;

		private readonly string[] _colors = (string[])IConfiguration.DefaultColors.Clone();

		private readonly ConfigEntry<string> _hitColor;

		private readonly ConfigEntry<string> _critColor;

		private readonly ConfigEntry<string> _pierceColor;

		private readonly ConfigEntry<string> _damageColor;

		private readonly ConfigEntry<string> _critDamageColor;

		private readonly ConfigEntry<PresetConfigSetting> _endScreenFormat;

		private readonly ConfigEntry<bool> _endScreenOverrideFormat;

		private readonly ConfigEntry<string> _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<PresetConfigSetting>(text, "Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage, text2);
			text2 = "Show full player names instead of shorthand names.";
			_formatFullName = _configFile.Bind<bool>(text, "Display Full Names", false, text2);
			text2 = "x, y offset of stat data.";
			_offset = _configFile.Bind<string>(text, "Display Offset", "0, 0", text2);
			text2 = "Preset display format to use for the mission end screen. Displayed for each weapon.";
			_endScreenFormat = _configFile.Bind<PresetConfigSetting>(text, "End Screen Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit, text2);
			text = "Preset Colors";
			text2 = "Color for Hit preset setting.";
			_hitColor = _configFile.Bind<string>(text, "Hit Color", _colors[0], text2);
			text2 = "Color for Crit preset setting.";
			_critColor = _configFile.Bind<string>(text, "Crit Color", _colors[1], text2);
			text2 = "Color for Pierce preset setting.";
			_pierceColor = _configFile.Bind<string>(text, "Pierce Color", _colors[2], text2);
			text2 = "Color for Damage preset setting.";
			_damageColor = _configFile.Bind<string>(text, "Damage Color", _colors[3], text2);
			text2 = "Color for CritDamage preset setting.";
			_critDamageColor = _configFile.Bind<string>(text, "CritDamage Color", _colors[4], text2);
			text = "Custom Settings";
			text2 = "Whether to use the custom format instead of the preset format.";
			_overrideFormat = _configFile.Bind<bool>(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<string>(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<bool>(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<string>(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<PresetConfigSetting> Format { get; set; } = new List<PresetConfigSetting>();

			[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<PresetConfigSetting> EndScreenFormat { get; set; } = new List<PresetConfigSetting>();

			[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>
				{
					PresetConfigSetting.Hit,
					PresetConfigSetting.Crit,
					PresetConfigSetting.Damage
				};
				Settings.EndScreenFormat = new List<PresetConfigSetting>
				{
					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
	{
	}
}