Decompiled source of EndGameStats v0.4.0

BepInEx/plugins/EndGameStats/EndGameStats.Core.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EndGameStats.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EndGameStats.Core")]
[assembly: AssemblyTitle("EndGameStats.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EndGameStats.Core
{
	public sealed class PlayerRunStats
	{
		public string PlayerId { get; }

		public string DisplayName { get; private set; }

		public int Deaths { get; private set; }

		public int TeammatesRescued { get; private set; }

		public int EnemyKills { get; private set; }

		public double HaulWork { get; private set; }

		public double ExtractedValue { get; private set; }

		public double ValuableDamage { get; private set; }

		public PlayerRunStats(string playerId, string displayName)
		{
			if (string.IsNullOrWhiteSpace(playerId))
			{
				throw new ArgumentException("A stable player ID is required.", "playerId");
			}
			PlayerId = playerId;
			DisplayName = displayName ?? string.Empty;
		}

		public void Rename(string displayName)
		{
			DisplayName = displayName ?? string.Empty;
		}

		public void RecordDeath()
		{
			Deaths++;
		}

		public void RecordRescue()
		{
			TeammatesRescued++;
		}

		public void RecordEnemyKill()
		{
			EnemyKills++;
		}

		public void AddHaulWork(double currentValue, double distanceMetres, int grabberCount = 1)
		{
			if (!IsFiniteNonNegative(currentValue))
			{
				throw new ArgumentOutOfRangeException("currentValue");
			}
			if (!IsFiniteNonNegative(distanceMetres))
			{
				throw new ArgumentOutOfRangeException("distanceMetres");
			}
			if (grabberCount < 1)
			{
				throw new ArgumentOutOfRangeException("grabberCount");
			}
			HaulWork += currentValue * distanceMetres / (double)grabberCount;
		}

		public void AddValuableDamage(double valueLost)
		{
			if (!IsFiniteNonNegative(valueLost))
			{
				throw new ArgumentOutOfRangeException("valueLost");
			}
			ValuableDamage += valueLost;
		}

		public void AddExtractedValue(double value)
		{
			if (!IsFiniteNonNegative(value))
			{
				throw new ArgumentOutOfRangeException("value");
			}
			ExtractedValue += value;
		}

		private static bool IsFiniteNonNegative(double value)
		{
			if (!double.IsNaN(value) && !double.IsInfinity(value))
			{
				return value >= 0.0;
			}
			return false;
		}
	}
	public sealed class RunStatsBoard
	{
		private readonly Dictionary<string, PlayerRunStats> _players = new Dictionary<string, PlayerRunStats>(StringComparer.Ordinal);

		public IReadOnlyCollection<PlayerRunStats> Players => _players.Values;

		public double UnattributedValuableDamage { get; private set; }

		public double UnattributedExtractedValue { get; private set; }

		public PlayerRunStats GetOrAddPlayer(string playerId, string displayName)
		{
			if (_players.TryGetValue(playerId, out PlayerRunStats value))
			{
				value.Rename(displayName);
				return value;
			}
			value = new PlayerRunStats(playerId, displayName);
			_players.Add(playerId, value);
			return value;
		}

		public void AddUnattributedDamage(double valueLost)
		{
			if (double.IsNaN(valueLost) || double.IsInfinity(valueLost) || valueLost < 0.0)
			{
				throw new ArgumentOutOfRangeException("valueLost");
			}
			UnattributedValuableDamage += valueLost;
		}

		public void AddUnattributedExtractedValue(double value)
		{
			if (double.IsNaN(value) || double.IsInfinity(value) || value < 0.0)
			{
				throw new ArgumentOutOfRangeException("value");
			}
			UnattributedExtractedValue += value;
		}

		public IReadOnlyList<PlayerRunStats> Leaders(Func<PlayerRunStats, double> value, bool lowerIsBetter = false, Func<PlayerRunStats, bool>? eligible = null)
		{
			PlayerRunStats[] array = _players.Values.Where(eligible ?? ((Func<PlayerRunStats, bool>)((PlayerRunStats _) => true))).ToArray();
			if (array.Length == 0)
			{
				return Array.Empty<PlayerRunStats>();
			}
			double best = (lowerIsBetter ? array.Min(value) : array.Max(value));
			return array.Where((PlayerRunStats player) => NearlyEqual(value(player), best)).OrderBy<PlayerRunStats, string>((PlayerRunStats player) => player.DisplayName, StringComparer.OrdinalIgnoreCase).ToArray();
		}

		private static bool NearlyEqual(double left, double right)
		{
			double num = Math.Max(1.0, Math.Max(Math.Abs(left), Math.Abs(right)));
			return Math.Abs(left - right) <= 1E-09 * num;
		}
	}
}

BepInEx/plugins/EndGameStats/EndGameStats.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using EndGameStats.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EndGameStats")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EndGameStats")]
[assembly: AssemblyTitle("EndGameStats")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EndGameStats
{
	[HarmonyPatch(typeof(ValuableObject), "Start")]
	internal static class ValuableRegistrationPatch
	{
		private static void Postfix(ValuableObject __instance)
		{
			Plugin.Instance.RegisterValuable(__instance);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPlayerAddRPC")]
	internal static class GrabStartedPatch
	{
		private static void Postfix(PhysGrabObject __instance, int photonViewID)
		{
			Plugin.Instance.RecordGrab(__instance, photonViewID, released: false);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPlayerRemoveRPC")]
	internal static class GrabEndedPatch
	{
		private static void Prefix(PhysGrabObject __instance, int photonViewID)
		{
			Plugin.Instance.RecordGrab(__instance, photonViewID, released: true);
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
	internal static class PlayerDeathPatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			Plugin.Instance.RecordDeath(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")]
	internal static class PlayerRevivePatch
	{
		private static void Prefix(PlayerAvatar __instance, bool _revivedByTruck, out PlayerAvatar? __state)
		{
			__state = Plugin.Instance.CaptureRescuer(__instance, _revivedByTruck);
		}

		private static void Postfix(PlayerAvatar? __state)
		{
			Plugin.Instance.RecordRescue(__state);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")]
	internal static class ValuableDamagePatch
	{
		private static void Prefix(PhysGrabObjectImpactDetector __instance, float valueLost, bool _loseValue)
		{
			Plugin.Instance.RecordDamage(__instance, valueLost, _loseValue);
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint), "ExtractionPointSurplus")]
	internal static class SuccessfulExtractionPatch
	{
		private static void Prefix()
		{
			Plugin.Instance.RecordSuccessfulExtraction();
		}
	}
	[HarmonyPatch(typeof(HurtCollider), "EnemyHurt")]
	internal static class WeaponEnemyHitPatch
	{
		private static void Prefix(HurtCollider __instance, Enemy _enemy)
		{
			Plugin.Instance.RecordWeaponEnemyHit(__instance, _enemy);
		}
	}
	[HarmonyPatch(typeof(EnemyHealth), "DeathImpulseRPC")]
	internal static class EnemyDeathPatch
	{
		private static void Postfix(EnemyHealth __instance)
		{
			Plugin.Instance.RecordEnemyDeath(__instance);
		}
	}
	[BepInPlugin("chaun.repo.endgamestats", "End Game Stats", "0.4.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class ValuableSample
		{
			public PlayerAvatar? LastCarrier;

			public float LastCarriedAt = float.NegativeInfinity;

			public ValuableObject Valuable { get; }

			public HashSet<string> Participants { get; } = new HashSet<string>(StringComparer.Ordinal);

			public ValuableSample(ValuableObject valuable)
			{
				Valuable = valuable;
			}
		}

		private sealed class EnemyAttribution
		{
			public PlayerAvatar Player { get; }

			public float At { get; }

			public EnemyAttribution(PlayerAvatar player, float at)
			{
				Player = player;
				At = at;
			}
		}

		public const string PluginGuid = "chaun.repo.endgamestats";

		public const string PluginName = "End Game Stats";

		public const string PluginVersion = "0.4.0";

		private readonly Dictionary<int, ValuableSample> _valuables = new Dictionary<int, ValuableSample>();

		private readonly HashSet<int> _creditedExtractions = new HashSet<int>();

		private readonly HashSet<string> _deadPlayers = new HashSet<string>(StringComparer.Ordinal);

		private readonly Dictionary<int, EnemyAttribution> _enemyAttribution = new Dictionary<int, EnemyAttribution>();

		private readonly HashSet<int> _creditedEnemyKills = new HashSet<int>();

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<float> _cartSampleInterval;

		private ConfigEntry<float> _releaseAttributionSeconds;

		private ConfigEntry<float> _enemyKillAttributionSeconds;

		private ConfigEntry<string> _language;

		private bool _visible;

		private bool _wasInLevel;

		private float _nextSample;

		private Rect _window = new Rect(40f, 60f, 1110f, 420f);

		private Texture2D _panelTexture;

		private Texture2D _screenTexture;

		private Texture2D _borderTexture;

		private Texture2D _rowTexture;

		private Texture2D _scanlineTexture;

		private GUIStyle _windowStyle;

		private Font? _terminalFont;

		internal static Plugin Instance { get; private set; }

		internal RunStatsBoard Board { get; private set; } = new RunStatsBoard();

		private void Awake()
		{
			//IL_0021: 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)
			Instance = this;
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Display", "ToggleKey", new KeyboardShortcut((KeyCode)284, Array.Empty<KeyCode>()), "Open or close the live stats board.");
			_language = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "Language", "Auto", "UI language: Auto, English, or SimplifiedChinese.");
			_cartSampleInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Tracking", "CartSampleIntervalSeconds", 1f, "Low-frequency fallback interval for valuables transported inside carts.");
			_releaseAttributionSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Tracking", "DamageAttributionSeconds", 3f, "Attribute damage to the most recent carrier for this long after release.");
			_enemyKillAttributionSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Tracking", "EnemyKillAttributionSeconds", 10f, "Credit an enemy kill to its most recent player interaction within this many seconds.");
			new Harmony("chaun.repo.endgamestats").PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"End Game Stats 0.4.0 loaded");
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_visible = !_visible;
			}
			bool flag = SafeRunIsLevel();
			if (flag && !_wasInLevel)
			{
				BeginLevel();
			}
			else if (!flag && _wasInLevel)
			{
				_visible = true;
			}
			_wasInLevel = flag;
			if (flag && !(Time.unscaledTime < _nextSample))
			{
				_nextSample = Time.unscaledTime + Mathf.Max(0.25f, _cartSampleInterval.Value);
				SampleCartParticipants();
			}
		}

		private void BeginLevel()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			Board = new RunStatsBoard();
			_valuables.Clear();
			_creditedExtractions.Clear();
			_deadPlayers.Clear();
			_enemyAttribution.Clear();
			_creditedEnemyKills.Clear();
			_visible = false;
			RegisterExistingValuables();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Started a new level stats snapshot");
		}

		private void RegisterExistingValuables()
		{
			if (!Object.op_Implicit((Object)(object)ValuableDirector.instance))
			{
				return;
			}
			foreach (ValuableObject valuable in ValuableDirector.instance.valuableList)
			{
				RegisterValuable(valuable);
			}
		}

		internal void RegisterValuable(ValuableObject? valuable)
		{
			if (valuable != null && Object.op_Implicit((Object)(object)valuable) && !_valuables.ContainsKey(((Object)valuable).GetInstanceID()))
			{
				_valuables.Add(((Object)valuable).GetInstanceID(), new ValuableSample(valuable));
			}
		}

		private void SampleCartParticipants()
		{
			RegisterExistingValuables();
			if (Object.op_Implicit((Object)(object)GameDirector.instance))
			{
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					GetPlayer(player);
				}
			}
			KeyValuePair<int, ValuableSample>[] array = _valuables.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<int, ValuableSample> keyValuePair = array[i];
				ValuableObject valuable = keyValuePair.Value.Valuable;
				if (!Object.op_Implicit((Object)(object)valuable))
				{
					_valuables.Remove(keyValuePair.Key);
					continue;
				}
				PhysGrabObjectImpactDetector component = ((Component)valuable).GetComponent<PhysGrabObjectImpactDetector>();
				if (Object.op_Implicit((Object)(object)component) && component.inCart)
				{
					PhysGrabCart value = Traverse.Create((object)component).Field("currentCart").GetValue<PhysGrabCart>();
					if (Object.op_Implicit((Object)(object)value))
					{
						AddParticipants(keyValuePair.Value, GetGrabbers(((Component)value).GetComponent<PhysGrabObject>()));
					}
				}
			}
		}

		internal void RecordGrab(PhysGrabObject grabObject, int grabberPhotonViewId, bool released)
		{
			PhotonView val = PhotonView.Find(grabberPhotonViewId);
			PhysGrabber val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<PhysGrabber>() : null);
			if (val2 == null || !Object.op_Implicit((Object)(object)val2) || val2.playerAvatar == null || !Object.op_Implicit((Object)(object)val2.playerAvatar))
			{
				return;
			}
			ValuableObject component = ((Component)grabObject).GetComponent<ValuableObject>();
			if (Object.op_Implicit((Object)(object)component))
			{
				RegisterValuable(component);
				ValuableSample valuableSample = _valuables[((Object)component).GetInstanceID()];
				PlayerRunStats player = GetPlayer(val2.playerAvatar);
				if (player != null)
				{
					valuableSample.Participants.Add(player.PlayerId);
				}
				valuableSample.LastCarrier = val2.playerAvatar;
				if (released)
				{
					valuableSample.LastCarriedAt = Time.unscaledTime;
				}
				return;
			}
			PhysGrabCart component2 = ((Component)grabObject).GetComponent<PhysGrabCart>();
			if (!Object.op_Implicit((Object)(object)component2))
			{
				EnemyRigidbody val3 = ((Component)grabObject).GetComponent<EnemyRigidbody>() ?? ((Component)grabObject).GetComponentInParent<EnemyRigidbody>();
				EnemyHealth val4 = (Object.op_Implicit((Object)(object)val3) ? ((Component)val3).GetComponent<EnemyHealth>() : null);
				if (Object.op_Implicit((Object)(object)val4))
				{
					RememberEnemyInteractor(val4, val2.playerAvatar);
				}
			}
			else
			{
				if (released)
				{
					return;
				}
				foreach (KeyValuePair<int, ValuableSample> valuable2 in _valuables)
				{
					ValuableObject valuable = valuable2.Value.Valuable;
					if (Object.op_Implicit((Object)(object)valuable))
					{
						PhysGrabObjectImpactDetector component3 = ((Component)valuable).GetComponent<PhysGrabObjectImpactDetector>();
						if ((Object)(object)(Object.op_Implicit((Object)(object)component3) ? Traverse.Create((object)component3).Field("currentCart").GetValue<PhysGrabCart>() : null) == (Object)(object)component2)
						{
							AddParticipants(valuable2.Value, (IEnumerable<PhysGrabber>)(object)new PhysGrabber[1] { val2 });
						}
					}
				}
			}
		}

		internal void RecordDeath(PlayerAvatar avatar)
		{
			PlayerRunStats player = GetPlayer(avatar);
			if (player != null && _deadPlayers.Add(player.PlayerId))
			{
				player.RecordDeath();
			}
		}

		internal PlayerAvatar? CaptureRescuer(PlayerAvatar revived, bool revivedByTruck)
		{
			PlayerRunStats player = GetPlayer(revived);
			if (player == null || !_deadPlayers.Remove(player.PlayerId))
			{
				return null;
			}
			if (revivedByTruck)
			{
				return null;
			}
			PlayerDeathHead value = Traverse.Create((object)revived).Field("playerDeathHead").GetValue<PlayerDeathHead>();
			if (!Object.op_Implicit((Object)(object)value))
			{
				return null;
			}
			return (from grabber in GetGrabbers(Traverse.Create((object)value).Field("physGrabObject").GetValue<PhysGrabObject>())
				select grabber.playerAvatar).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar val) => Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)revived));
		}

		internal void RecordRescue(PlayerAvatar? rescuer)
		{
			if (Object.op_Implicit((Object)(object)rescuer))
			{
				PlayerRunStats? player = GetPlayer(rescuer);
				if (player != null)
				{
					player.RecordRescue();
				}
			}
		}

		internal void RecordDamage(PhysGrabObjectImpactDetector detector, float valueLost, bool loseValue)
		{
			if (!loseValue || valueLost <= 0f)
			{
				return;
			}
			ValuableObject component = ((Component)detector).GetComponent<ValuableObject>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			PlayerAvatar val = null;
			List<PhysGrabber> grabbers = GetGrabbers(((Component)component).GetComponent<PhysGrabObject>());
			ValuableSample value;
			if (grabbers.Count > 0)
			{
				val = grabbers[0].playerAvatar;
			}
			else if (_valuables.TryGetValue(((Object)component).GetInstanceID(), out value) && Time.unscaledTime - value.LastCarriedAt <= _releaseAttributionSeconds.Value)
			{
				val = value.LastCarrier;
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				PlayerRunStats? player = GetPlayer(val);
				if (player != null)
				{
					player.AddValuableDamage((double)valueLost);
				}
			}
			else
			{
				Board.AddUnattributedDamage((double)valueLost);
			}
		}

		internal void RecordSuccessfulExtraction()
		{
			if (!Object.op_Implicit((Object)(object)RoundDirector.instance))
			{
				return;
			}
			GameObject[] array = RoundDirector.instance.dollarHaulList.ToArray();
			foreach (GameObject val in array)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				ValuableObject component = val.GetComponent<ValuableObject>();
				if (!Object.op_Implicit((Object)(object)component) || !_creditedExtractions.Add(((Object)component).GetInstanceID()))
				{
					continue;
				}
				float num = ReadFloat(component, "dollarValueCurrent");
				if (!_valuables.TryGetValue(((Object)component).GetInstanceID(), out ValuableSample value) || value.Participants.Count == 0)
				{
					Board.AddUnattributedExtractedValue((double)num);
					continue;
				}
				float num2 = num / (float)value.Participants.Count;
				foreach (string playerId in value.Participants)
				{
					PlayerRunStats val2 = ((IEnumerable<PlayerRunStats>)Board.Players).FirstOrDefault((Func<PlayerRunStats, bool>)((PlayerRunStats candidate) => candidate.PlayerId == playerId));
					if (val2 != null)
					{
						val2.AddExtractedValue((double)num2);
					}
					else
					{
						Board.AddUnattributedExtractedValue((double)num2);
					}
				}
			}
		}

		internal void RecordWeaponEnemyHit(HurtCollider hurtCollider, Enemy enemy)
		{
			if (!Object.op_Implicit((Object)(object)enemy) || hurtCollider.deathPit)
			{
				return;
			}
			EnemyHealth component = ((Component)enemy).GetComponent<EnemyHealth>();
			if (Object.op_Implicit((Object)(object)component))
			{
				PhysGrabObject componentInParent = ((Component)hurtCollider).GetComponentInParent<PhysGrabObject>();
				if (Object.op_Implicit((Object)(object)componentInParent))
				{
					PlayerAvatar value = Traverse.Create((object)componentInParent).Field("lastPlayerGrabbing").GetValue<PlayerAvatar>();
					RememberEnemyInteractor(component, value);
				}
			}
		}

		internal void RecordEnemyDeath(EnemyHealth enemyHealth)
		{
			if (!Object.op_Implicit((Object)(object)enemyHealth) || !_creditedEnemyKills.Add(((Object)enemyHealth).GetInstanceID()))
			{
				return;
			}
			PlayerAvatar val = null;
			if (_enemyAttribution.TryGetValue(((Object)enemyHealth).GetInstanceID(), out EnemyAttribution value) && Time.unscaledTime - value.At <= Mathf.Max(0f, _enemyKillAttributionSeconds.Value))
			{
				val = value.Player;
			}
			if (val == null)
			{
				val = Traverse.Create((object)enemyHealth).Field("onObjectHurtPlayer").GetValue<PlayerAvatar>();
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				PlayerRunStats? player = GetPlayer(val);
				if (player != null)
				{
					player.RecordEnemyKill();
				}
			}
		}

		private void RememberEnemyInteractor(EnemyHealth enemyHealth, PlayerAvatar? player)
		{
			if (Object.op_Implicit((Object)(object)enemyHealth) && Object.op_Implicit((Object)(object)player))
			{
				_enemyAttribution[((Object)enemyHealth).GetInstanceID()] = new EnemyAttribution(player, Time.unscaledTime);
			}
		}

		private void AddParticipants(ValuableSample sample, IEnumerable<PhysGrabber> grabbers)
		{
			foreach (PhysGrabber grabber in grabbers)
			{
				PlayerRunStats player = GetPlayer(grabber.playerAvatar);
				if (player != null)
				{
					sample.Participants.Add(player.PlayerId);
				}
			}
		}

		private PlayerRunStats? GetPlayer(PlayerAvatar? avatar)
		{
			if (!Object.op_Implicit((Object)(object)avatar))
			{
				return null;
			}
			string value = Traverse.Create((object)avatar).Field("playerName").GetValue<string>();
			string text = (Object.op_Implicit((Object)(object)avatar.photonView) ? ("photon:" + avatar.photonView.OwnerActorNr.ToString(CultureInfo.InvariantCulture)) : "local");
			return Board.GetOrAddPlayer(text, string.IsNullOrWhiteSpace(value) ? "Semibot" : value);
		}

		private static List<PhysGrabber> GetGrabbers(PhysGrabObject? grabObject)
		{
			List<PhysGrabber> list = new List<PhysGrabber>();
			if (!Object.op_Implicit((Object)(object)grabObject))
			{
				return list;
			}
			if (!(Traverse.Create((object)grabObject).Field("playerGrabbing").GetValue() is IEnumerable enumerable))
			{
				return list;
			}
			foreach (object item in enumerable)
			{
				PhysGrabber val = (PhysGrabber)((item is PhysGrabber) ? item : null);
				if (val != null && Object.op_Implicit((Object)(object)val))
				{
					list.Add(val);
				}
			}
			return list;
		}

		private static float ReadFloat(object instance, string field)
		{
			return Traverse.Create(instance).Field(field).GetValue<float>();
		}

		private static bool SafeRunIsLevel()
		{
			try
			{
				return SemiFunc.RunIsLevel();
			}
			catch
			{
				return false;
			}
		}

		private void OnGUI()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (_visible)
			{
				if (_windowStyle == null)
				{
					BuildVisualTheme();
				}
				_window = GUI.Window(948210, _window, new WindowFunction(DrawWindow), string.Empty, _windowStyle);
			}
		}

		private void DrawWindow(int id)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Expected O, but got Unknown
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0434: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			DrawTerminalFrame();
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				font = _terminalFont,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4
			};
			val.normal.textColor = new Color(0.52f, 1f, 0.52f);
			GUIStyle val2 = val;
			GUI.Label(new Rect(20f, 5f, ((Rect)(ref _window)).width - 40f, 24f), T("[ CRT-03 // SALVAGE TELEMETRY ]", "[ CRT-03 // 回收作业终端 ]"), val2);
			GUIStyle val3 = new GUIStyle(GUI.skin.label)
			{
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4,
				font = _terminalFont
			};
			val3.normal.textColor = new Color(0.52f, 1f, 0.52f);
			GUIStyle val4 = val3;
			GUIStyle val5 = new GUIStyle(GUI.skin.label)
			{
				alignment = (TextAnchor)4,
				font = _terminalFont
			};
			val5.normal.textColor = new Color(0.38f, 0.86f, 0.42f);
			GUIStyle val6 = val5;
			GUIStyle style = new GUIStyle(val6);
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.Label(T("SYS.READOUT / STATUS: ONLINE", "系统读数 / 状态:在线"), val4, Array.Empty<GUILayoutOption>());
			GUILayout.Space(3f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			Column(T("PLAYER", "玩家"), 190f, val4);
			Column(T("DEATHS", "死亡"), 70f, val4);
			Column(T("RESCUES", "救援"), 70f, val4);
			Column(T("KILLS", "击杀"), 70f, val4);
			Column(T("RECOVERED VALUE", "回收贡献"), 140f, val4);
			Column(T("GOODS DAMAGED", "物品损失"), 140f, val4);
			Column(T("TITLES", "称号"), 360f, val4);
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			foreach (PlayerRunStats item in Board.Players.OrderByDescending((PlayerRunStats p) => p.ExtractedValue))
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				Column(Truncate(item.DisplayName, 20), 190f, val6);
				Column(item.Deaths.ToString(CultureInfo.InvariantCulture), 70f, style);
				Column(item.TeammatesRescued.ToString(CultureInfo.InvariantCulture), 70f, style);
				Column(item.EnemyKills.ToString(CultureInfo.InvariantCulture), 70f, style);
				Column(Dollars(item.ExtractedValue), 140f, style);
				Column(Dollars(item.ValuableDamage), 140f, style);
				GUIStyle val7 = new GUIStyle(val6)
				{
					fontStyle = (FontStyle)1
				};
				val7.normal.textColor = new Color(0.7f, 1f, 0.5f);
				Column(GetTitles(item), 360f, val7);
				GUILayout.EndHorizontal();
			}
			GUILayout.FlexibleSpace();
			GUILayout.Label(T("Unattributed goods damage: " + Dollars(Board.UnattributedValuableDamage), "未归属的物品损失:" + Dollars(Board.UnattributedValuableDamage)), Array.Empty<GUILayoutOption>());
			GUILayout.Label(T("Unattributed recovered value: " + Dollars(Board.UnattributedExtractedValue), "未归属的回收价值:" + Dollars(Board.UnattributedExtractedValue)), Array.Empty<GUILayoutOption>());
			KeyboardShortcut value = _toggleKey.Value;
			string english = $"Press {((KeyboardShortcut)(ref value)).MainKey} to close";
			value = _toggleKey.Value;
			GUILayout.Label(T(english, $"按 {((KeyboardShortcut)(ref value)).MainKey} 关闭"), Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 28f));
		}

		private static string Truncate(string value, int max)
		{
			if (value.Length > max)
			{
				return value.Substring(0, max - 1) + "…";
			}
			return value;
		}

		private static string Dollars(double value)
		{
			return "$" + value.ToString("N0", CultureInfo.InvariantCulture);
		}

		private string GetTitles(PlayerRunStats player)
		{
			List<string> list = new List<string>();
			AddTitle(list, player, "Defective Unit", "残次品", (PlayerRunStats p) => p.Deaths);
			AddTitle(list, player, "Guardian Angel", "再生父母", (PlayerRunStats p) => p.TeammatesRescued);
			AddTitle(list, player, "Physics Exorcist", "物理超度师", (PlayerRunStats p) => p.EnemyKills);
			AddTitle(list, player, "Born to Grind", "天生牛马", (PlayerRunStats p) => p.ExtractedValue);
			AddTitle(list, player, "Financial Liability", "负资产", (PlayerRunStats p) => p.ValuableDamage);
			return string.Join(" / ", list);
		}

		private void AddTitle(List<string> titles, PlayerRunStats player, string englishTitle, string chineseTitle, Func<PlayerRunStats, double> value)
		{
			IReadOnlyList<PlayerRunStats> readOnlyList = Board.Leaders(value, false, (Func<PlayerRunStats, bool>)null);
			if (readOnlyList.Count != 0 && !(value(readOnlyList[0]) <= 0.0) && readOnlyList.Any((PlayerRunStats leader) => leader.PlayerId == player.PlayerId))
			{
				titles.Add(T(englishTitle, chineseTitle));
			}
		}

		private static void Column(string text, float width, GUIStyle style)
		{
			GUILayout.Label(text, style, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) });
		}

		private void BuildVisualTheme()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Expected O, but got Unknown
			//IL_0179: Expected O, but got Unknown
			_terminalFont = Font.CreateDynamicFontFromOSFont((!UseChinese()) ? new string[3] { "Consolas", "Courier New", "Arial" } : new string[3] { "SimSun", "Microsoft YaHei", "Arial" }, 16);
			_panelTexture = SolidTexture(new Color(0.025f, 0.027f, 0.023f, 0.99f));
			_screenTexture = SolidTexture(new Color(0.005f, 0.055f, 0.018f, 0.98f));
			_borderTexture = SolidTexture(new Color(0.12f, 0.16f, 0.11f, 1f));
			_rowTexture = SolidTexture(new Color(0.1f, 0.3f, 0.12f, 0.18f));
			_scanlineTexture = SolidTexture(new Color(0f, 0f, 0f, 0.24f));
			GUIStyle val = new GUIStyle(GUI.skin.window);
			val.normal.background = _panelTexture;
			val.normal.textColor = new Color(0.52f, 1f, 0.52f);
			val.font = _terminalFont;
			val.fontStyle = (FontStyle)1;
			val.alignment = (TextAnchor)1;
			val.padding = new RectOffset(22, 22, 48, 22);
			_windowStyle = val;
		}

		private void DrawTerminalFrame()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			GUI.DrawTexture(new Rect(10f, 27f, ((Rect)(ref _window)).width - 20f, ((Rect)(ref _window)).height - 37f), (Texture)(object)_borderTexture);
			GUI.DrawTexture(new Rect(17f, 34f, ((Rect)(ref _window)).width - 34f, ((Rect)(ref _window)).height - 51f), (Texture)(object)_screenTexture);
			GUI.DrawTexture(new Rect(17f, 34f, ((Rect)(ref _window)).width - 34f, 2f), (Texture)(object)_borderTexture);
			GUI.DrawTexture(new Rect(17f, ((Rect)(ref _window)).height - 19f, ((Rect)(ref _window)).width - 34f, 2f), (Texture)(object)_borderTexture);
			for (float num = 36f; num < ((Rect)(ref _window)).height - 20f; num += 3f)
			{
				GUI.DrawTexture(new Rect(18f, num, ((Rect)(ref _window)).width - 36f, 1f), (Texture)(object)_scanlineTexture);
			}
		}

		private static Texture2D SolidTexture(Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, color);
			val.Apply();
			return val;
		}

		private bool UseChinese()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (_language.Value.Equals("SimplifiedChinese", StringComparison.OrdinalIgnoreCase) || _language.Value.Equals("Chinese", StringComparison.OrdinalIgnoreCase) || _language.Value.Equals("简体中文", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (_language.Value.Equals("English", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString().IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private string T(string english, string chinese)
		{
			if (!UseChinese())
			{
				return english;
			}
			return chinese;
		}
	}
}