Decompiled source of LCBridgeOverlay v1.7.0

LCBridgeOverlay.dll

Decompiled a week 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 Unity.Collections;
using Unity.Netcode;
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.7.0.0")]
[assembly: AssemblyInformationalVersion("1.7.0+17fc156b395de3c6ab1418d8755000fb34456ed4")]
[assembly: AssemblyProduct("LCBridgeOverlay")]
[assembly: AssemblyTitle("LCBridgeOverlay")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.7.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 BcmeClientEvents
	{
		private class Entry
		{
			public string Header;

			public string Body;

			public bool Warn;
		}

		private static readonly List<Entry> _entries = new List<Entry>();

		private static readonly object _lock = new object();

		private static bool _knownSearched;

		private static FieldInfo[] _eventListFields;

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

		private static HashSet<string> _knownNames;

		private static float _knownRefreshed;

		private static bool _panelSearched;

		private static PropertyInfo _netInstance;

		private static PropertyInfo _netVarValue;

		private static PropertyInfo _uiInstance;

		private static FieldInfo _textUiField;

		private static FieldInfo _panelTextField;

		private static string _lastRaw;

		public static bool Any => Get().Count > 0;

		public static void OnDisplayTip(string headerText, string bodyText, bool isWarning)
		{
			try
			{
				string text = (headerText ?? "").Trim();
				string text2 = (bodyText ?? "").Trim();
				if (text.Length == 0 && text2.Length == 0)
				{
					return;
				}
				lock (_lock)
				{
					foreach (Entry entry in _entries)
					{
						if (entry.Header == text && entry.Body == text2)
						{
							return;
						}
					}
					_entries.Add(new Entry
					{
						Header = text,
						Body = text2,
						Warn = isWarning
					});
				}
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[bcme-client] анонс ивента: header=\"{text}\" body=\"{text2}\" warn={isWarning}");
				}
				BridgeTicker.ForceImmediate();
			}
			catch
			{
			}
		}

		public static void Clear()
		{
			lock (_lock)
			{
				if (_entries.Count > 0)
				{
					_entries.Clear();
				}
			}
			_lastRaw = null;
		}

		public static List<BcmerEvents.EventInfo> Get()
		{
			List<BcmerEvents.EventInfo> list = new List<BcmerEvents.EventInfo>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in NamesFromPanel())
			{
				if (hashSet.Add(item))
				{
					list.Add(new BcmerEvents.EventInfo
					{
						Name = (ConfigSettings.RussianActive ? EventTranslate.ToRu(item) : item),
						ColorHex = "#FFFFFF"
					});
				}
			}
			lock (_lock)
			{
				foreach (Entry entry in _entries)
				{
					string text = ((!string.IsNullOrEmpty(entry.Header)) ? entry.Header : entry.Body);
					if (!string.IsNullOrEmpty(text) && hashSet.Add(text))
					{
						list.Add(new BcmerEvents.EventInfo
						{
							Name = text,
							ColorHex = (entry.Warn ? "#FF5141" : "#FFFFFF")
						});
					}
				}
				return list;
			}
		}

		private static HashSet<string> KnownEventNames()
		{
			if (_knownNames != null && _knownNames.Count > 0 && Time.unscaledTime - _knownRefreshed < 30f)
			{
				return _knownNames;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			try
			{
				if (!_knownSearched)
				{
					_knownSearched = true;
					Type type = GameState.FindTypeByFullName("BrutalCompanyMinus.Minus.EventManager") ?? GameState.FindTypeFuzzy("BrutalCompany", new string[1] { "EventManager" });
					if (type != null)
					{
						string[] obj = new string[4] { "events", "vanillaEvents", "moddedEvents", "customEvents" };
						List<FieldInfo> list = new List<FieldInfo>();
						string[] array = obj;
						foreach (string name in array)
						{
							FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							if (field != null)
							{
								list.Add(field);
							}
						}
						_eventListFields = list.ToArray();
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)$"[bcme-client] списков ивентов найдено: {list.Count}");
						}
					}
				}
				if (_eventListFields == null)
				{
					return _knownNames ?? hashSet;
				}
				FieldInfo[] eventListFields = _eventListFields;
				for (int i = 0; i < eventListFields.Length; i++)
				{
					if (!(eventListFields[i].GetValue(null) is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							string text = EventName(item);
							if (!string.IsNullOrEmpty(text))
							{
								hashSet.Add(text.Trim());
							}
						}
					}
				}
			}
			catch
			{
			}
			if (hashSet.Count > 0)
			{
				if (_knownNames == null || _knownNames.Count != hashSet.Count)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)$"[bcme-client] известных имён ивентов: {hashSet.Count}");
					}
				}
				_knownNames = hashSet;
				_knownRefreshed = Time.unscaledTime;
			}
			return _knownNames ?? hashSet;
		}

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

		private static string PanelRaw()
		{
			try
			{
				if (!_panelSearched)
				{
					_panelSearched = true;
					Type type = GameState.FindTypeByFullName("BrutalCompanyMinus.Net") ?? GameState.FindTypeFuzzy("BrutalCompany", new string[1] { "Net" });
					if (type != null)
					{
						_netInstance = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						_textUiField = type.GetField("textUI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					}
					Type type2 = GameState.FindTypeByFullName("BrutalCompanyMinus.UI") ?? GameState.FindTypeFuzzy("BrutalCompany", new string[1] { "UI" });
					if (type2 != null)
					{
						_uiInstance = type2.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						_panelTextField = type2.GetField("panelText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					}
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("[bcme-client] Net.textUI=" + ((_textUiField != null) ? "OK" : "нет") + ", UI.panelText=" + ((_panelTextField != null) ? "OK" : "нет")));
					}
				}
				if (_netInstance != null && _textUiField != null)
				{
					object value = _netInstance.GetValue(null);
					if (value != null)
					{
						object value2 = _textUiField.GetValue(value);
						if (value2 != null)
						{
							if (_netVarValue == null)
							{
								_netVarValue = value2.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
							}
							string text = ((_netVarValue != null) ? _netVarValue.GetValue(value2) : null)?.ToString();
							if (!string.IsNullOrEmpty(text))
							{
								return text;
							}
						}
					}
				}
				if (_uiInstance != null && _panelTextField != null)
				{
					object value3 = _uiInstance.GetValue(null);
					if (value3 != null)
					{
						object value4 = _panelTextField.GetValue(value3);
						if (value4 != null)
						{
							PropertyInfo property = value4.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
							string text2 = ((property != null) ? property.GetValue(value4) : null)?.ToString();
							if (!string.IsNullOrEmpty(text2))
							{
								return text2;
							}
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static List<string> NamesFromPanel()
		{
			List<string> list = new List<string>();
			try
			{
				string text = PanelRaw();
				if (string.IsNullOrEmpty(text))
				{
					return list;
				}
				if (text != _lastRaw)
				{
					_lastRaw = text;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("[bcme-client] панель BCME: " + Regex.Replace(text, "<.*?>", "").Replace("\n", " | ")));
					}
				}
				HashSet<string> hashSet = KnownEventNames();
				if (hashSet == null || hashSet.Count == 0)
				{
					return list;
				}
				string[] array = text.Split('\n');
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = Regex.Replace(array[i] ?? "", "<.*?>", "").Trim();
					if (text2.Length == 0)
					{
						continue;
					}
					if (hashSet.Contains(text2))
					{
						if (!list.Contains(text2))
						{
							list.Add(text2);
						}
					}
					else
					{
						if (text2.IndexOf(',') < 0)
						{
							continue;
						}
						string[] array2 = text2.Split(',');
						for (int j = 0; j < array2.Length; j++)
						{
							string text3 = array2[j].Trim();
							if (text3.Length > 0 && hashSet.Contains(text3) && !list.Contains(text3))
							{
								list.Add(text3);
							}
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}
	}
	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
			{
			}
			if (list.Count == 0 && BcmeClientEvents.Any)
			{
				list.AddRange(BcmeClientEvents.Get());
			}
			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 bool IsRunning => _running;

		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 static volatile bool _forceNow;

		public static void ForceImmediate()
		{
			_forceNow = true;
		}

		private void Update()
		{
			_timer += Time.deltaTime;
			if (_forceNow)
			{
				_forceNow = false;
				_timer = 1f;
			}
			if (_timer < 1f)
			{
				return;
			}
			_timer = 0f;
			DataParser.Heartbeat = Time.unscaledTime;
			GameState.TickStats();
			RunStats.Tick();
			OverlayNet.Tick();
			MonsterState.Tick(GameState.GetAllLiveEnemies());
			string text = BuildJson();
			if (text != _lastPayload)
			{
				_lastPayload = text;
				if (BridgeServer.IsRunning)
				{
					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 value = (OverlayNet.HasHostState ? OverlayNet.HostDeaths : GameState.GetDeaths());
			int localHealth = GameState.GetLocalHealth();
			string moonName = GameState.GetMoonName();
			string weatherTweaksWeather = GameState.GetWeatherTweaksWeather();
			string s = ((!string.IsNullOrEmpty(weatherTweaksWeather)) ? weatherTweaksWeather : GameState.GetVanillaWeather());
			string text = (Gate.Events ? GameState.GetBrutalEvent() : null);
			List<string> list;
			List<string> list2;
			if (Gate.Monsters)
			{
				(list, list2) = GameState.GetMonsters();
			}
			else
			{
				List<string> list3 = new List<string>();
				List<string> list4 = new List<string>();
				list2 = list4;
				list = list3;
			}
			List<string> items = (Gate.Traps ? GameState.GetTraps() : new List<string>());
			bool onMoon = GameState.GetOnMoon();
			bool loading = GameState.GetLoading();
			bool inGame = GameState.GetInGame();
			int value2 = (OverlayNet.HasHostState ? OverlayNet.HostResetToken : GameState.GetResetToken());
			int value3 = (Gate.LevelScrap ? GameState.GetLevelScrap() : 0);
			(int quota, int fulfilled) quotaProgress = GameState.GetQuotaProgress();
			int item3 = quotaProgress.quota;
			int item4 = quotaProgress.fulfilled;
			int shipScrapSafe = GameState.GetShipScrapSafe();
			int quotaIndexSafe = GameState.GetQuotaIndexSafe();
			int dayCount = GameState.GetDayCount();
			int daysLeft = GameState.GetDaysLeft();
			string text2 = (Gate.Interior ? GameState.GetInterior() : null);
			int value4;
			int value5;
			int value6;
			if (Gate.LevelLoot)
			{
				(value4, value5, value6) = GameState.GetLootBreakdown();
			}
			else
			{
				value4 = 0;
				value5 = 0;
				value6 = 0;
			}
			bool oldBird = GameState.GetOldBird();
			bool onShip = GameState.GetOnShip();
			string topKiller = GameState.GetTopKiller();
			string topMonster = GameState.GetTopMonster();
			string deadliestEvent = GameState.GetDeadliestEvent();
			bool flag = GameExtras.PopupActive();
			bool flag2 = GameExtras.StoreAdActive();
			int value7 = (Gate.Countdown ? GameExtras.SecondsToEndOfDay() : (-1));
			bool flag3 = Gate.Apparatus && GameExtras.ApparatusInside();
			float num = (Gate.LootMult ? GameExtras.LootMultiplier() : 1f);
			int soldTotal = RunStats.SoldTotal;
			int num2 = list.Count + list2.Count;
			if (num2 != _lastMobCount)
			{
				_lastMobCount = num2;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("[monsters] улица={0} ({1}) | комплекс={2} ({3})", list.Count, string.Join(",", list), list2.Count, string.Join(",", list2)));
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('{');
			stringBuilder.Append("\"type\":\"bridge\",");
			stringBuilder.Append("\"deaths\":").Append(value).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(text ?? "")).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(value2).Append(',');
			stringBuilder.Append("\"levelScrap\":").Append(value3).Append(',');
			stringBuilder.Append("\"quotaValue\":").Append(item3).Append(',');
			stringBuilder.Append("\"quotaFulfilled\":").Append(item4).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(text2 ?? "")).Append(',');
			stringBuilder.Append("\"beehiveCount\":").Append(value4).Append(',');
			stringBuilder.Append("\"itemsInside\":").Append(value5).Append(',');
			stringBuilder.Append("\"itemsOutside\":").Append(value6).Append(',');
			stringBuilder.Append("\"hasOldBird\":").Append(oldBird ? "true" : "false").Append(',');
			stringBuilder.Append("\"onShip\":").Append(onShip ? "true" : "false").Append(',');
			stringBuilder.Append("\"popupActive\":").Append(flag ? "true" : "false").Append(',');
			stringBuilder.Append("\"storeAdActive\":").Append(flag2 ? "true" : "false").Append(',');
			stringBuilder.Append("\"endOfDaySec\":").Append(value7).Append(',');
			stringBuilder.Append("\"apparatusInside\":").Append(flag3 ? "true" : "false").Append(',');
			stringBuilder.Append("\"lootMultiplier\":").Append(num.ToString("0.##", CultureInfo.InvariantCulture)).Append(',');
			stringBuilder.Append("\"soldLoot\":").Append(soldTotal).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(list)).Append(',');
			stringBuilder.Append("\"monstersInside\":").Append(JsonArr(list2)).Append(',');
			stringBuilder.Append("\"traps\":").Append(JsonArr(items)).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;
		}
	}
	[HarmonyPatch(typeof(DepositItemsDesk), "SellAndDisplayItemProfits")]
	internal static class Patch_DepositItemsDesk_Sell
	{
		[HarmonyPrefix]
		public static void Prefix(int profit)
		{
			try
			{
				if (profit > 0)
				{
					RunStats.AddSold(profit);
				}
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[sold] продано на {profit} (всего за забег: {RunStats.SoldTotal})");
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class Patch_EnemyAI_HitEnemy
	{
		[HarmonyTargetMethods]
		internal static IEnumerable<MethodBase> Targets()
		{
			List<MethodBase> list = new List<MethodBase>();
			MethodInfo methodInfo = AccessTools.Method(typeof(EnemyAI), "HitEnemy", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				list.Add(methodInfo);
			}
			try
			{
				Type[] types = typeof(EnemyAI).Assembly.GetTypes();
				foreach (Type type in types)
				{
					if (!(type == null) && type.IsSubclassOf(typeof(EnemyAI)))
					{
						MethodInfo methodInfo2 = null;
						try
						{
							methodInfo2 = type.GetMethod("HitEnemy", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						}
						catch
						{
						}
						if (methodInfo2 != null && !methodInfo2.IsAbstract)
						{
							list.Add(methodInfo2);
						}
					}
				}
			}
			catch
			{
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"[hit] патчим HitEnemy: {list.Count} методов (база + переопределения).");
			}
			return list;
		}

		[HarmonyPostfix]
		public static void Postfix(EnemyAI __instance, int force)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && force > 0)
				{
					MonsterState.MarkHurt(((Object)__instance).GetInstanceID());
					string text = null;
					try
					{
						text = EnemyResolver.Resolve(__instance);
					}
					catch
					{
					}
					if (!string.IsNullOrEmpty(text))
					{
						OverlayManager.Instance?.FlashMonster(text, __instance.isOutside);
					}
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(StartMatchLever), "PullLever")]
	internal static class Patch_StartMatchLever_PullLever
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			try
			{
				RunSnapshot.OnLeverPulled();
				OverlayManager.Instance?.HideAnalytics();
			}
			catch
			{
			}
		}
	}
	internal static class RunSnapshot
	{
		public static string LastRunJson { get; private set; }

		public static bool ShowLastRun { get; private set; }

		public static void CaptureRunEnd()
		{
			try
			{
				LastRunJson = RunStats.ToJson();
				ShowLastRun = true;
				GameState.BumpResetToken();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[run] забег завершён — аналитика сохранена, таймер сброшен.");
				}
			}
			catch
			{
			}
		}

		public static void OnLeverPulled()
		{
			if (ShowLastRun || LastRunJson != null)
			{
				ShowLastRun = false;
				LastRunJson = null;
				RunStats.ResetRun();
				GameState.ResetDeaths();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[run] рычаг — статистика забега сброшена, начинаем новый.");
				}
			}
		}

		public static void ResetForNewSave()
		{
			ShowLastRun = false;
			LastRunJson = null;
			GameState.ResetDeaths();
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"[run] новый сейв — статистика и таймер обнулены.");
			}
		}
	}
	internal static class GameExtras
	{
		private const float AdMaxSeconds = 14f;

		private static float _adStarted = -1f;

		private static bool _mdSearched;

		private static Type _mdHandlerType;

		private static PropertyInfo _mdTimeLeft;

		private static PropertyInfo _mdStartedApi;

		private static bool _multSearched;

		private static FieldInfo _bcmeMulField;

		private static FieldInfo _bcmeMulNetField;

		private static PropertyInfo _bcmeMulNetInstance;

		private static PropertyInfo _bcmeMulNetValue;

		private static MethodInfo _wrGetCurrent;

		private static PropertyInfo _wrScrapEnabled;

		private static PropertyInfo _weatherScrapProp;

		private const BindingFlags F = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const float NearDoor = 14f;

		private static int _lastDoorId = int.MinValue;

		private static bool _lastDoorSide;

		public static bool PopupActive()
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				if (IsAnimatorShowing(instance.endgameStatsAnimator, "display", "displayStats", "showStats"))
				{
					return true;
				}
				if (IsAnimatorShowing(instance.globalNotificationAnimator, "display", "displayNotification"))
				{
					return true;
				}
				if (GetFloat(instance, "displayTipTextTimer") > 0.05f)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public static bool StoreAdActive()
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					_adStarted = -1f;
					return false;
				}
				bool num = IsAnimatorShowing(instance.advertAnimator, "display", "displayAd", "showAd");
				bool flag = (Object)(object)instance.advertAnimator != (Object)null && ((Component)instance.advertAnimator).gameObject.activeInHierarchy;
				if (num || flag)
				{
					if (_adStarted < 0f)
					{
						_adStarted = Time.unscaledTime;
					}
					if (Time.unscaledTime - _adStarted > 14f)
					{
						return false;
					}
					return true;
				}
				_adStarted = -1f;
			}
			catch
			{
				_adStarted = -1f;
			}
			return false;
		}

		public static int SecondsToEndOfDay()
		{
			try
			{
				TimeOfDay instance = TimeOfDay.Instance;
				StartOfRound instance2 = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || !instance2.shipHasLanded)
				{
					return -1;
				}
				float totalTime = instance.totalTime;
				if (totalTime <= 0f)
				{
					return -1;
				}
				float num = instance.normalizedTimeOfDay;
				if (num < 0f)
				{
					num = Mathf.Clamp01(instance.currentDayTime / totalTime);
				}
				float num2 = 1f;
				float shipLeaveAutomaticallyTime = instance.shipLeaveAutomaticallyTime;
				if (shipLeaveAutomaticallyTime > 0f && shipLeaveAutomaticallyTime <= 1f)
				{
					num2 = shipLeaveAutomaticallyTime;
				}
				float num3 = num2 - num;
				if (num3 <= 0f)
				{
					return 0;
				}
				float num4 = Mathf.Max(0.0001f, instance.globalTimeSpeedMultiplier);
				float num5 = totalTime / num4;
				int num6 = Mathf.RoundToInt(num3 * num5);
				num6 = Mathf.Clamp(num6, 0, 86400);
				int num7 = MeltdownSecondsLeft();
				if (num7 >= 0 && num7 < num6)
				{
					num6 = num7;
				}
				return num6;
			}
			catch
			{
				return -1;
			}
		}

		public static int MeltdownSecondsLeft()
		{
			try
			{
				if (!_mdSearched)
				{
					_mdSearched = true;
					_mdHandlerType = GameState.FindTypeFuzzy("FacilityMeltdown", new string[1] { "MeltdownHandler" });
					if (_mdHandlerType != null)
					{
						_mdTimeLeft = _mdHandlerType.GetProperty("TimeLeftUntilMeltdown", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					}
					Type type = GameState.FindTypeFuzzy("FacilityMeltdown", new string[1] { "MeltdownAPI" });
					if (type != null)
					{
						_mdStartedApi = type.GetProperty("MeltdownStarted", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("[meltdown] handler=" + ((_mdHandlerType != null) ? "OK" : "нет") + ", TimeLeftUntilMeltdown=" + ((_mdTimeLeft != null) ? "OK" : "нет")));
					}
				}
				if (_mdHandlerType == null || _mdTimeLeft == null)
				{
					return -1;
				}
				if (_mdStartedApi != null)
				{
					object value = _mdStartedApi.GetValue(null);
					if (value is bool && !(bool)value)
					{
						return -1;
					}
				}
				Object val = Object.FindObjectOfType(_mdHandlerType);
				if (val == (Object)null)
				{
					return -1;
				}
				if (!(_mdTimeLeft.GetValue(val) is float num) || num < 0f)
				{
					return -1;
				}
				return Mathf.Clamp(Mathf.RoundToInt(num), 0, 86400);
			}
			catch
			{
				return -1;
			}
		}

		public static bool ApparatusInside()
		{
			try
			{
				LungProp[] array = Object.FindObjectsOfType<LungProp>();
				if (array == null || array.Length == 0)
				{
					return false;
				}
				LungProp[] array2 = array;
				foreach (LungProp val in array2)
				{
					if (!((Object)(object)val == (Object)null))
					{
						if (val.isLungPowered)
						{
							return true;
						}
						GrabbableObject val2 = (GrabbableObject)(object)val;
						if ((Object)(object)val2 != (Object)null && val2.isInFactory && !val2.isHeld)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static float WeatherScrapMultiplier()
		{
			try
			{
				if (_wrGetCurrent == null)
				{
					return -1f;
				}
				if (_wrScrapEnabled != null)
				{
					object value = _wrScrapEnabled.GetValue(null);
					if (value is bool && !(bool)value)
					{
						return -1f;
					}
				}
				StartOfRound instance = StartOfRound.Instance;
				SelectableLevel val = (((Object)(object)instance != (Object)null) ? instance.currentLevel : null);
				if ((Object)(object)val == (Object)null)
				{
					return -1f;
				}
				object obj = _wrGetCurrent.Invoke(null, new object[1] { val });
				if (obj == null)
				{
					return -1f;
				}
				if (_weatherScrapProp == null || _weatherScrapProp.DeclaringType != obj.GetType())
				{
					_weatherScrapProp = obj.GetType().GetProperty("ScrapValueMultiplier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if (_weatherScrapProp == null)
				{
					return -1f;
				}
				return (_weatherScrapProp.GetValue(obj) is float num) ? num : (-1f);
			}
			catch
			{
				return -1f;
			}
		}

		public static float LootMultiplier()
		{
			float num = 1f;
			try
			{
				if (!_multSearched)
				{
					_multSearched = true;
					_bcmeMulField = FindStaticFloatFieldInAssembly("BrutalCompany", new string[1] { "Manager" }, new string[1] { "scrapValueMultiplier" });
					Type type = GameState.FindTypeByFullName("WeatherRegistry.WeatherManager") ?? GameState.FindTypeFuzzy("WeatherRegistry", new string[1] { "WeatherManager" });
					if (type != null)
					{
						_wrGetCurrent = type.GetMethod("GetCurrentWeather", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(SelectableLevel) }, null);
					}
					Type type2 = GameState.FindTypeFuzzy("WeatherRegistry", new string[1] { "Settings" });
					if (type2 != null)
					{
						_wrScrapEnabled = type2.GetProperty("ScrapMultipliers", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					try
					{
						Type type3 = GameState.FindTypeByFullName("BrutalCompanyMinus.Net") ?? GameState.FindTypeFuzzy("BrutalCompany", new string[1] { "Net" });
						if (type3 != null)
						{
							_bcmeMulNetInstance = type3.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							FieldInfo[] fields = type3.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							foreach (FieldInfo fieldInfo in fields)
							{
								string text = fieldInfo.Name.ToLowerInvariant();
								if (text.Contains("mult") && (text.Contains("scrap") || text.Contains("value") || text.Contains("loot")) && fieldInfo.FieldType.Name.StartsWith("NetworkVariable"))
								{
									_bcmeMulNetField = fieldInfo;
									ManualLogSource log = Plugin.Log;
									if (log != null)
									{
										log.LogInfo((object)("[loot-mult] сетевой множитель BCME: Net." + fieldInfo.Name));
									}
									break;
								}
							}
						}
					}
					catch
					{
					}
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)("[loot-mult] BCME-поле=" + ((_bcmeMulField != null) ? (_bcmeMulField.DeclaringType?.Name + "." + _bcmeMulField.Name) : "не найдено") + ", WeatherRegistry.GetCurrentWeather=" + ((_wrGetCurrent != null) ? "OK" : "не найден")));
					}
				}
				float num2 = BcmeMultiplier();
				if (num2 > 0f)
				{
					num += num2 - 1f;
				}
				float num3 = WeatherScrapMultiplier();
				if (num3 > 0f)
				{
					num += num3 - 1f;
				}
			}
			catch
			{
			}
			return Mathf.Clamp(num, 0f, 100f);
		}

		private static float BcmeMultiplier()
		{
			try
			{
				if (_bcmeMulNetField != null && _bcmeMulNetInstance != null)
				{
					object value = _bcmeMulNetInstance.GetValue(null);
					if (value != null)
					{
						object value2 = _bcmeMulNetField.GetValue(value);
						if (value2 != null)
						{
							if (_bcmeMulNetValue == null)
							{
								_bcmeMulNetValue = value2.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
							}
							if (((_bcmeMulNetValue != null) ? _bcmeMulNetValue.GetValue(value2) : null) is float num && num > 0f)
							{
								return num;
							}
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				if (_bcmeMulField != null && _bcmeMulField.GetValue(null) is float num2 && num2 > 0f)
				{
					return num2;
				}
			}
			catch
			{
			}
			return -1f;
		}

		private static FieldInfo FindStaticFloatFieldInAssembly(string asmContains, string[] typeNames, string[] fieldNames)
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if ((assembly.GetName().Name ?? "").IndexOf(asmContains, StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					Type[] array;
					Type[] types;
					try
					{
						array = assembly.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						List<Type> list = new List<Type>();
						types = ex.Types;
						foreach (Type type in types)
						{
							if (type != null)
							{
								list.Add(type);
							}
						}
						array = list.ToArray();
					}
					types = array;
					foreach (Type type2 in types)
					{
						bool flag = false;
						string[] array2 = typeNames;
						foreach (string b in array2)
						{
							if (string.Equals(type2.Name, b, StringComparison.OrdinalIgnoreCase))
							{
								flag = true;
								break;
							}
						}
						if (!flag)
						{
							continue;
						}
						array2 = fieldNames;
						foreach (string name in array2)
						{
							try
							{
								FieldInfo field = type2.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
								if (field != null && field.FieldType == typeof(float))
								{
									return field;
								}
							}
							catch
							{
							}
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool IsAnimatorShowing(Animator anim, params string[] boolParams)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			try
			{
				if ((Object)(object)anim == (Object)null || !((Component)anim).gameObject.activeInHierarchy)
				{
					return false;
				}
				AnimatorControllerParameter[] parameters = anim.parameters;
				foreach (AnimatorControllerParameter val in parameters)
				{
					if ((int)val.type != 4)
					{
						continue;
					}
					foreach (string b in boolParams)
					{
						if (string.Equals(val.name, b, StringComparison.OrdinalIgnoreCase) && anim.GetBool(val.nameHash))
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static object GetObj(object o, string name)
		{
			try
			{
				if (o == null)
				{
					return null;
				}
				FieldInfo field = o.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(o);
				}
				PropertyInfo property = o.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead)
				{
					return property.GetValue(o);
				}
			}
			catch
			{
			}
			return null;
		}

		private static float GetFloat(object o, string name)
		{
			object obj = GetObj(o, name);
			if (obj is float)
			{
				return (float)obj;
			}
			return 0f;
		}

		public static bool DoorProbe(Vector3 playerPos, out Vector3 otherSide, out bool otherSideIsInside)
		{
			//IL_0001: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			otherSide = Vector3.zero;
			otherSideIsInside = false;
			try
			{
				EntranceTeleport[] array = Object.FindObjectsOfType<EntranceTeleport>();
				if (array == null || array.Length == 0)
				{
					return false;
				}
				EntranceTeleport val = null;
				float num = 196f;
				EntranceTeleport[] array2 = array;
				foreach (EntranceTeleport val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						Vector3 val3 = (((Object)(object)val2.entrancePoint != (Object)null) ? val2.entrancePoint.position : ((Component)val2).transform.position) - playerPos;
						float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
						if (sqrMagnitude < num)
						{
							num = sqrMagnitude;
							val = val2;
						}
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				array2 = array;
				foreach (EntranceTeleport val4 in array2)
				{
					if ((Object)(object)val4 == (Object)null || (Object)(object)val4 == (Object)(object)val || val4.entranceId != val.entranceId || val4.isEntranceToBuilding == val.isEntranceToBuilding)
					{
						continue;
					}
					otherSide = (((Object)(object)val4.entrancePoint != (Object)null) ? val4.entrancePoint.position : ((Component)val4).transform.position);
					otherSideIsInside = val.isEntranceToBuilding;
					if (_lastDoorId != val.entranceId || _lastDoorSide != val.isEntranceToBuilding)
					{
						_lastDoorId = val.entranceId;
						_lastDoorSide = val.isEntranceToBuilding;
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)($"[door] у двери id={val.entranceId} " + "(" + (val.isEntranceToBuilding ? "вход внутрь" : "выход наружу") + "), " + $"смотрим за неё в радиусе {ConfigSettings.DoorRadarRadius.Value:0} м."));
						}
					}
					return true;
				}
			}
			catch
			{
			}
			return false;
		}
	}
	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 int _deathsBaseline;

		private static bool _baselineSet;

		private static int _deathsShown;

		private static int _deathsPending;

		private static bool _wasOnMoonDeaths;

		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 Type _grabTurret;

		private static Type _grabMine;

		private static bool _grabSearched;

		private static int _landedScrap;

		private static bool _wasLanded;

		private static bool _scrapLocked;

		private static float _scrapSettleUntil;

		private const float ScrapSettleSeconds = 20f;

		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 BumpResetToken()
		{
			_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();
			_baselineSet = false;
			_deathsShown = 0;
			_deathsPending = 0;
			_resetToken++;
			_killerCounts.Clear();
			_eventDeaths.Clear();
			_monsterSeen.Clear();
			RunStats.ResetRun();
		}

		public static int GetDeaths()
		{
			try
			{
				int num = _deaths;
				if (ConfigSettings.TeamDeaths.Value)
				{
					StartOfRound instance = StartOfRound.Instance;
					if ((Object)(object)instance != (Object)null && instance.gameStats != null)
					{
						int num2 = Mathf.Max(instance.gameStats.deaths, 0);
						if (!_baselineSet)
						{
							_deathsBaseline = num2;
							_baselineSet = true;
						}
						if (num2 < _deathsBaseline)
						{
							_deathsBaseline = num2;
						}
						num = num2 - _deathsBaseline;
					}
				}
				_deathsPending = num;
				if (!ConfigSettings.DeathsOnlyOnLeave.Value)
				{
					_deathsShown = num;
					return _deathsShown;
				}
				bool onMoon = GetOnMoon();
				if (_wasOnMoonDeaths && !onMoon)
				{
					_deathsShown = _deathsPending;
				}
				_wasOnMoonDeaths = onMoon;
				return _deathsShown;
			}
			catch
			{
				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()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			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>();
				Dictionary<string, float> dictionary3 = new Dictionary<string, float>();
				Dictionary<string, float> dictionary4 = new Dictionary<string, float>();
				Vector3 val = Vector3.zero;
				bool flag = false;
				try
				{
					PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
					if ((Object)(object)val2 != (Object)null)
					{
						val = ((Component)val2).transform.position;
						flag = true;
					}
				}
				catch
				{
				}
				Vector3 otherSide = Vector3.zero;
				bool flag2 = false;
				bool otherSideIsInside = false;
				float num = 0f;
				if (flag && Gate.DoorRadar)
				{
					flag2 = GameExtras.DoorProbe(val, out otherSide, out otherSideIsInside);
					num = Mathf.Clamp(Gate.DoorRadius, 5f, 60f);
				}
				foreach (EnemyAI allLiveEnemy in GetAllLiveEnemies())
				{
					if ((Object)(object)allLiveEnemy == (Object)null || allLiveEnemy.isEnemyDead || !MonsterState.VisibleToLocal(allLiveEnemy))
					{
						continue;
					}
					string text = "Unknown";
					try
					{
						string text2 = EnemyResolver.Resolve(allLiveEnemy);
						if (!string.IsNullOrEmpty(text2))
						{
							text = text2;
						}
					}
					catch
					{
					}
					text += MonsterState.TokensFor(allLiveEnemy);
					Dictionary<string, int> obj3 = (allLiveEnemy.isOutside ? dictionary : dictionary2);
					obj3.TryGetValue(text, out var value);
					obj3[text] = value + 1;
					if (!flag)
					{
						continue;
					}
					float num2 = Vector3.Distance(val, ((Component)allLiveEnemy).transform.position);
					if (flag2 && allLiveEnemy.isOutside != otherSideIsInside)
					{
						float num3 = Vector3.Distance(otherSide, ((Component)allLiveEnemy).transform.position);
						if (num3 <= num && num3 < num2)
						{
							num2 = num3;
						}
					}
					Dictionary<string, float> dictionary5 = (allLiveEnemy.isOutside ? dictionary3 : dictionary4);
					if (!dictionary5.TryGetValue(text, out var value2) || num2 < value2)
					{
						dictionary5[text] = num2;
					}
				}
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					list.Add(Fmt(item.Key, item.Value, dictionary3));
				}
				foreach (KeyValuePair<string, int> item2 in dictionary2)
				{
					list2.Add(Fmt(item2.Key, item2.Value, dictionary4));
				}
				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);
		}

		private static string Fmt(string name, int count, Dictionary<string, float> dist)
		{
			string text = ((count > 1) ? $"{name} x{count}" : name);
			if (dist != null && dist.TryGetValue(name, out var value))
			{
				text = text + " @" + Mathf.RoundToInt(value);
			}
			return text;
		}

		private static bool OnShip(Vector3 p)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				Vector3 val = p + Vector3.up * 0.25f;
				Bounds bounds;
				if ((Object)(object)instance.shipBounds != (Object)null)
				{
					bounds = instance.shipBounds.bounds;
					if (((Bounds)(ref bounds)).Contains(val))
					{
						return true;
					}
				}
				if ((Object)(object)instance.shipInnerRoomBounds != (Object)null)
				{
					bounds = instance.shipInnerRoomBounds.bounds;
					if (((Bounds)(ref bounds)).Contains(val))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public static List<string> GetTraps()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			Dictionary<string, List<Vector3>> pos;
			try
			{
				bool needScan = Gate.RequireScan;
				pos = new Dictionary<string, List<Vector3>>();
				if (!_grabSearched)
				{
					_grabSearched = true;
					_grabTurret = FindTypeByFullName("BrutalCompanyMinus.Minus.MonoBehaviours.GrabbableTurret");
					_grabMine = FindTypeByFullName("BrutalCompanyMinus.Minus.MonoBehaviours.GrabbableLandmine");
				}
				CollectType<Turret>("Turret");
				CollectType<Landmine>("Landmine");
				CollectType<SpikeRoofTrap>("Spike Trap");
				CollectGrabbable(_grabTurret, "Turret");
				CollectGrabbable(_grabMine, "Landmine");
				Vector3 val = Vector3.zero;
				bool flag = false;
				try
				{
					PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
					if ((Object)(object)val2 != (Object)null)
					{
						val = ((Component)val2).transform.position;
						flag = true;
					}
				}
				catch
				{
				}
				foreach (KeyValuePair<string, List<Vector3>> item in pos)
				{
					int count = item.Value.Count;
					string text = ((count > 1) ? $"{item.Key} x{count}" : item.Key);
					if (flag && count > 0)
					{
						float num = float.MaxValue;
						foreach (Vector3 item2 in item.Value)
						{
							float num2 = Vector3.Distance(val, item2);
							if (num2 < num)
							{
								num = num2;
							}
						}
						if (num < float.MaxValue)
						{
							text = text + " @" + Mathf.RoundToInt(num);
						}
					}
					list.Add(text);
				}
				void CollectGrabbable(Type t, string label)
				{
					//IL_0050: Unknown result type (might be due to invalid IL or missing references)
					//IL_007b: Unknown result type (might be due to invalid IL or missing references)
					try
					{
						if (!(t == null))
						{
							Object[] array = Object.FindObjectsOfType(t);
							if (array != null)
							{
								Object[] array2 = array;
								foreach (Object obj2 in array2)
								{
									GrabbableObject val3 = (GrabbableObject)(object)((obj2 is GrabbableObject) ? obj2 : null);
									if (!((Object)(object)val3 == (Object)null) && !val3.isHeld && !val3.isHeldByEnemy && !val3.isInShipRoom && !OnShip(((Component)val3).transform.position) && (!needScan || !ScanRegistry.Scannable((Component)(object)val3) || ScanRegistry.HasFor((Component)(object)val3)))
									{
										Add(label, ((Component)val3).transform.position);
									}
								}
							}
						}
					}
					catch
					{
					}
				}
				void CollectType<T>(string label) where T : Component
				{
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					//IL_006c: Unknown result type (might be due to invalid IL or missing references)
					try
					{
						T[] array = Object.FindObjectsOfType<T>();
						if (array != null)
						{
							T[] array2 = array;
							foreach (T val3 in array2)
							{
								if (!((Object)(object)val3 == (Object)null) && !OnShip(((Component)val3).transform.position) && (!needScan || !ScanRegistry.Scannable((Component)(object)val3) || ScanRegistry.HasFor((Component)(object)val3)))
								{
									Add(label, ((Component)val3).transform.position);
								}
							}
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("GetTraps fail: " + ex.Message));
				}
			}
			return list;
			void Add(string label, Vector3 p)
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				if (!pos.TryGetValue(label, out var value))
				{
					value = new List<Vector3>();
					pos[label] = value;
				}
				value.Add(p);
			}
		}

		public static int GetLevelScrap()
		{
			try
			{
				RoundManager instance = RoundManager.Instance;
				if (GetOnMoon())
				{
					if (!_wasLanded)
					{
						_wasLanded = true;
						_scrapLocked = false;
						_landedScrap = 0;
						_scrapSettleUntil = Time.unscaledTime + 20f;
					}
					if ((Object)(object)instance != (Object)null && !_scrapLocked)
					{
						int num = (int)instance.totalScrapValueInLevel;
						if (num > _landedScrap)
						{
							_landedScrap = num;
						}
						if (_landedScrap > 0 && Time.unscaledTime >= _scrapSettleUntil)
						{
							_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 не найден (мод выключен?)");
						}
					}
				}
				List<string> list = new List<string>();
				if (_curEventsField != null && _curEventsField.GetValue(null) is IEnumerable enumerable)
				{
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							string eventName = GetEventName(item);
							if (!string.IsNullOrEmpty(eventName))
							{
								list.Add(eventName);
							}
						}
					}
				}
				if (list.Count == 0 && OverlayNet.HasHostState && !string.IsNullOrEmpty(OverlayNet.HostEvents))
				{
					string[] array = OverlayNet.HostEvents.Split(',');
					for (int i = 0; i < array.Length; i++)
					{
						string text = array[i].Trim();
						if (text.Length > 0)
						{
							list.Add(text);
						}
					}
				}
				if (list.Count == 0)
				{
					foreach (BcmerEvents.EventInfo item2 in BcmeClientEvents.Get())
					{
						if (!string.IsNullOrEmpty(item2.Name))
						{
							list.Add(item2.Name);
						}
					}
				}
				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;
			}
		}

		internal 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;
		}

		internal 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;
		}
	}
	internal static class MonsterState
	{
		private const float Interval = 0.5f;

		private static float _next;

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

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

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

		private static bool _loggedOnce;

		private const float HurtHold = 1.6f;

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

		private static Terminal _terminal;

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

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

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

		private static readonly Dictionary<string, MemberInfo> _members = new Dictionary<string, MemberInfo>();

		private const BindingFlags F = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public static void Reset()
		{
			_tokens.Clear();
			_hauntTarget.Clear();
			_scanned.Clear();
			_scanIdCache.Clear();
			_hurt.Clear();
			_deviant.Clear();
			_windMax.Clear();
			_terminal = null;
		}

		public static void MarkHurt(int instanceId)
		{
			try
			{
				_hurt[instanceId] = Time.unscaledTime;
				_next = 0f;
				BridgeTicker.ForceImmediate();
			}
			catch
			{
			}
		}

		private static bool IsScannedByBestiary(EnemyAI ai)
		{
			try
			{
				if ((Object)(object)_terminal == (Object)null)
				{
					_terminal = Object.FindObjectOfType<Terminal>();
				}
				if ((Object)(object)_terminal == (Object)null || _terminal.scannedEnemyIDs == null)
				{
					return false;
				}
				int instanceID = ((Object)ai).GetInstanceID();
				if (!_scanIdCache.TryGetValue(instanceID, out var value))
				{
					value = -1;
					ScanNodeProperties componentInChildren = ((Component)ai).GetComponentInChildren<ScanNodeProperties>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						value = componentInChildren.creatureScanID;
					}
					_scanIdCache[instanceID] = value;
				}
				return value >= 0 && _terminal.scannedEnemyIDs.Contains(value);
			}
			catch
			{
				return false;
			}
		}

		public static void MarkScanned(int instanceId)
		{
			_scanned.Add(instanceId);
		}

		public static bool IsScanned(int instanceId)
		{
			return _scanned.Contains(instanceId);
		}

		public static string TokensFor(EnemyAI ai)
		{
			if ((Object)(object)ai == (Object)null)
			{
				return "";
			}
			if (!_tokens.TryGetValue(((Object)ai).GetInstanceID(), out var value))
			{
				return "";
			}
			return value;
		}

		public static bool VisibleToLocal(EnemyAI ai)
		{
			try
			{
				if ((Object)(object)ai == (Object)null)
				{
					return true;
				}
				int instanceID = ((Object)ai).GetInstanceID();
				if (!_hauntTarget.TryGetValue(instanceID, out var value))
				{
					return true;
				}
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return value == ((Object)val).GetInstanceID();
			}
			catch
			{
				return true;
			}
		}

		public static void Tick(List<EnemyAI> enemies)
		{
			try
			{
				if (Time.unscaledTime < _next)
				{
					return;
				}
				_next = Time.unscaledTime + 0.5f;
				if (enemies == null)
				{
					return;
				}
				_tokens.Clear();
				_hauntTarget.Clear();
				foreach (EnemyAI enemy in enemies)
				{
					if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead)
					{
						continue;
					}
					string text = ((object)enemy).GetType().Name ?? "";
					string text2 = "";
					int num = 0;
					try
					{
						num = enemy.currentBehaviourStateIndex;
					}
					catch
					{
					}
					if (text.IndexOf("Hoarder", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (GetBool(enemy, "isAngry") || GetBool(enemy, "inChase"))
						{
							text2 += "+Aggro";
						}
					}
					else if (text.IndexOf("Jester", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						int instanceID = ((Object)enemy).GetInstanceID();
						if (num >= 2)
						{
							text2 += "+Angry";
							_windMax.Remove(instanceID);
						}
						else if (num == 1)
						{
							float num2 = GetFloat(enemy, "popUpTimer");
							if (!_windMax.TryGetValue(instanceID, out var value) || num2 > value)
							{
								value = num2;
								_windMax[instanceID] = value;
							}
							float num3 = ((value > 0.01f) ? Mathf.Clamp01(1f - num2 / value) : 0f);
							text2 = text2 + "+w" + Mathf.Clamp(Mathf.RoundToInt(num3 * 9f), 0, 9);
						}
						else
						{
							_windMax.Remove(instanceID);
						}
					}
					else if (text.IndexOf("CaveDweller", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Maneater", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						bool flag = false;
						object obj2 = GetObj(enemy, "adultContainer");
						GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null);
						if ((Object)(object)val != (Object)null)
						{
							flag = val.activeSelf;
						}
						if (!flag)
						{
							flag = GetBool(enemy, "adultMode", "grownUp", "isAdult");
						}
						if (flag)
						{
							text2 += "+Adult";
						}
					}
					else if (text.IndexOf("Nutcracker", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (GetBool(enemy, "aimingGun") || GetBool(enemy, "isInspecting") || GetBool(enemy, "torsoTurning"))
						{
							text2 += "+Attack";
						}
					}
					else if (text.IndexOf("Centipede", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("SnareFlea", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (GetBool(enemy, "clingingToCeiling", "onCeiling", "hangingOnCeiling"))
						{
							text2 += "+Ceiling";
						}
					}
					else if (text.IndexOf("SpringMan", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Coil", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						bool flag2 = GetBool(enemy, "hasStopped", "stoppingMovement");
						if (!flag2)
						{
							flag2 = AnyPlayerLookingAt(((Component)enemy).transform);
						}
						if (flag2)
						{
							text2 += "+Frozen";
						}
					}
					else if (text.IndexOf("DressGirl", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("GhostGirl", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						object obj3 = GetObj(enemy, "hauntingPlayer", "targetPlayer", "hauntedPlayer");
						PlayerControllerB val2 = (PlayerControllerB)((obj3 is PlayerControllerB) ? obj3 : null);
						if ((Object)(object)val2 != (Object)null)
						{
							_hauntTarget[((Object)enemy).GetInstanceID()] = ((Object)val2).GetInstanceID();
						}
					}
					if (IsTurretFiring(enemy))
					{
						text2 += "+Firing";
					}
					if (IsDeviant(enemy))
					{
						text2 += "+Deviant";
					}
					if (ConfigSettings.DamageFlash.Value && _hurt.TryGetValue(((Object)enemy).GetInstanceID(), out var value2) && Time.unscaledTime - value2 <= 1.6f)
					{
						text2 += "+Hurt";
					}
					if ((!Gate.ResetScansDaily && IsScannedByBestiary(enemy)) || _scanned.Contains(((Object)enemy).GetInstanceID()) || ScanRegistry.HasFor((Component)(object)enemy))
					{
						text2 += "+Scanned";
					}
					if (text2.Length > 0)
					{
						_tokens[((Object)enemy).GetInstanceID()] = text2;
					}
				}
				if (_loggedOnce || _tokens.Count <= 0)
				{
					return;
				}
				_loggedOnce = true;
				StringBuilder stringBuilder = new StringBuilder();
				foreach (KeyValuePair<int, string> token in _tokens)
				{
					stringBuilder.Append(token.Value).Append(' ');
				}
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[states] первые состояния: " + stringBuilder));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogDebug((object)("MonsterState.Tick: " + ex.Message));
				}
			}
		}

		private static bool IsDeviant(EnemyAI ai)
		{
			try
			{
				int instanceID = ((Object)ai).GetInstanceID();
				if (_deviant.Contains(instanceID))
				{
					return true;
				}
				Component[] componentsInChildren = ((Component)ai).GetComponentsInChildren<Component>(true);
				if (componentsInChildren == null)
				{
					return false;
				}
				Component[] array = componentsInChildren;
				foreach (Component val in array)
				{
					if (!((Object)(object)val == (Object)null) && string.Equals(((object)val).GetType().Name, "DeviantMarker", StringComparison.Ordinal))
					{
						_deviant.Add(instanceID);
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)("[deviant] " + ((object)ai).GetType().Name + " помечен как девиант — иконка перевёрнута."));
						}
						BridgeTicker.ForceImmediate();
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool IsTurretFiring(EnemyAI ai)
		{
			try
			{
				Component[] componentsInChildren = ((Component)ai).GetComponentsInChildren<Component>(true);
				if (componentsInChildren == null)
				{
					return false;
				}
				Component[] array = componentsInChildren;
				foreach (Component val in array)
				{
					if ((Object)(object)val == (Object)null || ((object)val).GetType().Name.IndexOf("Turret", StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					object obj = GetObj(val, "turretMode", "mode", "currentMode");
					if (obj != null)
					{
						string text = obj.ToString();
						if (text.IndexOf("Fir", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Berserk", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool AnyPlayerLookingAt(Transform t)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)t == (Object)null)
				{
					return false;
				}
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null)
				{
					return false;
				}
				PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if ((Object)(object)val == (Object)null || !val.isPlayerControlled || val.isPlayerDead)
					{
						continue;
					}
					Camera gameplayCamera = val.gameplayCamera;
					if (!((Object)(object)gameplayCamera == (Object)null))
					{
						Vector3 val2 = t.position + Vector3.up * 1.2f - ((Component)gameplayCamera).transform.position;
						if (!(((Vector3)(ref val2)).magnitude > 40f) && Vector3.Dot(((Component)gameplayCamera).transform.forward, ((Vector3)(ref val2)).normalized) > 0.86f)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static MemberInfo FindMember(Type t, string[] names)
		{
			string key = t.FullName + "|" + string.Join(",", names);
			if (_members.TryGetValue(key, out var value))
			{
				return value;
			}
			foreach (string name in names)
			{
				FieldInfo field = t.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					_members[key] = field;
					return field;
				}
				PropertyInfo property = t.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead)
				{
					_members[key] = property;
					return property;
				}
			}
			_members[key] = null;
			return null;
		}

		private static object GetObj(object o, params string[] names)
		{
			try
			{
				if (o == null)
				{
					return null;
				}
				MemberInfo memberInfo = FindMember(o.GetType(), names);
				FieldInfo fieldInfo = memberInfo as FieldInfo;
				if (fieldInfo != null)
				{
					return fieldInfo.GetValue(o);
				}
				PropertyInfo propertyInfo = memberInfo as PropertyInfo;
				if (propertyInfo != null)
				{
					return propertyInfo.GetValue(o);
				}
			}
			catch
			{
			}
			return null;
		}

		private static float GetFloat(object o, params string[] names)
		{
			object obj = GetObj(o, names);
			if (obj is float)
			{
				return (float)obj;
			}