Decompiled source of CombatPanel v1.12.1
BepInEx/plugins/GTFOCombatPanel/GTFOCombatPanel.dll
Decompiled 9 hours ago
The result has been truncated due to the large size, download it to view full contents!
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))