Decompiled source of LCBridgeOverlay v1.3.0

LCBridgeOverlay.dll

Decompiled 12 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DunGen.Graph;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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("LCBridgeOverlay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+e471728a4a7d97b5c72ce26f5d901ac7f673b150")]
[assembly: AssemblyProduct("LCBridgeOverlay")]
[assembly: AssemblyTitle("LCBridgeOverlay")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 LCBridgeOverlay
{
	public static class BcmerEvents
	{
		public struct EventInfo
		{
			public string Name;

			public string ColorHex;
		}

		private static Type _emType;

		private static FieldInfo _curEventsField;

		private static bool _searched;

		private static readonly Dictionary<Type, MethodInfo> _nameCache = new Dictionary<Type, MethodInfo>();

		private static readonly Dictionary<Type, Func<object, string>> _typeGetterCache = new Dictionary<Type, Func<object, string>>();

		private static string ColorFor(string typeName)
		{
			string text = (typeName ?? "").ToLowerInvariant();
			if (text.Contains("verygood") || text.Contains("great"))
			{
				return "#00FF00";
			}
			if (text.Contains("good"))
			{
				return "#008000";
			}
			if (text.Contains("verybad"))
			{
				return "#8B0000";
			}
			if (text.Contains("bad"))
			{
				return "#FF0000";
			}
			if (text.Contains("black") || text.Contains("deadly") || text.Contains("impossible") || text.Contains("death"))
			{
				return "#000000";
			}
			return "#FFFFFF";
		}

		public static List<EventInfo> GetEvents()
		{
			List<EventInfo> list = new List<EventInfo>();
			try
			{
				EnsureSearched();
				if (_curEventsField == null)
				{
					return list;
				}
				if (!(_curEventsField.GetValue(null) is IEnumerable enumerable))
				{
					return list;
				}
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					string text = GetName(item);
					if (!string.IsNullOrEmpty(text))
					{
						if (ConfigSettings.RussianActive)
						{
							text = EventTranslate.ToRu(text);
						}
						list.Add(new EventInfo
						{
							Name = text,
							ColorHex = ColorFor(GetEventTypeName(item))
						});
					}
				}
			}
			catch
			{
			}
			return list;
		}

		public static bool BcmePresent()
		{
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					if (pluginInfo.Key.IndexOf("BrutalCompany", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
					PluginInfo value = pluginInfo.Value;
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.Name : null);
					}
					if (obj == null)
					{
						obj = "";
					}
					if (((string)obj).IndexOf("BrutalCompany", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static void EnsureSearched()
		{
			if (_searched)
			{
				return;
			}
			_searched = true;
			if (!BcmePresent())
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"BCME не найден в Chainloader.PluginInfos — плашка ивентов возьмёт данные моста.");
				}
				return;
			}
			_emType = FindTypeByFullName("BrutalCompanyMinus.Minus.EventManager") ?? FindTypeFuzzy("BrutalCompany", "EventManager");
			if (_emType != null)
			{
				_curEventsField = _emType.GetField("currentEvents", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[reflection] BCMER EventManager=" + _emType.FullName + ", currentEvents=" + ((_curEventsField != null) ? "OK" : "НЕ НАЙДЕНО")));
				}
			}
			else
			{
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)"[reflection] BCMER не найден (не установлен?) — плашка ивентов возьмёт данные моста.");
				}
			}
		}

		private static string GetName(object ev)
		{
			Type type = ev.GetType();
			if (!_nameCache.TryGetValue(type, out var value))
			{
				value = type.GetMethod("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_nameCache[type] = value;
			}
			if (value != null)
			{
				try
				{
					return value.Invoke(ev, null) as string;
				}
				catch
				{
				}
			}
			return ev.ToString();
		}

		private static string GetEventTypeName(object ev)
		{
			Type type = ev.GetType();
			if (!_typeGetterCache.TryGetValue(type, out var value))
			{
				value = BuildTypeGetter(type);
				_typeGetterCache[type] = value;
			}
			try
			{
				return value?.Invoke(ev);
			}
			catch
			{
				return null;
			}
		}

		private static Func<object, string> BuildTypeGetter(Type t)
		{
			string[] array = new string[6] { "Type", "EventType", "type", "eventType", "Rarity", "rarity" };
			string[] array2 = array;
			foreach (string name in array2)
			{
				MethodInfo m = t.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (m != null && (m.ReturnType.IsEnum || m.ReturnType == typeof(string)))
				{
					return (object o) => m.Invoke(o, null)?.ToString();
				}
			}
			array2 = array;
			foreach (string name2 in array2)
			{
				PropertyInfo p = t.GetProperty(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (p != null && p.CanRead && (p.PropertyType.IsEnum || p.PropertyType == typeof(string)))
				{
					return (object o) => p.GetValue(o)?.ToString();
				}
			}
			array2 = array;
			foreach (string name3 in array2)
			{
				FieldInfo f = t.GetField(name3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (f != null && (f.FieldType.IsEnum || f.FieldType == typeof(string)))
				{
					return (object o) => f.GetValue(o)?.ToString();
				}
			}
			return null;
		}

		private static Type FindTypeByFullName(string fullName)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					Type type = null;
					try
					{
						type = assembly.GetType(fullName, throwOnError: false);
					}
					catch
					{
					}
					if (type != null)
					{
						return type;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static Type FindTypeFuzzy(string asmNameContains, string typeName)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if ((assembly.GetName().Name ?? "").IndexOf(asmNameContains, StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					Type[] source;
					try
					{
						source = assembly.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = ex.Types.Where((Type x) => x != null).ToArray();
					}
					Type type = source.FirstOrDefault((Type x) => string.Equals(x.Name, typeName, StringComparison.OrdinalIgnoreCase));
					if (type != null)
					{
						return type;
					}
				}
			}
			catch
			{
			}
			return null;
		}
	}
	public static class BridgeServer
	{
		private static TcpListener _listener;

		private static Thread _acceptThread;

		private static volatile bool _running;

		private static readonly List<TcpClient> _clients = new List<TcpClient>();

		private static readonly object _lock = new object();

		private static volatile string _lastPayload;

		public static void Start(int port)
		{
			if (!_running)
			{
				_running = true;
				_listener = new TcpListener(IPAddress.Loopback, port);
				_listener.Start();
				_acceptThread = new Thread(AcceptLoop)
				{
					IsBackground = true,
					Name = "LCBridge-Accept"
				};
				_acceptThread.Start();
			}
		}

		public static void Stop()
		{
			_running = false;
			try
			{
				_listener?.Stop();
			}
			catch
			{
			}
			lock (_lock)
			{
				foreach (TcpClient client in _clients)
				{
					try
					{
						client.Close();
					}
					catch
					{
					}
				}
				_clients.Clear();
			}
		}

		private static void AcceptLoop()
		{
			while (_running)
			{
				try
				{
					TcpClient tcpClient = _listener.AcceptTcpClient();
					if (Handshake(tcpClient))
					{
						string lastPayload = _lastPayload;
						if (lastPayload != null)
						{
							try
							{
								byte[] array = EncodeTextFrame(lastPayload);
								tcpClient.GetStream().Write(array, 0, array.Length);
							}
							catch
							{
							}
						}
						lock (_lock)
						{
							_clients.Add(tcpClient);
						}
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"Оверлей подключился к мосту.");
						}
					}
					else
					{
						try
						{
							tcpClient.Close();
						}
						catch
						{
						}
					}
				}
				catch
				{
					if (!_running)
					{
						break;
					}
				}
			}
		}

		private static bool Handshake(TcpClient client)
		{
			try
			{
				NetworkStream stream = client.GetStream();
				byte[] array = new byte[4096];
				int num = stream.Read(array, 0, array.Length);
				if (num <= 0)
				{
					return false;
				}
				string text = Encoding.UTF8.GetString(array, 0, num);
				string text2 = null;
				string[] array2 = text.Split(new string[1] { "\r\n" }, StringSplitOptions.None);
				foreach (string text3 in array2)
				{
					if (text3.StartsWith("Sec-WebSocket-Key:", StringComparison.OrdinalIgnoreCase))
					{
						text2 = text3.Substring("Sec-WebSocket-Key:".Length).Trim();
						break;
					}
				}
				if (text2 == null)
				{
					return false;
				}
				string text4;
				using (SHA1 sHA = SHA1.Create())
				{
					text4 = Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(text2 + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
				}
				string s = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + text4 + "\r\n\r\n";
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				stream.Write(bytes, 0, bytes.Length);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static void Broadcast(string text)
		{
			_lastPayload = text;
			byte[] array = EncodeTextFrame(text);
			List<TcpClient> list = null;
			lock (_lock)
			{
				foreach (TcpClient client in _clients)
				{
					try
					{
						if (!client.Connected)
						{
							(list ?? (list = new List<TcpClient>())).Add(client);
						}
						else
						{
							client.GetStream().Write(array, 0, array.Length);
						}
					}
					catch
					{
						(list ?? (list = new List<TcpClient>())).Add(client);
					}
				}
				if (list == null)
				{
					return;
				}
				foreach (TcpClient item in list)
				{
					_clients.Remove(item);
					try
					{
						item.Close();
					}
					catch
					{
					}
				}
			}
		}

		private static byte[] EncodeTextFrame(string text)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(text);
			int num = bytes.Length;
			byte[] array;
			if (num <= 125)
			{
				array = new byte[2]
				{
					0,
					(byte)num
				};
			}
			else if (num <= 65535)
			{
				array = new byte[4]
				{
					0,
					126,
					(byte)((num >> 8) & 0xFF),
					(byte)(num & 0xFF)
				};
			}
			else
			{
				array = new byte[10] { 0, 127, 0, 0, 0, 0, 0, 0, 0, 0 };
				for (int i = 0; i < 8; i++)
				{
					array[9 - i] = (byte)((num >> 8 * i) & 0xFF);
				}
			}
			array[0] = 129;
			byte[] array2 = new byte[array.Length + bytes.Length];
			Buffer.BlockCopy(array, 0, array2, 0, array.Length);
			Buffer.BlockCopy(bytes, 0, array2, array.Length, bytes.Length);
			return array2;
		}
	}
	public class BridgeTicker : MonoBehaviour
	{
		private float _timer;

		private const float Interval = 1f;

		private string _lastPayload;

		private int _lastMobCount = -1;

		private void Update()
		{
			_timer += Time.deltaTime;
			if (!(_timer < 1f))
			{
				_timer = 0f;
				DataParser.Heartbeat = Time.unscaledTime;
				GameState.TickStats();
				RunStats.Tick();
				string text = BuildJson();
				if (text != _lastPayload)
				{
					_lastPayload = text;
					BridgeServer.Broadcast(text);
					DataParser.PushLocal(text);
				}
			}
		}

		private string BuildJson()
		{
			(int alive, int total) crew = GameState.GetCrew();
			int item = crew.alive;
			int item2 = crew.total;
			int deaths = GameState.GetDeaths();
			int localHealth = GameState.GetLocalHealth();
			string moonName = GameState.GetMoonName();
			string weatherTweaksWeather = GameState.GetWeatherTweaksWeather();
			string s = ((!string.IsNullOrEmpty(weatherTweaksWeather)) ? weatherTweaksWeather : GameState.GetVanillaWeather());
			string brutalEvent = GameState.GetBrutalEvent();
			(List<string> outside, List<string> inside) monsters = GameState.GetMonsters();
			List<string> item3 = monsters.outside;
			List<string> item4 = monsters.inside;
			List<string> traps = GameState.GetTraps();
			bool onMoon = GameState.GetOnMoon();
			bool loading = GameState.GetLoading();
			bool inGame = GameState.GetInGame();
			int resetToken = GameState.GetResetToken();
			int levelScrap = GameState.GetLevelScrap();
			(int quota, int fulfilled) quotaProgress = GameState.GetQuotaProgress();
			int item5 = quotaProgress.quota;
			int item6 = quotaProgress.fulfilled;
			int shipScrapSafe = GameState.GetShipScrapSafe();
			int quotaIndexSafe = GameState.GetQuotaIndexSafe();
			int dayCount = GameState.GetDayCount();
			int daysLeft = GameState.GetDaysLeft();
			string interior = GameState.GetInterior();
			(int hives, int inside, int outside) lootBreakdown = GameState.GetLootBreakdown();
			int item7 = lootBreakdown.hives;
			int item8 = lootBreakdown.inside;
			int item9 = lootBreakdown.outside;
			bool oldBird = GameState.GetOldBird();
			bool onShip = GameState.GetOnShip();
			string topKiller = GameState.GetTopKiller();
			string topMonster = GameState.GetTopMonster();
			string deadliestEvent = GameState.GetDeadliestEvent();
			int num = item3.Count + item4.Count;
			if (num != _lastMobCount)
			{
				_lastMobCount = num;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("[monsters] улица={0} ({1}) | комплекс={2} ({3})", item3.Count, string.Join(",", item3), item4.Count, string.Join(",", item4)));
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('{');
			stringBuilder.Append("\"type\":\"bridge\",");
			stringBuilder.Append("\"deaths\":").Append(deaths).Append(',');
			stringBuilder.Append("\"alive\":").Append(item).Append(',');
			stringBuilder.Append("\"total\":").Append(item2).Append(',');
			stringBuilder.Append("\"health\":").Append(localHealth).Append(',');
			stringBuilder.Append("\"moonName\":").Append(JsonStr(moonName)).Append(',');
			stringBuilder.Append("\"weatherFull\":").Append(JsonStr(s)).Append(',');
			stringBuilder.Append("\"brutalEvent\":").Append(JsonStr(brutalEvent ?? "")).Append(',');
			stringBuilder.Append("\"onMoon\":").Append(onMoon ? "true" : "false").Append(',');
			stringBuilder.Append("\"loading\":").Append(loading ? "true" : "false").Append(',');
			stringBuilder.Append("\"inGame\":").Append(inGame ? "true" : "false").Append(',');
			stringBuilder.Append("\"resetToken\":").Append(resetToken).Append(',');
			stringBuilder.Append("\"levelScrap\":").Append(levelScrap).Append(',');
			stringBuilder.Append("\"quotaValue\":").Append(item5).Append(',');
			stringBuilder.Append("\"quotaFulfilled\":").Append(item6).Append(',');
			stringBuilder.Append("\"shipLoot\":").Append(shipScrapSafe).Append(',');
			stringBuilder.Append("\"quotaIndex\":").Append(quotaIndexSafe).Append(',');
			stringBuilder.Append("\"dayCount\":").Append(dayCount).Append(',');
			stringBuilder.Append("\"daysLeft\":").Append(daysLeft).Append(',');
			stringBuilder.Append("\"interiorType\":").Append(JsonStr(interior ?? "")).Append(',');
			stringBuilder.Append("\"beehiveCount\":").Append(item7).Append(',');
			stringBuilder.Append("\"itemsInside\":").Append(item8).Append(',');
			stringBuilder.Append("\"itemsOutside\":").Append(item9).Append(',');
			stringBuilder.Append("\"hasOldBird\":").Append(oldBird ? "true" : "false").Append(',');
			stringBuilder.Append("\"onShip\":").Append(onShip ? "true" : "false").Append(',');
			stringBuilder.Append("\"topKiller\":").Append(JsonStr(topKiller ?? "")).Append(',');
			stringBuilder.Append("\"topMonster\":").Append(JsonStr(topMonster ?? "")).Append(',');
			stringBuilder.Append("\"deadliestEvent\":").Append(JsonStr(deadliestEvent ?? "")).Append(',');
			stringBuilder.Append("\"monstersOutside\":").Append(JsonArr(item3)).Append(',');
			stringBuilder.Append("\"monstersInside\":").Append(JsonArr(item4)).Append(',');
			stringBuilder.Append("\"traps\":").Append(JsonArr(traps)).Append(',');
			stringBuilder.Append("\"run\":").Append(RunStats.ToJson());
			stringBuilder.Append('}');
			return stringBuilder.ToString();
		}

		private static string JsonArr(List<string> items)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('[');
			for (int i = 0; i < items.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(JsonStr(items[i]));
			}
			stringBuilder.Append(']');
			return stringBuilder.ToString();
		}

		private static string JsonStr(string s)
		{
			if (s == null)
			{
				return "\"\"";
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4", CultureInfo.InvariantCulture));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
	internal static class EnemyResolver
	{
		private static readonly string[] _turretMarkers = new string[3] { "ToilHeadController", "MantiToilController", "TurretHeadController" };

		private static readonly string[] _slayerMarkers = new string[3] { "ToilSlayerController", "MantiSlayerController", "SlayerController" };

		public static string Resolve(object enemyAiObj)
		{
			string baseName = GetBaseName(enemyAiObj);
			if (string.IsNullOrEmpty(baseName))
			{
				return null;
			}
			try
			{
				switch (TurretKind(enemyAiObj))
				{
				case 2:
					return baseName + "+Turret+Slayer";
				case 1:
					return baseName + "+Turret";
				}
			}
			catch
			{
			}
			return baseName;
		}

		private static string GetBaseName(object enemyAiObj)
		{
			try
			{
				EnemyAI val = (EnemyAI)((enemyAiObj is EnemyAI) ? enemyAiObj : null);
				if ((Object)(object)val != (Object)null && (Object)(object)val.enemyType != (Object)null && !string.IsNullOrEmpty(val.enemyType.enemyName))
				{
					return val.enemyType.enemyName;
				}
			}
			catch
			{
			}
			return null;
		}

		private static int TurretKind(object enemyAiObj)
		{
			MonoBehaviour val = (MonoBehaviour)((enemyAiObj is MonoBehaviour) ? enemyAiObj : null);
			if ((Object)(object)val == (Object)null)
			{
				return 0;
			}
			Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(true);
			if (componentsInChildren == null)
			{
				return 0;
			}
			int num = 0;
			Component[] array = componentsInChildren;
			foreach (Component val2 in array)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string name = ((object)val2).GetType().Name;
				if (string.IsNullOrEmpty(name))
				{
					continue;
				}
				string[] slayerMarkers = _slayerMarkers;
				foreach (string value in slayerMarkers)
				{
					if (name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return 2;
					}
				}
				if (num != 0)
				{
					continue;
				}
				slayerMarkers = _turretMarkers;
				foreach (string value2 in slayerMarkers)
				{
					if (name.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num = 1;
						break;
					}
				}
			}
			return num;
		}
	}
	public static class GameState
	{
		private static int _deaths = 0;

		private static readonly HashSet<int> _deadThisRound = new HashSet<int>();

		private static int _resetToken = 0;

		private static readonly Dictionary<string, int> _killerCounts = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> _eventDeaths = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> _monsterSeen = new Dictionary<string, int>();

		private static Type _mgSpawnerType;

		private static bool _mgSearched;

		private static FieldInfo _mgOwnedField;

		private static PropertyInfo _mgInstanceProp;

		private static FieldInfo _mgInstanceField;

		private static int _landedScrap;

		private static bool _wasLanded;

		private static bool _scrapLocked;

		private static Type _emType;

		private static FieldInfo _curEventsField;

		private static bool _bcSearched;

		private static readonly Dictionary<Type, MethodInfo> _nameMethodCache = new Dictionary<Type, MethodInfo>();

		private static Type _wtVarsType;

		private static MethodInfo _wtGetCurrent;

		private static bool _wtSearched;

		private static float _lastLootLog;

		private static int _lastInside = -1;

		private static int _lastOutside = -1;

		public static int GetResetToken()
		{
			return _resetToken;
		}

		public static void RegisterDeath(PlayerControllerB p, string killer = null)
		{
			bool flag = false;
			try
			{
				int item = (int)p.playerClientId;
				if (_deadThisRound.Add(item))
				{
					_deaths++;
					flag = true;
				}
			}
			catch
			{
				_deaths++;
				flag = true;
			}
			if (!flag)
			{
				return;
			}
			try
			{
				if (!string.IsNullOrEmpty(killer))
				{
					_killerCounts.TryGetValue(killer, out var value);
					_killerCounts[killer] = value + 1;
				}
				string brutalEvent = GetBrutalEvent();
				if (!string.IsNullOrEmpty(brutalEvent) && brutalEvent != "—")
				{
					_eventDeaths.TryGetValue(brutalEvent, out var value2);
					_eventDeaths[brutalEvent] = value2 + 1;
				}
				RunStats.OnDeath(killer);
			}
			catch
			{
			}
		}

		public static void OnNewRound()
		{
			_deadThisRound.Clear();
		}

		public static void ResetDeaths()
		{
			_deaths = 0;
			_deadThisRound.Clear();
			_resetToken++;
			_killerCounts.Clear();
			_eventDeaths.Clear();
			_monsterSeen.Clear();
			RunStats.ResetRun();
		}

		public static int GetDeaths()
		{
			return _deaths;
		}

		public static void TickStats()
		{
			try
			{
				HashSet<string> hashSet = new HashSet<string>();
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if (!((Object)(object)allLiveEnemy == (Object)null) && !allLiveEnemy.isEnemyDead)
					{
						string text = EnemyResolver.Resolve(allLiveEnemy);
						if (text != null)
						{
							hashSet.Add(text);
						}
					}
				}
				foreach (string item in hashSet)
				{
					_monsterSeen.TryGetValue(item, out var value);
					_monsterSeen[item] = value + 1;
				}
			}
			catch
			{
			}
		}

		private static string TopOf(Dictionary<string, int> dict)
		{
			string result = null;
			int num = 0;
			foreach (KeyValuePair<string, int> item in dict)
			{
				if (item.Value > num)
				{
					num = item.Value;
					result = item.Key;
				}
			}
			return result;
		}

		public static string GetTopKiller()
		{
			return TopOf(_killerCounts);
		}

		public static string GetTopMonster()
		{
			return TopOf(_monsterSeen);
		}

		public static string GetDeadliestEvent()
		{
			return TopOf(_eventDeaths);
		}

		public static (int alive, int total) GetCrew()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return (alive: 0, total: 0);
				}
				int num = 0;
				int num2 = 0;
				PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if (!((Object)(object)val == (Object)null) && (val.isPlayerControlled || val.isPlayerDead))
					{
						num++;
						if (!val.isPlayerDead)
						{
							num2++;
						}
					}
				}
				if (num == 0)
				{
					num = 1;
				}
				return (alive: num2, total: num);
			}
			catch
			{
				return (alive: 0, total: 0);
			}
		}

		public static int GetLocalHealth()
		{
			try
			{
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				if ((Object)(object)val == (Object)null)
				{
					return 0;
				}
				return val.health;
			}
			catch
			{
				return 0;
			}
		}

		private static void EnsureMonstersGordion()
		{
			if (_mgSearched)
			{
				return;
			}
			_mgSearched = true;
			_mgSpawnerType = FindTypeByFullName("MonstersGordion.CompanyMonsterSpawner") ?? FindTypeFuzzy("MonstersGordion", new string[1] { "CompanyMonsterSpawner" });
			if (_mgSpawnerType != null)
			{
				_mgOwnedField = _mgSpawnerType.GetField("_ownedEnemies", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? _mgSpawnerType.GetField("ownedEnemies", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_mgInstanceProp = _mgSpawnerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
				_mgInstanceField = _mgSpawnerType.GetField("Instance", BindingFlags.Static | BindingFlags.Public);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[reflection] MonstersGordion=" + _mgSpawnerType.FullName + ", _ownedEnemies=" + ((_mgOwnedField != null) ? "OK" : "НЕ НАЙДЕНО")));
				}
			}
			else
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"[reflection] MonstersGordion не найден (не установлен?)");
				}
			}
		}

		public static List<EnemyAI> GetGordionEnemies()
		{
			List<EnemyAI> list = new List<EnemyAI>();
			try
			{
				EnsureMonstersGordion();
				if (_mgSpawnerType == null || _mgOwnedField == null)
				{
					return list;
				}
				object obj = _mgInstanceProp?.GetValue(null) ?? _mgInstanceField?.GetValue(null) ?? GetSingletonInstance(_mgSpawnerType);
				if (obj == null)
				{
					return list;
				}
				if (!(_mgOwnedField.GetValue(obj) is IEnumerable enumerable))
				{
					return list;
				}
				foreach (object item in enumerable)
				{
					EnemyAI val = (EnemyAI)((item is EnemyAI) ? item : null);
					if (val != null && (Object)(object)val != (Object)null && !val.isEnemyDead)
					{
						list.Add(val);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("GetGordionEnemies fail: " + ex.Message));
				}
			}
			return list;
		}

		public static List<EnemyAI> GetAllLiveEnemies()
		{
			List<EnemyAI> list = new List<EnemyAI>();
			HashSet<int> hashSet = new HashSet<int>();
			try
			{
				RoundManager instance = RoundManager.Instance;
				if ((Object)(object)instance != (Object)null && instance.SpawnedEnemies != null)
				{
					foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies)
					{
						if ((Object)(object)spawnedEnemy != (Object)null && !spawnedEnemy.isEnemyDead && hashSet.Add(((Object)spawnedEnemy).GetInstanceID()))
						{
							list.Add(spawnedEnemy);
						}
					}
				}
			}
			catch
			{
			}
			foreach (EnemyAI gordionEnemy in GetGordionEnemies())
			{
				if ((Object)(object)gordionEnemy != (Object)null && !gordionEnemy.isEnemyDead && hashSet.Add(((Object)gordionEnemy).GetInstanceID()))
				{
					list.Add(gordionEnemy);
				}
			}
			return list;
		}

		public static (List<string> outside, List<string> inside) GetMonsters()
		{
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			try
			{
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if ((Object)(object)allLiveEnemy == (Object)null || allLiveEnemy.isEnemyDead)
					{
						continue;
					}
					string key = "Unknown";
					try
					{
						string text = EnemyResolver.Resolve(allLiveEnemy);
						if (!string.IsNullOrEmpty(text))
						{
							key = text;
						}
					}
					catch
					{
					}
					Dictionary<string, int> obj2 = (allLiveEnemy.isOutside ? dictionary : dictionary2);
					obj2.TryGetValue(key, out var value);
					obj2[key] = value + 1;
				}
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					list.Add((item.Value > 1) ? $"{item.Key} x{item.Value}" : item.Key);
				}
				foreach (KeyValuePair<string, int> item2 in dictionary2)
				{
					list2.Add((item2.Value > 1) ? $"{item2.Key} x{item2.Value}" : item2.Key);
				}
				list.Sort(StringComparer.Ordinal);
				list2.Sort(StringComparer.Ordinal);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("GetMonsters fail: " + ex.Message));
				}
			}
			return (outside: list, inside: list2);
		}

		public static List<string> GetTraps()
		{
			List<string> list = new List<string>();
			try
			{
				Dictionary<string, int> counts = new Dictionary<string, int>();
				CountType<Turret>("Turret");
				CountType<Landmine>("Landmine");
				CountType<SpikeRoofTrap>("Spike Trap");
				foreach (KeyValuePair<string, int> item in counts)
				{
					list.Add((item.Value > 1) ? $"{item.Key} x{item.Value}" : item.Key);
				}
				void CountType<T>(string label) where T : Object
				{
					try
					{
						T[] array = Object.FindObjectsOfType<T>();
						if (array != null && array.Length != 0)
						{
							counts.TryGetValue(label, out var value);
							counts[label] = value + array.Length;
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("GetTraps fail: " + ex.Message));
				}
			}
			return list;
		}

		public static int GetLevelScrap()
		{
			try
			{
				RoundManager instance = RoundManager.Instance;
				if (GetOnMoon())
				{
					if (!_wasLanded)
					{
						_wasLanded = true;
						_scrapLocked = false;
						_landedScrap = 0;
					}
					if (!_scrapLocked && (Object)(object)instance != (Object)null)
					{
						int num = (int)instance.totalScrapValueInLevel;
						if (num > 0)
						{
							_landedScrap = num;
							_scrapLocked = true;
						}
					}
				}
				else
				{
					_wasLanded = false;
					_scrapLocked = false;
					_landedScrap = 0;
				}
				return _landedScrap;
			}
			catch
			{
				return _landedScrap;
			}
		}

		private static string GetEventName(object ev)
		{
			Type type = ev.GetType();
			if (!_nameMethodCache.TryGetValue(type, out var value))
			{
				value = type.GetMethod("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_nameMethodCache[type] = value;
			}
			if (value != null)
			{
				try
				{
					return value.Invoke(ev, null) as string;
				}
				catch
				{
				}
			}
			return ExtractName(ev);
		}

		public static string GetBrutalEvent()
		{
			try
			{
				if (!_bcSearched)
				{
					_bcSearched = true;
					_emType = FindTypeByFullName("BrutalCompanyMinus.Minus.EventManager") ?? FindTypeFuzzy("BrutalCompany", new string[1] { "EventManager" });
					if (_emType != null)
					{
						_curEventsField = _emType.GetField("currentEvents", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)("[reflection] BCMER EventManager=" + _emType.FullName + ", currentEvents field=" + ((_curEventsField != null) ? "OK" : "НЕ НАЙДЕНО")));
						}
					}
					else
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)"[reflection] BCMER EventManager не найден (мод выключен?)");
						}
					}
				}
				if (_curEventsField == null)
				{
					return null;
				}
				if (!(_curEventsField.GetValue(null) is IEnumerable enumerable))
				{
					return null;
				}
				List<string> list = new List<string>();
				foreach (object item in enumerable)
				{
					if (item != null)
					{
						string eventName = GetEventName(item);
						if (!string.IsNullOrEmpty(eventName))
						{
							list.Add(eventName);
						}
					}
				}
				if (list.Count == 0)
				{
					return null;
				}
				return string.Join(", ", list);
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogDebug((object)("GetBrutalEvent fail: " + ex.Message));
				}
				return null;
			}
		}

		public static string GetWeatherTweaksWeather()
		{
			try
			{
				if (!_wtSearched)
				{
					_wtSearched = true;
					_wtVarsType = FindTypeByFullName("WeatherTweaks.Variables") ?? FindTypeFuzzy("WeatherTweaks", new string[1] { "Variables" });
					if (_wtVarsType != null)
					{
						_wtGetCurrent = _wtVarsType.GetMethod("GetCurrentWeather", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)("[reflection] WeatherTweaks Variables=" + _wtVarsType.FullName + ", GetCurrentWeather=" + ((_wtGetCurrent != null) ? "OK" : "НЕ НАЙДЕНО")));
						}
					}
					else
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)"[reflection] WeatherTweaks Variables не найден (мод выключен?)");
						}
					}
				}
				if (_wtGetCurrent == null)
				{
					return null;
				}
				object obj = _wtGetCurrent.Invoke(null, null);
				if (obj == null)
				{
					return null;
				}
				string text = ExtractName(obj);
				return string.IsNullOrEmpty(text) ? null : text;
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogDebug((object)("GetWeatherTweaks fail: " + ex.Message));
				}
				return null;
			}
		}

		public static string GetVanillaWeather()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null)
				{
					return "None";
				}
				return ((object)Unsafe.As<LevelWeatherType, LevelWeatherType>(ref instance.currentLevel.currentWeather)/*cast due to .constrained prefix*/).ToString();
			}
			catch
			{
				return "None";
			}
		}

		public static string GetMoonName()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null)
				{
					return "—";
				}
				return instance.currentLevel.PlanetName;
			}
			catch
			{
				return "—";
			}
		}

		public static bool GetOnMoon()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null)
				{
					return false;
				}
				return instance.shipHasLanded;
			}
			catch
			{
				return false;
			}
		}

		public static bool GetLoading()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				return instance.travellingToNewLevel;
			}
			catch
			{
				return false;
			}
		}

		public static bool GetInGame()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				return instance.shipHasLanded || instance.travellingToNewLevel;
			}
			catch
			{
				return false;
			}
		}

		public static int GetDayCount()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance != (Object)null && instance.gameStats != null)
				{
					return instance.gameStats.daysSpent + 1;
				}
				TimeOfDay instance2 = TimeOfDay.Instance;
				if ((Object)(object)instance2 == (Object)null)
				{
					return 1;
				}
				return (instance2.daysUntilDeadline < 0) ? 1 : (3 - instance2.daysUntilDeadline);
			}
			catch
			{
				return 1;
			}
		}

		public static int GetQuotaIndexSafe()
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return 1;
				}
				return instance.timesFulfilledQuota + 1;
			}
			catch
			{
				return 1;
			}
		}

		public static int GetShipScrapSafe()
		{
			try
			{
				int num = 0;
				GameObject[] array = GameObject.FindGameObjectsWithTag("PhysicsProp");
				for (int i = 0; i < array.Length; i++)
				{
					GrabbableObject component = array[i].GetComponent<GrabbableObject>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.itemProperties == (Object)null) && component.itemProperties.isScrap && (component.isInShipRoom || component.isInElevator))
					{
						num += component.scrapValue;
					}
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		public static List<KeyValuePair<int, int>> GetShipScrapItems()
		{
			List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
			try
			{
				GameObject[] array = GameObject.FindGameObjectsWithTag("PhysicsProp");
				for (int i = 0; i < array.Length; i++)
				{
					GrabbableObject component = array[i].GetComponent<GrabbableObject>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.itemProperties == (Object)null) && component.itemProperties.isScrap && (component.isInShipRoom || component.isInElevator))
					{
						list.Add(new KeyValuePair<int, int>(((Object)component).GetInstanceID(), component.scrapValue));
					}
				}
			}
			catch
			{
			}
			return list;
		}

		public static List<KeyValuePair<int, string>> GetMonsterInstances()
		{
			List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
			try
			{
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if (!((Object)(object)allLiveEnemy == (Object)null) && !allLiveEnemy.isEnemyDead)
					{
						string text = EnemyResolver.Resolve(allLiveEnemy);
						if (text != null)
						{
							list.Add(new KeyValuePair<int, string>(((Object)allLiveEnemy).GetInstanceID(), text));
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		public static (List<string> outside, List<string> inside) GetMonstersRaw()
		{
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			try
			{
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if (!((Object)(object)allLiveEnemy == (Object)null) && !allLiveEnemy.isEnemyDead)
					{
						string text = EnemyResolver.Resolve(allLiveEnemy);
						if (text != null)
						{
							(allLiveEnemy.isOutside ? list : list2).Add(text);
						}
					}
				}
			}
			catch
			{
			}
			return (outside: list, inside: list2);
		}

		public static (int quota, int fulfilled) GetQuotaProgress()
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return (quota: 0, fulfilled: 0);
				}
				return (quota: instance.profitQuota, fulfilled: instance.quotaFulfilled);
			}
			catch
			{
				return (quota: 0, fulfilled: 0);
			}
		}

		public static int GetDaysLeft()
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				return ((Object)(object)instance != (Object)null) ? instance.daysUntilDeadline : (-1);
			}
			catch
			{
				return -1;
			}
		}

		public static string GetInterior()
		{
			try
			{
				if (!GetOnMoon())
				{
					return null;
				}
				DungeonFlow val = RoundManager.Instance?.dungeonGenerator?.Generator?.DungeonFlow;
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				string text = ((Object)val).name ?? "";
				if (text.IndexOf("Level1", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "Facility";
				}
				if (text.IndexOf("Level2", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "Mansion";
				}
				if (text.IndexOf("Level3", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return "Mineshaft";
				}
				text = text.Replace("DungeonFlow", "").Replace("Flow", "").Replace("flow", "")
					.Trim();
				return string.IsNullOrEmpty(text) ? null : text;
			}
			catch
			{
				return null;
			}
		}

		public static (int hives, int inside, int outside) GetLootBreakdown()
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			try
			{
				if (!GetOnMoon())
				{
					return (hives: 0, inside: 0, outside: 0);
				}
				GameObject[] array = GameObject.FindGameObjectsWithTag("PhysicsProp");
				for (int i = 0; i < array.Length; i++)
				{
					GrabbableObject component = array[i].GetComponent<GrabbableObject>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.itemProperties == (Object)null) && component.itemProperties.isScrap && !component.isInShipRoom && !component.isInElevator && !component.isHeld && !component.isHeldByEnemy)
					{
						if ((component.itemProperties.itemName ?? "").IndexOf("hive", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							num++;
						}
						else if (component.isInFactory)
						{
							num2++;
						}
						else
						{
							num3++;
						}
					}
				}
				if (Time.time - _lastLootLog > 5f && (num2 != _lastInside || num3 != _lastOutside))
				{
					_lastLootLog = Time.time;
					_lastInside = num2;
					_lastOutside = num3;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)$"[loot] внутри={num2} снаружи={num3} ульи={num}");
					}
				}
			}
			catch
			{
			}
			return (hives: num, inside: num2, outside: num3);
		}

		public static bool GetOldBird()
		{
			try
			{
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if (!((Object)(object)allLiveEnemy == (Object)null) && !allLiveEnemy.isEnemyDead)
					{
						string text = (((Object)(object)allLiveEnemy.enemyType != (Object)null) ? (allLiveEnemy.enemyType.enemyName ?? "") : "");
						if (text.IndexOf("RadMech", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Old Bird", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public static bool GetOnShip()
		{
			try
			{
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				return (Object)(object)val != (Object)null && val.isInHangarShipRoom;
			}
			catch
			{
				return false;
			}
		}

		public static bool GetInsideFactorySafe()
		{
			try
			{
				PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
				return (Object)(object)val != (Object)null && val.isInsideFactory;
			}
			catch
			{
				return false;
			}
		}

		private static Type FindTypeByFullName(string fullName)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					Type type = null;
					try
					{
						type = assembly.GetType(fullName, throwOnError: false);
					}
					catch
					{
					}
					if (type != null)
					{
						return type;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static Type FindTypeFuzzy(string asmNameContains, string[] typeNameCandidates)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					string text = assembly.GetName().Name ?? "";
					if (text.IndexOf(asmNameContains, StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					Type[] source;
					try
					{
						source = assembly.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = ex.Types.Where((Type t) => t != null).ToArray();
					}
					foreach (string cand in typeNameCandidates)
					{
						Type type = source.FirstOrDefault((Type t) => string.Equals(t.Name, cand, StringComparison.OrdinalIgnoreCase));
						if (type != null)
						{
							ManualLogSource log = Plugin.Log;
							if (log != null)
							{
								log.LogInfo((object)("[reflection] нашёл тип " + type.FullName + " в " + text));
							}
							return type;
						}
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogDebug((object)("FindTypeFuzzy fail: " + ex2.Message));
				}
			}
			return null;
		}

		private static object ReadStaticMember(Type t, string name)
		{
			try
			{
				FieldInfo field = t.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					if (field.IsStatic)
					{
						return field.GetValue(null);
					}
					object singletonInstance = GetSingletonInstance(t);
					if (singletonInstance != null)
					{
						return field.GetValue(singletonInstance);
					}
				}
				PropertyInfo property = t.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead)
				{
					MethodInfo? getMethod = property.GetGetMethod(nonPublic: true);
					if ((object)getMethod != null && getMethod.IsStatic)
					{
						return property.GetValue(null);
					}
					object singletonInstance2 = GetSingletonInstance(t);
					if (singletonInstance2 != null)
					{
						return property.GetValue(singletonInstance2);
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static object GetSingletonInstance(Type t)
		{
			try
			{
				PropertyInfo propertyInfo = t.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public) ?? t.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
				if (propertyInfo != null)
				{
					return propertyInfo.GetValue(null);
				}
				FieldInfo fieldInfo = t.GetField("Instance", BindingFlags.Static | BindingFlags.Public) ?? t.GetField("instance", BindingFlags.Static | BindingFlags.Public);
				if (fieldInfo != null)
				{
					return fieldInfo.GetValue(null);
				}
			}
			catch
			{
			}
			return null;
		}

		private static string ExtractName(object val)
		{
			if (val == null)
			{
				return null;
			}
			try
			{
				if (val is string result)
				{
					return result;
				}
				if (val.GetType().IsEnum)
				{
					return val.ToString();
				}
				Type type = val.GetType();
				PropertyInfo propertyInfo = type.GetProperty("Name") ?? type.GetProperty("name");
				if (propertyInfo != null)
				{
					string text = propertyInfo.GetValue(val) as string;
					if (!string.IsNullOrEmpty(text))
					{
						return text;
					}
				}
				FieldInfo fieldInfo = type.GetField("Name") ?? type.GetField("name");
				if (fieldInfo != null)
				{
					string text2 = fieldInfo.GetValue(val) as string;
					if (!string.IsNullOrEmpty(text2))
					{
						return text2;
					}
				}
				string text3 = val.ToString();
				if (!string.IsNullOrEmpty(text3) && text3 != type.FullName && text3 != type.Name)
				{
					return text3;
				}
			}
			catch
			{
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	public static class PlayerControllerB_Patches
	{
		[HarmonyPatch("KillPlayer")]
		[HarmonyPostfix]
		public static void OnKillPlayer(PlayerControllerB __instance, CauseOfDeath causeOfDeath)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)null && __instance.isPlayerDead)
			{
				string killer = ResolveKiller(__instance, causeOfDeath);
				GameState.RegisterDeath(__instance, killer);
			}
		}

		private static string ResolveKiller(PlayerControllerB player, CauseOfDeath cause)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected I4, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((int)cause != 2)
				{
					switch (cause - 5)
					{
					case 4:
						return "Drowning";
					case 0:
						return "Suffocation";
					case 8:
						return "Fire";
					case 6:
						return "Shock";
					case 3:
						return "Crushed";
					default:
					{
						RoundManager instance = RoundManager.Instance;
						if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null)
						{
							return "Unknown";
						}
						Vector3 position = ((Component)player).transform.position;
						float num = 25f;
						string text = null;
						foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies)
						{
							if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead)
							{
								float num2 = Vector3.Distance(position, ((Component)spawnedEnemy).transform.position);
								if (num2 < num)
								{
									num = num2;
									text = EnemyResolver.Resolve(spawnedEnemy);
								}
							}
						}
						return string.IsNullOrEmpty(text) ? "Unknown" : text;
					}
					}
				}
				return "Fall";
			}
			catch
			{
				return "Unknown";
			}
		}
	}
	public static class RunStats
	{
		public class QuotaSlice
		{
			public int index;

			public int scrapStart;

			public int scrapEnd;

			public int seconds;

			public int deaths;
		}

		private class MoonStat
		{
			public int visits;

			public int profit;

			public int seconds;
		}

		private static readonly List<QuotaSlice> _quotas = new List<QuotaSlice>();

		private static QuotaSlice _curQuota;

		private static int _lastQuotaIndex = -1;

		private static readonly Dictionary<string, MoonStat> _moons = new Dictionary<string, MoonStat>();

		private static string _curMoon;

		private static int _moonScrapStart;

		private static readonly Dictionary<string, int> _monsterTime = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> _monsterCount = new Dictionary<string, int>();

		private static readonly HashSet<int> _seenMonsterIds = new HashSet<int>();

		private static int _peakMonsters;

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

		private static int _secInside;

		private static int _secOutside;

		private static readonly List<string> _timeline = new List<string>();

		private static int _lastDayLogged = -1;

		private static string _lastEventLogged;

		private static int _runSeconds;

		public static void ResetRun()
		{
			_quotas.Clear();
			_curQuota = null;
			_lastQuotaIndex = -1;
			_moons.Clear();
			_curMoon = null;
			_moonScrapStart = 0;
			_monsterTime.Clear();
			_peakMonsters = 0;
			_monsterCount.Clear();
			_seenMonsterIds.Clear();
			_collectedScrap.Clear();
			_secInside = 0;
			_secOutside = 0;
			_timeline.Clear();
			_lastDayLogged = -1;
			_lastEventLogged = null;
			_runSeconds = 0;
		}

		public static void OnDeath(string killer)
		{
			try
			{
				int dayCount = GameState.GetDayCount();
				string moonName = GameState.GetMoonName();
				string arg = (string.IsNullOrEmpty(killer) ? "?" : killer);
				_timeline.Add($"{dayCount}|death|{arg}@{moonName}");
				if (_curQuota != null)
				{
					_curQuota.deaths++;
				}
			}
			catch
			{
			}
		}

		public static void Tick()
		{
			try
			{
				if (!GameState.GetInGame())
				{
					return;
				}
				_runSeconds++;
				int dayCount = GameState.GetDayCount();
				int quotaIndexSafe = GameState.GetQuotaIndexSafe();
				bool onMoon = GameState.GetOnMoon();
				string moonName = GameState.GetMoonName();
				string brutalEvent = GameState.GetBrutalEvent();
				foreach (KeyValuePair<int, int> shipScrapItem in GameState.GetShipScrapItems())
				{
					if (!_collectedScrap.ContainsKey(shipScrapItem.Key))
					{
						_collectedScrap[shipScrapItem.Key] = shipScrapItem.Value;
					}
				}
				int num = 0;
				foreach (int value in _collectedScrap.Values)
				{
					num += value;
				}
				if (quotaIndexSafe != _lastQuotaIndex)
				{
					if (_curQuota != null)
					{
						_curQuota.scrapEnd = num;
						_quotas.Add(_curQuota);
					}
					_curQuota = new QuotaSlice
					{
						index = quotaIndexSafe,
						scrapStart = num,
						scrapEnd = num,
						seconds = 0,
						deaths = 0
					};
					_lastQuotaIndex = quotaIndexSafe;
				}
				if (_curQuota != null)
				{
					_curQuota.seconds++;
					_curQuota.scrapEnd = num;
				}
				if (onMoon && !string.IsNullOrEmpty(moonName))
				{
					if (_curMoon != moonName)
					{
						_curMoon = moonName;
						_moonScrapStart = num;
						if (!_moons.ContainsKey(moonName))
						{
							_moons[moonName] = new MoonStat();
						}
						_moons[moonName].visits++;
					}
					MoonStat moonStat = _moons[moonName];
					moonStat.seconds++;
					int num2 = num - _moonScrapStart;
					if (num2 > 0)
					{
						moonStat.profit += num2;
						_moonScrapStart = num;
					}
				}
				else
				{
					_curMoon = null;
				}
				List<KeyValuePair<int, string>> monsterInstances = GameState.GetMonsterInstances();
				foreach (KeyValuePair<int, string> item in monsterInstances)
				{
					Add(_monsterTime, item.Value);
					if (_seenMonsterIds.Add(item.Key))
					{
						Add(_monsterCount, item.Value);
					}
				}
				int count = monsterInstances.Count;
				if (count > _peakMonsters)
				{
					_peakMonsters = count;
				}
				if (onMoon)
				{
					if (GameState.GetInsideFactorySafe())
					{
						_secInside++;
					}
					else
					{
						_secOutside++;
					}
				}
				if (onMoon && !string.IsNullOrEmpty(moonName) && moonName != "—" && dayCount > 0 && dayCount != _lastDayLogged)
				{
					_lastDayLogged = dayCount;
					_timeline.Add($"{dayCount}|day|{moonName}");
				}
				if (!string.IsNullOrEmpty(brutalEvent) && brutalEvent != "—" && brutalEvent != _lastEventLogged)
				{
					_lastEventLogged = brutalEvent;
					_timeline.Add($"{dayCount}|event|{brutalEvent}");
				}
			}
			catch
			{
			}
		}

		private static void Add(Dictionary<string, int> d, string k)
		{
			if (!string.IsNullOrEmpty(k))
			{
				d.TryGetValue(k, out var value);
				d[k] = value + 1;
			}
		}

		public static string ToJson()
		{
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append('{');
				stringBuilder.Append("\"quotas\":[");
				List<QuotaSlice> list = new List<QuotaSlice>(_quotas);
				if (_curQuota != null)
				{
					list.Add(_curQuota);
				}
				for (int i = 0; i < list.Count; i++)
				{
					QuotaSlice quotaSlice = list[i];
					if (i > 0)
					{
						stringBuilder.Append(',');
					}
					int value = Math.Max(0, quotaSlice.scrapEnd - quotaSlice.scrapStart);
					stringBuilder.Append('{').Append("\"i\":").Append(quotaSlice.index)
						.Append(',')
						.Append("\"money\":")
						.Append(value)
						.Append(',')
						.Append("\"sec\":")
						.Append(quotaSlice.seconds)
						.Append(',')
						.Append("\"deaths\":")
						.Append(quotaSlice.deaths)
						.Append('}');
				}
				stringBuilder.Append("],");
				stringBuilder.Append("\"moons\":[");
				int num = 0;
				foreach (KeyValuePair<string, MoonStat> item in _moons.OrderByDescending((KeyValuePair<string, MoonStat> x) => x.Value.profit))
				{
					if (num++ > 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append('{').Append("\"name\":").Append(JsonStr(item.Key))
						.Append(',')
						.Append("\"visits\":")
						.Append(item.Value.visits)
						.Append(',')
						.Append("\"profit\":")
						.Append(item.Value.profit)
						.Append(',')
						.Append("\"sec\":")
						.Append(item.Value.seconds)
						.Append('}');
				}
				stringBuilder.Append("],");
				stringBuilder.Append("\"monsters\":[");
				int num2 = 0;
				foreach (KeyValuePair<string, int> item2 in _monsterCount.OrderByDescending((KeyValuePair<string, int> x) => x.Value).Take(20))
				{
					if (num2++ > 0)
					{
						stringBuilder.Append(',');
					}
					_monsterTime.TryGetValue(item2.Key, out var value2);
					stringBuilder.Append('{').Append("\"name\":").Append(JsonStr(item2.Key))
						.Append(',')
						.Append("\"count\":")
						.Append(item2.Value)
						.Append(',')
						.Append("\"sec\":")
						.Append(value2)
						.Append('}');
				}
				stringBuilder.Append("],");
				stringBuilder.Append("\"peak\":").Append(_peakMonsters).Append(',');
				stringBuilder.Append("\"inside\":").Append(_secInside).Append(',');
				stringBuilder.Append("\"outside\":").Append(_secOutside).Append(',');
				stringBuilder.Append("\"runSec\":").Append(_runSeconds).Append(',');
				stringBuilder.Append("\"timeline\":[");
				for (int num3 = 0; num3 < _timeline.Count && num3 < 120; num3++)
				{
					if (num3 > 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append(JsonStr(_timeline[num3]));
				}
				stringBuilder.Append(']');
				stringBuilder.Append('}');
				return stringBuilder.ToString();
			}
			catch
			{
				return "{}";
			}
		}

		private static string JsonStr(string s)
		{
			if (s == null)
			{
				return "\"\"";
			}
			StringBuilder stringBuilder = new StringBuilder("\"");
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
				case '\\':
					stringBuilder.Append('\\').Append(c);
					break;
				case '\n':
				case '\r':
					stringBuilder.Append(' ');
					break;
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	public static class StartOfRound_Patches
	{
		[HarmonyPatch("ResetShip")]
		[HarmonyPostfix]
		public static void OnResetShip()
		{
			GameState.ResetDeaths();
		}

		[HarmonyPatch("SetTimeAndPlanetToSavedSettings")]
		[HarmonyPostfix]
		public static void OnLoadSavedSettings()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if (!((Object)(object)instance == (Object)null) && ((instance.gameStats != null && instance.gameStats.daysSpent <= 0) || ((Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.timesFulfilledQuota <= 0 && TimeOfDay.Instance.daysUntilDeadline >= 3)))
				{
					GameState.ResetDeaths();
				}
			}
			catch
			{
			}
		}

		[HarmonyPatch("StartGame")]
		[HarmonyPostfix]
		public static void OnStartGame()
		{
			GameState.OnNewRound();
		}
	}
	public static class ConfigSettings
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<string> Style;

		public static ConfigEntry<bool> AlwaysVisible;

		public static ConfigEntry<string> ToggleKey;

		public static ConfigEntry<string> Language;

		public static ConfigEntry<float> Scale;

		public static ConfigEntry<int> RightOffsetPx;

		public static ConfigEntry<float> PerspectiveStrength;

		public static ConfigEntry<bool> FadeWhenIdle;

		public static ConfigEntry<float> IdleFadeSeconds;

		public static ConfigEntry<float> IdleMinOpacity;

		public static ConfigEntry<bool> ShowPanel;

		public static ConfigEntry<bool> ShowTimer;

		public static ConfigEntry<bool> ShowLocation;

		public static ConfigEntry<bool> ShowQuota;

		public static ConfigEntry<bool> ShowDayDeaths;

		public static ConfigEntry<bool> ShowMonsters;

		public static ConfigEntry<bool> ShowTraps;

		public static ConfigEntry<bool> ShowBrutalEvent;

		public static ConfigEntry<bool> ShowVictoryBanner;

		public static ConfigEntry<bool> ShowTicker;

		public static ConfigEntry<bool> AutoTimer;

		public static ConfigEntry<bool> ShowAllEvents;

		public static ConfigEntry<string> TimerPauseKey;

		public static ConfigEntry<string> TimerResetKey;

		public static ConfigEntry<bool> Scanlines;

		public static ConfigEntry<bool> ScaleMonstersByCount;

		public static ConfigEntry<int> Port;

		public static Key ToggleKeyParsed { get; private set; } = (Key)23;

		public static Key TimerPauseKeyParsed { get; private set; } = (Key)29;

		public static Key TimerResetKeyParsed { get; private set; } = (Key)0;

		public static bool LegacyStyleActive => string.Equals(Style.Value?.Trim(), "Legacy", StringComparison.OrdinalIgnoreCase);

		public static bool RussianActive
		{
			get
			{
				string a = Language.Value?.Trim();
				if (string.Equals(a, "ru", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				if (string.Equals(a, "en", StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
				return Rtlc.Present;
			}
		}

		public static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("General", "Enabled", true, "Включить/выключить оверлей целиком.");
			Style = cfg.Bind<string>("General", "Style", "Game", "Стиль оверлея: \"Legacy\" (старый пиксельный из HTML) или \"Game\" (как внутриигровой чат). Требует перезапуска игры.");
			AlwaysVisible = cfg.Bind<bool>("General", "AlwaysVisible", false, "true — оверлей виден всегда (даже вне корабля), клавиша показа/скрытия работает везде. false — оверлей виден только когда игрок на корабле.");
			ToggleKey = cfg.Bind<string>("General", "ToggleKey", "I", "Клавиша для переключения видимости (имя из UnityEngine.InputSystem.Key, например I, F7, Numpad0).");
			Language = cfg.Bind<string>("General", "Language", "auto", "Язык надписей оверлея: \"auto\" (русский, если установлен русификатор RTLC, иначе английский), \"en\" или \"ru\".");
			Scale = cfg.Bind<float>("General", "Scale", 1f, "Масштаб оверлея (0.5–2.0).");
			RightOffsetPx = cfg.Bind<int>("General", "RightOffsetPx", 20, "Отступ оверлея от правого края экрана в пикселях.");
			PerspectiveStrength = cfg.Bind<float>("General", "PerspectiveStrength", 0f, "ЭКСПЕРИМЕНТ: эффект перспективы (как чат «уходит вдаль») — ближняя к центру экрана сторона сужается. 0 — выключить (по умолчанию). Попробуй 0.16. Искажает и текст, и рамки. Требует перезапуска.");
			FadeWhenIdle = cfg.Bind<bool>("General", "FadeWhenIdle", true, "Приглушать оверлей, если долго не двигать камерой (возвращается при движении камеры).");
			IdleFadeSeconds = cfg.Bind<float>("General", "IdleFadeSeconds", 4f, "Сколько секунд без движения камеры до приглушения оверлея.");
			IdleMinOpacity = cfg.Bind<float>("General", "IdleMinOpacity", 0.32f, "Насколько приглушать: итоговая непрозрачность в бездействии (0 — полностью прозрачный, 1 — без приглушения).");
			ShowPanel = cfg.Bind<bool>("Widgets", "ShowPanel", true, "Общая панель: фон, рамка/уголки и логотип GDLP.");
			ShowTimer = cfg.Bind<bool>("Widgets", "ShowTimer", true, "Таймер (запуск/пауза/сброс — клавиши в секции Behavior; авто-режим — AutoTimer).");
			ShowLocation = cfg.Bind<bool>("Widgets", "ShowLocation", true, "Блок локации: луна, погода, тип интерьера, ульи, предметы внутри/снаружи, Old Bird.");
			ShowQuota = cfg.Bind<bool>("Widgets", "ShowQuota", true, "Прогресс квоты: табы Q1–Q3, полоса выполнения, собранный лут.");
			ShowDayDeaths = cfg.Bind<bool>("Widgets", "ShowDayDeaths", true, "День и счётчик смертей.");
			ShowMonsters = cfg.Bind<bool>("Widgets", "ShowMonsters", true, "Монстры: слева — наружные (outside), справа — внутренние (inside).");
			ShowTraps = cfg.Bind<bool>("Widgets", "ShowTraps", true, "Ловушки (турели, мины, шипы) в нижней части оверлея. При турельном ивенте — анимация стрельбы.");
			ShowBrutalEvent = cfg.Bind<bool>("Widgets", "ShowBrutalEvent", true, "Плашка с ивентом от BCME (BrutalCompanyMinusExtraReborn). Видна только на луне.");
			ShowVictoryBanner = cfg.Bind<bool>("Widgets", "ShowVictoryBanner", true, "Баннер победы после выполнения 3-й квоты (с аналитикой забега: квоты, луны, монстры, хроника).");
			ShowTicker = cfg.Bind<bool>("Widgets", "ShowTicker", true, "Бегущая строка с краткой сводкой: экипаж, луна, погода, день, квота, смерти.");
			AutoTimer = cfg.Bind<bool>("Behavior", "AutoTimer", true, "true — таймер автоматически запускается при начале рейда и встаёт на паузу при загрузках/меню. false — только ручное управление.");
			ShowAllEvents = cfg.Bind<bool>("Behavior", "ShowAllEvents", false, "true — показывать ВСЕ ивенты BCME (через запятую). false — только первый из списка.");
			TimerPauseKey = cfg.Bind<string>("Behavior", "TimerPauseKey", "O", "Клавиша паузы/запуска таймера. None — отключить.");
			TimerResetKey = cfg.Bind<string>("Behavior", "TimerResetKey", "None", "Клавиша сброса таймера. None — отключить (сброс всё равно происходит при новом сейве).");
			Scanlines = cfg.Bind<bool>("Behavior", "Scanlines", true, "Едва заметные горизонтальные полосы (CRT/LSD-эффект как в ванильных меню). Требует перезапуска игры.");
			ScaleMonstersByCount = cfg.Bind<bool>("Behavior", "ScaleMonstersByCount", false, "ЭКСПЕРИМЕНТ: убрать цифры количества у монстров И ловушек — вместо этого чем их больше, тем крупнее иконка и тем сильнее она трясётся.");
			Port = cfg.Bind<int>("WebSocket", "Port", 8181, "Порт встроенного WebSocket-моста. По нему HTML-оверлей (OBS) получает те же данные. Если у тебя ещё стоит отдельный мод LCBridge — удали его, иначе порт будет занят.");
			ReparseKeys();
			ToggleKey.SettingChanged += delegate
			{
				ReparseKeys();
			};
			TimerPauseKey.SettingChanged += delegate
			{
				ReparseKeys();
			};
			TimerResetKey.SettingChanged += delegate
			{
				ReparseKeys();
			};
		}

		private static void ReparseKeys()
		{
			//IL_000c: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			ToggleKeyParsed = ParseKey(ToggleKey.Value, (Key)23);
			TimerPauseKeyParsed = ParseKey(TimerPauseKey.Value, (Key)0);
			TimerResetKeyParsed = ParseKey(TimerResetKey.Value, (Key)0);
		}

		private static Key ParseKey(string s, Key fallback)
		{
			//IL_001a: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(s))
			{
				return (Key)0;
			}
			if (Enum.TryParse<Key>(s.Trim(), ignoreCase: true, out Key result))
			{
				return result;
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)$"Не удалось распознать клавишу '{s}', использую {fallback}.");
			}
			return fallback;
		}
	}
	[Serializable]
	public class BridgePayload
	{
		public string type;

		public int deaths;

		public int alive;

		public int total;

		public int health;

		public string moonName;

		public string weatherFull;

		public string brutalEvent;

		public bool onMoon;

		public bool loading;

		public bool inGame;

		public int resetToken;

		public int levelScrap;

		public string topKiller;

		public string topMonster;

		public string deadliestEvent;

		public string[] monstersOutside;

		public string[] monstersInside;

		public string[] traps;

		public RunInfo run;

		public int quotaValue;

		public int quotaFulfilled;

		public int shipLoot;

		public int quotaIndex = 1;

		public int dayCount = 1;

		public int daysLeft = -1;

		public string interiorType;

		public int beehiveCount;

		public int itemsInside;

		public int itemsOutside;

		public bool hasOldBird;

		public bool onShip;
	}
	[Serializable]
	public class RunInfo
	{
		public RunQuota[] quotas;

		public RunMoon[] moons;

		public RunMonster[] monsters;

		public int peak;

		public int inside;

		public int outside;

		public int runSec;

		public string[] timeline;
	}
	[Serializable]
	public class RunQuota
	{
		public int i;

		public int money;

		public int sec;

		public int deaths;
	}
	[Serializable]
	public class RunMoon
	{
		public string name;

		public int visits;

		public int profit;

		public int sec;
	}
	[Serializable]
	public class RunMonster
	{
		public string name;

		public int count;

		public int sec;
	}
	public static class DataParser
	{
		private static string _pendingLocal;

		private static readonly object _localLock = new object();

		public static float Heartbeat = -999f;

		public static BridgePayload Current { get; private set; }

		public static void PushLocal(string json)
		{
			lock (_localLock)
			{
				_pendingLocal = json;
			}
		}

		public static string TakeLocal()
		{
			lock (_localLock)
			{
				string pendingLocal = _pendingLocal;
				_pendingLocal = null;
				return pendingLocal;
			}
		}

		public static bool TryParse(string json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return false;
			}
			BridgePayload bridgePayload;
			try
			{
				bridgePayload = JsonUtility.FromJson<BridgePayload>(json);
			}
			catch
			{
				return false;
			}
			if (bridgePayload == null || bridgePayload.type != "bridge")
			{
				return false;
			}
			Current = bridgePayload;
			return true;
		}

		public static void Clear()
		{
			Current = null;
		}
	}
	public static class EventTranslate
	{
		private static readonly HashSet<string> _loggedMiss = new HashSet<string>();

		private static readonly Dictionary<string, string> Map = new Dictionary<string, string>
		{
			["allweather"] = "Вся погода сразу",
			["antibounty"] = "Награда снята",
			["anticoilhead"] = "Анти-койлхед",
			["arachnophobia"] = "Арахнофобия",
			["baboonhorde"] = "Орда павианов",
			["badproduce"] = "Гнилой урожай",
			["bees"] = "Пчёлы",
			["berserkturrets"] = "Турели в ярости",
			["bigbonus"] = "Большой бонус",
			["bigdelivery"] = "Большая доставка",
			["birds"] = "Птицы",
			["blackfriday"] = "Чёрная пятница",
			["bonus"] = "Бонус",
			["bounty"] = "Награда за голову",
			["bracken"] = "Бракен",
			["bughorde"] = "Орда жуков",
			["butlers"] = "Дворецкие",
			["cadaver"] = "Трупоцвет",
			["catsanddogs"] = "Кошки и собаки",
			["clock"] = "Часы",
			["coilhead"] = "Койлхед",
			["controlpad"] = "Пульт управления",
			["cruiserfailure"] = "Поломка крейсера",
			["dentures"] = "Вставная челюсть",
			["dogs"] = "Слепые псы",
			["doorcircuitfailure"] = "Сбой цепи дверей",
			["doorfailure"] = "Поломка дверей",
			["dooroverdriveev"] = "Двери вразнос",
			["dustpans"] = "Совки",
			["dweller"] = "Пещерный житель",
			["earlyship"] = "Ранний отлёт",
			["eastereggs"] = "Пасхалки",
			["explodingitems"] = "Взрывающиеся предметы",
			["facilityghost"] = "Призрак комплекса",
			["flashlightsfailure"] = "Сбой фонарей",
			["flowersnake"] = "Тюльпановая змейка",
			["footballscrap"] = "Ценный мяч",
			["forestgiant"] = "Лесной великан",
			["fragileenemies"] = "Хрупкие враги",
			["fullaccess"] = "Полный доступ",
			["garbagelid"] = "Крышка мусорки",
			["giantsoutside"] = "Великаны снаружи",
			["gloomy"] = "Пасмурно",
			["goldenbars"] = "Золотые слитки",
			["goldenfacility"] = "Золотой комплекс",
			["grabbablelandmines"] = "Переносные мины",
			["grabbableturrets"] = "Переносные турели",
			["heavyrain"] = "Ливень",
			["hell"] = "Ад",
			["higherscrapvalue"] = "Лут дороже",
			["hoardingbugs"] = "Жуки-барахольщики",
			["holidayseason"] = "Праздники",
			["honk"] = "Гудок",
			["insidebees"] = "Пчёлы внутри",
			["ismetal"] = "Металлический день",
			["itemchargerfailure"] = "Сбой зарядной станции",
			["jester"] = "Шут",
			["jetpackfailure"] = "Сбой джетпака",
			["kamikaziebugs"] = "Жуки-камикадзе",
			["kidnapperfox"] = "Лис-похититель",
			["kiwibird"] = "Гигантский киви",
			["landmines"] = "Мины",
			["lateship"] = "Поздний отлёт",
			["leaflessbrowntrees"] = "Голые бурые деревья",
			["leaflesstrees"] = "Голые деревья",
			["leverfailure"] = "Сбой рычага отлёта",
			["littlegirl"] = "Девочка-призрак",
			["lizard"] = "Спор-ящер",
			["lockedentrance"] = "Запертый вход",
			["locusts"] = "Саранча",
			["manualcamerafailure"] = "Сбой камер",
			["masked"] = "Маскед",
			["maskitem"] = "Маска",
			["metalswitch"] = "Металлический рубильник",
			["meteors"] = "Метеоры",
			["moreexits"] = "Больше выходов",
			["morescrap"] = "Больше лута",
			["nobaboons"] = "Без павианов",
			["nobirds"] = "Без птиц",
			["nobracken"] = "Без бракена",
			["nobutlers"] = "Без дворецких",
			["nocoilhead"] = "Без койлхедов",
			["nodogs"] = "Без псов",
			["noghosts"] = "Без призраков",
			["nogiants"] = "Без великанов",
			["nohoardingbugs"] = "Без жуков-барахольщиков",
			["nojester"] = "Без шута",
			["nolandmines"] = "Без мин",
			["nolizards"] = "Без ящеров",
			["nomasks"] = "Без масок",
			["nonutcracker"] = "Без щелкунчиков",
			["nooldbird"] = "Без старых птиц",
			["noslimes"] = "Без слизней",
			["nosnarefleas"] = "Без блох",
			["nospiders"] = "Без пауков",
			["nospiketraps"] = "Без шипов",
			["nothing"] = "Ничего",
			["nothumpers"] = "Без тамперов",
			["notmetal"] = "Не металл",
			["noturrets"] = "Без турелей",
			["noworm"] = "Без червя",
			["nutcracker"] = "Щелкунчик",
			["nutslayer"] = "Щелкунчик-палач",
			["nutslayersmore"] = "Щелкунчики-палачи",
			["oldbirds"] = "Старые птицы (мехи)",
			["outsidelandmines"] = "Мины снаружи",
			["outsideturrets"] = "Турели снаружи",
			["pickles"] = "Огурчики",
			["plasticcup"] = "Пластиковый стаканчик",
			["plentyoutsidescrap"] = "Много лута снаружи",
			["puma"] = "Феиопар",
			["raining"] = "Дождь",
			["realityshift"] = "Сдвиг реальности",
			["safeoutside"] = "Снаружи безопасно",
			["scarceoutsidescrap"] = "Мало лута снаружи",
			["scrapgalore"] = "Лут в изобилии",
			["severedbits"] = "Расчленёнка",
			["shipcorefailure"] = "Сбой ядра корабля",
			["shiplightsfailure"] = "Сбой света корабля",
			["shipmentfees"] = "Плата за доставку",
			["sid"] = "SID",
			["slimeinside"] = "Слизень внутри",
			["slimes"] = "Слизни",
			["smalldelivery"] = "Малая доставка",
			["smallermap"] = "Карта поменьше",
			["snarefleas"] = "Блохи-душители",
			["spiders"] = "Пауки",
			["spiketraps"] = "Шипы-ловушки",
			["stingray"] = "Скат",
			["strongenemies"] = "Сильные враги",
			["sussypaintings"] = "Подозрительные картины",
			["targetingfailureevent"] = "Сбой наведения",
			["teleporterfailure"] = "Сбой телепорта",
			["teleportertraps"] = "Ловушки-телепорты",
			["teleportin"] = "Телепортация внутрь",
			["terminalfailure"] = "Сбой терминала",
			["thumpers"] = "Тамперы",
			["timechaos"] = "Хаос времени",
			["toiletpaper"] = "Туалетная бумага",
			["train"] = "Поезд",
			["transmutescrapbig"] = "Трансмутация лута (много)",
			["transmutescrapsmall"] = "Трансмутация лута (мало)",
			["trapsfailure"] = "Сбой ловушек",
			["trees"] = "Деревья",
			["turrets"] = "Турели",
			["turretseverywhere"] = "Турели повсюду",
			["veryearlyship"] = "Очень ранний отлёт",
			["verylateship"] = "Очень поздний отлёт",
			["walkiefailure"] = "Сбой рации",
			["warzone"] = "Зона боевых действий",
			["welcometothefactory"] = "Добро пожаловать на завод",
			["worms"] = "Земляные левиафаны",
			["zeddog"] = "Пёс-зомби",
			["allslayers"] = "Все палачи",
			["baddice"] = "Дурные кости",
			["baldi"] = "Балди",
			["barbers"] = "Барберы",
			["bellcrab"] = "Краб-звоночек",
			["bertha"] = "Берта",
			["bloodmoon"] = "Кровавая луна",
			["cityofgold"] = "Золотой город",
			["cleaners"] = "Чистильщики",
			["critters"] = "Мелкие твари",
			["dice"] = "Кости",
			["football"] = "Футбол",
			["forsaken"] = "Забытый",
			["foxy"] = "Фокси",
			["giantshowdown"] = "Битва великанов",
			["hallowed"] = "Хэллоуин",
			["heatwave"] = "Аномальная жара",
			["herobrine"] = "Херобрин",
			["hotbarhassle"] = "Свистопляска инвентаря",
			["hotbarmania"] = "Мания инвентаря",
			["hurricane"] = "Ураган",
			["immortalsnail"] = "Бессмертная улитка",
			["itsplaytime"] = "Playtime",
			["leafboys"] = "Листовые парни",
			["lighteaterenemy"] = "Пожиратель света",
			["lockers"] = "Локеры",
			["majoramoon"] = "Луна Majora",
			["manstalker"] = "Преследователь",
			["mantitoil"] = "Мантикоил с турелью",
			["mantitoilslayer"] = "Мантикоил-палач",
			["meltdown"] = "Расплавление реактора",
			["meteorshower"] = "Метеоритный дождь",
			["moaienemy"] = "Моаи",
			["mobileturrets"] = "Мобильные турели",
			["needycats"] = "Назойливые коты",
			["nemo"] = "Немо",
			["nofiend"] = "Без Изверга",
			["noimmortalsnails"] = "Без улиток",
			["nolockers"] = "Без локеров",
			["nomantitoil"] = "Без мантитоила",
			["nomantitoilslayer"] = "Без мантитоила-палача",
			["nopeepers"] = "Без Пиперов",
			["noshyguy"] = "Без Шайгая",
			["noslayers"] = "Без палачей",
			["notoilslayer"] = "Без тойл-палача",
			["peepers"] = "Пиперы",
			["phonesout"] = "Телефоны наружу",
			["playtimebig"] = "Playtime (большой)",
			["rollinggiants"] = "Катящиеся великаны",
			["roomba"] = "Робот-пылесос",
			["scp682"] = "SCP-682",
			["scp939"] = "SCP-939",
			["seamine"] = "Морская мина",
			["shiba"] = "Сиба-ину",
			["shockwavedrones"] = "Дроны с ударной волной",
			["shrimp"] = "Креветка",
			["shyguy"] = "Шайгай",
			["sirenhead"] = "Сиреноголовый",
			["skullenemy"] = "Череп",
			["slenderman"] = "Слендермен",
			["solarflare"] = "Солнечная вспышка",
			["souldev"] = "Souldev",
			["takeygokubracken"] = "Бракен-Гоку",
			["thefiend"] = "Изверг",
			["toilhead"] = "Тойлхед (койл с турелью)",
			["toilslayer"] = "Тойл-палач",
			["walkers"] = "Ходоки",
			["welcometoooblterra"] = "Добро пожаловать в Ooblterra",
			["windy"] = "Ветрено",
			["yeetbomb"] = "Бомба-йит"
		};

		public static string ToRu(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return raw;
			}
			string text = Norm(raw);
			if (text.Length == 0)
			{
				return raw;
			}
			if (Map.TryGetValue(text, out var value))
			{
				return value;
			}
			if (_loggedMiss.Add(text))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[event-ru] нет перевода для ивента \"" + raw + "\" (ключ: " + text + ") — показываю как есть."));
				}
			}
			return raw;
		}

		private static string Norm(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			string text = s.ToLowerInvariant();
			foreach (char c in text)
			{
				if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
	internal static class Patch_GameNetworkManager_Disconnect
	{
		private static void Postfix()
		{
			OverlayManager.Instance?.NotifyDisconnectedFromGame();
		}
	}
	public static class Localization
	{
		private static readonly Dictionary<string, string> En = new Dictionary<string, string>
		{
			["location"] = "LOCATION",
			["deadline"] = "DEADLINE PROGRESS",
			["lootQuota"] = "LOOT / QUOTA",
			["onPlanet"] = "ON PLANET",
			["day"] = "DAY",
			["left"] = "LEFT",
			["crew"] = "CREW",
			["deaths"] = "DEATHS",
			["outside"] = "OUTSIDE",
			["inside"] = "INSIDE",
			["traps"] = "TRAPS",
			["brutalEvent"] = "!! BRUTAL EVENT",
			["noData"] = "- no data -",
			["offline"] = "OFFLINE",
			["bridge"] = "BRIDGE",
			["interior"] = "INTERIOR",
			["items"] = "ITEMS",
			["in"] = "IN",
			["out"] = "OUT",
			["hives"] = "HIVES",
			["oldBird"] = "!! OLD BIRD !!",
			["met"] = "MET",
			["tMoon"] = "MOON",
			["tWx"] = "WX",
			["tQuota"] = "QUOTA",
			["tEvent"] = "EVENT",
			["tObjective"] = "OBJECTIVE: SURVIVE 3 QUOTAS",
			["vicStamp"] = "CHALLENGE",
			["vicTitle"] = "COMPLETE",
			["vicSub"] = "3 QUOTAS DONE - YOU SURVIVED",
			["vicTime"] = "TIME",
			["vicLoot"] = "LOOT",
			["vicDeaths"] = "DEATHS",
			["vicQuotas"] = "BY QUOTA",
			["vicMoons"] = "MOONS",
			["vicMonsters"] = "MOST ENCOUNTERED",
			["vicTimeline"] = "DAY BY DAY",
			["vicVisits"] = "visits",
			["vicNoLosses"] = "no losses",
			["vicEvent"] = "event",
			["vicDeath"] = "deaths"
		};

		private static readonly Dictionary<string, string> Ru = new Dictionary<string, string>
		{
			["location"] = "ЛОКАЦИЯ",
			["deadline"] = "ПРОГРЕСС КВОТЫ",
			["lootQuota"] = "ЛУТ / КВОТА",
			["onPlanet"] = "НА ПЛАНЕТЕ",
			["day"] = "ДЕНЬ",
			["left"] = "ОСТ.",
			["crew"] = "ЭКИПАЖ",
			["deaths"] = "СМЕРТИ",
			["outside"] = "УЛИЦА",
			["inside"] = "КОМПЛЕКС",
			["traps"] = "ЛОВУШКИ",
			["brutalEvent"] = "!! BRUTAL ИВЕНТ",
			["noData"] = "— нет данных —",
			["offline"] = "НЕТ СВЯЗИ",
			["bridge"] = "МОСТ",
			["interior"] = "ИНТЕРЬЕР",
			["items"] = "ПРЕДМЕТЫ",
			["in"] = "ВНУТРИ",
			["out"] = "СНАРУЖИ",
			["hives"] = "УЛЬИ",
			["oldBird"] = "!! OLD BIRD !!",
			["met"] = "ЕСТЬ",
			["tMoon"] = "ЛУНА",
			["tWx"] = "ПОГОДА",
			["tQuota"] = "КВОТА",
			["tEvent"] = "ИВЕНТ",
			["tObjective"] = "ЦЕЛЬ: ПЕРЕЖИТЬ 3 КВОТЫ",
			["vicStamp"] = "ИСПЫТАНИЕ",
			["vicTitle"] = "ПРОЙДЕНО",
			["vicSub"] = "3 КВОТЫ ВЫПОЛНЕНЫ — ТЫ ВЫЖИЛ",
			["vicTime"] = "ВРЕМЯ",
			["vicLoot"] = "ЛУТ",
			["vicDeaths"] = "СМЕРТИ",
			["vicQuotas"] = "ПО КВОТАМ",
			["vicMoons"] = "ЛУНЫ",
			["vicMonsters"] = "КОГО ВСТРЕЧАЛИ ЧАЩЕ",
			["vicTimeline"] = "ХРОНИКА ПО ДНЯМ",
			["vicVisits"] = "визитов",
			["vicNoLosses"] = "без потерь",
			["vicEvent"] = "ивент",
			["vicDeath"] = "смерти"
		};

		public static string T(string key)
		{
			if ((ConfigSettings.RussianActive ? Ru : En).TryGetValue(key, out var value))
			{
				return value;
			}
			if (En.TryGetValue(key, out var value2))
			{
				return value2;
			}
			return key;
		}
	}
	public class OverlayManager : MonoBehaviour
	{
		private const float PanelWidth = 340f;

		private const float SlideTime = 0.3f;

		private const float RailReserve = 92f;

		private const float TiltDeg = 1f;

		private Canvas _canvas;

		private bool _dirty;

		private OverlayStyle S;

		private RectTransform _root;

		private CanvasGroup _group;

		private Image _bgImage;

		private readonly List<Image> _frameImages = new List<Image>();

		private readonly List<PixbitFlicker> _pixbits = new List<PixbitFlicker>();

		private readonly List<TextMeshProUGUI> _allTexts = new List<TextMeshProUGUI>();

		private readonly List<TextMeshProUGUI> _bigTexts = new List<TextMeshProUGUI>();

		private TMP_FontAsset _fontBody;

		private TMP_FontAsset _fontBig;

		private TMP_FontAsset _dynFont;

		private bool _fontsResolved;

		private float _fontRetryT;

		private static readonly Dictionary<string, TMP_FontAsset> _osFontCache = new Dictionary<string, TMP_FontAsset>();

		private GameObject _headerGo;

		private GameObject _headerDivider;

		private GameObject _locationGo;

		private GameObject _quotaGo;

		private GameObject _dayDeathsGo;

		private GameObject _tickerGo;

		private TextMeshProUGUI _timerText;

		private EyeWidget _topEye;

		private TextMeshProUGUI _moonText;

		private TextMeshProUGUI _interiorText;

		private TextMeshProUGUI _itemsText;

		private TextMeshProUGUI _oldBirdText;

		private readonly Image[] _qtabBgs = (Image[])(object)new Image[3];

		private readonly TextMeshProUGUI[] _qtabTexts = (TextMeshProUGUI[])(object)new TextMeshProUGUI[3];

		private TextMeshProUGUI _lootQuotaText;

		private TextMeshProUGUI _barText;

		private GameObject _onPlanetGo;

		private TextMeshProUGUI _onPlanetVal;

		private RectTransform _barFill;

		private Image _barFillImg;

		private TextMeshProUGUI _dayText;

		private TextMeshProUGUI _deathsText;

		private MobRailWidget _mobRail;

		private TrapFireEffect _trapFire;

		private EventPlateWidget _eventPlate;

		private RectTransform _eventPlateRt;

		private RectTransform _trapRailRt;

		private RectTransform _trapFxRt;

		private TextMeshProUGUI _eventText;

		private TickerWidget _ticker;

		private VictoryWidget _victory;

		private bool _userHidden;

		private float _vis;

		private Quaternion _lastCamRot = Quaternion.identity;

		private float _idleT;

		private float _idleAlpha = 1f;

		private const float IdleFadeTime = 0.8f;

		private float _timerSec;

		private bool _timerRunning;

		private bool? _prevLoading;

		private bool? _prevInGame;

		private int? _prevResetToken;

		private int? _lastQuotaIndex;

		private List<BcmerEvents.EventInfo> _events = new List<BcmerEvents.EventInfo>();

		private bool _loggedFirstPacket;

		private bool _loggedParseFail;

		private bool? _loggedOnShip;

		private static readonly string[] TurretEventKeys = new string[11]
		{
			"turret", "турел", "berserk", "mobile", "everywhere", "toilhead", "toil", "hell", "quad", "artillery",
			"артилл"
		};

		private const float PauseDimAlpha = 0.06f;

		private float _pauseFade;

		private static readonly (string key, string hex)[] WxColors = new(string, string)[6]
		{
			("eclips", "#FF3A2E"),
			("storm", "#FFCF3A"),
			("rain", "#5FB6E6"),
			("flood", "#3FA9C9"),
			("fog", "#C8C2B0"),
			("dust", "#D9A05A")
		};

		private float _lastPanelH = -1f;

		private static Texture2D _scanTex;

		private static Sprite _grunge;

		public static OverlayManager Instance { get; private set; }

		internal OverlayStyle Style => S;

		private void Awake()
		{
			Instance = this;
			S = (ConfigSettings.LegacyStyleActive ? OverlayStyle.Legacy() : OverlayStyle.Game());
			BuildUi();
			if (DataParser.Current != null)
			{
				OnPayload(DataParser.Current);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			string text = DataParser.TakeLocal();
			if (text != null)
			{
				if (DataParser.TryParse(text))
				{
					if (!_loggedFirstPacket)
					{
						_loggedFirstPacket = true;
						BridgePayload current = DataParser.Current;
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)$"Первый пакет моста разобран: moon={current.moonName}, onShip={current.onShip}, inGame={current.inGame}, quota={current.shipLoot}/{current.quotaValue}");
						}
					}
					OnPayload(DataParser.Current);
				}
				else if (!_loggedParseFail)
				{
					_loggedParseFail = true;
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Не удалось разобрать пакет моста (начало): " + text.Substring(0, Math.Min(300, text.Length))));
					}
				}
			}
			HandleInput();
			if (_timerRunning)
			{
				_timerSec += Time.unscaledDeltaTime;
			}
			if (!_fontsResolved)
			{
				_fontRetryT += Time.unscaledDeltaTime;
				if (_fontRetryT >= 1f)
				{
					_fontRetryT = 0f;
					EnsureFonts();
					if (_fontsResolved)
					{
						_dirty = true;
					}
				}
			}
			UpdateVisibility(Time.unscaledDeltaTime);
			PositionTrapRail();
			if (((Component)_root).gameObject.activeSelf && ConfigSettings.ShowTimer.Value)
			{
				((TMP_Text)_timerText).text = FmtTimeMs(_timerSec);
				((Graphic)_timerText).color = (Color)(_timerRunning ? Color.white : new Color(1f, 1f, 1f, 0.6f));
			}
			if (_dirty)
			{
				_dirty = false;
				EnsureFonts();
				Refresh();
			}
			RewarpOnResize();
		}

		public void NotifyDisconnectedFromGame()
		{
			DataParser.Clear();
			_timerRunning = false;
			_prevLoading = null;
			_prevInGame = null;
			_victory?.Hide();
			_dirty = true;
		}

		private void OnPayload(BridgePayload p)
		{
			_dirty = true;
			if (_loggedOnShip != p.onShip)
			{
				_loggedOnShip = p.onShip;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("onShip={0} → панель {1} (AlwaysVisible={2})", p.onShip, p.onShip ? "разрешена" : "будет скрыта", ConfigSettings.AlwaysVisible.Value));
				}
			}
			if (_prevResetToken.HasValue && p.resetToken != _prevResetToken)
			{
				_timerSec = 0f;
				_timerRunning = false;
				_prevLoading = null;
				_prevInGame = null;
				_lastQuotaIndex = null;
				_victory?.Hide();
			}
			_prevResetToken = p.resetToken;
			if (ConfigSettings.AutoTimer.Value && (p.loading != _prevLoading || p.inGame != _prevInGame))
			{
				_timerRunning = p.inGame && !p.loading;
				_prevLoading = p.loading;
				_prevInGame = p.inGame;
			}
			_events = BcmerEvents.GetEvents();
			if (_events.Count == 0 && !string.IsNullOrEmpty(p.brutalEvent))
			{
				string[] array = p.brutalEvent.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0)
					{
						if (ConfigSettings.RussianActive)
						{
							text = EventTranslate.ToRu(text);
						}
						_events.Add(new BcmerEvents.EventInfo
						{
							Name = text,
							ColorHex = "#FFFFFF"
						});
					}
				}
			}
			int num = Mathf.Max(1, p.quotaIndex);
			if (_lastQuotaIndex.HasValue && num > _lastQuotaIndex && num >= 4 && ConfigSettings.ShowVictoryBanner.Value)
			{
				_victory.Show(p, (int)_timerSec);
			}
			_lastQuotaIndex = num;
		}

		private void HandleInput()
		{
			//IL_0008: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			if (IsTypingChat())
			{
				return;
			}
			if (KeyPressed(ConfigSettings.ToggleKeyParsed))
			{
				bool flag = DataParser.Current?.onShip ?? false;
				if (ConfigSettings.AlwaysVisible.Value || flag)
				{
					_userHidden = !_userHidden;
				}
			}
			if (KeyPressed(ConfigSettings.TimerPauseKeyParsed))
			{
				_timerRunning = !_timerRunning;
			}
			if (KeyPressed(ConfigSettings.TimerResetKeyParsed))
			{
				_timerSec = 0f;
			}
		}

		private static bool IsTypingChat()
		{
			try
			{
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				return (Object)(object)val != (Object)null && val.isTypingChat;
			}
			catch
			{
				return false;
			}
		}

		private static bool KeyPressed(Key k)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((int)k == 0)
			{
				return false;
			}
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			try
			{
				return ((ButtonControl)current[k]).wasPressedThisFrame;
			}
			catch
			{
				return false;
			}
		}

		private void UpdateVisibility(float dt)
		{
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			BridgePayload current = DataParser.Current;
			bool flag = DataParser.Current != null && Time.unscaledTime - DataParser.Heartbeat < 5f;
			bool flag2 = false;
			bool flag3 = false;
			try
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null);
				flag2 = (Object)(object)val != (Object)null;
				if ((Object)(object)val != (Object)null && (Object)(object)val.quickMenuManager != (Object)null)
				{
					flag3 = val.quickMenuManager.isMenuOpen;
				}
			}
			catch
			{
			}
			if ((Object)(object)_canvas != (Object)null)
			{
				_canvas.sortingOrder = (flag3 ? (-1000) : 500);
			}
			UpdateIdleFade(dt);
			bool flag4 = ConfigSettings.Enabled.Value && flag2 && (ConfigSettings.AlwaysVisible.Value || (flag && current != null && current.onShip)) && !_userHidden;
			_topEye?.SetOpen(flag4 ? 1f : 0f);
			_vis = Mathf.Clamp01(_vis + (flag4 ? 1f : (-1f)) * (dt / 0.3f));
			float num = EaseOutCubic(_vis);
			float num2 = 340f * ConfigSettings.Scale.Value + 200f;
			float num3 = 0f - Mathf.Max((float)ConfigSettings.RightOffsetPx.Value, 92f);
			_root.anchoredPosition = new Vector2(Mathf.Lerp(num2, num3, num), 0f);
			_pauseFade = Mathf.MoveTowards(_pauseFade, flag3 ? 1f : 0f, dt / 0.2f);
			float num4 = Mathf.Lerp(1f, 0.06f, _pauseFade);
			_group.alpha = num * _idleAlpha * num4;
			bool flag5 = _vis > 0.001f;
			if (((Component)_root).gameObject.activeSelf != flag5)
			{
				((Component)_root).gameObject.SetActive(flag5);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)(flag5 ? "Оверлей появляется." : "Оверлей скрыт."));
				}
				if (flag5)
				{
					_dirty = true;
				}
			}
		}

		private static float EaseOutCubic(float t)
		{
			return 1f - Mathf.Pow(1f - t, 3f);
		}

		private void UpdateIdleFade(float dt)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigSettings.FadeWhenIdle.Value)
			{
				_idleAlpha = 1f;
				return;
			}
			float num = 999f;
			try
			{
				PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
				Camera val2 = (((Object)(object)val != (Object)null) ? val.gameplayCamera : null);
				if ((Object)(object)val2 == (Object)null && (Object)(object)Camera.main != (Object)null)
				{
					val2 = Camera.main;
				}
				if ((Object)(object)val2 != (Object)null)
				{
					num = Quaternion.Angle(((Component)val2).transform.rotation, _lastCamRot);
					_lastCamRot = ((Component)val2).transform.rotation;
				}
			}
			catch
			{
			}
			if (num > 0.4f)
			{
				_idleT = 0f;
			}
			else
			{
				_idleT += dt;
			}
			float num2 = ((_idleT > ConfigSettings.IdleFadeSeconds.Value) ? Mathf.Clamp01(ConfigSettings.IdleMinOpacity.Value) : 1f);
			_idleAlpha = Mathf.MoveTowards(_idleAlpha, num2, dt / 0.8f);
		}

		private void Refresh()
		{
			//IL_01bb: 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_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_047b: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_053d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_0604: Unknown result type (might be due to invalid IL or missing references)
			if (!((Component)_root).gameObject.activeSelf)
			{
				return;
			}
			BridgePayload current = DataParser.Current;
			bool flag = DataParser.Current != null && Time.unscaledTime - DataParser.Heartbeat < 5f;
			bool flag2 = current?.onMoon ?