Decompiled source of StatDisplay v1.3.0

StatDisplay.dll

Decompiled 2 weeks 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.Enums;
using EWC.CustomWeapon.Properties;
using EWC.CustomWeapon.Properties.Traits;
using EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components;
using EWC.CustomWeapon.Structs;
using EWC.Utils;
using Enemies;
using GTFO.API;
using GTFO.API.Utilities;
using GameData;
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 OptionalNetworking;
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.Patches;
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+3b1949ea65becb85835140d68ad8a173692d2c02")]
[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.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[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);
				}
			}
			GearCreationPatches.Init(val);
			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);
			}
		}
	}
}
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 CharacterSlotPatches
	{
		[HarmonyPatch(typeof(SNet_SyncManager), "OnSyncPlayerData_Session")]
		[HarmonyWrapSafe]
		[HarmonyPrefix]
		private static void Pre_SyncData(pPlayerData_Session data, ref (SNet_Player player, int index) __state)
		{
			//IL_0031: 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)
			__state.index = ((pPlayerData_Session)(ref data)).characterSlotIndex;
			if (__state.index == -1)
			{
				return;
			}
			if (((pPlayer)(ref data.player)).GetPlayer(ref __state.player))
			{
				pPlayerData_Session session = __state.player.Session;
				if (((pPlayerData_Session)(ref session)).characterSlotIndex != __state.index)
				{
					return;
				}
			}
			__state.index = -1;
		}

		[HarmonyPatch(typeof(SNet_SyncManager), "OnSyncPlayerData_Session")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_SyncData((SNet_Player player, int index) __state)
		{
			if (__state.index != -1 && __state.player.CharacterIndex == __state.index)
			{
				StatHandler.RefreshMeshList();
			}
		}
	}
	[HarmonyPatch]
	internal static class DamagePatches
	{
		private struct CacheInfo
		{
			public DamSlotType slot;

			public bool inFireFunc;

			public ulong lookup;

			public readonly bool Valid
			{
				get
				{
					if (slot != DamSlotType.All)
					{
						return lookup != 0;
					}
					return false;
				}
			}

			public CacheInfo()
			{
				slot = DamSlotType.All;
				inFireFunc = false;
				lookup = 0uL;
			}
		}

		private static CacheInfo _cache = new CacheInfo();

		private static readonly Random _random = new Random();

		[InvokeOnLoad]
		private static void Init()
		{
			EWCWrapper.AddProjectileHitCallback(AssignCacheIfDone);
			EWCWrapper.AddProjectilePostHitCallback(ClearCacheIfDone);
			EWCWrapper.AddPreShotFiredCallback(AssignCacheIfDone);
			EWCWrapper.AddPostShotFiredCallback(ClearCacheIfDone);
		}

		private static void AssignCacheIfDone(ulong lookup, DamSlotType slot)
		{
			if (!_cache.inFireFunc)
			{
				_cache.slot = slot;
				_cache.lookup = lookup;
			}
		}

		private static void ClearCacheIfDone()
		{
			if (!_cache.inFireFunc)
			{
				_cache.slot = DamSlotType.All;
				_cache.lookup = 0uL;
			}
		}

		private static void ClearCache()
		{
			_cache.slot = DamSlotType.All;
			_cache.inFireFunc = false;
			_cache.lookup = 0uL;
		}

		[HarmonyPatch(typeof(ShotgunSynced), "Fire")]
		[HarmonyPatch(typeof(BulletWeaponSynced), "Fire")]
		[HarmonyPatch(typeof(Shotgun), "Fire")]
		[HarmonyPatch(typeof(BulletWeapon), "Fire")]
		[HarmonyPriority(600)]
		[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]
		[HarmonyPriority(200)]
		[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]
		[HarmonyPriority(600)]
		[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 (!NetworkManager.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 (!NetworkManager.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);
			}
		}
	}
	internal class GearCreationPatches
	{
		public static void Init(Harmony harmony)
		{
			if (!EWCWrapper.HasEWC)
			{
				harmony.PatchAll(typeof(GearCreationPatches));
			}
		}

		[HarmonyPatch(typeof(PlayerBackpack), "CreateAndStoreBackpackItem")]
		[HarmonyPostfix]
		private static void Post_AddItem(PlayerBackpack __instance, Item item, 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
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (((int)slot == 1 || (int)slot == 2) && StatHandler.TryGetData(__instance.Owner, out StatData data))
			{
				data.SetAccuracyWeights(item, slot.ToAccSlotType());
			}
		}
	}
	[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 || __instance.m_playerReports == 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 && !NetworkManager.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 struct pAccDeltaData
	{
		private const int Count = 18;

		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(short[,,] deltaData)
		{
			return new pAccDeltaData
			{
				DeltaData = deltaData
			};
		}
	}
	internal struct pDamDeltaData
	{
		private const int Count = 8;

		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(float[,] deltaData)
		{
			return new pDamDeltaData
			{
				DeltaData = deltaData
			};
		}
	}
	internal static class NetworkManager
	{
		public static ModInfo ModInfo { get; }

		public static bool MasterHasMod => ModInfo.MasterHasMod;

		static NetworkManager()
		{
			ModInfo = OptionalNetworkAPI.RegisterMod("StatDisplay");
		}
	}
	internal static class StatSyncManager
	{
		private static readonly CustomDataSender<pAccDeltaData> _accSender;

		private static readonly CustomDataSender<pDamDeltaData> _damSender;

		static StatSyncManager()
		{
			_accSender = NetworkManager.ModInfo.AddCustomDataSender<pAccDeltaData>((Action<SNet_Player, pAccDeltaData>)ReceiveData, (Func<SNet_Player, pAccDeltaData>)null, (SenderMode)1);
			_damSender = NetworkManager.ModInfo.AddCustomDataSender<pDamDeltaData>((Action<SNet_Player, pDamDeltaData>)ReceiveData, (Func<SNet_Player, pDamDeltaData>)null, (SenderMode)1);
		}

		[InvokeOnLoad]
		private static void Init()
		{
		}

		internal static void BroadcastAccData(StatData data)
		{
			if (data.PopRemoteAccDelta(out short[,,] deltaAccuracy))
			{
				_accSender.SendData(data.Owner, pAccDeltaData.Create(deltaAccuracy), (SNet_Player)null, (SNet_ChannelType)0);
			}
		}

		internal static void BroadcastDamData(StatData data)
		{
			if (data.PopRemoteDamDelta(out float[,] deltaDamage))
			{
				_damSender.SendData(data.Owner, pDamDeltaData.Create(deltaDamage), (SNet_Player)null, (SNet_ChannelType)0);
			}
		}

		internal static void ReceiveData(SNet_Player player, pAccDeltaData data)
		{
			StatHandler.AddRemoteAccDelta(player.Lookup, data.DeltaData);
		}

		internal static void ReceiveData(SNet_Player player, pDamDeltaData data)
		{
			StatHandler.AddRemoteDamDelta(player.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.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>();
		}

		[InvokeOnEnter]
		private static void RefreshOnEnter()
		{
			RefreshMeshList();
		}

		[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;
			ModInfo modInfo = NetworkManager.ModInfo;
			modInfo.OnPlayerJoined += OnPlayerJoined;
			modInfo.OnPlayerModSet += OnPlayerModSet;
			modInfo.OnMasterModSet += delegate
			{
				RefreshMeshList_Internal();
			};
			modInfo.OnPlayerSpawned += OnPlayerSpawned;
			modInfo.OnPlayerDespawned += delegate
			{
				RefreshMeshList_Internal();
			};
			modInfo.OnPlayerLeft += OnPlayerLeft;
			modInfo.OnLobbyLeft += OnLobbyLeft;
		}

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

		private void OnPlayerJoined(SNet_Player player)
		{
			OnPlayerModSet(player, hasMod: false);
		}

		private void OnPlayerModSet(SNet_Player player, bool hasMod)
		{
			if (_data.TryGetValue(player.Lookup, out StatData value))
			{
				if (value.HasMod == hasMod)
				{
					return;
				}
				value.HasMod = hasMod;
			}
			else
			{
				_data.Add(player.Lookup, new StatData(player, hasMod));
			}
			if (player.CharacterIndex != -1)
			{
				RefreshMeshList_Internal();
			}
		}

		private void OnPlayerLeft(SNet_Player player)
		{
			if (_data.Remove(player.Lookup, out StatData value) && (value.HasMod || NetworkManager.MasterHasMod))
			{
				RefreshMeshList_Internal();
			}
		}

		private void OnPlayerSpawned(PlayerAgent player)
		{
			if (player.Sync.Replicator.OwningPlayer.CharacterIndex != -1)
			{
				RefreshMeshList_Internal();
			}
		}

		private void OnLobbyLeft()
		{
			_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 SetVisible(bool visible)
		{
			Instance.SetVisible_Internal(visible);
		}

		private void SetVisible_Internal(bool visible)
		{
			if (visible == _visible)
			{
				return;
			}
			_visible = visible;
			if (visible)
			{
				RefreshMeshList_Internal();
				return;
			}
			foreach (GameObject meshGO in _meshGOList)
			{
				meshGO.SetActive(false);
			}
		}

		internal static void RefreshMeshList()
		{
			Instance.RefreshMeshList_Internal();
		}

		private void RefreshMeshList_Internal()
		{
			//IL_00d9: 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++)
			{
				SNet_Player player = ((Il2CppArrayBase<SNet_Slot>)(object)playerSlots)[i].player;
				if (!((Object)(object)player == (Object)null) && _data.TryGetValue(player.Lookup, out StatData value) && player.CharacterIndex != -1)
				{
					if (value.HasMod || NetworkManager.MasterHasMod)
					{
						EnsureMeshCapacity(num + 1);
						value.StatText.SetToSlot(_textMeshList[num], player.CharacterIndex);
						_meshGOList[num].transform.localPosition = GetMeshPos(num);
						_meshGOList[num].SetActive(true);
						num++;
					}
					else
					{
						value.StatText.SetToEmpty();
					}
				}
			}
		}

		internal static void ForceSync()
		{
			Instance.DoSync();
		}

		private void DoSync()
		{
			_nextSyncTime = Clock.Time + 2f;
			foreach (StatData value in _data.Values)
			{
				StatSyncManager.BroadcastAccData(value);
				StatSyncManager.BroadcastDamData(value);
			}
		}

		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_0016: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _textMeshList.Count; i++)
			{
				_meshGOList[i].transform.localPosition = GetMeshPos(i);
			}
		}
	}
}
namespace StatDisplay.Dependencies
{
	internal static class ArchiveWrapper
	{
		public const string PLUGIN_GUID = "dev.AuriRex.gtfo.TheArchive";

		public static readonly bool HasArchive;

		static ArchiveWrapper()
		{
			if (((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("dev.AuriRex.gtfo.TheArchive", out var value))
			{
				HasArchive = value.Metadata.Version.Major > 0;
				if (!HasArchive)
				{
					DinoLogger.Warning("StatDisplay is only compatible with Auri's Archive release. Using Config file.");
				}
			}
			else
			{
				HasArchive = false;
			}
		}
	}
	internal static class EWCWrapper
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static AccuracyCallback <>9__10_0;

			public static GearSpawnedCallback <>9__10_1;

			internal void <AddCallbacks_Unsafe>b__10_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__DisplayClass10_0 <>c__DisplayClass10_ = default(<>c__DisplayClass10_0);
					<>c__DisplayClass10_.deltaArr = new short[3, 3];
					<AddCallbacks_Unsafe>g__Copy|10_2(AccShotType.Shot, delta.Shots, ref <>c__DisplayClass10_);
					<AddCallbacks_Unsafe>g__Copy|10_2(AccShotType.Group, delta.Groups, ref <>c__DisplayClass10_);
					<AddCallbacks_Unsafe>g__Copy|10_2(AccShotType.Full, delta.FullShots, ref <>c__DisplayClass10_);
					StatHandler.AddAccuracyDelta(accuracy.ParentStats.Owner.Owner.Lookup, accuracy.Slot.ToAccSlotType(), <>c__DisplayClass10_.deltaArr);
				}
			}

			internal void <AddCallbacks_Unsafe>b__10_1(ItemEquippable item, CustomWeaponComponent? cwc)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if (StatHandler.TryGetData(((Item)item).Owner.Owner, out StatData data))
				{
					AccSlotType slot = item.AmmoType.ToAccSlotType();
					if ((Object)(object)cwc == (Object)null)
					{
						data.SetAccuracyWeights((Item)(object)item, slot);
					}
					else
					{
						SetAccuracyWeight_Unsafe(data, (Item)(object)item, cwc, slot);
					}
				}
			}
		}

		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass10_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);
			}
		}

		public static void AddProjectilePostHitCallback(Action hitCallback)
		{
			if (HasEWC)
			{
				AddPostHitCallback_Unsafe(hitCallback);
			}
		}

		public static void AddPreShotFiredCallback(Action<ulong, DamSlotType> fireCallback)
		{
			if (HasEWC)
			{
				AddPreShotCallback_Unsafe(fireCallback);
			}
		}

		public static void AddPostShotFiredCallback(Action fireCallback)
		{
			if (HasEWC)
			{
				AddPostShotCallback_Unsafe(fireCallback);
			}
		}

		public static bool TrySetAccuracyWeight(StatData data, Item item, AccSlotType slot)
		{
			if (HasEWC)
			{
				return TrySetAccuracyWeight_Unsafe(data, item, slot);
			}
			return false;
		}

		[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
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			object obj = <>c.<>9__10_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__10_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);
			object obj2 = <>c.<>9__10_1;
			if (obj2 == null)
			{
				GearSpawnedCallback val2 = delegate(ItemEquippable item, CustomWeaponComponent? cwc)
				{
					//IL_0016: Unknown result type (might be due to invalid IL or missing references)
					if (StatHandler.TryGetData(((Item)item).Owner.Owner, out StatData data))
					{
						AccSlotType slot = item.AmmoType.ToAccSlotType();
						if ((Object)(object)cwc == (Object)null)
						{
							data.SetAccuracyWeights((Item)(object)item, slot);
						}
						else
						{
							SetAccuracyWeight_Unsafe(data, (Item)(object)item, cwc, slot);
						}
					}
				};
				<>c.<>9__10_1 = val2;
				obj2 = (object)val2;
			}
			GearAPI.OnGearSpawned += (GearSpawnedCallback)obj2;
		}

		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 (!NetworkManager.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());
				}
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AddPostHitCallback_Unsafe(Action hitCallback)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			ProjectileAPI.OnProjectilePostHit += (ProjectileHitCallback)delegate
			{
				hitCallback();
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AddPreShotCallback_Unsafe(Action<ulong, DamSlotType> fireCallback)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			FireShotAPI.PreShotFired += (PreShotFiredCallback)delegate(HitData data, Ray _, WeaponType _)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)data.owner == (Object)null))
				{
					fireCallback(data.owner.Owner.Lookup, data.shotInfo.CWC.Weapon.Component.AmmoType.ToDamSlotType());
				}
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AddPostShotCallback_Unsafe(Action fireCallback)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			FireShotAPI.OnShotFired += (ShotFiredCallback)delegate(HitData _, Vector3 _, Vector3 _, WeaponType weaponType)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				if (((Enum)weaponType).HasFlag((Enum)(object)(WeaponType)8))
				{
					fireCallback();
				}
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool TrySetAccuracyWeight_Unsafe(StatData data, Item item, AccSlotType slot)
		{
			CustomWeaponComponent component = ((Component)item).GetComponent<CustomWeaponComponent>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			SetAccuracyWeight_Unsafe(data, item, component, slot);
			return true;
		}

		private static void SetAccuracyWeight_Unsafe(StatData data, Item item, CustomWeaponComponent cwc, AccSlotType slot)
		{
			if (!cwc.Owner.IsType((OwnerType)4) || !cwc.Weapon.IsType((WeaponType)8))
			{
				return;
			}
			ArchetypeDataBlock archetypeData = cwc.Weapon.ArchetypeData;
			int num = ((!cwc.Weapon.IsShotgun) ? 1 : archetypeData.ShotgunBulletCount);
			MultiShot val = default(MultiShot);
			if (cwc.TryGetTrait<MultiShot>(ref val))
			{
				int num2 = val.Offsets.Length / 2 * (int)(val.Repeat + 1);
				if (!val.ForceSingleBullet)
				{
					num2 *= num;
				}
				num = ((!val.CancelShot) ? (num + num2) : num2);
			}
			float num3 = archetypeData.CostOfBullet;
			AmmoCap val2 = default(AmmoCap);
			if (cwc.TryGetTrait<AmmoCap>(ref val2))
			{
				num3 /= val2.AmmoCapRel;
			}
			data.SetAccuracyWeights(slot, 1f / (float)num, num3);
		}

		[CompilerGenerated]
		internal static void <AddCallbacks_Unsafe>g__Copy|10_2(AccShotType shotType, StatDelta stat, ref <>c__DisplayClass10_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,
		Secondary,
		Count,
		All
	}
	[Flags]
	public enum AccWeightType : byte
	{
		None = 0,
		Pellet = 1,
		Ammo = 2,
		All = 3
	}
	public enum AccShotType : byte
	{
		Shot,
		Group,
		Full,
		Count
	}
	public enum AccStatType : byte
	{
		Hit,
		Crit,
		Fired,
		Count
	}
	public enum DamSlotType : byte
	{
		Primary,
		Secondary,
		Tool,
		Melee,
		Count,
		All
	}
	public enum DamStatType : byte
	{
		Any,
		Crit,
		Count
	}
	public sealed class StatData
	{
		public abstract class Retriever
		{
			protected readonly StatData _data;

			public abstract double Value { get; }

			public Retriever(StatData data)
			{
				_data = data;
			}

			public virtual void AlignWith(Retriever? retriever)
			{
			}
		}

		private struct SlotWeights
		{
			public float Pellet;

			public float Ammo;

			public SlotWeights()
			{
				Pellet = 1f;
				Ammo = 1f;
			}
		}

		private class AccuracyRetriever : Retriever
		{
			private readonly int[] _slotTypes;

			private readonly int _shotType;

			private readonly int _statType;

			private AccWeightType _weightType;

			public override double Value
			{
				get
				{
					if (_weightType != AccWeightType.None && _slotTypes.Length > 1)
					{
						double num = _data._accData[0, _shotType, _statType];
						double num2 = _data._accData[1, _shotType, _statType];
						if (_weightType.HasFlag(AccWeightType.Pellet))
						{
							num *= (double)_data._primaryWeights.Pellet;
							num2 *= (double)_data._secondaryWeights.Pellet;
						}
						if (_weightType.HasFlag(AccWeightType.Ammo))
						{
							float ammo = _data._primaryWeights.Ammo;
							float ammo2 = _data._secondaryWeights.Ammo;
							float num3 = ammo + ammo2;
							if (num3 > 0.0001f)
							{
								ammo *= 2f / num3;
								ammo2 *= 2f / num3;
								num *= (double)ammo;
								num2 *= (double)ammo2;
							}
						}
						return num + num2;
					}
					if (_weightType.HasFlag(AccWeightType.Pellet) && _slotTypes.Length == 1)
					{
						int num4 = _slotTypes[0];
						double num5 = _data._accData[num4, _shotType, _statType];
						if (num4 == 0)
						{
							return num5 * (double)_data._primaryWeights.Pellet;
						}
						return num5 * (double)_data._secondaryWeights.Pellet;
					}
					double num6 = 0.0;
					int[] slotTypes = _slotTypes;
					foreach (int num7 in slotTypes)
					{
						num6 += (double)_data._accData[num7, _shotType, _statType];
					}
					return num6;
				}
			}

			public AccuracyRetriever(StatData data, AccSlotType[] slotTypes, AccShotType shotType, AccStatType statType, AccWeightType weightType)
				: base(data)
			{
				if (slotTypes.Length != 0)
				{
					if (slotTypes[0] == AccSlotType.All)
					{
						_slotTypes = new int[2] { 0, 1 };
					}
					else
					{
						_slotTypes = Array.ConvertAll(slotTypes, (AccSlotType type) => (int)type);
					}
				}
				else
				{
					_slotTypes = Array.Empty<int>();
				}
				_shotType = (int)shotType;
				_statType = (int)statType;
				_weightType = weightType;
			}

			public override void AlignWith(Retriever? retriever)
			{
				if (retriever != null && retriever is AccuracyRetriever accuracyRetriever)
				{
					if (_weightType != AccWeightType.None)
					{
						accuracyRetriever._weightType = _weightType;
					}
					else
					{
						_weightType = accuracyRetriever._weightType;
					}
				}
			}
		}

		private class DamageRetriever : Retriever
		{
			private readonly int[] _slotTypes;

			private readonly int _statType;

			public override double Value
			{
				get
				{
					double num = 0.0;
					int[] slotTypes = _slotTypes;
					foreach (int num2 in slotTypes)
					{
						num += _data._damData[num2, _statType];
					}
					return num;
				}
			}

			public DamageRetriever(StatData data, DamSlotType[] slotTypes, DamStatType statType)
				: base(data)
			{
				if (slotTypes.Length != 0)
				{
					if (slotTypes[0] == DamSlotType.All)
					{
						_slotTypes = new int[4];
						for (int i = 0; i < 4; i++)
						{
							_slotTypes[i] = i;
						}
					}
					else
					{
						_slotTypes = Array.ConvertAll(slotTypes, (DamSlotType type) => (int)type);
					}
				}
				else
				{
					_slotTypes = Array.Empty<int>();
				}
				_statType = (int)statType;
			}
		}

		private readonly long[,,] _accData;

		private readonly short[,,] _deltaAccData;

		private readonly double[,] _damData;

		private readonly float[,] _deltaDamData;

		private bool _needsAccSync;

		private bool _needsDamSync;

		private SlotWeights _primaryWeights = new SlotWeights();

		private SlotWeights _secondaryWeights = new SlotWeights();

		public readonly StatParser StatText;

		public readonly SNet_Player Owner;

		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);
			Owner = player;
			InitAccuracyWeights();
		}

		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;
					}
				}
			}
			for (int l = 0; l < _damData.GetLength(0); l++)
			{
				for (int m = 0; m < _damData.GetLength(1); m++)
				{
					_damData[l, m] = 0.0;
					_deltaDamData[l, m] = 0f;
				}
			}
			_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);
		}

		internal void SetAccuracyWeights(Item item, AccSlotType slot)
		{
			if (EWCWrapper.TrySetAccuracyWeight(this, item, slot))
			{
				return;
			}
			BulletWeapon val = ((Il2CppObjectBase)item).TryCast<BulletWeapon>();
			if (!((Object)(object)val == (Object)null))
			{
				ArchetypeDataBlock archetypeData = ((ItemEquippable)val).ArchetypeData;
				if (archetypeData != null)
				{
					float pelletWeight = ((Owner.IsLocal ? Object.op_Implicit((Object)(object)((Il2CppObjectBase)val).TryCast<Shotgun>()) : Object.op_Implicit((Object)(object)((Il2CppObjectBase)val).TryCast<ShotgunSynced>())) ? (1f / (float)archetypeData.ShotgunBulletCount) : 1f);
					float costOfBullet = archetypeData.CostOfBullet;
					SetAccuracyWeights(slot, pelletWeight, costOfBullet);
				}
			}
		}

		internal void SetAccuracyWeights(AccSlotType slot, float pelletWeight, float ammoWeight)
		{
			switch (slot)
			{
			case AccSlotType.Primary:
				_primaryWeights = new SlotWeights
				{
					Pellet = pelletWeight,
					Ammo = ammoWeight
				};
				break;
			case AccSlotType.Secondary:
				_secondaryWeights = new SlotWeights
				{
					Pellet = pelletWeight,
					Ammo = ammoWeight
				};
				break;
			}
			StatText.Update();
		}

		private void InitAccuracyWeights()
		{
			PlayerBackpack val = default(PlayerBackpack);
			if (PlayerBackpackManager.TryGetBackpack(Owner, ref val))
			{
				BackpackItem val2 = default(BackpackItem);
				if (val.TryGetBackpackItem((InventorySlot)1, ref val2))
				{
					SetAccuracyWeights(val2.Instance, AccSlotType.Primary);
				}
				if (val.TryGetBackpackItem((InventorySlot)2, ref val2))
				{
					SetAccuracyWeights(val2.Instance, AccSlotType.Secondary);
				}
			}
		}

		public static Retriever CreateAccuracyRetriever(StatData data, AccSlotType[] slotTypes, AccShotType shotType, AccStatType statType, AccWeightType weightType)
		{
			return new AccuracyRetriever(data, slotTypes, shotType, statType, weightType);
		}

		public static Retriever CreateDamageRetriever(StatData data, DamSlotType[] slotTypes, DamStatType statType)
		{
			return new DamageRetriever(data, slotTypes, statType);
		}
	}
	public sealed class StatParser
	{
		private class Token
		{
			private static readonly string[] s_accSlotTypes = Array.ConvertAll(Enum.GetValues<AccSlotType>()[..2], (AccSlotType slot) => slot.ToString().ToLower());

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

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

			private static readonly string[] s_accWeightTypes = Array.ConvertAll(Enum.GetValues<AccWeightType>()[1..], (AccWeightType stat) => stat.ToString().ToLower());

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

			private static readonly string[] s_damStatTypes = Array.ConvertAll(Enum.GetValues<DamStatType>()[..2], (DamStatType stat) => stat.ToString().ToLower());

			private static readonly Dictionary<string, AccSlotType> s_accSlotExtraMap = new Dictionary<string, AccSlotType>
			{
				{
					"main",
					AccSlotType.Primary
				},
				{
					"special",
					AccSlotType.Secondary
				}
			};

			private static readonly Dictionary<string, DamSlotType> s_damSlotExtraMap = new Dictionary<string, DamSlotType>
			{
				{
					"main",
					DamSlotType.Primary
				},
				{
					"special",
					DamSlotType.Secondary
				},
				{
					"class",
					DamSlotType.Tool
				}
			};

			private readonly StatData.Retriever _base;

			private readonly StatData.Retriever? _divider;

			public Token(StatData data, string text, InventorySlot slot)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				int num = text.IndexOf('/');
				if (num != -1)
				{
					_divider = ParseRetriever(data, text.Substring(num + 1).Trim().ToLower(), slot);
					text = text.Substring(0, num);
				}
				else
				{
					_divider = null;
				}
				_base = ParseRetriever(data, text.Trim().ToLower(), slot);
				_base.AlignWith(_divider);
			}

			public string GetString()
			{
				double value = _base.Value;
				if (_divider != null)
				{
					double value2 = _divider.Value;
					if (value2 == 0.0)
					{
						if (!(value > 0.0))
						{
							return "-";
						}
						return "100%";
					}
					return (value / value2).ToString("P0");
				}
				return ((long)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_000b: Unknown result type (might be due to invalid IL or missing references)
				DamSlotType[] value;
				if ((int)slot != 0)
				{
					value = new DamSlotType[1] { slot.ToDamSlotType() };
				}
				else if (!TryGetEnumsInText(text, s_damSlotTypes, s_damSlotExtraMap, out value))
				{
					value = new DamSlotType[1] { 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_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				RemoveWeightFromText(ref text, out var value);
				AccSlotType[] value2;
				if ((int)slot != 0)
				{
					value2 = new AccSlotType[1] { slot.ToAccSlotType() };
				}
				else if (!TryGetEnumsInText(text, s_accSlotTypes, s_accSlotExtraMap, out value2))
				{
					value2 = new AccSlotType[1] { AccSlotType.All };
				}
				if (!TryGetEnumInText<AccShotType>(text, s_accShotTypes, out var value3))
				{
					value3 = AccShotType.Shot;
				}
				if (!TryGetEnumInText<AccStatType>(text, s_accStatTypes, out var value4))
				{
					value4 = AccStatType.Hit;
				}
				return StatData.CreateAccuracyRetriever(data, value2, value3, value4, value);
			}

			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)
				{
					string value2 = text.Substring(num, length);
					if (value2.TryToEnum<T>(out value))
					{
						return true;
					}
					DinoLogger.Error($"Unable to parse {typeof(T).Name} from {value2} in token \"{text}\"");
					return false;
				}
				value = default(T);
				return false;
			}

			private static void RemoveWeightFromText(ref string text, out AccWeightType value)
			{
				int num = text.IndexOf('(');
				if (num == -1)
				{
					value = AccWeightType.None;
					return;
				}
				int num2 = text.LastIndexOf(')');
				if (num2 == -1)
				{
					value = AccWeightType.None;
					return;
				}
				string obj = text;
				int num3 = num + 1;
				string text2 = obj.Substring(num3, num2 - num3);
				text = text.Remove(num, num2 - num + 1);
				if (!TryGetEnumInText<AccWeightType>(text2, s_accWeightTypes, out value))
				{
					value = AccWeightType.None;
				}
			}

			private static bool TryGetEnumsInText<T>(string text, string[] enumStrings, Dictionary<string, T> extraStrings, out T[] value) where T : struct
			{
				List<(int, int)> list = new List<(int, int)>();
				foreach (string text2 in enumStrings)
				{
					int num = text.IndexOf(text2);
					if (num > -1)
					{
						list.Add((num, text2.Length));
					}
				}
				HashSet<T> hashSet = new HashSet<T>(list.Count);
				foreach (var (value2, item) in extraStrings)
				{
					if (text.IndexOf(value2) > -1)
					{
						hashSet.Add(item);
					}
				}
				foreach (var item4 in list)
				{
					int item2 = item4.Item1;
					int item3 = item4.Item2;
					string value3 = text.Substring(item2, item3);
					if (value3.TryToEnum<T>(out var enumValue))
					{
						hashSet.Add(enumValue);
						continue;
					}
					DinoLogger.Error($"Unable to parse {typeof(T).Name} from {value3} in token \"{text}\"");
				}
				if (hashSet.Count > 0)
				{
					value = hashSet.ToArray();
					return true;
				}
				value = Array.Empty<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 string _characterName;

		private int _characterSlot;

		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 => _textMesh;

		public bool HasMod
		{
			get
			{
				return _hasMod;
			}
			set
			{
				_hasMod = value;
				SetFormat();
			}
		}

		public StatParser(StatData data, SNet_Player player, bool hasMod)
		{
			_characterName = player.NickName;
			_parent = data;
			_hasMod = hasMod;
			SetFormat();
			Configuration.Config.OnChanged += SetFormat;
		}

		public void SetToSlot(TextMeshPro textMesh, int slot)
		{
			_textMesh = textMesh;
			_characterSlot = slot;
			Update(updateMesh: true);
		}

		public void SetToEmpty()
		{
			_textMesh = null;
			Update(updateMesh: true);
		}

		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 | PresetConfigSetting.Weight;

		const PresetConfigSetting DefaultEndScreenFormat = PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Weight;

		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();
			string value = (settings.HasFlag(PresetConfigSetting.Weight) ? "(All)" : string.Empty);
			if (settings.HasFlag(PresetConfigSetting.Hit))
			{
				StringBuilder stringBuilder = sb;
				StringBuilder stringBuilder2 = stringBuilder;
				StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(21, 2, stringBuilder);
				handler.AppendLiteral("<");
				handler.AppendFormatted(colors[0]);
				handler.AppendLiteral(">{Hit/Fired");
				handler.AppendFormatted(value);
				handler.AppendLiteral("}</color>");
				stringBuilder2.Append(ref handler);
			}
			if (settings.HasFlag(PresetConfigSetting.Crit))
			{
				AppendPrefixNotFirst('/', $"<{colors[1]}>{{Crit/Hit{value}}}</color>");
			}
			if (settings.HasFlag(PresetConfigSetting.Pierce))
			{
				AppendPrefixNotFirst('/', $"<{colors[2]}>{{FullHit/Hit{value}}}</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 value2)
			{
				if (sb.Length == 0)
				{
					sb.Append(value2);
				}
				else
				{
					sb.Append(prefix);
					sb.Append(value2);
				}
			}
		}

		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,
		Weight = 0x20
	}
}
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
			_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- Weight: Scales accuracy stats per-weapon by ammo capacity/pellets\n- Damage: All Damage Dealt\n- CritDamage: Crit Damage Dealt";
			_format = _configFile.Bind<PresetConfigSetting>(text, "Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage | PresetConfigSetting.Weight, 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 | PresetConfigSetting.Weight, 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 types (one or more), shot type, and stat type. They may occur in any order and are case-insensitive.\nAccuracy also includes Weight type, specified in parentheses as (Weight Type). Percentages apply Weight to both variables.\nAccepted slots (One or more; 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.\nAccepted accuracy weight types (Unspecified = None):\n- Pellet: Inversely scales each weapon's accuracy by pellet count.\n- Ammo: Inversely scales each weapon's accuracy by ammo capacity.\n- All: Includes both Pellet and Ammo.";
			_customFormat = _configFile.Bind<string>(text, "Custom Format", IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage | PresetConfigSetting.Weight), 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 | PresetConfigSetting.Weight), text2);
			CacheValues();
			SafeFileSystemWatcher.Create(_configFile);
			_configFile.ConfigReloaded += OnConfigReloaded;
		}

		private void OnConfigReloaded(object? obj, EventArgs e)
		{
			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- Weight: Scales accuracy stats per-weapon by ammo capacity/pellets\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")