Decompiled source of TowerStatsMod v1.0.10

TowerStatsMod.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;

[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("TowerStatsMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.10.0")]
[assembly: AssemblyInformationalVersion("1.0.10+ded0321878575e1e2dd3053142a71cc979c4a6c1")]
[assembly: AssemblyProduct("TowerStatsMod")]
[assembly: AssemblyTitle("TowerStatsMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.10.0")]
[module: UnverifiableCode]
[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 TowerStatsMod
{
	internal static class Patches
	{
		public static bool IsTower(Unit? unit)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			if ((Object)(object)unit == (Object)null || !unit.isBuilding)
			{
				return false;
			}
			if ((Object)(object)unit.playerBuilding != (Object)null)
			{
				HouseType houseType = unit.playerBuilding.HouseType;
				if ((int)houseType == 3 || (int)houseType == 9 || (int)houseType == 10 || (int)houseType == 17)
				{
					return true;
				}
				return false;
			}
			try
			{
				if ((Object)(object)((Component)unit).GetComponent<AutoTower>() != (Object)null)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public static TowerStatsComponent? GetOrCreateTowerStats(Unit unit, bool resetStats = false)
		{
			if ((Object)(object)unit == (Object)null || !IsTower(unit))
			{
				return null;
			}
			TowerStatsComponent component = ((Component)unit).GetComponent<TowerStatsComponent>();
			if ((Object)(object)component != (Object)null)
			{
				if (resetStats)
				{
					component.ResetStats();
				}
				return component;
			}
			try
			{
				TowerStatsComponent towerStatsComponent = ((Component)unit).gameObject.AddComponent<TowerStatsComponent>();
				towerStatsComponent.Init(unit);
				if (resetStats)
				{
					towerStatsComponent.ResetStats();
				}
				return towerStatsComponent;
			}
			catch
			{
				return null;
			}
		}

		public static void EnsureComponentAttachedToBuilding(PlayerBuilding? building, bool resetStats = false)
		{
			if (!((Object)(object)building == (Object)null))
			{
				Unit val = building.Owner;
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)building).GetComponent<Unit>();
				}
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)building).GetComponentInParent<Unit>();
				}
				if ((Object)(object)val != (Object)null && IsTower(val))
				{
					GetOrCreateTowerStats(val, resetStats);
				}
			}
		}

		public static void EnsureComponentAttachedToSpot(BuildingSpot? spot, bool resetStats = false)
		{
			if (!((Object)(object)spot == (Object)null) && (Object)(object)spot.PlayerBuilding != (Object)null)
			{
				EnsureComponentAttachedToBuilding(spot.PlayerBuilding, resetStats);
			}
		}

		[HarmonyPatch(typeof(SimpleDamageable), "TakeDamage")]
		[HarmonyPostfix]
		private static void SimpleDamageable_TakeDamage_Postfix(SimpleDamageable __instance, float amount, IDamageSource source)
		{
			if (amount <= 0f || source == null)
			{
				return;
			}
			try
			{
				Unit sourceUnit = source.SourceUnit;
				if ((Object)(object)sourceUnit != (Object)null && IsTower(sourceUnit))
				{
					TowerStatsComponent orCreateTowerStats = GetOrCreateTowerStats(sourceUnit);
					if ((Object)(object)orCreateTowerStats != (Object)null)
					{
						orCreateTowerStats.RecordDamage(amount);
					}
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(typeof(BuildingDamageable), "TakeDamage")]
		[HarmonyPostfix]
		private static void BuildingDamageable_TakeDamage_Postfix(BuildingDamageable __instance, float amount, IDamageSource source)
		{
			if (amount <= 0f || source == null)
			{
				return;
			}
			try
			{
				Unit sourceUnit = source.SourceUnit;
				if ((Object)(object)sourceUnit != (Object)null && IsTower(sourceUnit))
				{
					TowerStatsComponent orCreateTowerStats = GetOrCreateTowerStats(sourceUnit);
					if ((Object)(object)orCreateTowerStats != (Object)null)
					{
						orCreateTowerStats.RecordDamage(amount);
					}
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(typeof(DamageBatchManager), "ApplyHitLocally")]
		[HarmonyPostfix]
		private static void DamageBatchManager_ApplyHitLocally_Postfix(ulong targetNetId, float damage, HitResultType type, bool isBuilding)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (damage <= 0f)
			{
				return;
			}
			try
			{
				if (!((Object)(object)NetworkManager.Singleton != (Object)null) || NetworkManager.Singleton.SpawnManager == null || !NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetNetId, out var value) || !((Object)(object)value != (Object)null))
				{
					return;
				}
				Unit val = FindNearestTower(((Component)value).transform.position);
				if ((Object)(object)val != (Object)null)
				{
					TowerStatsComponent orCreateTowerStats = GetOrCreateTowerStats(val);
					if ((Object)(object)orCreateTowerStats != (Object)null)
					{
						orCreateTowerStats.RecordDamage(damage);
					}
					Unit component = ((Component)value).GetComponent<Unit>();
					if ((Object)(object)component != (Object)null && (component.IsDead || (component.Damageable != null && component.Damageable.CurrentHealth <= 0f)))
					{
						orCreateTowerStats?.RecordKill();
					}
				}
			}
			catch
			{
			}
		}

		private static Unit? FindNearestTower(Vector3 targetPos)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			float num = 1600f;
			Unit result = null;
			IReadOnlyList<TowerStatsComponent> activeTowers = TowerStatsManager.ActiveTowers;
			for (int i = 0; i < activeTowers.Count; i++)
			{
				TowerStatsComponent towerStatsComponent = activeTowers[i];
				if ((Object)(object)towerStatsComponent == (Object)null)
				{
					continue;
				}
				Unit component = ((Component)towerStatsComponent).GetComponent<Unit>();
				if ((Object)(object)component != (Object)null && ((Component)component).gameObject.activeInHierarchy)
				{
					Vector3 val = ((Component)component).transform.position - targetPos;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = component;
					}
				}
			}
			return result;
		}

		[HarmonyPatch(typeof(PlayerStatisticsManager), "OnUnitKilled")]
		[HarmonyPostfix]
		private static void PlayerStatisticsManager_OnUnitKilled_Postfix(Unit victim, Unit killer)
		{
			if ((Object)(object)killer == (Object)null)
			{
				return;
			}
			try
			{
				if (IsTower(killer))
				{
					TowerStatsComponent orCreateTowerStats = GetOrCreateTowerStats(killer);
					if ((Object)(object)orCreateTowerStats != (Object)null)
					{
						orCreateTowerStats.RecordKill();
					}
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch(typeof(PlayerBuilding), "OnNetworkSpawn")]
		[HarmonyPostfix]
		private static void PlayerBuilding_OnNetworkSpawn_Postfix(PlayerBuilding __instance)
		{
			EnsureComponentAttachedToBuilding(__instance, resetStats: true);
		}

		[HarmonyPatch(typeof(BuildingSpot), "OnEnable")]
		[HarmonyPostfix]
		private static void BuildingSpot_OnEnable_Postfix(BuildingSpot __instance)
		{
			EnsureComponentAttachedToSpot(__instance, resetStats: true);
		}

		[HarmonyPatch(typeof(BuildingDamageable), "Initialize")]
		[HarmonyPostfix]
		private static void BuildingDamageable_Initialize_Postfix(BuildingDamageable __instance, Unit owner)
		{
			if ((Object)(object)owner != (Object)null && IsTower(owner))
			{
				GetOrCreateTowerStats(owner, resetStats: true);
			}
		}
	}
	[BepInPlugin("com.antigravity.towerstatsmod", "Tower Stats Mod", "1.0.10")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.antigravity.towerstatsmod";

		public const string PluginName = "Tower Stats Mod";

		public const string PluginVersion = "1.0.10";

		private Harmony _harmony;

		public static Plugin Instance { get; private set; } = null;

		internal static ManualLogSource Log { get; private set; } = null;

		public static bool IsModEnabled { get; private set; } = true;

		public static ConfigEntry<KeyboardShortcut> ToggleKey { get; private set; } = null;

		public static ConfigEntry<float> DpsWindowSeconds { get; private set; } = null;

		public static ConfigEntry<float> ShowRadius { get; private set; } = null;

		public static ConfigEntry<float> HeightOffset { get; private set; } = null;

		public static ConfigEntry<float> UiScale { get; private set; } = null;

		public static ConfigEntry<int> FontSize { get; private set; } = null;

		public static ConfigEntry<bool> RenderThrough { get; private set; } = null;

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			_harmony = new Harmony("com.antigravity.towerstatsmod");
			_harmony.PatchAll(typeof(Patches));
			Log.LogInfo((object)"\ud83c\udff0 Tower Stats Mod v1.0.10 initialized! Press F6 to toggle stats display.");
		}

		private void Update()
		{
			//IL_0005: 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)
			KeyboardShortcut value = ToggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				IsModEnabled = !IsModEnabled;
				Log.LogInfo((object)("[TowerStatsMod] Tower stats display toggled " + (IsModEnabled ? "ON" : "OFF")));
			}
		}

		private void BindConfig()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "ToggleKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Key to toggle tower stats display (F6).");
			DpsWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "DpsWindowSeconds", 5f, "Time window in seconds to calculate rolling current DPS.");
			ShowRadius = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "ShowRadius", 32f, "Maximum distance (in units) from player to show tower stats.");
			HeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Appearance", "HeightOffset", 4f, "Height position offset above tower base.");
			UiScale = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Appearance", "UiScale", 0.019f, "Overall scale of the overhead badge.");
			FontSize = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Appearance", "FontSize", 16, "Font size of the kills and DPS text.");
			RenderThrough = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Appearance", "RenderThrough", true, "Render text through/on top of tower geometry (ZTest Always).");
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
	public sealed class TowerStatsComponent : MonoBehaviour
	{
		private struct DamageSample
		{
			public float Time;

			public float Amount;
		}

		private Unit? _unit;

		private readonly Queue<DamageSample> _rollingSamples = new Queue<DamageSample>(128);

		private Canvas? _canvas;

		private RectTransform? _canvasRT;

		private Text? _killsText;

		private Text? _dpsText;

		private Image? _bgImage;

		private Camera? _cam;

		private static Material? _alwaysOnTopTextMaterial;

		private static Material? _alwaysOnTopImageMaterial;

		private static PlayerInteract? _staticLocalPlayer;

		private static float _staticLastPlayerCheckTime;

		private bool _uiInitialized;

		private float _nextUIUpdate;

		private float _lastRenderedDPS = -1f;

		private int _lastRenderedKills = -1;

		public float TotalDamage { get; private set; }

		public int Kills { get; private set; }

		public float CurrentDPS { get; private set; }

		public static Material AlwaysOnTopTextMaterial
		{
			get
			{
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Expected O, but got Unknown
				if ((Object)(object)_alwaysOnTopTextMaterial == (Object)null)
				{
					try
					{
						Shader val = Shader.Find("GUI/Text Shader") ?? Shader.Find("UI/Default");
						if ((Object)(object)val != (Object)null)
						{
							_alwaysOnTopTextMaterial = new Material(val);
							((Object)_alwaysOnTopTextMaterial).name = "TowerStatsTextOverlayMaterial";
							_alwaysOnTopTextMaterial.SetInt("unity_GUIZTestMode", 8);
							_alwaysOnTopTextMaterial.SetInt("_ZTest", 8);
							_alwaysOnTopTextMaterial.renderQueue = 3000;
						}
					}
					catch
					{
					}
				}
				return _alwaysOnTopTextMaterial;
			}
		}

		public static Material AlwaysOnTopImageMaterial
		{
			get
			{
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Expected O, but got Unknown
				if ((Object)(object)_alwaysOnTopImageMaterial == (Object)null)
				{
					try
					{
						Shader val = Shader.Find("UI/Default") ?? Shader.Find("Sprites/Default");
						if ((Object)(object)val != (Object)null)
						{
							_alwaysOnTopImageMaterial = new Material(val);
							((Object)_alwaysOnTopImageMaterial).name = "TowerStatsImageOverlayMaterial";
							_alwaysOnTopImageMaterial.SetInt("unity_GUIZTestMode", 8);
							_alwaysOnTopImageMaterial.SetInt("_ZTest", 8);
							_alwaysOnTopImageMaterial.renderQueue = 3000;
						}
					}
					catch
					{
					}
				}
				return _alwaysOnTopImageMaterial;
			}
		}

		public void Init(Unit unit)
		{
			_unit = unit;
		}

		public void ResetStats()
		{
			TotalDamage = 0f;
			Kills = 0;
			CurrentDPS = 0f;
			_rollingSamples.Clear();
			_lastRenderedDPS = -1f;
			_lastRenderedKills = -1;
			UpdateUIContent();
		}

		private void Start()
		{
			BuildUI();
			_uiInitialized = true;
		}

		private void OnEnable()
		{
			if (_uiInitialized && ((Object)(object)_canvas == (Object)null || (Object)(object)_killsText == (Object)null))
			{
				BuildUI();
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_canvas != (Object)null && (Object)(object)((Component)_canvas).gameObject != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvas).gameObject);
			}
		}

		public void RecordDamage(float amount)
		{
			if (!(amount <= 0f))
			{
				TotalDamage += amount;
				_rollingSamples.Enqueue(new DamageSample
				{
					Time = Time.time,
					Amount = amount
				});
			}
		}

		public void RecordKill(int count = 1)
		{
			if (count > 0)
			{
				Kills += count;
			}
		}

		private void Update()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsModEnabled)
			{
				return;
			}
			if ((Object)(object)_canvas == (Object)null || (Object)(object)_canvasRT == (Object)null || (Object)(object)_killsText == (Object)null || (Object)(object)_dpsText == (Object)null)
			{
				BuildUI();
				if ((Object)(object)_canvas == (Object)null || (Object)(object)_canvasRT == (Object)null)
				{
					return;
				}
			}
			PlayerInteract localPlayer = GetLocalPlayer();
			float value = Plugin.ShowRadius.Value;
			if ((Object)(object)localPlayer != (Object)null && (Object)(object)((Component)localPlayer).gameObject != (Object)null && ((Component)localPlayer).gameObject.activeInHierarchy && value > 0f && Vector3.Distance(((Component)this).transform.position, ((Component)localPlayer).transform.position) > value)
			{
				if (((Component)_canvas).gameObject.activeSelf)
				{
					((Component)_canvas).gameObject.SetActive(false);
				}
				return;
			}
			if (!((Component)_canvas).gameObject.activeSelf)
			{
				((Component)_canvas).gameObject.SetActive(true);
			}
			if ((Object)(object)_cam == (Object)null || !((Behaviour)_cam).isActiveAndEnabled)
			{
				_cam = Camera.main;
			}
			if ((Object)(object)_cam != (Object)null)
			{
				Vector3 val = ((Component)_canvas).transform.position - ((Component)_cam).transform.position;
				if (val != Vector3.zero)
				{
					((Component)_canvas).transform.rotation = Quaternion.LookRotation(val);
				}
			}
			float value2 = Plugin.DpsWindowSeconds.Value;
			float num = Time.time - value2;
			while (_rollingSamples.Count > 0 && _rollingSamples.Peek().Time < num)
			{
				_rollingSamples.Dequeue();
			}
			float num2 = 0f;
			foreach (DamageSample rollingSample in _rollingSamples)
			{
				num2 += rollingSample.Amount;
			}
			CurrentDPS = ((value2 > 0.1f) ? (num2 / value2) : 0f);
			if (Time.time >= _nextUIUpdate)
			{
				_nextUIUpdate = Time.time + 0.1f;
				UpdateUIContent();
			}
		}

		private void BuildUI()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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_0131: Expected O, but got Unknown
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Expected O, but got Unknown
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)this).transform.Find("TowerStatsCanvas");
			if ((Object)(object)val != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
			}
			GameObject val2 = new GameObject("TowerStatsCanvas");
			val2.transform.SetParent(((Component)this).transform, false);
			_canvas = val2.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)2;
			_canvas.overrideSorting = true;
			_canvas.sortingOrder = 30000;
			_canvasRT = val2.GetComponent<RectTransform>();
			_canvasRT.sizeDelta = new Vector2(100f, 52f);
			((Transform)_canvasRT).localScale = Vector3.one * Plugin.UiScale.Value;
			((Transform)_canvasRT).localPosition = new Vector3(0f, Plugin.HeightOffset.Value, 0f);
			_bgImage = val2.AddComponent<Image>();
			((Graphic)_bgImage).color = new Color(0.04f, 0.06f, 0.1f, 0.9f);
			Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf");
			int value = Plugin.FontSize.Value;
			GameObject val3 = new GameObject("KillsLabel");
			val3.transform.SetParent(val2.transform, false);
			_killsText = val3.AddComponent<Text>();
			_killsText.font = builtinResource;
			_killsText.fontSize = value;
			_killsText.fontStyle = (FontStyle)1;
			_killsText.alignment = (TextAnchor)4;
			((Graphic)_killsText).color = new Color(0.95f, 0.95f, 0.95f, 1f);
			RectTransform component = val3.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0.5f);
			component.anchorMax = new Vector2(1f, 1f);
			component.offsetMin = new Vector2(2f, 0f);
			component.offsetMax = new Vector2(-2f, 0f);
			GameObject val4 = new GameObject("DPSLabel");
			val4.transform.SetParent(val2.transform, false);
			_dpsText = val4.AddComponent<Text>();
			_dpsText.font = builtinResource;
			_dpsText.fontSize = value;
			_dpsText.fontStyle = (FontStyle)1;
			_dpsText.alignment = (TextAnchor)4;
			((Graphic)_dpsText).color = new Color(0.35f, 0.85f, 1f, 1f);
			RectTransform component2 = val4.GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(0f, 0f);
			component2.anchorMax = new Vector2(1f, 0.5f);
			component2.offsetMin = new Vector2(2f, 0f);
			component2.offsetMax = new Vector2(-2f, 0f);
			ApplyOverlayMaterials();
			UpdateUIContent();
		}

		private void ApplyOverlayMaterials()
		{
			if (!Plugin.RenderThrough.Value)
			{
				return;
			}
			Material alwaysOnTopImageMaterial = AlwaysOnTopImageMaterial;
			if ((Object)(object)alwaysOnTopImageMaterial != (Object)null && (Object)(object)_bgImage != (Object)null)
			{
				((Graphic)_bgImage).material = alwaysOnTopImageMaterial;
			}
			Material alwaysOnTopTextMaterial = AlwaysOnTopTextMaterial;
			if ((Object)(object)alwaysOnTopTextMaterial != (Object)null)
			{
				if ((Object)(object)_killsText != (Object)null)
				{
					((Graphic)_killsText).material = alwaysOnTopTextMaterial;
				}
				if ((Object)(object)_dpsText != (Object)null)
				{
					((Graphic)_dpsText).material = alwaysOnTopTextMaterial;
				}
			}
		}

		private void UpdateUIContent()
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_killsText == (Object)null) && !((Object)(object)_dpsText == (Object)null) && !((Object)(object)_canvasRT == (Object)null) && (!(Mathf.Abs(CurrentDPS - _lastRenderedDPS) < 0.1f) || Kills != _lastRenderedKills))
			{
				_lastRenderedDPS = CurrentDPS;
				_lastRenderedKills = Kills;
				int value = Plugin.FontSize.Value;
				if (_killsText.fontSize != value)
				{
					_killsText.fontSize = value;
				}
				if (_dpsText.fontSize != value)
				{
					_dpsText.fontSize = value;
				}
				ApplyOverlayMaterials();
				string text = FormatNumber(CurrentDPS);
				string text2 = Kills.ToString(CultureInfo.InvariantCulture);
				_killsText.text = "⚔ Kills: " + text2;
				_dpsText.text = "⚡ DPS: " + text;
				float num = Mathf.Clamp(Mathf.Max(_killsText.preferredWidth, _dpsText.preferredWidth) + 20f, 75f, 320f);
				if (Mathf.Abs(_canvasRT.sizeDelta.x - num) > 1f)
				{
					_canvasRT.sizeDelta = new Vector2(num, 52f);
				}
			}
		}

		public static string FormatNumber(float val)
		{
			if (val < 0.1f)
			{
				return "0";
			}
			if (val < 1000f)
			{
				return val.ToString("F0", CultureInfo.InvariantCulture);
			}
			if (val < 1000000f)
			{
				return (val / 1000f).ToString("F1", CultureInfo.InvariantCulture) + "k";
			}
			return (val / 1000000f).ToString("F2", CultureInfo.InvariantCulture) + "M";
		}

		private static PlayerInteract? GetLocalPlayer()
		{
			if ((Object)(object)_staticLocalPlayer != (Object)null && (Object)(object)((Component)_staticLocalPlayer).gameObject != (Object)null && ((Component)_staticLocalPlayer).gameObject.activeInHierarchy)
			{
				return _staticLocalPlayer;
			}
			if (Time.time - _staticLastPlayerCheckTime < 0.5f)
			{
				return _staticLocalPlayer;
			}
			_staticLastPlayerCheckTime = Time.time;
			try
			{
				PlayerInteract[] array = Object.FindObjectsOfType<PlayerInteract>();
				if (array != null)
				{
					PlayerInteract[] array2 = array;
					foreach (PlayerInteract val in array2)
					{
						if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy && ((NetworkBehaviour)val).IsOwner)
						{
							_staticLocalPlayer = val;
							return _staticLocalPlayer;
						}
					}
				}
			}
			catch
			{
			}
			return _staticLocalPlayer;
		}
	}
	public static class TowerStatsManager
	{
		private static readonly List<TowerStatsComponent> _activeTowers = new List<TowerStatsComponent>();

		public static IReadOnlyList<TowerStatsComponent> ActiveTowers => _activeTowers;

		public static void RegisterTower(TowerStatsComponent tower)
		{
			if ((Object)(object)tower != (Object)null && !_activeTowers.Contains(tower))
			{
				_activeTowers.Add(tower);
			}
		}

		public static void UnregisterTower(TowerStatsComponent tower)
		{
			if ((Object)(object)tower != (Object)null)
			{
				_activeTowers.Remove(tower);
			}
		}

		public static void Clear()
		{
			_activeTowers.Clear();
		}
	}
}