Decompiled source of Denis UI Killfeed v1.1.1

plugins/DenisUIKillFeed/DenisUIKillFeed.dll

Decompiled 3 days ago
using System;
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 System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("DenisUIKillFeed")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+0d77aba4ef99a87d6be4daeb81b0de1f2e0c0dca")]
[assembly: AssemblyProduct("DenisUIKillFeed")]
[assembly: AssemblyTitle("DenisUIKillFeed")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 DenisUIKillFeed
{
	internal static class EnemyFeedTracker
	{
		internal static Enemy GetEnemyFromParent(EnemyParent enemyParent)
		{
			try
			{
				object? obj = Plugin.EnemyParentEnemyField?.GetValue(enemyParent);
				return (Enemy)((obj is Enemy) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		internal static bool GetParentSpawned(EnemyParent enemyParent)
		{
			try
			{
				object obj = Plugin.EnemyParentSpawnedField?.GetValue(enemyParent);
				if (obj is bool)
				{
					bool result = (bool)obj;
					if (true)
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return true;
		}

		internal static bool GetEnemyHasHealth(Enemy enemy)
		{
			try
			{
				if ((Object)(object)enemy == (Object)null)
				{
					return false;
				}
				object obj = Plugin.EnemyHasHealthField?.GetValue(enemy);
				if (obj is bool)
				{
					bool result = (bool)obj;
					if (true)
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static EnemyHealth GetEnemyHealth(Enemy enemy)
		{
			try
			{
				object? obj = Plugin.EnemyHealthRefField?.GetValue(enemy);
				return (EnemyHealth)((obj is EnemyHealth) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		internal static int GetEnemyHealthCurrent(EnemyHealth health)
		{
			try
			{
				if ((Object)(object)health == (Object)null)
				{
					return 0;
				}
				object obj = Plugin.EnemyHealthCurrentField?.GetValue(health);
				if (obj is int)
				{
					int result = (int)obj;
					if (true)
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return 0;
		}

		internal static bool GetEnemyHealthDead(EnemyHealth health)
		{
			try
			{
				if ((Object)(object)health == (Object)null)
				{
					return false;
				}
				object obj = Plugin.EnemyHealthDeadField?.GetValue(health);
				if (obj is bool)
				{
					bool result = (bool)obj;
					if (true)
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static float GetMapValueTotal()
		{
			return 0f;
		}

		internal static bool EnemyShouldRequireOrbConfirmation(string enemyName)
		{
			return !Plugin.NoOrbEnemyNames.Contains(enemyName);
		}

		internal static void SeedSeenValuables()
		{
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
			if (array == null)
			{
				return;
			}
			ValuableObject[] array2 = array;
			foreach (ValuableObject val in array2)
			{
				if ((Object)(object)val != (Object)null)
				{
					Plugin.SeenValuableIds.Add(((Object)((Component)val).gameObject).GetInstanceID());
				}
			}
		}

		internal static bool ObserveNewEnemyOrbFor(string enemyName, float mapValueBeforeDeath)
		{
			ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>();
			if (array == null)
			{
				return false;
			}
			bool flag = EnemyShouldRequireOrbConfirmation(enemyName);
			float mapValueTotal = GetMapValueTotal();
			ValuableObject[] array2 = array;
			foreach (ValuableObject val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)((Component)val).gameObject).GetInstanceID();
				if (!Plugin.SeenValuableIds.Contains(instanceID))
				{
					Plugin.SeenValuableIds.Add(instanceID);
					string text = ((Object)((Component)val).gameObject).name ?? string.Empty;
					if (text.IndexOf("Enemy Valuable", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			return !flag && mapValueTotal > mapValueBeforeDeath;
		}

		internal static void ObserveEnemiesFromDirector()
		{
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_0494: Unknown result type (might be due to invalid IL or missing references)
			HashSet<int> seen = new HashSet<int>();
			try
			{
				EnemyDirector instance = EnemyDirector.instance;
				if ((Object)(object)instance == (Object)null || instance.enemiesSpawned == null)
				{
					return;
				}
				foreach (EnemyParent item in instance.enemiesSpawned)
				{
					if ((Object)(object)item == (Object)null)
					{
						continue;
					}
					int instanceID = ((Object)item).GetInstanceID();
					seen.Add(instanceID);
					string text = (string.IsNullOrWhiteSpace(item.enemyName) ? "Enemy" : item.enemyName);
					Enemy enemyFromParent = GetEnemyFromParent(item);
					bool parentSpawned = GetParentSpawned(item);
					EnemyHealth enemyHealth = GetEnemyHealth(enemyFromParent);
					bool flag = (Object)(object)enemyHealth != (Object)null && GetEnemyHasHealth(enemyFromParent);
					int num = (flag ? GetEnemyHealthCurrent(enemyHealth) : 0);
					bool flag2 = flag && (GetEnemyHealthDead(enemyHealth) || num <= 0);
					bool flag3 = flag2 || !parentSpawned;
					if (!Plugin.EnemyStates.TryGetValue(instanceID, out var value))
					{
						Plugin.EnemyStates[instanceID] = new ObservedEnemyState
						{
							EnemyParent = item,
							Name = text,
							WasDead = flag3,
							LastHealth = num,
							FirstSeenAt = Time.time,
							AliveSince = ((!flag3 && parentSpawned) ? Time.time : (-1f)),
							PendingDeathSince = (flag3 ? Time.time : (-1f)),
							DeathAnnounced = false,
							Spawned = parentSpawned,
							HasEnteredStableAlivePhase = false,
							HadAliveHealthSample = (!flag3 && num > 0),
							HadDamageEvidence = false
						};
						continue;
					}
					value.EnemyParent = item;
					value.Name = text;
					value.Spawned = parentSpawned;
					bool flag4 = Time.time - value.FirstSeenAt >= 1f;
					bool flag5 = Time.time - value.FirstSeenAt < 1f;
					if (!flag3 && parentSpawned)
					{
						if (value.AliveSince < 0f)
						{
							value.AliveSince = Time.time;
						}
						if (num > 0)
						{
							value.HadAliveHealthSample = true;
						}
						if (value.LastHealth > 0 && num > 0 && num < value.LastHealth)
						{
							value.HadDamageEvidence = true;
						}
						if (!value.HasEnteredStableAlivePhase && value.AliveSince >= 0f && Time.time - value.AliveSince >= 0.35f)
						{
							value.HasEnteredStableAlivePhase = true;
						}
					}
					bool flag6 = EnemyShouldRequireOrbConfirmation(text);
					bool flag7 = !flag6 && value.HadAliveHealthSample && (flag2 || value.HadDamageEvidence || (value.LastHealth > 0 && num <= 0));
					if (!value.WasDead && flag3)
					{
						if (flag4 && !flag5 && value.HasEnteredStableAlivePhase && (flag6 || flag7))
						{
							value.PendingDeathSince = Time.time;
							value.MapValueBeforeDeath = GetMapValueTotal();
							value.OrbConfirmed = false;
						}
						else
						{
							value.PendingDeathSince = -1f;
							value.OrbConfirmed = false;
						}
					}
					else if (!flag3)
					{
						value.PendingDeathSince = -1f;
						value.DeathAnnounced = false;
						value.OrbConfirmed = false;
					}
					if (!value.DeathAnnounced && flag4 && flag3 && value.PendingDeathSince >= 0f && Time.time - value.PendingDeathSince >= 0.2f)
					{
						if (!value.OrbConfirmed)
						{
							value.OrbConfirmed = ObserveNewEnemyOrbFor(text, value.MapValueBeforeDeath);
						}
						bool flag8 = !flag6 && flag7;
						bool flag9 = value.LastAnnouncementAt <= 0f || Time.time - value.LastAnnouncementAt >= 6f;
						if ((value.OrbConfirmed || flag8) && flag9)
						{
							if (Plugin.ShowEnemyKillsConfig.Value && Time.time - Plugin.LevelStartTime >= 1f)
							{
								Plugin.AddEntry(text + " killed", new Color(0.96f, 0.82f, 0.28f, 1f));
							}
							value.DeathAnnounced = true;
							value.LastAnnouncementAt = Time.time;
						}
					}
					value.WasDead = flag3;
					value.LastHealth = num;
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[DenisUIKillFeed] ObserveEnemiesFromDirector error: {arg}");
			}
			List<int> list = Plugin.EnemyStates.Keys.Where((int id) => !seen.Contains(id)).ToList();
			foreach (int item2 in list)
			{
				ObservedEnemyState observedEnemyState = Plugin.EnemyStates[item2];
				bool flag10 = Time.time - observedEnemyState.FirstSeenAt >= 1f;
				if (!observedEnemyState.DeathAnnounced && flag10 && observedEnemyState.PendingDeathSince >= 0f)
				{
					if (!observedEnemyState.OrbConfirmed)
					{
						observedEnemyState.OrbConfirmed = ObserveNewEnemyOrbFor(observedEnemyState.Name, observedEnemyState.MapValueBeforeDeath);
					}
					bool flag11 = !EnemyShouldRequireOrbConfirmation(observedEnemyState.Name) && observedEnemyState.HadAliveHealthSample && observedEnemyState.HadDamageEvidence;
					bool flag12 = observedEnemyState.LastAnnouncementAt <= 0f || Time.time - observedEnemyState.LastAnnouncementAt >= 6f;
					if ((observedEnemyState.OrbConfirmed || flag11) && flag12)
					{
						if (Plugin.ShowEnemyKillsConfig.Value && Time.time - Plugin.LevelStartTime >= 1f)
						{
							Plugin.AddEntry(observedEnemyState.Name + " killed", new Color(0.96f, 0.82f, 0.28f, 1f));
						}
						observedEnemyState.LastAnnouncementAt = Time.time;
					}
				}
				Plugin.EnemyStates.Remove(item2);
			}
		}
	}
	[HarmonyPatch(typeof(RoundDirector))]
	internal static class KillFeedHud
	{
		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void Update_Postfix()
		{
			bool flag = SemiFunc.RunIsLevel();
			if (flag)
			{
				Plugin.LastLevelActiveAt = Time.time;
			}
			if (!EnsureHud())
			{
				return;
			}
			if (!flag)
			{
				if (Time.time - Plugin.LastLevelActiveAt <= 8f)
				{
					UpdateKillFeedHud();
					return;
				}
				SetInactive();
				Plugin.ResetObservedState();
			}
			else
			{
				PlayerFeedTracker.ObservePlayers();
				EnemyFeedTracker.ObserveEnemiesFromDirector();
				UpdateKillFeedHud();
			}
		}

		internal static bool EnsureHud()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("Game Hud");
			GameObject val2 = GameObject.Find("Tax Haul");
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return false;
			}
			TMP_Text component = val2.GetComponent<TMP_Text>();
			TMP_FontAsset val3 = ((component != null) ? component.font : null);
			if ((Object)(object)val3 == (Object)null)
			{
				return false;
			}
			if ((Object)(object)Plugin.FeedInstance == (Object)null)
			{
				Plugin.FeedInstance = new GameObject("Denis UI Killfeed HUD");
				Plugin.FeedInstance.SetActive(false);
				Plugin.FeedInstance.transform.SetParent(val.transform, false);
				Plugin.FeedText = Plugin.FeedInstance.AddComponent<TextMeshProUGUI>();
				((TMP_Text)Plugin.FeedText).font = val3;
				((TMP_Text)Plugin.FeedText).fontSize = 24f;
				((TMP_Text)Plugin.FeedText).enableWordWrapping = false;
				((TMP_Text)Plugin.FeedText).alignment = (TextAlignmentOptions)260;
				((TMP_Text)Plugin.FeedText).horizontalAlignment = (HorizontalAlignmentOptions)4;
				((TMP_Text)Plugin.FeedText).verticalAlignment = (VerticalAlignmentOptions)256;
				((Graphic)Plugin.FeedText).raycastTarget = false;
				((TMP_Text)Plugin.FeedText).margin = Vector4.zero;
				((TMP_Text)Plugin.FeedText).characterSpacing = 0f;
				((TMP_Text)Plugin.FeedText).wordSpacing = 0f;
				((TMP_Text)Plugin.FeedText).fontStyle = (FontStyles)0;
				ContentSizeFitter val4 = Plugin.FeedInstance.AddComponent<ContentSizeFitter>();
				val4.verticalFit = (FitMode)2;
				val4.horizontalFit = (FitMode)0;
				RectTransform component2 = Plugin.FeedInstance.GetComponent<RectTransform>();
				component2.pivot = new Vector2(1f, 1f);
				component2.anchorMin = new Vector2(1f, 1f);
				component2.anchorMax = new Vector2(1f, 1f);
				component2.sizeDelta = new Vector2(520f, 220f);
				component2.anchoredPosition = new Vector2(-10f, -250f);
			}
			else if ((Object)(object)Plugin.FeedText != (Object)null && (Object)(object)((TMP_Text)Plugin.FeedText).transform.parent != (Object)(object)val.transform)
			{
				((TMP_Text)Plugin.FeedText).transform.SetParent(val.transform, false);
			}
			return true;
		}

		internal static void UpdateKillFeedHud()
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Entries.RemoveAll((KillFeedEntry e) => Time.time > e.ExpiresAt);
			if (Plugin.Entries.Count == 0)
			{
				Plugin.FeedInstance.SetActive(false);
				Plugin.LastRenderedFeedText = string.Empty;
				Plugin.LastRenderedFontSize = -1f;
				Plugin.HudShowStartedAt = -100000f;
				return;
			}
			bool activeSelf = Plugin.FeedInstance.activeSelf;
			Plugin.FeedInstance.SetActive(true);
			if (!activeSelf)
			{
				Plugin.HudShowStartedAt = Time.time;
			}
			RectTransform component = Plugin.FeedInstance.GetComponent<RectTransform>();
			float num = Mathf.Max(0f, Time.time - Plugin.HudShowStartedAt);
			float num2 = Mathf.Clamp01(num / 0.18f);
			float num3 = 1f - Mathf.Pow(1f - num2, 3f);
			float num4 = Mathf.Lerp(24f, 0f, num3);
			component.anchoredPosition = new Vector2(-10f + num4, -250f);
			int num5 = Plugin.Entries.Max((KillFeedEntry e) => e.Text.Length);
			int num6 = Mathf.Clamp((num5 - 1) / 46, 0, 3);
			float num7 = Mathf.Clamp(Plugin.TextScaleConfig?.Value ?? 1f, 0.7f, 1f);
			float num8 = Plugin.MobHudSizes[num6] * num7;
			if (!Mathf.Approximately(Plugin.LastRenderedFontSize, num8))
			{
				((TMP_Text)Plugin.FeedText).fontSize = num8;
				((TMP_Text)Plugin.FeedText).lineSpacing = (0f - num8) * 1.05f;
				Plugin.LastRenderedFontSize = num8;
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < Plugin.Entries.Count; i++)
			{
				KillFeedEntry killFeedEntry = Plugin.Entries[i];
				if (i > 0)
				{
					stringBuilder.Append('\n');
				}
				float num9 = Mathf.Max(0f, Time.time - killFeedEntry.CreatedAt);
				float num10 = Mathf.Max(0f, killFeedEntry.ExpiresAt - Time.time);
				float num11 = Mathf.Clamp01(num9 / 0.16f);
				float num12 = Mathf.Clamp01(num10 / 0.24f);
				float num13 = Mathf.Min(num11, num12);
				string value = Mathf.Clamp(Mathf.RoundToInt(num13 * 255f), 0, 255).ToString("X2");
				stringBuilder.Append("<color=#").Append(killFeedEntry.Color).Append(value)
					.Append('>')
					.Append(killFeedEntry.Text)
					.Append("</color>");
			}
			string text = stringBuilder.ToString();
			if (text != Plugin.LastRenderedFeedText)
			{
				((TMP_Text)Plugin.FeedText).text = text;
				Plugin.LastRenderedFeedText = text;
			}
		}

		internal static void SetInactive()
		{
			if ((Object)(object)Plugin.FeedInstance != (Object)null)
			{
				Plugin.FeedInstance.SetActive(false);
			}
		}
	}
	internal sealed class KillFeedEntry
	{
		public string Text;

		public string Color;

		public float ExpiresAt;

		public float CreatedAt;
	}
	internal sealed class ObservedPlayerState
	{
		public PlayerAvatar Avatar;

		public string Name;

		public bool WasDead;

		public float LastDeathAt;
	}
	internal sealed class PendingDisconnectState
	{
		public string Name;

		public float MissingSince;

		public bool HadRecentDeath;
	}
	internal sealed class ObservedEnemyState
	{
		public EnemyParent EnemyParent;

		public string Name;

		public bool WasDead;

		public int LastHealth;

		public float FirstSeenAt;

		public float PendingDeathSince;

		public bool DeathAnnounced;

		public bool Spawned;

		public float MapValueBeforeDeath;

		public bool OrbConfirmed;

		public float AliveSince;

		public bool HasEnteredStableAlivePhase;

		public bool HadAliveHealthSample;

		public bool HadDamageEvidence;

		public float LastAnnouncementAt;
	}
	internal static class PlayerFeedTracker
	{
		[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
		internal static class PlayerAvatarDeathPatch
		{
			[HarmonyPostfix]
			private static void Postfix(PlayerAvatar __instance)
			{
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				string text = ResolvePlayerName(__instance);
				string item = SanitizeDisplayName(text);
				if (!Plugin.PlayersWithDisconnectFeed.Contains(item) && Plugin.ShowPlayerDeathsConfig.Value && Time.time - Plugin.LevelStartTime >= 1f)
				{
					Plugin.AddEntry(text + " died", new Color(0.7f, 0.23f, 0.23f, 1f));
				}
				int num = (((Object)(object)__instance != (Object)null) ? ((Object)__instance).GetInstanceID() : 0);
				if (num != 0 && Plugin.PlayerStates.TryGetValue(num, out var value))
				{
					value.WasDead = true;
					value.LastDeathAt = Time.time;
				}
			}
		}

		internal static string ResolvePlayerName(PlayerAvatar avatar)
		{
			if ((Object)(object)avatar == (Object)null)
			{
				return "Player";
			}
			try
			{
				string raw = Plugin.PlayerNameField?.GetValue(avatar) as string;
				raw = SanitizeDisplayName(raw);
				if (!string.IsNullOrWhiteSpace(raw))
				{
					return raw;
				}
			}
			catch
			{
			}
			return SanitizeDisplayName((!string.IsNullOrWhiteSpace(((Object)avatar).name)) ? ((Object)avatar).name : "Player");
		}

		internal static string SanitizeDisplayName(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return raw;
			}
			StringBuilder stringBuilder = new StringBuilder(raw.Length);
			foreach (char c in raw)
			{
				if (!char.IsControl(c))
				{
					UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
					if (unicodeCategory != UnicodeCategory.Format)
					{
						stringBuilder.Append(c);
					}
				}
			}
			return stringBuilder.ToString().Trim();
		}

		internal static bool IsPlayerDead(PlayerAvatar avatar)
		{
			if ((Object)(object)avatar == (Object)null)
			{
				return false;
			}
			try
			{
				object obj = Plugin.PlayerDeadSetField?.GetValue(avatar);
				if (obj is bool)
				{
					bool result = (bool)obj;
					if (true)
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static void ObservePlayers()
		{
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
			HashSet<int> seen = new HashSet<int>();
			try
			{
				List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
				if (list == null)
				{
					return;
				}
				foreach (PlayerAvatar item2 in list)
				{
					if ((Object)(object)item2 == (Object)null)
					{
						continue;
					}
					int instanceID = ((Object)item2).GetInstanceID();
					seen.Add(instanceID);
					string text = ResolvePlayerName(item2);
					bool flag = IsPlayerDead(item2);
					if (!Plugin.PlayerStates.TryGetValue(instanceID, out var value))
					{
						value = new ObservedPlayerState
						{
							Avatar = item2,
							Name = text,
							WasDead = flag
						};
						Plugin.PlayerStates[instanceID] = value;
					}
					else
					{
						value.Avatar = item2;
						value.Name = text;
					}
					Plugin.PlayerStatesByName[text] = value;
					Plugin.PendingDisconnects.Remove(text);
					if (!value.WasDead && flag)
					{
						string item = SanitizeDisplayName(text);
						if (!Plugin.PlayersWithDisconnectFeed.Contains(item) && Plugin.ShowPlayerDeathsConfig.Value && Time.time - Plugin.LevelStartTime >= 1f)
						{
							Plugin.AddEntry(text + " died", new Color(0.7f, 0.23f, 0.23f, 1f));
						}
						value.LastDeathAt = Time.time;
					}
					value.WasDead = flag;
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[DenisUIKillFeed] ObservePlayers error: {arg}");
			}
			List<int> list2 = Plugin.PlayerStates.Keys.Where((int id) => !seen.Contains(id)).ToList();
			foreach (int item3 in list2)
			{
				ObservedPlayerState observedPlayerState = Plugin.PlayerStates[item3];
				if (observedPlayerState != null && !string.IsNullOrWhiteSpace(observedPlayerState.Name) && !Plugin.SceneResetInProgress)
				{
					bool hadRecentDeath = observedPlayerState.LastDeathAt > 0f && Time.time - observedPlayerState.LastDeathAt <= 2.5f;
					string text2 = SanitizeDisplayName(observedPlayerState.Name);
					if (!string.IsNullOrWhiteSpace(text2) && !Plugin.PendingDisconnects.ContainsKey(text2))
					{
						Plugin.PendingDisconnects[text2] = new PendingDisconnectState
						{
							Name = text2,
							MissingSince = Time.time,
							HadRecentDeath = hadRecentDeath
						};
					}
				}
				if (observedPlayerState != null && !string.IsNullOrWhiteSpace(observedPlayerState.Name))
				{
					Plugin.PlayerStatesByName.Remove(observedPlayerState.Name);
				}
				Plugin.PlayerStates.Remove(item3);
			}
			List<string> list3 = Plugin.PendingDisconnects.Keys.ToList();
			foreach (string item4 in list3)
			{
				if (Plugin.PlayerStatesByName.ContainsKey(item4))
				{
					Plugin.PendingDisconnects.Remove(item4);
				}
				else
				{
					if (Time.time - Plugin.LevelStartTime < 4f)
					{
						continue;
					}
					PendingDisconnectState pendingDisconnectState = Plugin.PendingDisconnects[item4];
					if (Time.time - pendingDisconnectState.MissingSince < 1.25f)
					{
						continue;
					}
					if (Plugin.RecentlyDisconnectedPlayers.Add(pendingDisconnectState.Name))
					{
						if (Plugin.ShowDisconnectsConfig.Value)
						{
							Plugin.AddEntry(pendingDisconnectState.Name + " disconnected", new Color(0.58f, 0.58f, 0.62f, 1f));
						}
						Plugin.PlayersWithDisconnectFeed.Add(pendingDisconnectState.Name);
					}
					Plugin.PendingDisconnects.Remove(item4);
				}
			}
			if (Plugin.SceneResetInProgress && seen.Count > 0)
			{
				Plugin.SceneResetInProgress = false;
			}
		}
	}
	[BepInPlugin("denis.repo.denisuikillfeed", "Denis UI Killfeed", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static GameObject FeedInstance;

		internal static TextMeshProUGUI FeedText;

		internal static readonly List<KillFeedEntry> Entries = new List<KillFeedEntry>();

		internal static readonly Dictionary<int, ObservedPlayerState> PlayerStates = new Dictionary<int, ObservedPlayerState>();

		internal static readonly Dictionary<string, ObservedPlayerState> PlayerStatesByName = new Dictionary<string, ObservedPlayerState>(StringComparer.OrdinalIgnoreCase);

		internal static readonly HashSet<string> RecentlyDisconnectedPlayers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly HashSet<string> PlayersWithDisconnectFeed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly Dictionary<string, PendingDisconnectState> PendingDisconnects = new Dictionary<string, PendingDisconnectState>(StringComparer.OrdinalIgnoreCase);

		internal static readonly Dictionary<int, ObservedEnemyState> EnemyStates = new Dictionary<int, ObservedEnemyState>();

		internal static readonly HashSet<int> SeenValuableIds = new HashSet<int>();

		internal static readonly HashSet<string> NoOrbEnemyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Gnome", "Banger", "Tick" };

		internal static readonly float[] MobHudSizes = new float[4] { 24f, 18f, 14f, 11f };

		internal static bool SceneResetInProgress;

		internal static float LastLevelActiveAt;

		internal static float LevelStartTime;

		internal static float HudShowStartedAt = -100000f;

		internal static string LastRenderedFeedText = string.Empty;

		internal static float LastRenderedFontSize = -1f;

		internal static readonly FieldInfo PlayerNameField = typeof(PlayerAvatar).GetField("playerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo PlayerDeadSetField = typeof(PlayerAvatar).GetField("deadSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyParentEnemyField = typeof(EnemyParent).GetField("Enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(EnemyParent).GetField("enemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyParentSpawnedField = typeof(EnemyParent).GetField("Spawned", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(EnemyParent).GetField("spawned", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyHasHealthField = typeof(Enemy).GetField("HasHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Enemy).GetField("hasHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyHealthRefField = typeof(Enemy).GetField("Health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Enemy).GetField("health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyHealthCurrentField = typeof(EnemyHealth).GetField("healthCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static readonly FieldInfo EnemyHealthDeadField = typeof(EnemyHealth).GetField("dead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static ConfigEntry<bool> ShowEnemyKillsConfig;

		internal static ConfigEntry<bool> ShowPlayerDeathsConfig;

		internal static ConfigEntry<bool> ShowDisconnectsConfig;

		internal static ConfigEntry<float> TextScaleConfig;

		internal const float HudGraceAfterLevelEnds = 8f;

		internal const float DisconnectConfirmSeconds = 1.25f;

		internal const float DisconnectStartupGuardSeconds = 4f;

		internal const float SpawnGraceSeconds = 1f;

		internal const float AliveStableSeconds = 0.35f;

		internal const float EnemyAnnouncementCooldownSeconds = 6f;

		internal const float HudSlideSeconds = 0.18f;

		internal const float EntryFadeInSeconds = 0.16f;

		internal const float EntryFadeOutSeconds = 0.24f;

		internal static Plugin Instance { get; private set; }

		internal Harmony Harmony { get; private set; }

		private void Awake()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			Instance = this;
			((Component)this).transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			TextScaleConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Killfeed Activity", "Text Scale", 1f, new ConfigDescription("Scales the killfeed text size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.7f, 1f), Array.Empty<object>()));
			ShowEnemyKillsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Killfeed Activity", "Enemy deaths", true, "Show enemy death events in the killfeed.");
			ShowPlayerDeathsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Killfeed Activity", "Player deaths", true, "Show player death events in the killfeed.");
			ShowDisconnectsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Killfeed Activity", "Player disconnects", true, "Show player disconnect events in the killfeed.");
			Harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
			Harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Denis UI Killfeed loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void AddEntry(string text, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string text2 = ColorUtility.ToHtmlStringRGB(color);
			if (Entries.Count > 0)
			{
				KillFeedEntry killFeedEntry = Entries[0];
				if (killFeedEntry.Text == text && killFeedEntry.Color == text2 && Time.time - killFeedEntry.CreatedAt <= 0.35f)
				{
					killFeedEntry.ExpiresAt = Time.time + 7f;
					return;
				}
			}
			Entries.Insert(0, new KillFeedEntry
			{
				Text = text,
				Color = text2,
				ExpiresAt = Time.time + 7f,
				CreatedAt = Time.time
			});
			if (Entries.Count > 5)
			{
				Entries.RemoveRange(5, Entries.Count - 5);
			}
		}

		internal static void ResetObservedState()
		{
			PlayerStates.Clear();
			PlayerStatesByName.Clear();
			EnemyStates.Clear();
			SeenValuableIds.Clear();
			RecentlyDisconnectedPlayers.Clear();
			PlayersWithDisconnectFeed.Clear();
			PendingDisconnects.Clear();
			SceneResetInProgress = true;
			LevelStartTime = Time.time;
			HudShowStartedAt = -100000f;
			LastRenderedFeedText = string.Empty;
			LastRenderedFontSize = -1f;
		}
	}
}