Decompiled source of CombatPanel v1.12.1

BepInEx/plugins/GTFOCombatPanel/GTFOCombatPanel.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using Enemies;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 HawChow.GTFOCombatPanel
{
	[HarmonyPatch]
	internal static class WeaponFirePatch
	{
		internal static IEnumerable<MethodBase> TargetMethods()
		{
			Type[] array = new Type[4]
			{
				typeof(BulletWeapon),
				typeof(Shotgun),
				typeof(BulletWeaponSynced),
				typeof(ShotgunSynced)
			};
			foreach (Type type in array)
			{
				yield return AccessTools.DeclaredMethod(type, "Fire", new Type[1] { typeof(bool) }, (Type[])null);
			}
		}

		[HarmonyPrefix]
		private static void Prefix(BulletWeapon __instance, out FireCapture __state)
		{
			__state = CombatTracker.BeginFire(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix(FireCapture __state)
		{
			CombatTracker.EndFire(__state, succeeded: true);
		}

		[HarmonyFinalizer]
		private static void Finalizer(Exception __exception, FireCapture __state)
		{
			if (__exception != null)
			{
				CombatTracker.EndFire(__state, succeeded: false);
			}
		}
	}
	[HarmonyPatch]
	internal static class ProjectileRayPatch
	{
		internal static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Weapon), "CastWeaponRay", new Type[4]
			{
				typeof(Transform),
				typeof(WeaponHitData).MakeByRefType(),
				typeof(Vector3),
				typeof(int)
			}, (Type[])null);
		}

		[HarmonyPostfix]
		private static void Postfix(WeaponHitData weaponRayData)
		{
			CombatTracker.OnWeaponRay(weaponRayData);
		}
	}
	[HarmonyPatch(typeof(Dam_EnemyDamageLimb), "BulletDamage")]
	internal static class EnemyPelletContactPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Dam_EnemyDamageLimb __instance, Agent sourceAgent)
		{
			CombatTracker.OnEnemyLimbHit(__instance, sourceAgent);
		}
	}
	[HarmonyPatch(typeof(PlayerInventorySynced), "GetSync")]
	internal static class RemoteShotsPatch
	{
		[HarmonyPrefix]
		private static void Prefix(PlayerInventorySynced __instance)
		{
			CombatTracker.OnRemoteSync(__instance);
		}
	}
	[HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireMaster")]
	internal static class SentryDamageSourcePatch
	{
		[HarmonyPrefix]
		private static void Prefix(SentryGunInstance_Firing_Bullets __instance, out PlayerAgent __state)
		{
			__state = CombatTracker.ToolOwner;
			CombatTracker.ToolOwner = __instance.m_core.Owner;
		}

		[HarmonyFinalizer]
		private static void Finalizer(PlayerAgent __state)
		{
			CombatTracker.ToolOwner = __state;
		}
	}
	public enum PanelColumn
	{
		PlayerName,
		DamageAndShare,
		FriendlyFire,
		Accuracy,
		WeakspotAccuracy,
		Projectiles,
		ImpactLabel,
		ImpactValue
	}
	public enum PanelTextRole
	{
		Neutral,
		PlayerName
	}
	public sealed class ShotSample
	{
		private readonly Dictionary<int, bool> _hits = new Dictionary<int, bool>();

		private int _pellet = -1;

		public int Fired { get; private set; }

		public int Hits => _hits.Count;

		public int WeakspotHits
		{
			get
			{
				int num = 0;
				foreach (bool value in _hits.Values)
				{
					if (value)
					{
						num++;
					}
				}
				return num;
			}
		}

		public void BeginPellet()
		{
			_pellet = Fired++;
		}

		public void Contact(bool weakspot)
		{
			if (_pellet >= 0)
			{
				_hits.TryGetValue(_pellet, out var value);
				_hits[_pellet] = value || weakspot;
			}
		}
	}
	public sealed class PlayerCombatStats
	{
		public long ShotsFired { get; private set; }

		public long Hits { get; private set; }

		public long WeakspotHits { get; private set; }

		public double EnemyDamage { get; private set; }

		public double FriendlyFireDamage { get; private set; }

		public bool AccuracyObserved { get; private set; }

		public bool AccuracyEstimated { get; private set; }

		public void AddAccuracy(long fired, long hits, long weak, bool estimated = false)
		{
			if (fired < 0 || hits < 0 || weak < 0 || weak > hits)
			{
				throw new ArgumentOutOfRangeException("fired");
			}
			if (!estimated && hits > fired)
			{
				throw new ArgumentOutOfRangeException("hits");
			}
			ShotsFired = Add(ShotsFired, fired);
			Hits = Add(Hits, hits);
			WeakspotHits = Add(WeakspotHits, weak);
			AccuracyObserved = true;
			AccuracyEstimated |= estimated;
		}

		public void AddEnemyDamage(double amount)
		{
			EnemyDamage = AddDamage(EnemyDamage, amount);
		}

		public void AddFriendlyFireDamage(double amount)
		{
			FriendlyFireDamage = AddDamage(FriendlyFireDamage, amount);
		}

		private static long Add(long value, long increment)
		{
			if (value <= long.MaxValue - increment)
			{
				return value + increment;
			}
			return long.MaxValue;
		}

		private static double AddDamage(double value, double amount)
		{
			if (double.IsFinite(amount) && !(amount <= 0.0))
			{
				return Math.Min(double.MaxValue, value + amount);
			}
			return value;
		}
	}
	public sealed class WeaponCombatStats
	{
		public int Slot { get; }

		public uint ArchetypeId { get; }

		public string Name { get; set; }

		public PlayerCombatStats Counters { get; } = new PlayerCombatStats();

		internal WeaponCombatStats(int slot, uint id, string name)
		{
			Slot = slot;
			ArchetypeId = id;
			Name = name;
		}
	}
	public sealed class CombatPlayer
	{
		private readonly Dictionary<(int, uint), WeaponCombatStats> _weapons = new Dictionary<(int, uint), WeaponCombatStats>();

		public ulong Lookup { get; }

		public int DisplaySlot { get; set; }

		public string Name { get; set; }

		public PlayerCombatStats Total { get; } = new PlayerCombatStats();

		public ImpactCounters Impact { get; } = new ImpactCounters();

		public bool SupportRole { get; set; }

		public IEnumerable<WeaponCombatStats> Weapons => _weapons.Values;

		public CombatPlayer(ulong lookup)
		{
			Lookup = lookup;
		}

		public WeaponCombatStats Weapon(int slot, uint id, string name)
		{
			if (!_weapons.TryGetValue((slot, id), out var value))
			{
				value = new WeaponCombatStats(slot, id, name);
				_weapons.Add((slot, id), value);
			}
			else if (!string.IsNullOrEmpty(name))
			{
				value.Name = name;
			}
			return value;
		}

		public WeaponCombatStats FindWeapon(int slot, uint id)
		{
			_weapons.TryGetValue((slot, id), out var value);
			return value;
		}

		public void RecordAccuracy(WeaponCombatStats weapon, long fired, long hits, long weak, bool estimated = false)
		{
			Total.AddAccuracy(fired, hits, weak, estimated);
			weapon.Counters.AddAccuracy(fired, hits, weak, estimated);
		}

		public void RecordDamage(WeaponCombatStats weapon, double amount, bool friendly)
		{
			if (friendly)
			{
				Total.AddFriendlyFireDamage(amount);
				weapon.Counters.AddFriendlyFireDamage(amount);
			}
			else
			{
				Total.AddEnemyDamage(amount);
				weapon.Counters.AddEnemyDamage(amount);
			}
		}
	}
	public sealed class CombatStatsStore
	{
		private readonly Dictionary<ulong, CombatPlayer> _players = new Dictionary<ulong, CombatPlayer>();

		public IEnumerable<CombatPlayer> Players => _players.Values;

		public CombatPlayer GetOrCreate(ulong lookup)
		{
			if (!_players.TryGetValue(lookup, out var value))
			{
				value = new CombatPlayer(lookup);
				_players.Add(lookup, value);
			}
			return value;
		}

		public bool TryGet(ulong lookup, out CombatPlayer player)
		{
			return _players.TryGetValue(lookup, out player);
		}

		public void Reset()
		{
			_players.Clear();
		}
	}
	public sealed class ExpeditionLifecycle
	{
		private bool _armed;

		private bool _started;

		public bool ShouldReset(string state)
		{
			switch (state)
			{
			case "Generating":
				if (_armed && !_started)
				{
					return false;
				}
				_armed = true;
				_started = false;
				return true;
			case "InLevel":
				if (_started)
				{
					return false;
				}
				_started = true;
				if (_armed)
				{
					return false;
				}
				_armed = true;
				return true;
			case "Lobby":
			case "NoLobby":
				_armed = false;
				_started = false;
				break;
			}
			return false;
		}
	}
	public static class CombatStatsFormatter
	{
		public const string Separator = " ";

		public static readonly PanelColumn[] Columns = new PanelColumn[6]
		{
			PanelColumn.PlayerName,
			PanelColumn.DamageAndShare,
			PanelColumn.FriendlyFire,
			PanelColumn.Accuracy,
			PanelColumn.WeakspotAccuracy,
			PanelColumn.Projectiles
		};

		public static PanelTextRole TextRoleFor(PanelColumn column)
		{
			if (column != PanelColumn.PlayerName)
			{
				return PanelTextRole.Neutral;
			}
			return PanelTextRole.PlayerName;
		}

		public static bool IsRightAligned(PanelColumn column)
		{
			return true;
		}

		public static string TruncatePlayerName(string name, int maximumTextElements = 6)
		{
			if (string.IsNullOrEmpty(name) || maximumTextElements <= 0)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in name)
			{
				if (!char.IsControl(c))
				{
					stringBuilder.Append(c);
				}
			}
			StringInfo stringInfo = new StringInfo(stringBuilder.ToString());
			return stringInfo.SubstringByTextElements(0, Math.Min(stringInfo.LengthInTextElements, maximumTextElements));
		}

		public static string FormatDamage(double amount)
		{
			return ((double.IsFinite(amount) && amount >= 0.0) ? amount : 0.0).ToString("#,0.0", CultureInfo.InvariantCulture);
		}

		public static string FormatPercent(long numerator, long denominator)
		{
			if (denominator <= 0 || numerator < 0)
			{
				return "—";
			}
			return ((double)numerator * 100.0 / (double)denominator).ToString("0.0", CultureInfo.InvariantCulture) + "%";
		}

		public static string FormatShare(PlayerCombatStats stats, double? squadDamage)
		{
			if (stats == null)
			{
				return "—";
			}
			if (!squadDamage.HasValue || !double.IsFinite(squadDamage.Value) || squadDamage.Value <= 0.0)
			{
				return "—";
			}
			return (stats.EnemyDamage / squadDamage.Value * 100.0).ToString("0.0", CultureInfo.InvariantCulture) + "%";
		}

		private static string EstimateMark(PlayerCombatStats stats)
		{
			if (!stats.AccuracyEstimated)
			{
				return string.Empty;
			}
			return "~";
		}

		public static string AccuracyText(PlayerCombatStats stats)
		{
			if (!stats.AccuracyObserved)
			{
				return "—";
			}
			return EstimateMark(stats) + FormatPercent(stats.Hits, stats.ShotsFired);
		}

		public static string WeakspotText(PlayerCombatStats stats)
		{
			if (!stats.AccuracyObserved)
			{
				return "—";
			}
			return EstimateMark(stats) + FormatPercent(stats.WeakspotHits, stats.Hits);
		}

		public static string[] Fields(PlayerCombatStats stats, PanelColumn column, double? squadDamage = null)
		{
			return column switch
			{
				PanelColumn.DamageAndShare => new string[2]
				{
					FormatDamage(stats.EnemyDamage),
					FormatShare(stats, squadDamage)
				}, 
				PanelColumn.FriendlyFire => new string[1] { FormatDamage(stats.FriendlyFireDamage) }, 
				PanelColumn.Accuracy => new string[1] { AccuracyText(stats) }, 
				PanelColumn.WeakspotAccuracy => new string[1] { WeakspotText(stats) }, 
				PanelColumn.Projectiles => ProjectileFields(stats), 
				_ => new string[1] { string.Empty }, 
			};
		}

		public static string[] ProjectileFields(PlayerCombatStats stats)
		{
			if (stats.AccuracyObserved)
			{
				return new string[3]
				{
					EstimateMark(stats) + stats.ShotsFired.ToString(CultureInfo.InvariantCulture),
					stats.Hits.ToString(CultureInfo.InvariantCulture),
					stats.WeakspotHits.ToString(CultureInfo.InvariantCulture)
				};
			}
			return new string[1] { "—" };
		}

		public static string ProjectilesText(PlayerCombatStats stats)
		{
			return string.Join(" ", ProjectileFields(stats));
		}

		public static string Value(PlayerCombatStats stats, PanelColumn column, double? squadDamage = null)
		{
			if (column == PanelColumn.PlayerName)
			{
				return string.Empty;
			}
			return string.Join(" ", Fields(stats, column, squadDamage));
		}

		public static string Summary(PlayerCombatStats stats, double? squadDamage = null, bool includeShare = false)
		{
			return CombatStrings.Damage + " " + FormatDamage(stats.EnemyDamage) + (includeShare ? (" " + CombatStrings.Share + " " + FormatShare(stats, squadDamage)) : string.Empty) + "\n" + CombatStrings.FriendlyFire + " " + FormatDamage(stats.FriendlyFireDamage) + "\n" + CombatStrings.Accuracy + " " + AccuracyText(stats) + "  " + CombatStrings.Weakspot + " " + WeakspotText(stats) + "\n" + CombatStrings.Pellets + " " + ProjectilesText(stats);
		}
	}
	internal enum DamageKind
	{
		Bullet,
		Melee,
		Fire,
		Push
	}
	internal readonly struct DamageSample
	{
		internal readonly float HealthBefore;

		internal readonly CombatPlayer Player;

		internal readonly WeaponCombatStats Weapon;

		internal readonly bool Estimated;

		internal DamageSample(float health, CombatPlayer player, WeaponCombatStats weapon, bool estimated)
		{
			HealthBefore = health;
			Player = player;
			Weapon = weapon;
			Estimated = estimated;
		}
	}
	internal sealed class FireCapture
	{
		internal FireCapture Parent;

		internal BulletWeapon Weapon;

		internal CombatPlayer Player;

		internal WeaponCombatStats Entry;

		internal ShotSample Sample = new ShotSample();

		internal int ClipBefore;

		internal bool Exact;

		internal bool IsRoot = true;
	}
	internal static class CombatTracker
	{
		internal readonly struct ColorRecord
		{
			internal readonly Color Color;

			internal ColorRecord(Color color)
			{
				//IL_0001: 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)
				Color = color;
			}
		}

		private sealed class ScanProbe
		{
			public bool Announced;

			public readonly ScanSession Session;

			public ScanProbe(ScanReveals reveals)
			{
				Session = new ScanSession(reveals);
			}
		}

		internal static readonly CombatStatsStore Store = new CombatStatsStore();

		private static readonly ExpeditionLifecycle Lifecycle = new ExpeditionLifecycle();

		internal static FireCapture ActiveFire;

		internal static PlayerAgent ToolOwner;

		private static readonly Dictionary<ulong, ColorRecord> Colors = new Dictionary<ulong, ColorRecord>();

		private static readonly Dictionary<ulong, int> PendingRemoteShots = new Dictionary<ulong, int>();

		internal static readonly InventorySlot[] GearSlots;

		private static readonly Dictionary<IntPtr, ulong> LastAttacker;

		private static readonly HashSet<IntPtr> KillCredited;

		private static readonly Dictionary<IntPtr, int> InterruptedFrame;

		private static readonly Dictionary<ulong, bool> Alive;

		private static readonly Dictionary<ulong, ScanProbe> ScanLedger;

		private static readonly ScanReveals ScanRevealed;

		[ThreadStatic]
		private static SNet_Player _botScanning;

		private static int NearbyBotCandidates;

		private static float NearbyBotDistance;

		private static bool _loneSurvivorAwarded;

		private static bool _loggedStagger;

		private static bool _loggedKill;

		private static bool _loggedRevive;

		private static bool _loggedScan;

		private static bool _loggedScanRepeat;

		private static bool _loggedDown;

		private static bool _loggedLone;

		private static bool _loggedAttackState;

		private static bool _loggedBotScan;

		private static bool _loggedBotScanUnresolved;

		private static bool _loggedBotAction;

		internal static double? SquadEnemyDamage
		{
			get
			{
				if (!SNet.IsMaster)
				{
					return null;
				}
				double num = 0.0;
				foreach (CombatPlayer player in Store.Players)
				{
					num += player.Total.EnemyDamage;
				}
				return num;
			}
		}

		internal static Color PlayerColor(ulong lookup)
		{
			//IL_0016: 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)
			if (!Colors.TryGetValue(lookup, out var value))
			{
				return Color.white;
			}
			return value.Color;
		}

		internal unsafe static void ObserveState(eGameStateName state)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if (!Lifecycle.ShouldReset(((object)(*(eGameStateName*)(&state))/*cast due to .constrained prefix*/).ToString()))
			{
				if ((int)state == 10)
				{
					Tick();
				}
				return;
			}
			Store.Reset();
			Colors.Clear();
			PendingRemoteShots.Clear();
			ActiveFire = null;
			LastAttacker.Clear();
			KillCredited.Clear();
			Alive.Clear();
			ScanLedger.Clear();
			ScanRevealed.Clear();
			InterruptedFrame.Clear();
			SupplyTracker.Reset();
			_loneSurvivorAwarded = false;
			_loggedScan = (_loggedBotScan = (_loggedBotScanUnresolved = (_loggedScanRepeat = (_loggedBotAction = false))));
			Plugin.Logger.LogInfo((object)"[DIAG] New expedition: counters reset; success/failure screens retain the final counters.");
		}

		internal static bool ShouldTrack(SNet_Player player)
		{
			if ((Object)(object)player != (Object)null && player.IsInSlot)
			{
				if (!SNet.IsMaster)
				{
					return player.IsLocal;
				}
				return true;
			}
			return false;
		}

		internal static CombatPlayer ObservePlayer(SNet_Player owner)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			CombatPlayer orCreate = Store.GetOrCreate(owner.Lookup);
			orCreate.DisplaySlot = owner.PlayerSlotIndex();
			orCreate.Name = CombatStatsFormatter.TruncatePlayerName(owner.GetName());
			Colors[owner.Lookup] = new ColorRecord(owner.PlayerColor);
			return orCreate;
		}

		internal static void RefreshRoster()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Invalid comparison between Unknown and I4
			if ((Object)(object)SNet.Current == (Object)null || (Object)(object)SNet.Slots == (Object)null || SNet.Slots.SlottedPlayers == null)
			{
				return;
			}
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if (!ShouldTrack(current))
				{
					continue;
				}
				CombatPlayer combatPlayer = ObservePlayer(current);
				if (!combatPlayer.Total.AccuracyObserved)
				{
					combatPlayer.Total.AddAccuracy(0L, 0L, 0L, !current.IsLocal && !current.IsBot);
				}
				InventorySlot[] gearSlots = GearSlots;
				foreach (InventorySlot val in gearSlots)
				{
					WeaponCombatStats weaponCombatStats = ResolveWeapon(combatPlayer, current, val);
					if (((int)val == 1 || (int)val == 2) && !weaponCombatStats.Counters.AccuracyObserved)
					{
						weaponCombatStats.Counters.AddAccuracy(0L, 0L, 0L, !current.IsLocal && !current.IsBot);
					}
				}
				WeaponCombatStats weaponCombatStats2 = ResolveWeapon(combatPlayer, current, (InventorySlot)3);
				combatPlayer.SupportRole = SupportTools.IsSupport(new string[1] { weaponCombatStats2.Name }, (Plugin.SupportToolNames != null) ? Plugin.SupportToolNames.Value : "Bio Tracker,生物追踪仪,Glue Gun,Lock Melter,C-Foam");
			}
		}

		internal unsafe static WeaponCombatStats ResolveWeapon(CombatPlayer player, SNet_Player owner, InventorySlot slot, ItemEquippable hint = null)
		{
			//IL_000c: 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_0081: Expected I4, but got Unknown
			ItemEquippable val = hint;
			BackpackItem val2 = default(BackpackItem);
			if ((Object)(object)val == (Object)null && PlayerBackpackManager.TryGetItem(owner, slot, ref val2) && (Object)(object)((val2 != null) ? val2.Instance : null) != (Object)null)
			{
				val = ((Il2CppObjectBase)val2.Instance).TryCast<ItemEquippable>();
			}
			string text = (((Object)(object)val == (Object)null) ? ((object)(*(InventorySlot*)(&slot))/*cast due to .constrained prefix*/).ToString() : ((Item)val).ArchetypeName);
			if (string.IsNullOrEmpty(text) && (Object)(object)val != (Object)null)
			{
				text = ((Item)val).PublicName;
			}
			return player.Weapon((int)slot, (val != null) ? val.ArchetypeID : 0u, text);
		}

		internal static FireCapture BeginFire(BulletWeapon weapon)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (weapon == null)
			{
				obj = null;
			}
			else
			{
				PlayerAgent owner = ((Item)weapon).Owner;
				obj = ((owner != null) ? owner.Owner : null);
			}
			if ((Object)obj == (Object)null || !ShouldTrack(((Item)weapon).Owner.Owner))
			{
				return null;
			}
			if (ActiveFire != null && ((Il2CppObjectBase)ActiveFire.Weapon).Pointer == ((Il2CppObjectBase)weapon).Pointer)
			{
				return new FireCapture
				{
					IsRoot = false
				};
			}
			SNet_Player owner2 = ((Item)weapon).Owner.Owner;
			CombatPlayer player = ObservePlayer(owner2);
			InventorySlot inventorySlot = ((Item)weapon).ItemDataBlock.inventorySlot;
			FireCapture obj2 = new FireCapture
			{
				Parent = ActiveFire,
				Weapon = weapon,
				Player = player,
				Entry = ResolveWeapon(player, owner2, inventorySlot, (ItemEquippable)(object)weapon),
				ClipBefore = weapon.m_clip,
				Exact = (owner2.IsLocal || (SNet.IsMaster && owner2.IsBot))
			};
			ActiveFire = obj2;
			return obj2;
		}

		internal static void EndFire(FireCapture capture, bool succeeded)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			if (capture == null || !capture.IsRoot)
			{
				return;
			}
			ActiveFire = capture.Parent;
			if (!succeeded || !capture.Exact)
			{
				return;
			}
			int num = capture.Sample.Fired;
			if (num == 0 && capture.Weapon.m_clip < capture.ClipBefore)
			{
				num = ((!((Object)(object)((Il2CppObjectBase)capture.Weapon).TryCast<Shotgun>() != (Object)null) && !((Object)(object)((Il2CppObjectBase)capture.Weapon).TryCast<ShotgunSynced>() != (Object)null)) ? 1 : Math.Max(1, ((ItemEquippable)capture.Weapon).ArchetypeData.ShotgunBulletCount));
			}
			if (num == 0)
			{
				return;
			}
			bool num2 = capture.Entry.Counters.ShotsFired == 0;
			capture.Player.RecordAccuracy(capture.Entry, num, capture.Sample.Hits, capture.Sample.WeakspotHits);
			if (num2)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(48, 4, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] Fire captured: slot=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(capture.Entry.Slot);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", fired=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", hit=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(capture.Sample.Hits);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", weak=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(capture.Sample.WeakspotHits);
				}
				logger.LogInfo(val);
			}
		}

		internal static void OnWeaponRay(WeaponHitData data)
		{
			if (ActiveFire != null && ActiveFire.Exact && !((Object)(object)ToolOwner != (Object)null) && data != null && data.maxRayDist >= 100f)
			{
				ActiveFire.Sample.BeginPellet();
			}
		}

		internal static void OnEnemyLimbHit(Dam_EnemyDamageLimb limb, Agent source)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			if (ActiveFire != null && ActiveFire.Exact && !((Object)(object)ToolOwner != (Object)null) && !((Object)(object)limb == (Object)null) && !((Object)(object)source == (Object)null))
			{
				PlayerAgent obj = ((Il2CppObjectBase)source).TryCast<PlayerAgent>();
				ulong? obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					SNet_Player owner = obj.Owner;
					obj2 = ((owner != null) ? new ulong?(owner.Lookup) : ((ulong?)null));
				}
				if (obj2 == ActiveFire.Player.Lookup)
				{
					ActiveFire.Sample.Contact((int)limb.m_type == 1);
				}
			}
		}

		internal static void OnRemoteSync(PlayerInventorySynced inventory)
		{
			//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_0073: Invalid comparison between Unknown and I4
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Invalid comparison between Unknown and I4
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			PlayerAgent val = ((inventory != null) ? ((PlayerInventoryBase)inventory).Owner : null);
			SNet_Player val2 = ((val != null) ? val.Owner : null);
			if ((Object)(object)val2 == (Object)null || !SNet.IsMaster || val2.IsLocal || val2.IsBot)
			{
				return;
			}
			int num = Math.Max(0, val.Sync.FireCountSync);
			PendingRemoteShots.TryGetValue(val2.Lookup, out var value);
			num += value;
			if (num == 0)
			{
				return;
			}
			InventorySlot wieldedSlot = ((PlayerInventoryBase)inventory).WieldedSlot;
			if ((int)wieldedSlot != 1 && (int)wieldedSlot != 2)
			{
				PendingRemoteShots[val2.Lookup] = num;
				return;
			}
			ItemEquippable wieldedItem = ((PlayerInventoryBase)inventory).WieldedItem;
			BulletWeapon val3 = ((wieldedItem != null) ? ((Il2CppObjectBase)wieldedItem).TryCast<BulletWeapon>() : null);
			if ((Object)(object)val3 == (Object)null)
			{
				PendingRemoteShots[val2.Lookup] = num;
				return;
			}
			PendingRemoteShots.Remove(val2.Lookup);
			int num2 = ((!((Object)(object)((Il2CppObjectBase)val3).TryCast<ShotgunSynced>() != (Object)null)) ? 1 : Math.Max(1, ((ItemEquippable)val3).ArchetypeData.ShotgunBulletCount));
			CombatPlayer combatPlayer = ObservePlayer(val2);
			combatPlayer.RecordAccuracy(ResolveWeapon(combatPlayer, val2, wieldedSlot, (ItemEquippable)(object)val3), (long)num * (long)num2, 0L, 0L, estimated: true);
		}

		internal static DamageSample Capture(float health, pAgent source, DamageKind kind)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Invalid comparison between Unknown and I4
			Agent val = default(Agent);
			if (!((pAgent)(ref source)).TryGet(ref val))
			{
				return default(DamageSample);
			}
			PlayerAgent val2 = ((Il2CppObjectBase)val).TryCast<PlayerAgent>();
			SNet_Player val3 = ((val2 != null) ? val2.Owner : null);
			if (!ShouldTrack(val3))
			{
				return default(DamageSample);
			}
			CombatPlayer player = ObservePlayer(val3);
			bool estimated = !val3.IsLocal && !val3.IsBot;
			int num;
			if (kind != DamageKind.Melee && kind != DamageKind.Push)
			{
				PlayerInventoryBase inventory = val2.Inventory;
				num = ((inventory != null) ? ((int)inventory.WieldedSlot) : 0);
			}
			else
			{
				num = 10;
			}
			InventorySlot val4 = (InventorySlot)num;
			PlayerAgent toolOwner = ToolOwner;
			ulong? obj;
			if (toolOwner == null)
			{
				obj = null;
			}
			else
			{
				SNet_Player owner = toolOwner.Owner;
				obj = ((owner != null) ? new ulong?(owner.Lookup) : ((ulong?)null));
			}
			if (obj == val3.Lookup)
			{
				val4 = (InventorySlot)3;
			}
			WeaponCombatStats weapon = ((ActiveFire?.Player?.Lookup == val3.Lookup && (int)val4 != 3) ? ActiveFire.Entry : ResolveWeapon(player, val3, val4));
			return new DamageSample(health, player, weapon, estimated);
		}

		internal static void RecordDamage(Dam_SyncedDamageBase target, DamageSample sample, pBulletDamageData? bullet = null)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Invalid comparison between Unknown and I4
			if (sample.Player == null || sample.Weapon == null || (Object)(object)target == (Object)null)
			{
				return;
			}
			float num = Math.Max(0f, sample.HealthBefore - Math.Max(0f, target.Health));
			Dam_EnemyDamageBase val = ((Il2CppObjectBase)target).TryCast<Dam_EnemyDamageBase>();
			Dam_PlayerDamageBase val2 = ((Il2CppObjectBase)target).TryCast<Dam_PlayerDamageBase>();
			if ((Object)(object)val2 != (Object)null)
			{
				PlayerAgent owner = val2.Owner;
				ulong? obj;
				if (owner == null)
				{
					obj = null;
				}
				else
				{
					SNet_Player owner2 = owner.Owner;
					obj = ((owner2 != null) ? new ulong?(owner2.Lookup) : ((ulong?)null));
				}
				if (obj == sample.Player.Lookup)
				{
					return;
				}
			}
			if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null)
			{
				return;
			}
			if (float.IsFinite(num) && num > 0f)
			{
				sample.Player.RecordDamage(sample.Weapon, num, (Object)(object)val2 != (Object)null);
			}
			if ((Object)(object)val != (Object)null && bullet.HasValue && sample.Estimated && sample.Weapon.Slot != 3 && num > 0f)
			{
				int limbID = bullet.Value.limbID;
				int num2;
				if (limbID < ((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)val.DamageLimbs).Length)
				{
					Dam_EnemyDamageLimb obj2 = ((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)val.DamageLimbs)[limbID];
					num2 = ((obj2 != null && (int)obj2.m_type == 1) ? 1 : 0);
				}
				else
				{
					num2 = 0;
				}
				bool flag = (byte)num2 != 0;
				sample.Player.RecordAccuracy(sample.Weapon, 0L, 1L, flag ? 1 : 0, estimated: true);
			}
		}

		internal static void CreditStagger(Dam_EnemyDamageBase enemy, float damage, Agent source, ES_HitreactType hitreact, bool push = false)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)enemy == (Object)null || (Object)(object)source == (Object)null || (!push && !(damage > 0f)))
				{
					return;
				}
				PlayerAgent val = ((Il2CppObjectBase)source).TryCast<PlayerAgent>();
				if (!ShouldTrack((val != null) ? val.Owner : null))
				{
					return;
				}
				RememberAttacker(enemy, val.Owner.Lookup);
				bool needsAttack = Plugin.StaggerNeedsAttack == null || Plugin.StaggerNeedsAttack.Value;
				bool flag = IsAttackUnderWay(enemy);
				float num = ReadHealth(enemy);
				if ((push && !flag) || !StaggerRule.IsInterrupt(KindOf(hitreact), damage, num, flag, needsAttack, push))
				{
					return;
				}
				int num2 = FrameNumber();
				if (num2 >= 0 && ((Il2CppObjectBase)enemy).Pointer != IntPtr.Zero)
				{
					if (InterruptedFrame.TryGetValue(((Il2CppObjectBase)enemy).Pointer, out var value) && value == num2)
					{
						return;
					}
					InterruptedFrame[((Il2CppObjectBase)enemy).Pointer] = num2;
				}
				CombatPlayer combatPlayer = ObservePlayer(val.Owner);
				combatPlayer.Impact.CreditInterrupt();
				if (!_loggedStagger)
				{
					_loggedStagger = true;
					ManualLogSource logger = Plugin.Logger;
					bool flag2 = default(bool);
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(122, 5, ref flag2);
					if (flag2)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] stagger credited: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(combatPlayer.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" interrupted an attack that was already under way with ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(damage, "0.0");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" damage (hitreact=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<ES_HitreactType>(hitreact);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", push=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<bool>(push);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", enemy health ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(num, "0.0");
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(").");
					}
					logger.LogInfo(val2);
				}
			}
			catch (Exception error)
			{
				LogImpact("stagger", error);
			}
		}

		private static int FrameNumber()
		{
			try
			{
				return Time.frameCount;
			}
			catch
			{
				return -1;
			}
		}

		internal static void CreditPushInterrupt(Dam_EnemyDamageBase enemy, pAgent source)
		{
			try
			{
				Agent source2 = default(Agent);
				if (!((Object)(object)enemy == (Object)null) && ((pAgent)(ref source)).TryGet(ref source2))
				{
					CreditStagger(enemy, 0f, source2, (ES_HitreactType)3, push: true);
				}
			}
			catch (Exception error)
			{
				LogImpact("push interrupt", error);
			}
		}

		private static StaggerKind KindOf(ES_HitreactType hitreact)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if ((int)hitreact == 3)
			{
				return StaggerKind.Light;
			}
			if ((int)hitreact == 4)
			{
				return StaggerKind.Heavy;
			}
			if ((int)hitreact == 5 || (int)hitreact == 6)
			{
				return StaggerKind.Lethal;
			}
			return StaggerKind.None;
		}

		private static float ReadHealth(Dam_EnemyDamageBase enemy)
		{
			try
			{
				float health = ((Dam_SyncedDamageBase)enemy).Health;
				return float.IsFinite(health) ? health : 0f;
			}
			catch
			{
				return 0f;
			}
		}

		private static void RememberAttacker(Dam_EnemyDamageBase enemy, ulong lookup)
		{
			if (!((Object)(object)enemy == (Object)null) && !(((Il2CppObjectBase)enemy).Pointer == IntPtr.Zero))
			{
				LastAttacker[((Il2CppObjectBase)enemy).Pointer] = lookup;
			}
		}

		private static bool IsAttackUnderWay(Dam_EnemyDamageBase enemy)
		{
			try
			{
				EnemyLocomotion val = (((Object)(object)enemy.Owner != (Object)null) ? enemy.Owner.Locomotion : null);
				if ((Object)(object)val == (Object)null)
				{
					if (!_loggedAttackState)
					{
						_loggedAttackState = true;
						Plugin.Logger.LogInfo((object)"[DIAG] the enemy's locomotion could not be read, so no hit react is credited as a stagger; the row stays empty rather than counting hits that interrupted nothing.");
					}
					return false;
				}
				return val.IsAttacking() || val.IsChargingAttack();
			}
			catch (Exception error)
			{
				LogImpact("attack state", error);
				return false;
			}
		}

		internal static void CreditKill(Dam_EnemyDamageBase enemy)
		{
			try
			{
				if (!((Object)(object)enemy == (Object)null))
				{
					CreditKill(((Il2CppObjectBase)enemy).Pointer, ((Dam_SyncedDamageBase)enemy).HealthMax);
				}
			}
			catch (Exception error)
			{
				LogImpact("kill", error);
			}
		}

		internal static void CreditKill(EnemyAgent agent)
		{
			try
			{
				if (!((Object)(object)agent == (Object)null))
				{
					Dam_EnemyDamageBase damage = agent.Damage;
					if ((Object)(object)damage != (Object)null)
					{
						CreditKill(((Il2CppObjectBase)damage).Pointer, ((Dam_SyncedDamageBase)damage).HealthMax);
					}
					else
					{
						CreditKill(((Il2CppObjectBase)agent).Pointer, 0f);
					}
				}
			}
			catch (Exception error)
			{
				LogImpact("kill", error);
			}
		}

		private static void CreditKill(IntPtr enemy, float healthMax)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			if (enemy == IntPtr.Zero || !SNet.IsMaster || !KillCredited.Add(enemy) || !LastAttacker.TryGetValue(enemy, out var value) || !Store.TryGet(value, out var player))
			{
				return;
			}
			double num = ((Plugin.KillMinorMaxHealth != null) ? ((double)Plugin.KillMinorMaxHealth.Value) : 120.0);
			double num2 = ((Plugin.KillEliteMaxHealth != null) ? ((double)Plugin.KillEliteMaxHealth.Value) : 1000.0);
			KillTier killTier = KillClassifier.Classify(healthMax, num, num2);
			player.Impact.CreditKill(killTier);
			if (!_loggedKill)
			{
				_loggedKill = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(69, 5, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] kill credited: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(player.Name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" took down a ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<KillTier>(killTier);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" enemy (healthMax=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(healthMax, "0");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", thresholds ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(num, "0");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(num2, "0");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
				}
				logger.LogInfo(val);
			}
		}

		internal static void CreditRevive(PlayerAgent source)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			try
			{
				if (!ShouldTrack((source != null) ? source.Owner : null))
				{
					return;
				}
				CombatPlayer combatPlayer = ObservePlayer(source.Owner);
				combatPlayer.Impact.CreditRevive();
				if (!_loggedRevive)
				{
					_loggedRevive = true;
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(51, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] revive credited: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(combatPlayer.Name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" picked a teammate back up.");
					}
					logger.LogInfo(val);
				}
			}
			catch (Exception error)
			{
				LogImpact("revive", error);
			}
		}

		internal static void Tick()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			try
			{
				List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
				if (playerAgentsInLevel == null)
				{
					return;
				}
				int num = 0;
				CombatPlayer combatPlayer = null;
				Enumerator<PlayerAgent> enumerator = playerAgentsInLevel.GetEnumerator();
				bool flag = default(bool);
				while (enumerator.MoveNext())
				{
					PlayerAgent current = enumerator.Current;
					if ((Object)(object)current == (Object)null)
					{
						continue;
					}
					SNet_Player owner = current.Owner;
					if (!ShouldTrack(owner))
					{
						continue;
					}
					CombatPlayer combatPlayer2 = ObservePlayer(owner);
					bool alive;
					try
					{
						alive = current.m_alive;
					}
					catch
					{
						continue;
					}
					if (Alive.TryGetValue(combatPlayer2.Lookup, out var value) && value && !alive)
					{
						combatPlayer2.Impact.CreditDown();
						if (!_loggedDown)
						{
							_loggedDown = true;
							ManualLogSource logger = Plugin.Logger;
							BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(57, 1, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] down credited: ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(combatPlayer2.Name);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" went down (m_alive true -> false).");
							}
							logger.LogInfo(val);
						}
					}
					Alive[combatPlayer2.Lookup] = alive;
					if (alive)
					{
						num++;
						combatPlayer = combatPlayer2;
					}
				}
				if (num == 1 && !_loneSurvivorAwarded && Store.Players != null)
				{
					int num2 = 0;
					foreach (CombatPlayer player in Store.Players)
					{
						_ = player;
						num2++;
					}
					if (num2 >= 2 && combatPlayer != null)
					{
						_loneSurvivorAwarded = true;
						combatPlayer.Impact.MarkLoneSurvivor();
						if (!_loggedLone)
						{
							_loggedLone = true;
							ManualLogSource logger2 = Plugin.Logger;
							BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(58, 1, ref flag);
							if (flag)
							{
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] lone survivor credited: ");
								((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(combatPlayer.Name);
								((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" was the last one standing.");
							}
							logger2.LogInfo(val);
						}
					}
				}
				ObserveScans();
			}
			catch (Exception error)
			{
				LogImpact("tick", error);
			}
		}

		private static ScanProbe Ledger(SNet_Player owner)
		{
			if (!ScanLedger.TryGetValue(owner.Lookup, out var value))
			{
				value = new ScanProbe(ScanRevealed);
				ScanLedger[owner.Lookup] = value;
			}
			return value;
		}

		private static void ObserveScans()
		{
			if ((Object)(object)SNet.Current == (Object)null)
			{
				return;
			}
			SNet_PlayerSlotManager slots = SNet.Slots;
			if (((slots != null) ? slots.SlottedPlayers : null) == null)
			{
				return;
			}
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if (ShouldTrack(current) && !current.IsBot)
				{
					EnemyScanner val = ResolveScanner(current);
					if ((Object)(object)val != (Object)null)
					{
						ObserveHumanScan(val);
					}
					else
					{
						Ledger(current).Session.ObserveTagging(active: false, null);
					}
				}
			}
		}

		internal static void ObserveHumanScan(EnemyScanner scanner)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			try
			{
				if ((int)GameStateManager.CurrentStateName != 10 || (Object)(object)scanner == (Object)null)
				{
					return;
				}
				PlayerAgent owner = ((Item)scanner).Owner;
				SNet_Player val = ((owner != null) ? owner.Owner : null);
				if (!ShouldTrack(val) || val.IsBot)
				{
					return;
				}
				ScanProbe scanProbe = Ledger(val);
				CombatPlayer combatPlayer = ObservePlayer(val);
				bool num = scanProbe.Session.ObserveTagging(scanner.m_tagging, combatPlayer.Impact);
				int repeats = MarkEnemies(scanner.m_taggableEnemies, scanProbe.Session, combatPlayer.Impact, bot: false);
				LogScanRepeats(combatPlayer, repeats);
				bool flag = default(bool);
				if (!scanProbe.Announced && (scanner.m_tagging || ListCount(scanner.m_enemiesDetected) > 0))
				{
					scanProbe.Announced = true;
					ManualLogSource logger = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(87, 4, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] scan probe: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(combatPlayer.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" tagging=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<bool>(scanner.m_tagging);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", detected=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(ListCount(scanner.m_enemiesDetected));
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", taggable=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(ListCount(scanner.m_taggableEnemies));
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; instance owner, tag-attempt ledger.");
					}
					logger.LogInfo(val2);
				}
				if (num && !_loggedScan)
				{
					_loggedScan = true;
					ManualLogSource logger2 = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(52, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] scan attempt credited: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(combatPlayer.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; uses=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<long>(combatPlayer.Impact.ScanUses);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", first marks=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<long>(combatPlayer.Impact.ScanFirstMarks);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
					}
					logger2.LogInfo(val2);
				}
			}
			catch (Exception error)
			{
				LogImpact("human scan", error);
			}
		}

		internal static void BeginBotScanAction(PlayerBotActionUseEnemyScanner action)
		{
			try
			{
				_botScanning = ((action != null && (Object)(object)((PlayerBotActionBase)action).m_agent != (Object)null) ? ((PlayerBotActionBase)action).m_agent.Owner : null);
				if (ShouldTrack(_botScanning) && !_loggedBotAction)
				{
					_loggedBotAction = true;
					Plugin.Logger.LogInfo((object)"[DIAG] bot scan action observed: the tagging bot is read from its own scan action instance, so bot scans no longer depend on a proximity guess.");
				}
			}
			catch (Exception error)
			{
				_botScanning = null;
				LogImpact("bot scan action", error);
			}
		}

		internal static void EndBotScanAction()
		{
			_botScanning = null;
		}

		internal static void CreditBotScan(Vector3 centre, List<EnemyAgent> found)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			try
			{
				if (!SNet.IsMaster || (int)GameStateManager.CurrentStateName != 10 || found == null)
				{
					return;
				}
				SNet_Player val = (ShouldTrack(_botScanning) ? _botScanning : BotOwnerNear(centre));
				bool flag = default(bool);
				if ((Object)(object)val == (Object)null)
				{
					if (!_loggedBotScanUnresolved)
					{
						_loggedBotScanUnresolved = true;
						string text = (float.IsFinite(NearbyBotDistance) ? $"{NearbyBotDistance:0.0} m" : "no readable distance");
						ManualLogSource logger = Plugin.Logger;
						BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(250, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] bot tag observed but its owner could not be resolved: the scan action owner was unreadable and the proximity fallback matched ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(NearbyBotCandidates);
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" tracker-carrying bot(s) within a metre (closest ");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(text);
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("); the scan stays unavailable rather than being assigned to a human.");
						}
						logger.LogInfo(val2);
					}
					return;
				}
				ScanProbe scanProbe = Ledger(val);
				CombatPlayer combatPlayer = ObservePlayer(val);
				bool num = scanProbe.Session.BeginBotTag(Time.time, EnemyScanner.s_tagDuration, combatPlayer.Impact);
				LogScanRepeats(combatPlayer, MarkEnemies(found, scanProbe.Session, combatPlayer.Impact, bot: true));
				if (num && !_loggedBotScan)
				{
					_loggedBotScan = true;
					ManualLogSource logger2 = Plugin.Logger;
					BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(94, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] bot tag credited: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(combatPlayer.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; uses=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<long>(combatPlayer.Impact.ScanUses);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", first marks=");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<long>(combatPlayer.Impact.ScanFirstMarks);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; the owner came from the bot's own scan action.");
					}
					logger2.LogInfo(val2);
				}
			}
			catch (Exception error)
			{
				LogImpact("bot scan", error);
			}
		}

		private static SNet_Player BotOwnerNear(Vector3 centre)
		{
			//IL_0071: 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)
			NearbyBotCandidates = 0;
			NearbyBotDistance = float.PositiveInfinity;
			try
			{
				List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
				if (playerAgentsInLevel == null)
				{
					return null;
				}
				SNet_Player val = null;
				Enumerator<PlayerAgent> enumerator = playerAgentsInLevel.GetEnumerator();
				while (enumerator.MoveNext())
				{
					PlayerAgent current = enumerator.Current;
					if ((Object)(object)current == (Object)null)
					{
						continue;
					}
					SNet_Player owner = current.Owner;
					if (!ShouldTrack(owner) || !owner.IsBot || (Object)(object)ResolveScanner(owner) == (Object)null)
					{
						continue;
					}
					float num = Vector3.Distance(((Component)current).transform.position, centre);
					if (num < NearbyBotDistance)
					{
						NearbyBotDistance = num;
					}
					if (!(num > 1f))
					{
						NearbyBotCandidates++;
						if ((Object)(object)val != (Object)null)
						{
							return null;
						}
						val = owner;
					}
				}
				return val;
			}
			catch (Exception error)
			{
				LogImpact("bot proximity", error);
				return null;
			}
		}

		private static EnemyScanner ResolveScanner(SNet_Player owner)
		{
			BackpackItem val = default(BackpackItem);
			if (!PlayerBackpackManager.TryGetItem(owner, (InventorySlot)3, ref val) || (Object)(object)((val != null) ? val.Instance : null) == (Object)null)
			{
				return null;
			}
			return ((Il2CppObjectBase)val.Instance).TryCast<EnemyScanner>();
		}

		private static int ListCount(List<EnemyAgent> enemies)
		{
			return enemies?.Count ?? 0;
		}

		private static int MarkEnemies(List<EnemyAgent> enemies, ScanSession session, ImpactCounters counters, bool bot)
		{
			if (enemies == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < enemies.Count; i++)
			{
				EnemyAgent val = enemies[i];
				if (!((Object)(object)val == (Object)null) && session.Observe(((Object)val).GetInstanceID(), counters, bot) == ScanMark.Repeat)
				{
					num++;
				}
			}
			return num;
		}

		private static void LogScanRepeats(CombatPlayer player, int repeats)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			if (repeats > 0 && !_loggedScanRepeat)
			{
				_loggedScanRepeat = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(162, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DIAG] scan repeats suppressed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(repeats);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" of ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(player.Name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'s swept enemies were already revealed, so they are not first marks for anyone, including the sweeper who revealed them first.");
				}
				logger.LogInfo(val);
			}
		}

		private static void LogImpact(string what, Exception error)
		{
			ManualLogSource logger = Plugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[DIAG] " + what + " hook could not read its input and was skipped: " + error.Message));
			}
		}

		static CombatTracker()
		{
			InventorySlot[] array = new InventorySlot[4];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			GearSlots = (InventorySlot[])(object)array;
			LastAttacker = new Dictionary<IntPtr, ulong>();
			KillCredited = new HashSet<IntPtr>();
			InterruptedFrame = new Dictionary<IntPtr, int>();
			Alive = new Dictionary<ulong, bool>();
			ScanLedger = new Dictionary<ulong, ScanProbe>();
			ScanRevealed = new ScanReveals();
			NearbyBotDistance = float.PositiveInfinity;
		}
	}
	internal static class ConfigWatcher
	{
		private const int DebounceMilliseconds = 300;

		private static FileSystemWatcher _watcher;

		private static long _dueTicks;

		private static volatile bool _armed;

		internal static string WatchedPath { get; private set; }

		internal static bool Watching => _watcher != null;

		internal static void Watch(ConfigFile config)
		{
			try
			{
				string text = ((config != null) ? config.ConfigFilePath : null);
				if (string.IsNullOrEmpty(text))
				{
					return;
				}
				string directoryName = Path.GetDirectoryName(text);
				if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName))
				{
					WatchedPath = text;
					_watcher = new FileSystemWatcher(directoryName, Path.GetFileName(text));
					_watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime;
					FileSystemEventHandler value = delegate
					{
						Arm();
					};
					_watcher.Changed += value;
					_watcher.Created += value;
					_watcher.Deleted += value;
					_watcher.Renamed += delegate
					{
						Arm();
					};
					_watcher.EnableRaisingEvents = true;
				}
			}
			catch (Exception)
			{
				_watcher = null;
			}
		}

		private static void Arm()
		{
			Interlocked.Exchange(ref _dueTicks, DateTime.UtcNow.AddMilliseconds(300.0).Ticks);
			_armed = true;
		}

		internal static bool ConsumeReload()
		{
			if (!_armed)
			{
				return false;
			}
			if (DateTime.UtcNow.Ticks < Interlocked.Read(in _dueTicks))
			{
				return false;
			}
			_armed = false;
			return true;
		}
	}
	internal static class DamageTargets
	{
		internal static IEnumerable<MethodBase> Methods(string name)
		{
			yield return AccessTools.DeclaredMethod(typeof(Dam_EnemyDamageBase), name, (Type[])null, (Type[])null);
			yield return AccessTools.DeclaredMethod(typeof(Dam_PlayerDamageBase), name, (Type[])null, (Type[])null);
		}
	}
	[HarmonyPatch]
	internal static class BulletDamagePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return DamageTargets.Methods("ReceiveBulletDamage");
		}

		[HarmonyPrefix]
		private static void Prefix(Dam_SyncedDamageBase __instance, pBulletDamageData data, out DamageSample __state)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			__state = CombatTracker.Capture(__instance.Health, data.source, DamageKind.Bullet);
		}

		[HarmonyPostfix]
		private static void Postfix(Dam_SyncedDamageBase __instance, pBulletDamageData data, DamageSample __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			CombatTracker.RecordDamage(__instance, __state, data);
		}
	}
	[HarmonyPatch]
	internal static class MeleeDamagePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return DamageTargets.Methods("ReceiveMeleeDamage");
		}

		[HarmonyPrefix]
		private static void Prefix(Dam_SyncedDamageBase __instance, pFullDamageData data, out DamageSample __state)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			__state = CombatTracker.Capture(__instance.Health, data.source, DamageKind.Melee);
		}

		[HarmonyPostfix]
		private static void Postfix(Dam_SyncedDamageBase __instance, DamageSample __state)
		{
			CombatTracker.RecordDamage(__instance, __state);
		}
	}
	[HarmonyPatch]
	internal static class FireDamagePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return DamageTargets.Methods("ReceiveFireDamage");
		}

		[HarmonyPrefix]
		private static void Prefix(Dam_SyncedDamageBase __instance, pSmallDamageData data, out DamageSample __state)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			__state = CombatTracker.Capture(__instance.Health, data.source, DamageKind.Fire);
		}

		[HarmonyPostfix]
		private static void Postfix(Dam_SyncedDamageBase __instance, DamageSample __state)
		{
			CombatTracker.RecordDamage(__instance, __state);
		}
	}
	[HarmonyPatch]
	internal static class PushDamagePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return DamageTargets.Methods("ReceivePushDamage");
		}

		[HarmonyPrefix]
		private static void Prefix(Dam_SyncedDamageBase __instance, pFullDamageData data, out DamageSample __state)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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)
			__state = CombatTracker.Capture(__instance.Health, data.source, DamageKind.Push);
			CombatTracker.CreditPushInterrupt(((Il2CppObjectBase)__instance).TryCast<Dam_EnemyDamageBase>(), data.source);
		}

		[HarmonyPostfix]
		private static void Postfix(Dam_SyncedDamageBase __instance, DamageSample __state)
		{
			CombatTracker.RecordDamage(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(GameStateManager), "DoChangeState")]
	internal static class ExpeditionStatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(eGameStateName nextState)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			CombatTracker.ObserveState(nextState);
		}
	}
	internal sealed class TableContent
	{
		internal PanelColumn[] Columns;

		internal string[][][] Text;

		internal Color[,] Colors;

		internal string FieldSeparator;

		internal bool[] Dividers;

		internal ImpactRowKind[] Kinds;

		internal int Rows
		{
			get
			{
				if (Text == null)
				{
					return 0;
				}
				return Text.Length;
			}
		}

		internal int ColumnCount
		{
			get
			{
				if (Columns == null)
				{
					return 0;
				}
				return Columns.Length;
			}
		}

		internal string[] Cell(int row, int column)
		{
			if (Text == null || row < 0 || row >= Text.Length)
			{
				return Array.Empty<string>();
			}
			string[][] array = Text[row];
			if (array == null || column < 0 || column >= array.Length)
			{
				return Array.Empty<string>();
			}
			return array[column] ?? Array.Empty<string>();
		}
	}
	internal static class HudTable
	{
		private static GUIStyle _style;

		private static int _styleFontSize = -1;

		private static Font _font;

		private static Texture2D _backdrop;

		private static bool _loggedFont;

		internal static GUIStyle Style(int fontSize)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			if (fontSize < 1)
			{
				fontSize = 1;
			}
			if (_style != null && _styleFontSize == fontSize)
			{
				return _style;
			}
			_styleFontSize = fontSize;
			GUIStyle val = new GUIStyle(GUI.skin.label);
			val.richText = false;
			val.wordWrap = false;
			val.clipping = (TextClipping)1;
			val.alignment = (TextAnchor)2;
			val.fontSize = fontSize;
			val.padding = new RectOffset(0, 4, 0, 0);
			Font val2 = FontFor(fontSize);
			if ((Object)(object)val2 != (Object)null)
			{
				val.font = val2;
			}
			_style = val;
			return _style;
		}

		internal static Font FontFor(int fontSize)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			if ((Object)(object)_font != (Object)null && ((Il2CppObjectBase)_font).Pointer != IntPtr.Zero)
			{
				return _font;
			}
			try
			{
				_font = new Font();
				Il2CppStringArray val = new Il2CppStringArray(new string[6] { "Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "SimSun", "Noto Sans CJK SC", "Arial" });
				Font.Internal_CreateDynamicFont(_font, val, Math.Max(8, fontSize));
				if ((Object)(object)_font != (Object)null)
				{
					ManualLogSource logger = Plugin.Logger;
					if (logger != null)
					{
						bool flag = default(bool);
						BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(69, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[DIAG] table font: '");
							((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)_font).name);
							((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' loaded from the operating system (CJK capable).");
						}
						logger.LogInfo(val2);
					}
				}
			}
			catch (Exception ex)
			{
				_font = null;
				if (!_loggedFont)
				{
					_loggedFont = true;
					ManualLogSource logger2 = Plugin.Logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)("No CJK-capable system font could be loaded; the table falls back to the built-in font: " + ex.Message));
					}
				}
			}
			return _font;
		}

		internal static float Measure(string text, int fontSize, bool includeBold = false)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(text))
			{
				return 0f;
			}
			GUIStyle val = Style(fontSize);
			if (val == null)
			{
				return 0f;
			}
			try
			{
				FontStyle fontStyle = val.fontStyle;
				try
				{
					val.fontStyle = (FontStyle)0;
					float x = val.CalcSize(new GUIContent(text)).x;
					val.fontStyle = (FontStyle)1;
					float num = (includeBold ? Mathf.Max(x, val.CalcSize(new GUIContent(text)).x) : x);
					return (float.IsFinite(num) && num > 0f) ? num : 0f;
				}
				finally
				{
					val.fontStyle = fontStyle;
				}
			}
			catch
			{
				return 0f;
			}
		}

		internal static PanelGrid BuildGrid(TableContent content, int fontSize, PanelDensity density, float maxWidth, float maxHeight)
		{
			if (content == null || content.ColumnCount == 0 || content.Rows == 0)
			{
				return null;
			}
			float separatorWidth = PanelGeometry.FieldGap(fontSize, density);
			PanelColumnMetrics[] array = new PanelColumnMetrics[content.ColumnCount];
			for (int i = 0; i < content.ColumnCount; i++)
			{
				if (content.FieldSeparator != null)
				{
					float num = PanelGeometry.MinimumWidth(content.Columns[i], fontSize);
					for (int j = 0; j < content.Rows; j++)
					{
						string text = string.Join(content.FieldSeparator, content.Cell(j, i));
						num = Mathf.Max(num, Mathf.Max(PanelGeometry.EstimateTextWidth(text, fontSize), Measure(text, fontSize, includeBold: true)));
					}
					array[i] = new PanelColumnMetrics(new float[1] { num });
					continue;
				}
				int num2 = 1;
				for (int k = 0; k < content.Rows; k++)
				{
					num2 = Mathf.Max(num2, content.Cell(k, i).Length);
				}
				float num3 = PanelGeometry.MinimumWidth(content.Columns[i], fontSize) / (float)num2;
				float[] array2 = new float[num2];
				for (int l = 0; l < num2; l++)
				{
					array2[l] = num3;
				}
				for (int m = 0; m < content.Rows; m++)
				{
					string[] array3 = content.Cell(m, i);
					for (int n = 0; n < array3.Length; n++)
					{
						int num4 = num2 - array3.Length + n;
						if (num4 >= 0 && num4 < num2)
						{
							float num5 = Mathf.Max(PanelGeometry.EstimateTextWidth(array3[n], fontSize), Measure(array3[n], fontSize));
							array2[num4] = Mathf.Max(array2[num4], num5);
						}
					}
				}
				array[i] = new PanelColumnMetrics(array2);
			}
			bool[] array4 = null;
			if (content.Kinds != null)
			{
				array4 = new bool[content.ColumnCount];
				for (int num6 = 0; num6 < content.ColumnCount; num6++)
				{
					array4[num6] = content.Columns[num6] != PanelColumn.ImpactLabel;
				}
			}
			PanelGrid panelGrid = PanelGeometry.Build(array, separatorWidth, fontSize, content.Rows, density, 1f, array4);
			float num7 = panelGrid.FitFactor(maxWidth, maxHeight);
			if (num7 < 0.999f)
			{
				panelGrid = PanelGeometry.Build(array, separatorWidth, fontSize, content.Rows, density, num7, array4);
			}
			return panelGrid;
		}

		internal static void Draw(PanelGrid grid, float originX, float originY, TableContent content, float opacity = 1f)
		{
			//IL_004e: 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)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			if (grid == null || grid.Columns == 0 || content == null)
			{
				return;
			}
			int num = Math.Min(grid.Rows, content.Rows);
			GUIStyle val = Style(Mathf.Max(1, Mathf.RoundToInt(grid.FontPixels)));
			if (val == null)
			{
				return;
			}
			Texture2D val2 = Backdrop();
			if ((Object)(object)val2 != (Object)null)
			{
				Color color = GUI.color;
				GUI.color = ((content.Kinds != null) ? new Color(0.035f, 0.045f, 0.055f, 0.5f * opacity) : new Color(0.04f, 0.05f, 0.06f, 0.48f * opacity));
				GUI.DrawTexture(new Rect(originX, originY, grid.Width, grid.Height), (Texture)(object)val2);
				int num2 = 1;
				while (content.Kinds != null && num2 < num)
				{
					GUI.color = new Color(1f, 1f, 1f, 0.02f * opacity);
					GUI.DrawTexture(new Rect(originX, originY + (float)num2 * grid.RowHeight, grid.Width, grid.RowHeight), (Texture)(object)val2);
					num2 += 2;
				}
				for (int i = 0; i < num; i++)
				{
					if (content.Dividers != null && i < content.Dividers.Length && content.Dividers[i])
					{
						GUI.color = new Color(1f, 1f, 1f, 0.12f * opacity);
						GUI.DrawTexture(new Rect(originX, originY + (float)i * grid.RowHeight, grid.Width, 1f), (Texture)(object)val2);
					}
				}
				GUI.color = new Color(1f, 1f, 1f, ((content.Kinds != null) ? 0.12f : 0.22f) * opacity);
				GUI.DrawTexture(new Rect(originX, originY, grid.Width, 1f), (Texture)(object)val2);
				GUI.DrawTexture(new Rect(originX, originY + grid.Height - 1f, grid.Width, 1f), (Texture)(object)val2);
				GUI.DrawTexture(new Rect(originX, originY, 1f, grid.Height), (Texture)(object)val2);
				GUI.DrawTexture(new Rect(originX + grid.Width - 1f, originY, 1f, grid.Height), (Texture)(object)val2);
				GUI.color = color;
			}
			for (int j = 0; j < num; j++)
			{
				for (int k = 0; k < grid.Columns; k++)
				{
					string[] array = content.Cell(j, k);
					Color textColor = ((content.Colors != null) ? content.Colors[j, k] : Color.white);
					textColor.a *= opacity;
					val.normal.textColor = textColor;
					bool flag = content.Kinds != null && j < content.Kinds.Length && (content.Kinds[j] == ImpactRowKind.PlayerName || content.Kinds[j] == ImpactRowKind.Rating || (content.Kinds[j] >= ImpactRowKind.PrimaryWeapon && content.Kinds[j] <= ImpactRowKind.ToolWeapon));
					val.alignment = (TextAnchor)((content.Kinds == null || (!flag && grid.IsRightAligned(k))) ? 2 : 0);
					val.fontStyle = (FontStyle)((content.Kinds != null && j < content.Kinds.Length && (content.Kinds[j] == ImpactRowKind.Score || content.Kinds[j] == ImpactRowKind.PlayerName || content.Kinds[j] == ImpactRowKind.Separator)) ? 1 : 0);
					if (content.FieldSeparator != null)
					{
						LayoutRect layoutRect = grid.FieldRect(j, k, 0);
						GUI.Label(new Rect(originX + layoutRect.X, originY + layoutRect.Y, layoutRect.Width, layoutRect.Height), string.Join(content.FieldSeparator, array), val);
						continue;
					}
					for (int l = 0; l < array.Length; l++)
					{
						string text = array[l];
						if (!string.IsNullOrEmpty(text))
						{
							LayoutRect layoutRect2 = grid.FieldRectAligned(j, k, array.Length, l);
							GUI.Label(new Rect(originX + layoutRect2.X, originY + layoutRect2.Y, layoutRect2.Width, layoutRect2.Height), text, val);
						}
					}
				}
			}
		}

		private static Texture2D Backdrop()
		{
			if ((Object)(object)_backdrop != (Object)null && ((Il2CppObjectBase)_backdrop).Pointer != IntPtr.Zero)
			{
				return _backdrop;
			}
			try
			{
				_backdrop = Texture2D.whiteTexture;
			}
			catch
			{
				_backdrop = null;
			}
			return _backdrop;
		}
	}
	public enum ImpactTone
	{
		Neutral,
		PlayerColor,
		Gold,
		Silver,
		Copper
	}
	public enum ImpactRowKind
	{
		PlayerName,
		Score,
		Rating,
		PrimaryWeapon,
		SecondaryWeapon,
		MeleeWeapon,
		ToolWeapon,
		PrimaryDamage,
		PrimaryHit,
		PrimaryPellets,
		SecondaryDamage,
		SecondaryHit,
		SecondaryPellets,
		Separator,
		Metric
	}
	public sealed class ImpactBoardLayout
	{
		public PanelColumn[] Columns;

		public string[][][] Text;

		public ImpactTone[][] Tones;

		public ImpactRowKind[] Kinds;

		public int Rows
		{
			get
			{
				if (Text == null)
				{
					return 0;
				}
				return Text.Length;
			}
		}

		public int ColumnCount
		{
			get
			{
				if (Columns == null)
				{
					return 0;
				}
				return Columns.Length;
			}
		}

		public string[] Cell(int row, int column)
		{
			if (Text == null || row < 0 || row >= Text.Length)
			{
				return Array.Empty<string>();
			}
			string[][] array = Text[row];
			if (array == null || column < 0 || column >= array.Length)
			{
				return Array.Empty<string>();
			}
			return array[column] ?? Array.Empty<string>();
		}

		public ImpactTone Tone(int row, int column)
		{
			if (Tones == null || row < 0 || row >= Tones.Length)
			{
				return ImpactTone.Neutral;
			}
			ImpactTone[] array = Tones[row];
			if (array == null || column < 0 || column >= array.Length)
			{
				return ImpactTone.Neutral;
			}
			return array[column];
		}

		public string Joined(int row, int column)
		{
			return string.Join(" / ", Cell(row, column));
		}
	}
	public static class ImpactBoard
	{
		public const int FixedRows = 13;

		public const int SeparatorRows = 1;

		public const string ValueSeparator = " / ";

		private const int MetricHeaderRow = 13;

		public static int MetricRowCount(bool splitKillTiers)
		{
			if (!splitKillTiers)
			{
				return 10;
			}
			return 12;
		}

		public static int RowCount(bool splitKillTiers)
		{
			return 14 + MetricRowCount(splitKillTiers);
		}

		public static ImpactTone RankTone(int rank)
		{
			return rank switch
			{
				1 => ImpactTone.Gold, 
				2 => ImpactTone.Silver, 
				3 => ImpactTone.Copper, 
				_ => ImpactTone.Neutral, 
			};
		}

		public static string[] Single(string text)
		{
			return new string[1] { text ?? string.Empty };
		}

		public static string[] WeaponFields(PlayerCombatStats stats, PanelColumn column, double squadDamage)
		{
			if (stats == null)
			{
				return new string[1] { CombatStrings.Placeholder };
			}
			return column switch
			{
				PanelColumn.DamageAndShare => new string[2]
				{
					CombatStatsFormatter.FormatDamage(stats.EnemyDamage),
					CombatStatsFormatter.FormatShare(stats, squadDamage)
				}, 
				PanelColumn.Accuracy => new string[1] { CombatStatsFormatter.AccuracyText(stats) }, 
				PanelColumn.WeakspotAccuracy => new string[1] { CombatStatsFormatter.WeakspotText(stats) }, 
				PanelColumn.Projectiles => CombatStatsFormatter.ProjectileFields(stats), 
				_ => new string[1] { CombatStrings.Placeholder }, 
			};
		}

		public static string[] WeaponFields(PlayerCombatStats stats, PanelColumn first, PanelColumn second, double squadDamage)
		{
			string[] array = WeaponFields(stats, first, squadDamage);
			string[] array2 = WeaponFields(stats, second, squadDamage);
			string[] array3 = new string[array.Length + array2.Length];
			Array.Copy(array, 0, array3, 0, array.Length);
			Array.Copy(array2, 0, array3, array.Length, array2.Length);
			return array3;
		}

		public static ImpactBoardLayout Build(ImpactReport report, bool splitKillTiers, bool expeditionSucceeded)
		{
			if (report == null)
			{
				report = ImpactScoring.Evaluate(null, null, 1.0, 1.0);
			}
			List<ImpactEntry> entries = report.Entries;
			int count = entries.Count;
			int num = count + 1;
			int num2 = RowCount(splitKillTiers);
			PanelColumn[] array = new PanelColumn[num];
			array[0] = PanelColumn.ImpactLabel;
			for (int i = 1; i < num; i++)
			{
				array[i] = PanelColumn.ImpactValue;
			}
			string[][][] array2 = new string[num2][][];
			ImpactTone[][] array3 = new ImpactTone[num2][];
			ImpactRowKind[] array4 = new ImpactRowKind[num2];
			for (int j = 0; j < num2; j++)
			{
				array2[j] = new string[num][];
				array3[j] = new ImpactTone[num];
				for (int k = 0; k < num; k++)
				{
					array2[j][k] = Single(string.Empty);
					array3[j][k] = ImpactTone.Neutral;
				}
			}
			string[] array5 = CombatStrings.ImpactRowLabels();
			double num3 = 0.0;
			for (int l = 0; l < count; l++)
			{
				num3 += Math.Max(0.0, entries[l].Input.EnemyDamage);
			}
			for (int m = 0; m < 13; m++)
			{
				Put(array2, array3, m, 0, Single(array5[m]));
				array4[m] = (ImpactRowKind)m;
			}
			Put(array2, array3, 13, 0, Single(CombatStrings.ImpactMetricHeader));
			array4[13] = ImpactRowKind.Separator;
			for (int n = 0; n < count; n++)
			{
				int column = n + 1;
				ImpactEntry impactEntry = entries[n];
				ImpactInput input = impactEntry.Input;
				Put(array2, array3, 0, column, Single("#" + impactEntry.Rank.ToString(CultureInfo.InvariantCulture) + " " + input.Name), ImpactTone.PlayerColor);
				Put(array2, array3, 1, column, Single(ImpactScoring.ScoreText(impactEntry.Score)), RankTone(impactEntry.Rank));
				Put(array2, array3, 2, column, Single(CombatStrings.Title(impactEntry.Rating)));
				Put(array2, array3, 3, column, Single(Name(input.PrimaryName)));
				Put(array2, array3, 4, column, Single(Name(input.SecondaryName)));
				Put(array2, array3, 5, column, Single(Name(input.MeleeName)));
				Put(array2, array3, 6, column, Single(input.SupportRole ? (Name(input.ToolName) + CombatStrings.SupportMark) : Name(input.ToolName)));
				Put(array2, array3, 7, column, WeaponFields(input.Primary, PanelColumn.DamageAndShare, num3));
				Put(array2, array3, 8, column, WeaponFields(input.Primary, PanelColumn.Accuracy, PanelColumn.WeakspotAccuracy, num3));
				Put(array2, array3, 9, column, WeaponFields(input.Primary, PanelColumn.Projectiles, num3));
				Put(array2, array3, 10, column, WeaponFields(input.Secondary, PanelColumn.DamageAndShare, num3));
				Put(array2, array3, 11, column, WeaponFields(input.Secondary, PanelColumn.Accuracy, PanelColumn.WeakspotAccuracy, num3));
				Put(array2, array3, 12, column, WeaponFields(input.Secondary, PanelColumn.Projectiles, num3));
			}
			int row = 14;
			for (int num4 = 0; num4 < ImpactScoring.Order.Length; num4++)
			{
				ImpactMetric impactMetric = ImpactScoring.Order[num4];
				if (impactMetric == ImpactMetric.Kills && splitKillTiers)
				{
					row = MetricRow(report, array2, array3, array4, row, entries, impactMetric, KillTier.Minor, split: true, expeditionSucceeded);
					row = MetricRow(report, array2, array3, array4, row, entries, impactMetric, KillTier.Elite, split: true, expeditionSucceeded);
					row = MetricRow(report, array2, array3, array4, row, entries, impactMetric, KillTier.Boss, split: true, expeditionSucceeded);
				}
				else
				{
					row = MetricRow(report, array2, array3, array4, row, entries, impactMetric, KillTier.Minor, split: false, expeditionSucceeded);
				}
			}
			return new ImpactBoardLayout
			{
				Columns = array,
				Text = array2,
				Tones = array3,
				Kinds = array4
			};
		}

		private static int MetricRow(ImpactReport report, string[][][] text, ImpactTone[][] tones, ImpactRowKind[] kinds, int row, List<ImpactEntry> entries, ImpactMetric metric, KillTier tier, bool split, bool expeditionSucceeded)
		{
			if (row < 0 || row >= text.Length)
			{
				return Math.Max(0, row);
			}
			kinds[row] = ImpactRowKind.Metric;
			string text2 = CombatStrings.MetricLabel(metric);
			if (split)
			{
				text2 = CombatStrings.KillRowLabel(tier);
			}
			Put(text, tones, row, 0, Single(text2));
			for (int i = 0; i < entries.Count; i++)
			{
				ImpactEntry entry = entries[i];
				Put(text, tones, row, i + 1, MetricFields(entry, metric, tier, split, expeditionSucceeded), IsMetricGold(report, entry, i, metric, tier, split, expeditionSucceeded));
			}
			return row + 1;
		}

		public static string[] MetricFields(ImpactEntry entry, ImpactMetric metric, KillTier tier, bool split, bool expeditionSucceeded)
		{
			if (entry == null)
			{
				return Single(CombatStrings.Placeholder);
			}
			ImpactCounters impactCounters = entry.Counters ?? new ImpactCounters();
			switch (metric)
			{
			case ImpactMetric.TotalDamage:
				return Single(CombatStatsFormatter.FormatDamage(entry.Input.EnemyDamage));
			case ImpactMetric.Stagger:
				return Single(Count(impactCounters.Stagger));
			case ImpactMetric.Kills:
				if (!split)
				{
					return new string[3]
					{
						Count(impactCounters.MinorKills) + CombatStrings.KillTierMark(KillTier.Minor),
						Count(impactCounters.EliteKills) + CombatStrings.KillTierMark(KillTier.Elite),
						Count(impactCounters.BossKills) + CombatStrings.KillTierMark(KillTier.Boss)
					};
				}
				return tier switch
				{
					KillTier.Minor => Single(Count(impactCounters.MinorKills)), 
					KillTier.Elite => Single(Count(impactCounters.EliteKills)), 
					_ => Single(Count(impactCounters.BossKills)), 
				};
			case ImpactMetric.Revives:
				return Single(Count(impactCounters.Revives));
			case ImpactMetric.WeakspotRate:
				return Single(RateText(entry.Input, weakspot: true));
			case ImpactMetric.Accuracy:
				return Single(RateText(entry.Input, weakspot: false));
			case ImpactMetric.SupplyEfficiency:
				return Single((impactCounters.SupplyUses > 0) ? Percent(impactCounters.SupplyEfficiency, 1.0) : CombatStrings.Placeholder);
			case ImpactMetric.ScanEfficiency:
				return Single((impactCounters.ScanUses > 0) ? ((impactCounters.ScanEstimated ? "~" : string.Empty) + impactCounters.ScanEfficiency.ToString("0.00", CultureInfo.InvariantCulture)) : CombatStrings.Placeholder);
			case ImpactMetric.LoneSurvivor:
				return Single(expeditionSucceeded ? Count(impactCounters.LoneSurvivor) : CombatStrings.Placeholder);
			case ImpactMetric.Downs:
				return Single(Count(impactCounters.Downs));
			default:
				return Single(CombatStrings.Placeholder);
			}
		}

		public static ImpactTone IsMetricGold(ImpactReport report, ImpactEntry entry, int index, ImpactMetric metric, KillTier tier, bool split, bool expeditionSucceeded)
		{
			if (report == null || entry == null)
			{
				return ImpactTone.Neutral;
			}
			if (metric == ImpactMetric.LoneSurvivor && !expeditionSucceeded)
			{
				return ImpactTone.Neutral;
			}
			if (metric == ImpactMetric.Kills && split)
			{
				switch (tier)
				{
				case KillTier.Minor:
					if (!report.IsGoldBy(index, (ImpactEntry e) => (e.Counters != null) ? e.Counters.MinorKills : 0))
					{
						return ImpactTone.Neutral;
					}
					return ImpactTone.Gold;
				case KillTier.Elite:
					if (!report.IsGoldBy(index, (ImpactEntry e) => (e.Counters != null) ? e.Counters.EliteKills : 0))
					{
						return ImpactTone.Neutral;
					}
					return ImpactTone.Gold;
				default:
					if (!report.IsGoldBy(index, (ImpactEntry e) => (e.Counters != null) ? e.Counters.BossKills : 0))
					{
						return ImpactTone.Neutral;
					}
					return ImpactTone.Gold;
				}
			}
			if (!report.IsGold(index, metric))
			{
				return ImpactTone.Neutral;
			}
			return ImpactTone.Gold;
		}

		private static void Put(string[][][] text, ImpactTone[][] tones, int row, int column, string[] fields)
		{
			Put(text, tones, row, column, fields, ImpactTone.Neutral);
		}

		private static void Put(string[][][] text, ImpactTone[][] tones, int row, int column, string[] fields, ImpactTone tone)
		{
			if (text == null || row < 0 || row >= text.Length)
			{
				return;
			}
			string[][] array = text[row];
			if (array != null && column >= 0 && column < array.Length)
			{
				array[column] = ((fields == null || fields.Length == 0) ? Single(string.Empty) : fields);
				if (tones != null && tones[row] != null && column >= 0 && column < tones[row].Length)
				{
					tones[row][column] = tone;
				}
			}
		}

		private static string Name(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return name;
			}
			return CombatStrings.Placeholder;
		}

		private static string Count(long value)
		{
			if (value <= 0)
			{
				return "0";
			}
			return value.ToString(CultureInfo.InvariantCulture);
		}

		private static string Percent(double numerator, double denominator)
		{
			if (!(denominator > 0.0) || !double.IsFinite(numerator) || numerator < 0.0)
			{
				return CombatStrings.Placeholder;
			}
			return (numerator * 100.0 / denominator).ToString("0.0", CultureInfo.InvariantCulture) + "%";
		}

		public static string RateText(ImpactInput input, bool weakspot)
		{
			if (input == null)
			{
				return CombatStrings.Placeholder;
			}
			long num = (weakspot ? input.WeakspotHits : input.Hits);
			long num2 = (weakspot ? input.Hits : input.ShotsFired);
			if (num2 <= 0 || num < 0)
			{
				return CombatStrings.Placeholder;
			}
			return (input.AccuracyEstimated ? "~" : string.Empty) + Percent(num, num2);
		}
	}
	public enum ImpactMetric
	{
		TotalDamage,
		Stagger,
		Kills,
		Revives,
		WeakspotRate,
		Accuracy,
		SupplyEfficiency,
		ScanEfficiency,
		LoneSurvivor,
		Downs
	}
	public enum KillTier
	{
		Minor,
		Elite,
		Boss
	}
	public enum ImpactRating
	{
		TeamPillar,
		Damage,
		Stagger,
		MinorKills,
		EliteKills,
		BossKills,
		Revives,
		Weakspot,
		Accuracy,
		Supply,
		Scan,
		LoneSurvivor,
		Downs,
		ZeroScore
	}
	public static class KillClassifier
	{
		public const double DefaultMinorMaxHealth = 120.0;

		public const double DefaultEliteMaxHealth = 1000.0;

		public static KillTier Classify(double healthMax, double minorMaxHealth = 120.0, double eliteMaxHealth = 1000.0)
		{
			if (!double.IsFinite(healthMax))
			{
				return KillTier.Minor;
			}
			if (healthMax < minorMaxHealth)
			{
				return KillTier.Minor;
			}
			if (healthMax < eliteMaxHealth)
			{
				return KillTier.Elite;
			}
			return KillTier.Boss;
		}

		public static void Credit(ImpactCounters counters, KillTier tier)
		{
			if (counters != null)
			{
				switch (tier)
				{
				case KillTier.Minor:
					counters.MinorKills++;
					break;
				case KillTier.Elite:
					counters.EliteKills++;
					break;
				default:
					counters.BossKills++;
					break;
				}
			}
		}
	}
	public enum StaggerKind
	{
		None,
		Light,
		Heavy,
		Lethal
	}
	public static class StaggerRule
	{
		public static bool IsInterrupt(StaggerKind react, float damage, float health, bool attacking, bool needsAttack, bool push = false)
		{
			if (!float.IsFinite(damage) || damage < 0f || (damage == 0f && !push))
			{
				return false;
			}
			if (push && (!attacking || !float.IsFinite(health) || health <= 0f))
			{
				return false;
			}
			if (react != StaggerKind.Light && react != StaggerKind.Heavy)
			{
				return false;
			}
			if (float.IsFinite(health) && health > 0f && damage >= health)
			{
				return false;
			}
			if (needsAttack && !attacking)
			{
				return false;
			}
			return true;
		}
	}
	public sealed class ImpactCounters
	{
		public long MinorKills;

		public long EliteKills;

		public long BossKills;

		public long Stagger;

		public long Revives;

		public long SupplyUses;

		public double SupplyEfficiencySum;

		public long ScanUses;

		public long ScanFirstMarks;

		public bool ScanEstimated;

		public int LoneSurvivor;

		public int Downs;

		public long Kills => MinorKills + EliteKills + BossKills;

		public double WeightedKills => 0.5 * (double)MinorKills + 1.0 * (double)EliteKills + 1.5 * (double)BossKills;

		public double SupplyEfficiency
		{
			get
			{
				if (SupplyUses <= 0)
				{
					return 0.0;
				}
				return Clamp01(SupplyEfficiencySum / (double)SupplyUses);
			}
		}

		public double ScanEfficiency
		{
			get
			{
				if (ScanUses <= 0)
				{
					return 0.0;
				}
				return (double)ScanFirstMarks / (double)ScanUses;
			}
		}

		public void RecordSupply(double effective, double intended)
		{
			if (double.IsFinite(effective) && !(effective < 0.0) && double.IsFinite(intended) && !(intended <= 0.0))
			{
				SupplyEfficiencySum += Clamp01(effective / intended);
				SupplyUses++;
			}
		}

		public void CreditInterrupt()
		{
			if (Stagger < long.MaxValue)
			{
				Stagger++;
			}
		}

		public void CreditKill(KillTier tier)
		{
			KillClassifier.Credit(this, tier);
		}

		public void CreditRevive()
		{
			if (Revives < long.MaxValue)
			{
				Revives++;
			}
		}

		public void CreditDown()
		{
			if (Downs < int.MaxValue)
			{
				Downs++;
			}
		}

		public void CreditScan(long firstMarks, bool estimated = false)
		{
			if (ScanUses < long.MaxValue)
			{
				ScanUses++;
			}
			if (firstMarks > 0)
			{
				ScanFirstMarks = Math.Min(long.MaxValue, ScanFirstMarks + firstMarks);
			}
			if (estimated)
			{
				ScanEstimated = true;
			}
		}

		public void MarkLoneSurvivor()
		{
			if (LoneSurvivor < int.MaxValue)
			{
				LoneSurvivor++;
			}
		}

		public void Reset()
		{
			MinorKills = (EliteKills = (BossKills = 0L));
			Stagger = 0L;
			Revives = 0L;
			SupplyUses = 0L;
			SupplyEfficiencySum = 0.0;
			ScanUses = (ScanFirstMarks = 0L);
			ScanEstimated = false;
			LoneSurvivor = 0;
			Downs = 0;
		}

		private static double Clamp01(double value)
		{
			if (double.IsFinite(value) && !(value < 0.0))
			{
				if (!(value > 1.0))
				{
					return value;
				}
				return 1.0;
			}
			return 0.0;
		}
	}
	public sealed class ImpactInput
	{
		public string Name = string.Empty;

		public ulong Lookup;

		public bool SupportRole;

		public double EnemyDamage;

		public long ShotsFired;

		public long Hits;

		public long WeakspotHits;

		public bool AccuracyEstimated;

		public ImpactCounters Counters = new ImpactCounters();

		public PlayerCombatStats Primary;

		public PlayerCombatStats Secondary;

		public string PrimaryName = string.Empty;

		public string SecondaryName = string.Empty;

		public string MeleeName = string.Empty;

		public string ToolName = string.Empty;

		public static ImpactInput From(string name, PlayerCombatStats combat, ImpactCounters counters, bool support, PlayerCombatStats primary = null, PlayerCombatStats secondary = null)
		{
			ImpactInput impactInput = new ImpactInput
			{
				Name = (name ?? string.Empty),
				SupportRole = support,
				Counters = (counters ?? new ImpactCounters()),
				Primary = primary,
				Secondary = secondary
			};
			if (combat != null)
			{
				impactInput.EnemyDamage = combat.EnemyDamage;
				impactInput.ShotsFired = combat.ShotsFired;
				impactInput.Hits = combat.Hits;
				impactInput.WeakspotHits = combat.WeakspotHits;
				impactInput.AccuracyEstimated = combat.AccuracyEstimated;
			}
			return impactInput;
		}
	}
	public sealed class ImpactEntry
	{
		public readonly ImpactInput Input;

		public readonly double[] Raw;

		public readonly double[] Normalized;

		public readonly double Score;

		public readonly int Rank;

		public readonly ImpactRating Rating;

		public readonly int RatingMetric;

		public string Name => Input.Name;

		public ImpactCounters Counters => Input.Counters;

		internal ImpactEntry(ImpactInput input, double[] raw, double[] normalized, double score, int rank, ImpactRating rating, int ratingMetric)
		{
			Input = input;
			Raw = raw;
			Normalized = normalized;
			Score = score;
			Rank = rank;
			Rating = rating;
			RatingMetric = ratingMetric;
		}
	}
	public sealed class ImpactReport
	{
		public readonly List<ImpactEntry> Entries = new List<ImpactEntry>();

		public readonly double[] Weights;

		public readonly double[] MaxRaw;

		public readonly int[] CountAtMax;

		public readonly double[] MinRaw;

		public readonly int[] CountAtMin;

		public readonly double FirepowerBonus;

		public readonly double UtilityBonus;

		internal ImpactReport(double[] weights, double firepowerBonus, double utilityBonus)
		{
			Weights = weights;
			int num = ((weights != null) ? weights.Length : 0);
			MaxRaw = new double[num];
			CountAtMax = new int[num];
			MinRaw = new double[num];
			CountAtMin = new int[num];
			FirepowerBonus = firepowerBonus;
			UtilityBonus = utilityBonus;
		}

		public bool IsGold(int entry, ImpactMetric metric)
		{
			if (entry < 0 || entry >= Entries.Count || metric < ImpactMetric.TotalDamage || (int)metric >= MaxRaw.Length)
			{
				return false;
			}
			if (!MetricActive(metric))
			{
				return false;
			}
			double num = Entries[entry].Raw[(int)metric];
			if (ImpactScoring.IsReverse(metric))
			{
				if (CountAtMin[(int)metric] == 1)
				{
					return num <= MinRaw[(int)metric] + 1E-09;
				}
				return false;
			}
			if (num > 0.0 && num >= MaxRaw[(int)metric] - 1E-09)
			{
				return CountAtMax[(int)metric] == 1;
			}
			return false;
		}

		public bool IsGoldBy(int entry, Func<ImpactEntry, double> value)
		{
			if (value == null || entry < 0 || entry >= Entries.Count)
			{
				return false;
			}
			double num = value(Entries[entry]);
			if (!(num > 0.0))
			{
				return false;
			}
			double num2 = 0.0;
			int num3 = 0;
			for (int i = 0; i < Entries.Count; i++)
			{
				double val = Sanitize(value(Entries[i]));
				num2 = Math.Max(num2, val);
			}
			for (int j = 0; j < Entries.Count; j++)
			{
				if (Sanitize(value(Entries[j])) >= num2)
				{
					num3++;
				}
			}
			if (num3 == 1)
			{
				return num >= num2 - 1E-09;
			}
			return false;
		}

		private static double Sanitize(double value)
		{
			if (double.IsFinite(value) && !(value < 0.0))
			{
				return value;
			}
			return 0.0;
		}

		public bool MetricActive(ImpactMetric metric)
		{
			if (metric >= ImpactMetric.TotalDamage && (int)metric < MaxRaw.Length)
			{
				return MaxRaw[(int)metric] > 0.0;
			}
			return false;
		}
	}
	public static class ImpactScoring
	{
		public static readonly ImpactMetric[] Order = (ImpactMetric[])Enum.GetValues(typeof(ImpactMetric));

		public static readonly double[] DefaultWeights = new double[10] { 1.1, 1.2, 0.9, 1.5, 0.7, 0.6, 1.4, 1.3, 0.9, 0.7 };

		public const string DefaultWeightsText = "1.1,1.2,0.9,1.5,0.7,0.6,1.4,1.3,0.9,0.7";

		public static double[] ParseWeights(string text)
		{
			if (!string.IsNullOrWhiteSpace(text))
			{
				string[] array = text.Split(',');
				if (array.Length == Order.Length)
				{
					double[] array2 = new double[Order.Length];
					bool flag = true;
					for (int i = 0; i < array.Length; i++)
					{
						if (!double.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]) || !double.IsFinite(array2[i]) || array2[i] < 0.0)
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						return array2;
					}
				}
			}
			return (double[])DefaultWeights.Clone();
		}

		public static string Describe(double[] weights)
		{
			return string.Join(",", Array.ConvertAll(weights ?? DefaultWeights, (double value) => value.ToString("0.0##", CultureInfo.InvariantCulture)));
		}

		public static bool UsesFirepower(ImpactMetric metric)
		{
			if (metric != ImpactMetric.TotalDamage && metric != ImpactMetric.Stagger && metric != ImpactMetric.Kills && metric != ImpactMetric.Accuracy)
			{
				return metric == ImpactMetric.WeakspotRate;
			}
			return true;
		}

		public static bool UsesUtility(ImpactMetric metric)
		{
			if (metric != ImpactMetric.ScanEfficiency && metric != ImpactMetric.SupplyEfficiency && metric != ImpactMetric.Revives)
			{
				return metric == ImpactMetric.LoneSurvivor;
			}
			return true;
		}

		public static bool IsRate(ImpactMetric metric)
		{
			if (metric != ImpactMetric.WeakspotRate && metric != ImpactMetric.Accuracy && metric != ImpactMetric.SupplyEfficiency)
			{
				return metric == ImpactMetric.ScanEfficiency;
			}
			return true;
		}

		public static bool IsReverse(ImpactMetric metric)
		{
			return metric == ImpactMetric.Downs;
		}

		public static double RoleFactor(ImpactMetric metric, bool support, double firepowerBonus, double utilityBonus)
		{
			if (!support)
			{
				return 1.0;
			}
			if (UsesFirepower(metric))
			{
				return Safe(firepowerBonus, 1.0);
			}
			if (UsesUtility(metric))
			{
				return Safe(utilityBonus, 1.0);
			}
			return 1.0;
		}

		public static double RawValue(ImpactInput player, ImpactMetric metric)
		{
			if (player == null)
			{
				return 0.0;
			}
			ImpactCounters impactCounters = player.Counters ?? new ImpactCounters();
			switch (metric)
			{
			case ImpactMetric.TotalDamage:
				return Safe(player.EnemyDamage, 0.0);
			case ImpactMetric.Stagger:
				return Safe(impactCounters.Stagger, 0.0);
			case ImpactMetric.Kills:
				return Safe(impactCounters.WeightedKills, 0.0);
			case ImpactMetric.Revives:
				return (impactCounters.Revives > 0) ? impactCounters.Revives : 0;
			case ImpactMetric.WeakspotRate:
				if (player.Hits <= 0)
				{
					return 0.0;
				}
				return Clamp01((double)player.WeakspotHits / (double)player.Hits);
			case ImpactMetric.Accuracy:
				if (player.ShotsFired <= 0)
				{
					return 0.0;
				}
				return Clamp01((double)player.Hits / (double)player.ShotsFired);
			case ImpactMetric.SupplyEfficiency:
				return impactCounters.SupplyEfficiency;
			case ImpactMetric.ScanEfficiency:
				return Safe(impactCounters.ScanEfficiency, 0.0);
			case ImpactMetric.LoneSurvivor:
				return (impactCounters.LoneSurvivor > 0) ? impactCounters.LoneSurvivor : 0;
			case ImpactMetric.Downs:
				return (impactCounters.Downs > 0) ? impactCounters.Downs : 0;
			default:
				return 0.0;
			}
		}

		public static bool HasValue(ImpactInput player, ImpactMetric metric)
		{
			if (player == null)
			{
				return false;
			}
			ImpactCounters impactCounters = player.Counters ?? new ImpactCounters();
			return metric switch
			{
				ImpactMetric.WeakspotRate => player.Hits > 0, 
				ImpactMetric.Accuracy => player.ShotsFired > 0, 
				ImpactMetric.SupplyEfficiency => impactCounters.SupplyUses > 0, 
				ImpactMetric.ScanEfficiency => impactCounters.ScanUses > 0, 
				_ => true, 
			};
		}

		public static ImpactReport Evaluate(IReadOnlyList<ImpactInput> squad, double[] weights, double firepowerBonus, double utilityBonus, bool loneSurvivorCounts = true)
		{
			double[] array = Normalize(weights);
			if (squad == null)
			{
				squad = new List<ImpactInput>();
			}
			ImpactReport impactReport = new ImpactReport(array, Safe(firepowerBonus, 1.0), Safe(utilityBonus, 1.0));
			int count = squad.Count;
			int num = Order.Length;
			double[][] array2 = new double[count][];
			for (int i = 0; i < count; i++)
			{
				array2[i] = new double[num];
				for (int j = 0; j < num; j++)
				{
					array2[i][j] = RawValue(squad[i], Order[j]);
				}
			}
			if (!loneSurvivorCounts)
			{
				int num2 = 8;
				for (int k = 0; k < count; k++)
				{
					array2[k][num2] = 0.0;
				}
			}
			for (int l = 0; l < num; l++)
			{
				double num3 = 0.0;
				double num4 = double.MaxValue;
				for (int m = 0; m < count; m++)
				{
					double val = array2[m][l];
					num3 = Math.Max(num3, val);
					num4 = Math.Min(num4, val);
				}
				if (count == 0 || !double.IsFinite(num4))