Decompiled source of Iconsee v1.3.4

Iconsee.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Iconsee")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.4.0")]
[assembly: AssemblyInformationalVersion("1.3.4")]
[assembly: AssemblyProduct("Iconsee")]
[assembly: AssemblyTitle("Iconsee")]
[assembly: AssemblyVersion("1.3.4.0")]
[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 Momoti.Iconsee
{
	public enum LanguageMode
	{
		Auto,
		English,
		Russian,
		French,
		German,
		Spanish,
		Chinese,
		Turkish,
		Japanese,
		Portuguese,
		Ukrainian,
		Italian,
		Korean,
		Polish
	}
	public enum TeamPanelPlacement
	{
		BelowMap,
		LeftOfMap
	}
	[BepInPlugin("momotie.Iconsee.valheim", "Iconsee", "1.3.4")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<ZRoutedRpc> <0>__Register;

			public static Action<ZRoutedRpc> <1>__Register;

			public static Action<ZRoutedRpc> <2>__Register;

			public static Action<ZRoutedRpc> <3>__Register;

			public static Action<ZRoutedRpc> <4>__Register;

			public static Action<ZRoutedRpc> <5>__Register;

			public static UnityAction <6>__OnBeforeRender;
		}

		internal static ConfigEntry<LanguageMode> LanguageSetting;

		internal static ConfigEntry<string> IconArmorColor;

		internal static ConfigEntry<bool> RecolorOwnArrow;

		internal static ConfigEntry<string> TrackedMapColor;

		internal static ConfigEntry<float> Opacity;

		internal static ConfigEntry<float> IconSize;

		internal static ConfigEntry<KeyboardShortcut> ToggleKey;

		internal static ConfigEntry<bool> ShowLabel;

		internal static ConfigEntry<bool> ClampToEdge;

		internal static ConfigEntry<float> HideWithin;

		internal static ConfigEntry<float> HeightOffset;

		internal static ConfigEntry<bool> BehindPlayer;

		internal static ConfigEntry<KeyboardShortcut> PlaceMarkKey;

		internal static ConfigEntry<float> TempMarkSeconds;

		internal static ConfigEntry<string> TempMarkColor;

		internal static ConfigEntry<float> TempMarkOpacity;

		internal static ConfigEntry<float> TempMarkSize;

		internal static ConfigEntry<bool> ShowShipMarkers;

		internal static ConfigEntry<bool> ShowTeammateIcons;

		internal static ConfigEntry<float> TeammateIconOpacity;

		internal static ConfigEntry<float> TeammateIconSize;

		internal static ConfigEntry<int> TeammateIconMax;

		internal static ConfigEntry<float> IconsShowBeyond;

		internal static ConfigEntry<bool> IconsClampToEdge;

		internal static ConfigEntry<bool> ShowTeamPanel;

		internal static ConfigEntry<int> TeamPanelMaxRows;

		internal static ConfigEntry<TeamPanelPlacement> TeamPanelPosition;

		internal static ConfigEntry<float> TeamPanelOffsetX;

		internal static ConfigEntry<float> TeamPanelOffsetY;

		internal static ConfigEntry<float> TeamPanelScale;

		internal static ConfigEntry<float> TeamPanelCompactBelow;

		internal static ConfigEntry<bool> DeathChatMessages;

		internal static ConfigEntry<bool> SignStyle;

		internal static ConfigEntry<bool> SkipIntro;

		internal static ConfigEntry<bool> CombatEnabled;

		internal static ConfigEntry<KeyboardShortcut> CombatToggleKey;

		internal static ConfigEntry<float> CombatPanelScale;

		internal static ConfigEntry<bool> CombatAltCursor;

		internal static ConfigEntry<string> CombatExtraBosses;

		internal static ConfigEntry<KeyCode> CombatGamepadKey;

		public const string PluginGuid = "momotie.Iconsee.valheim";

		public const string PluginName = "Iconsee";

		public const string PluginVersion = "1.3.4";

		internal static ManualLogSource Log;

		private static readonly HashSet<string> Reported = new HashSet<string>();

		private static readonly Action[] Ticks = new Action[14]
		{
			Net.Tick,
			MapUI.Tick,
			TrackedPins.Tick,
			TempMarks.Tick,
			ColorSync.Tick,
			ShipMarkers.Tick,
			QuickPin.Tick,
			HudMarkers.Tick,
			TeamHud.Tick,
			SignTweaks.Tick,
			CombatSync.Tick,
			CombatCursor.Tick,
			StatusTooltip.Tick,
			CombatPanel.Tick
		};

		private static readonly string[] TickNames = new string[14]
		{
			"Net", "MapUI", "TrackedPins", "TempMarks", "ColorSync", "ShipMarkers", "QuickPin", "HudMarkers", "TeamHud", "SignTweaks",
			"CombatSync", "CombatCursor", "StatusTooltip", "CombatPanel"
		};

		private void BindConfig()
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Expected O, but got Unknown
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Expected O, but got Unknown
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Expected O, but got Unknown
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Expected O, but got Unknown
			//IL_03be: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Expected O, but got Unknown
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Expected O, but got Unknown
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Expected O, but got Unknown
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Expected O, but got Unknown
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Expected O, but got Unknown
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_0541: Expected O, but got Unknown
			//IL_0628: Unknown result type (might be due to invalid IL or missing references)
			//IL_066a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0674: Expected O, but got Unknown
			LanguageSetting = ((BaseUnityPlugin)this).Config.Bind<LanguageMode>("General", "Language", LanguageMode.Auto, "Interface language: Auto (follow the game), English, Russian, French, German, Spanish, Chinese, Turkish, Japanese, Portuguese, Ukrainian, Italian, Korean, Polish. Combat Analyzer is translated into all of them, other texts exist in English and Russian only.\nЯзык интерфейса: Auto (как в игре) или любой из списка. Combat Analyzer переведён на все языки, остальные тексты — на английский и русский.");
			IconArmorColor = ((BaseUnityPlugin)this).Config.Bind<string>("Player", "IconArmorColor", "", "Color of the sword and shield on your player icon (what your teammates see), HEX RRGGBB. Empty = default look. The body of the icon always stays red. Usually set with the palette on the map.\nЦвет меча и щита на вашей иконке игрока (её видят тиммейты), HEX RRGGBB. Пусто = стандартный вид. Тело иконки всегда остаётся красным. Обычно выбирается палитрой на карте.");
			RecolorOwnArrow = ((BaseUnityPlugin)this).Config.Bind<bool>("Player", "RecolorOwnArrow", true, "Also paint your own arrow on your map/minimap in the chosen color.\nТакже красить вашу собственную стрелку на карте и мини-карте в выбранный цвет.");
			TrackedMapColor = ((BaseUnityPlugin)this).Config.Bind<string>("TrackedPins", "MapColor", "FF2A2A", "Color of tracked pins on the map and minimap (HEX RRGGBB).\nЦвет маршрутных меток на карте и мини-карте (HEX RRGGBB).");
			Opacity = ((BaseUnityPlugin)this).Config.Bind<float>("TrackedPins", "Opacity", 0.35f, new ConfigDescription("On-screen icon opacity (0 = invisible, 1 = solid).\nПрозрачность иконки на экране (0 — невидима, 1 — непрозрачная).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			IconSize = ((BaseUnityPlugin)this).Config.Bind<float>("TrackedPins", "Size", 32f, new ConfigDescription("On-screen icon size in pixels.\nРазмер иконки на экране в пикселях.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 128f), Array.Empty<object>()));
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("TrackedPins", "ToggleKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Key that temporarily hides/shows all tracked on-screen icons.\nКлавиша для временного скрытия/показа всех экранных иконок маршрутных меток.");
			ShowLabel = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackedPins", "ShowLabel", true, "Show pin name and distance under the icon.\nПоказывать название метки и расстояние под иконкой.");
			ClampToEdge = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackedPins", "ClampToScreenEdge", true, "If the pin is off-screen, keep the icon at the screen edge pointing to it.\nЕсли метка вне экрана, держать иконку у края экрана в её сторону.");
			HideWithin = ((BaseUnityPlugin)this).Config.Bind<float>("TrackedPins", "HideWithinMeters", 8f, new ConfigDescription("Hide the icon when you are closer than this many meters.\nСкрывать иконку, когда вы ближе указанного числа метров.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			HeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("TrackedPins", "HeightOffset", 2f, new ConfigDescription("Height above ground where the icon is anchored (meters).\nВысота над землёй, к которой привязана иконка (метры).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			BehindPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackedPins", "BehindPlayer", true, "Draw on-screen markers behind your character (the character covers them exactly by its silhouette). Turn off if markers near the character disappear.\nРисовать экранные метки позади вашего персонажа (персонаж закрывает их ровно по своему силуэту). Выключите, если метки рядом с персонажем пропадают.");
			PlaceMarkKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("TempMarker", "PlaceKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Place a temporary blue marker on whatever is under your crosshair (a mob, a player, a ship, the ground). It appears on the map and on the screen for everyone with the mod, follows moving targets, replaces your previous one and disappears by itself.\nПоставить временную синюю метку на то, что в прицеле (моб, игрок, корабль, земля). Она появляется на карте и на экране у всех игроков с модом, следует за движущейся целью, стирает вашу прежнюю метку и сама пропадает.");
			TempMarkSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("TempMarker", "Seconds", 10f, new ConfigDescription("How long the temporary marker lasts (seconds).\nСколько секунд живёт временная метка.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 300f), Array.Empty<object>()));
			TempMarkColor = ((BaseUnityPlugin)this).Config.Bind<string>("TempMarker", "Color", "1E6BFF", "Color of temporary markers (HEX RRGGBB).\nЦвет временных меток (HEX RRGGBB).");
			TempMarkOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("TempMarker", "Opacity", 0.95f, new ConfigDescription("Opacity of temporary markers on the screen (0–1).\nНепрозрачность временных меток на экране (0–1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			TempMarkSize = ((BaseUnityPlugin)this).Config.Bind<float>("TempMarker", "Size", 44f, new ConfigDescription("Size of temporary markers in pixels.\nРазмер временных меток в пикселях.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(12f, 128f), Array.Empty<object>()));
			ShowShipMarkers = ((BaseUnityPlugin)this).Config.Bind<bool>("Ships", "ShowShipMarkers", true, "Show markers of ships on the map and minimap. Toggled by the ship button on the map.\nПоказывать метки кораблей на карте и мини-карте. Переключается кнопкой кораблика на карте.");
			ShowTeammateIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("Team", "ShowTeammateIcons", true, "Show a semi-transparent player icon on screen for teammates who are so far that the vanilla name plate is gone.\nПоказывать на экране полупрозрачную иконку игрока для тиммейтов, которые так далеко, что ванильная плашка пропала.");
			TeammateIconOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeammateIconOpacity", 0.6f, new ConfigDescription("Opacity of the teammate icons (0–1).\nПрозрачность иконок тиммейтов (0–1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			TeammateIconSize = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeammateIconSize", 30f, new ConfigDescription("Size of the teammate icons in pixels.\nРазмер иконок тиммейтов в пикселях.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 128f), Array.Empty<object>()));
			TeammateIconMax = ((BaseUnityPlugin)this).Config.Bind<int>("Team", "TeammateIconMax", 3, new ConfigDescription("How many teammate icons are shown at once (the nearest ones).\nСколько иконок тиммейтов показывать одновременно (самых ближних).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			IconsShowBeyond = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "IconsShowBeyond", 0f, new ConfigDescription("Show the icons beyond this distance (m). 0 = automatic: the vanilla name plate range.\nПоказывать иконки дальше этого расстояния (м). 0 = автоматически: дальность ванильной плашки.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			IconsClampToEdge = ((BaseUnityPlugin)this).Config.Bind<bool>("Team", "IconsClampToScreenEdge", true, "If a teammate is off-screen, keep the icon at the screen edge pointing to them.\nЕсли тиммейт вне экрана, держать иконку у края экрана в его сторону.");
			ShowTeamPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Team", "ShowTeamPanel", true, "Show the teammates panel (name, HP, status badges).\nПоказывать панель тиммейтов (ник, HP, значки состояния).");
			TeamPanelMaxRows = ((BaseUnityPlugin)this).Config.Bind<int>("Team", "TeamPanelMaxRows", 3, new ConfigDescription("How many teammates the panel shows.\nСколько тиммейтов показывает панель.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			TeamPanelPosition = ((BaseUnityPlugin)this).Config.Bind<TeamPanelPlacement>("Team", "TeamPanelPosition", TeamPanelPlacement.BelowMap, "Where the teammates panel is placed: BelowMap (default) or LeftOfMap.\nГде расположена панель тиммейтов: BelowMap (под картой, по умолчанию) или LeftOfMap (слева от карты).");
			TeamPanelScale = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeamPanelScale", 0.75f, new ConfigDescription("Teammates panel size. Below TeamPanelCompactBelow only the HP bar is shown, on a background of the player's map color.\nРазмер панели тиммейтов. Ниже TeamPanelCompactBelow остаётся только полоска HP на фоне цвета игрока с карты.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			TeamPanelCompactBelow = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeamPanelCompactBelow", 0.6f, new ConfigDescription("Panel size below which the compact mode (HP bar only) turns on.\nРазмер панели, ниже которого включается компактный режим (только полоска HP).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			TeamPanelOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeamPanelOffsetX", 0f, "Fine-tune: shift the teammates panel horizontally (px, + = right).\nПодстройка: сдвиг панели тиммейтов по горизонтали (px, + = вправо).");
			TeamPanelOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Team", "TeamPanelOffsetY", 0f, "Fine-tune: shift the teammates panel vertically (px, + = up). Use it if the panel overlaps status effect icons.\nПодстройка: сдвиг панели тиммейтов по вертикали (px, + = вверх). Пригодится, если панель перекрывает значки эффектов.");
			DeathChatMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Extras", "DeathChatMessages", true, "Write who or what killed a player to the chat (\"<cause> humiliated <name>\", a falling tree: \"A tree penetrated <name>\").\nПисать в чат, кто или что убило игрока («<причина> унизил <ник>», упавшее дерево: «Дерево проникло в <ник>»).");
			SignStyle = ((BaseUnityPlugin)this).Config.Bind<bool>("Extras", "SignStyle", true, "Visual color and size presets when you edit a sign (they insert the usual <color>/<size> tags into the sign text).\nВизуальные пресеты цвета и размера при редактировании таблички (вставляют в текст таблички обычные теги <color>/<size>).");
			SkipIntro = ((BaseUnityPlugin)this).Config.Bind<bool>("Extras", "SkipIntro", false, "EXPERIMENTAL: try to skip the intro video shown before the main menu. Off by default — if the menu gets stuck after enabling it, turn it back off.\nПропуск вступительного ролика перед главным меню. По умолчанию выключено.");
			CombatEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("CombatAnalyzer", "Enabled", true, "Enable Combat Analyzer (DPS meter, boss fight statistics, rankings).\nВключить Combat Analyzer (счётчик DPS, статистика боёв с боссами, рейтинги).");
			CombatToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("CombatAnalyzer", "ToggleKey", new KeyboardShortcut((KeyCode)285, Array.Empty<KeyCode>()), "Key that opens and closes the Combat Analyzer panel.\nКлавиша открытия и закрытия панели Combat Analyzer.");
			CombatPanelScale = ((BaseUnityPlugin)this).Config.Bind<float>("CombatAnalyzer", "PanelScale", 1f, new ConfigDescription("Combat Analyzer panel size.\nРазмер панели Combat Analyzer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			CombatAltCursor = ((BaseUnityPlugin)this).Config.Bind<bool>("CombatAnalyzer", "AltShowsCursor", true, "Hold Alt to show the mouse cursor (click the Combat Analyzer panel, hover buffs to read them). Movement keeps working; the mouse does not turn the camera or attack. Not active in the inventory and chests (other mods use Alt there).\nУдерживайте Alt, чтобы показать курсор (кнопки панели Combat Analyzer, подсказки баффов). Движение работает; мышь не вращает камеру и не атакует. Не работает в инвентаре и сундуках (там Alt используют другие моды).");
			CombatExtraBosses = ((BaseUnityPlugin)this).Config.Bind<string>("CombatAnalyzer", "ExtraBossPrefabs", "", "Extra prefab names (comma separated) counted as bosses besides the main Valheim bosses (Eikthyr, The Elder, Bonemass, Moder, Yagolth, The Queen, Fader). Use it for the Deep North boss if it is not recognised.\nДополнительные имена префабов (через запятую), которые считаются боссами, помимо главных боссов Valheim (Эйктюр, Старейшина, Костемас, Модер, Ягат, Королева, Фейдер). Для босса Дальнего Севера, если он не распознан.");
			CombatGamepadKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("CombatAnalyzer", "GamepadKey", (KeyCode)0, "Gamepad button that opens and closes the panel (Unity key code, e.g. JoystickButton9). None = not assigned. The game does not remap it, so pick a button that is free in your setup.\nКнопка геймпада для открытия и закрытия панели (код Unity, например JoystickButton9). None = не назначена. Игра её не переназначает, выберите кнопку, свободную в вашей раскладке.");
		}

		private void Awake()
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			//IL_041e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			Net.Add(ColorSync.Register);
			Net.Add(TempMarks.Register);
			Net.Add(ShipMarkers.Register);
			Net.Add(TeamHud.Register);
			Net.Add(DeathMessages.Register);
			Net.Add(CombatSync.Register);
			Harmony harmony = new Harmony("momotie.Iconsee.valheim");
			TryPatch(harmony, typeof(Minimap), "RemovePin", new Type[1] { typeof(PinData) }, typeof(MinimapPatches), "RemovePinPostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(Minimap), "OnMapMiddleClick", null, typeof(MinimapPatches), "MiddleClickPrefix", prefix: true, declaredOnly: false);
			TryPatch(harmony, typeof(Chat), "SendPing", new Type[1] { typeof(Vector3) }, typeof(ChatPatches), "SendPingPrefix", prefix: true, declaredOnly: false);
			TryPatch(harmony, typeof(Character), "ApplyDamage", null, typeof(CharacterPatches), "DamagePostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(Player), "ApplyDamage", null, typeof(CharacterPatches), "DamagePostfix", prefix: false, declaredOnly: true);
			TryPatch(harmony, typeof(Player), "OnDeath", null, typeof(PlayerPatches), "DeathPostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(Attack), "DoMeleeAttack", null, typeof(AttackPatches), "MeleePostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(Player), "TryPlacePiece", new Type[1] { typeof(Piece) }, typeof(PlayerPatches), "TryPlacePiecePostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(Player), "RemovePiece", null, typeof(PlayerPatches), "RemovePiecePostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(CookingStation), "OnUseItem", new Type[2]
			{
				typeof(Humanoid),
				typeof(ItemData)
			}, typeof(CookingStationPatches), "UseItemPostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(CookingStation), "OnInteract", new Type[1] { typeof(Humanoid) }, typeof(CookingStationPatches), "InteractPostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(CookingStation), "OnAddFoodSwitch", new Type[3]
			{
				typeof(Switch),
				typeof(Humanoid),
				typeof(ItemData)
			}, typeof(CookingStationPatches), "AddFoodPostfix", prefix: false, declaredOnly: false);
			TryPatch(harmony, typeof(TextInput), "RequestText", null, typeof(TextInputPatches), "SignRequestPostfix", prefix: false, declaredOnly: false);
			if (!SignTweaks.Available)
			{
				Log.LogWarning((object)(Loc.T("log.fieldMissing") + "TextInput.m_panel"));
			}
			TryPatch(harmony, typeof(FejdStartup), "Start", null, typeof(FejdStartupPatches), "IntroSkipPrefix", prefix: true, declaredOnly: false);
			SkipIntro = ((BaseUnityPlugin)this).Config.Bind<bool>("Extras", "SkipIntro", false, "Пропускать вступительный ролик при запуске игры.");
			TryPatch(harmony, typeof(FejdStartup), "SetupGui", null, typeof(FejdStartupPatches), "IntroButtonPostfix", prefix: false, declaredOnly: false);
			CombatPatches.Apply(harmony);
			object obj = <>O.<6>__OnBeforeRender;
			if (obj == null)
			{
				UnityAction val = CombatCursor.OnBeforeRender;
				<>O.<6>__OnBeforeRender = val;
				obj = (object)val;
			}
			Application.onBeforeRender += (UnityAction)obj;
			MapAccess.CheckFields();
			IconArmorColor.SettingChanged += delegate
			{
				ColorSync.OnOwnColorChanged();
			};
		}

		private static void TryPatch(Harmony harmony, Type type, string method, Type[] args, Type patchClass, string patchName, bool prefix, bool declaredOnly)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			string text = type.Name + "." + method;
			try
			{
				MethodInfo methodInfo = (declaredOnly ? AccessTools.DeclaredMethod(type, method, args, (Type[])null) : AccessTools.Method(type, method, args, (Type[])null));
				if (methodInfo == null)
				{
					if (!declaredOnly)
					{
						Log.LogWarning((object)(Loc.T("log.patchMissing") + text));
					}
					return;
				}
				HarmonyMethod val = new HarmonyMethod(patchClass, patchName, (Type[])null);
				if (prefix)
				{
					harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)(Loc.T("log.patchFailed") + text + " — " + ex.Message));
			}
		}

		private void LateUpdate()
		{
			for (int i = 0; i < Ticks.Length; i++)
			{
				try
				{
					Ticks[i]();
				}
				catch (Exception ex)
				{
					ReportOnce(TickNames[i], ex);
				}
			}
		}

		internal static void ReportOnce(string module, Exception ex)
		{
			if (Reported.Add(module + ex.GetType().Name + ex.Message))
			{
				Log.LogError((object)(Loc.T("log.error") + module + "\n" + ex));
			}
		}
	}
	internal static class ColorSync
	{
		private struct Choice
		{
			public bool Custom;

			public Color Armor;
		}

		private const string RpcName = "Icons_SetArmor";

		private const float BroadcastInterval = 10f;

		private static readonly Dictionary<string, Choice> Others = new Dictionary<string, Choice>();

		private static Choice own;

		private static string lastHex;

		private static bool ownApplied;

		private static float nextBroadcast;

		private static int nextOwnApply;

		private static int nextTintOthers;

		private static int nextPrune;

		internal static bool TryParseHex(string hex, out Color c)
		{
			//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)
			c = Color.white;
			if (string.IsNullOrEmpty(hex))
			{
				return false;
			}
			string text = hex.Trim();
			if (!text.StartsWith("#"))
			{
				text = "#" + text;
			}
			if (ColorUtility.TryParseHtmlString(text, ref c))
			{
				c.a = 1f;
				return true;
			}
			return false;
		}

		internal static Color ParseHex(string hex)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (!TryParseHex(hex, out var c))
			{
				return Color.white;
			}
			return c;
		}

		internal static string ToHex(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return ColorUtility.ToHtmlStringRGB(c);
		}

		private static Choice Make(string hex)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Choice result = default(Choice);
			if (TryParseHex(hex, out var c))
			{
				result.Custom = true;
				result.Armor = c;
			}
			return result;
		}

		internal static bool TryGetPlayerColor(string playerName, out Color color)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			color = Color.white;
			if (Others.TryGetValue(playerName, out var value) && value.Custom)
			{
				color = value.Armor;
				return true;
			}
			return false;
		}

		internal static void Register(ZRoutedRpc rpc)
		{
			Others.Clear();
			nextBroadcast = Time.time + 3f;
			rpc.Register<string, string>("Icons_SetArmor", (Action<long, string, string>)RpcReceive);
		}

		internal static void OnOwnColorChanged()
		{
			nextBroadcast = 0f;
		}

		private static void RpcReceive(long sender, string playerName, string hex)
		{
			if (!string.IsNullOrEmpty(playerName) && playerName.Length <= 64 && (hex == null || hex.Length <= 9))
			{
				Others[playerName] = Make(hex);
			}
		}

		private static void Broadcast()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc instance = ZRoutedRpc.instance;
			Player localPlayer = Player.m_localPlayer;
			if (instance != null && !((Object)(object)localPlayer == (Object)null))
			{
				instance.InvokeRoutedRPC(0L, "Icons_SetArmor", new object[2]
				{
					localPlayer.GetPlayerName(),
					own.Custom ? ToHex(own.Armor) : ""
				});
			}
		}

		internal static void Tick()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			string value = Plugin.IconArmorColor.Value;
			if (value != lastHex)
			{
				lastHex = value;
				own = Make(value);
				nextBroadcast = 0f;
				nextOwnApply = 0;
			}
			if ((Object)(object)Minimap.instance != (Object)null)
			{
				if (Time.frameCount >= nextOwnApply)
				{
					nextOwnApply = Time.frameCount + 4;
					if (own.Custom && Plugin.RecolorOwnArrow.Value)
					{
						MapAccess.RecolorOwn(own.Armor);
						ownApplied = true;
					}
					else if (ownApplied)
					{
						MapAccess.RestoreOwn();
						ownApplied = false;
					}
				}
				if (Others.Count > 0 && Time.frameCount >= nextTintOthers)
				{
					nextTintOthers = Time.frameCount + 8;
					TintOthers();
				}
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && Time.time >= nextBroadcast)
			{
				nextBroadcast = Time.time + 10f;
				Broadcast();
			}
			if (Time.frameCount >= nextPrune)
			{
				nextPrune = Time.frameCount + 600;
				MapAccess.PruneOriginals();
			}
		}

		private static void TintOthers()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			List<PinData> list = MapAccess.Pins();
			if (list == null)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				PinData val = list[i];
				if (val != null && (int)val.m_type == 10 && !string.IsNullOrEmpty(val.m_name) && !((Object)(object)val.m_uiElement == (Object)null) && Others.TryGetValue(val.m_name, out var value))
				{
					if (value.Custom)
					{
						MapAccess.RecolorImages((Transform)(object)val.m_uiElement, "teammate", value.Armor, armorOnly: true);
					}
					else
					{
						MapAccess.Restore((Transform)(object)val.m_uiElement);
					}
				}
			}
		}
	}
	internal struct CombatPart
	{
		public long Id;

		public string Name;

		public float Amount;
	}
	internal static class CombatCollector
	{
		internal static readonly DpsMeter Meter = new DpsMeter();

		private const float MinLanded = 0.0001f;

		private static readonly float[] Types = new float[12];

		private static readonly List<CombatPart> Parts = new List<CombatPart>();

		internal static float Before(Character target, HitData hit)
		{
			CombatDot.ClearSplit();
			if ((Object)(object)target == (Object)null || !Plugin.CombatEnabled.Value)
			{
				return -1f;
			}
			if (target.IsPlayer() || target.IsTamed())
			{
				return -1f;
			}
			CombatDot.ConsumeTick(target, hit);
			return target.GetHealth();
		}

		internal static void After(Character target, HitData hit, float healthBefore)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			if (healthBefore < 0f || hit == null || (Object)(object)target == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)target).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || !(target.GetHealth() < healthBefore - 0.0001f))
			{
				return;
			}
			float totalDamage = hit.GetTotalDamage();
			if (!(totalDamage > 0f) || float.IsInfinity(totalDamage))
			{
				return;
			}
			DamageTypes damage = hit.m_damage;
			Types[0] = damage.m_damage;
			Types[1] = damage.m_blunt;
			Types[2] = damage.m_slash;
			Types[3] = damage.m_pierce;
			Types[4] = damage.m_chop;
			Types[5] = damage.m_pickaxe;
			Types[6] = damage.m_fire;
			Types[7] = damage.m_frost;
			Types[8] = damage.m_lightning;
			Types[9] = damage.m_poison;
			Types[10] = damage.m_spirit;
			Types[11] = damage.m_nonPlayer;
			float num = 0f;
			for (int i = 0; i < Types.Length; i++)
			{
				num += Types[i];
			}
			if (totalDamage - num > 0.01f)
			{
				Types[11] += totalDamage - num;
			}
			float num2 = (float)CombatRules.LandedFraction(healthBefore, totalDamage);
			float num3 = totalDamage * num2;
			if (!(num3 > 0.0001f))
			{
				return;
			}
			if (num2 < 1f)
			{
				for (int j = 0; j < Types.Length; j++)
				{
					Types[j] *= num2;
				}
			}
			BuildParts(hit, target, num3);
			for (int k = 0; k < Parts.Count; k++)
			{
				if (Parts[k].Id != 0L)
				{
					RouteDps(Parts[k].Id, Parts[k].Amount);
				}
			}
			if (CombatRules.IsMainBoss(target))
			{
				CombatFights.OnBossHit(target, component, Types, Parts);
				if (target.GetHealth() <= 0f)
				{
					CombatFights.OnBossDeath(target, component);
				}
			}
		}

		private static void BuildParts(HitData hit, Character target, float landed)
		{
			Parts.Clear();
			if (CombatDot.HasSplit)
			{
				AddShares(CombatDot.FireShares, Types[6]);
				AddShares(CombatDot.SpiritShares, Types[10]);
				AddShares(CombatDot.PoisonShares, Types[9]);
				float num = 0f;
				for (int i = 0; i < Parts.Count; i++)
				{
					num += Parts[i].Amount;
				}
				float num2 = landed - num;
				if (num2 > 0.0001f)
				{
					Parts.Add(new CombatPart
					{
						Id = 0L,
						Name = null,
						Amount = num2
					});
				}
			}
			else
			{
				Character attacker = hit.GetAttacker();
				string name = null;
				long id = (((Object)(object)attacker != (Object)null && (Object)(object)attacker != (Object)(object)target) ? CombatOwner.Resolve(attacker, out name) : 0);
				Parts.Add(new CombatPart
				{
					Id = id,
					Name = name,
					Amount = landed
				});
			}
		}

		private static void AddShares(List<CombatDot.Share> shares, float channelAmount)
		{
			if (!(channelAmount > 0f))
			{
				return;
			}
			for (int i = 0; i < shares.Count; i++)
			{
				float num = channelAmount * shares[i].Frac;
				if (num > 0.0001f)
				{
					Parts.Add(new CombatPart
					{
						Id = shares[i].Id,
						Name = shares[i].Name,
						Amount = num
					});
				}
			}
		}

		private static void RouteDps(long playerId, float amount)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && localPlayer.GetPlayerID() == playerId)
			{
				Meter.Add(amount, Time.time);
				return;
			}
			Player val = CombatOwner.FindPlayer(playerId);
			if ((Object)(object)val != (Object)null)
			{
				CombatSync.QueueHit(val, amount);
			}
		}

		internal static void OnDeath(Character target)
		{
			if (!((Object)(object)target == (Object)null) && Plugin.CombatEnabled.Value && CombatRules.IsMainBoss(target))
			{
				ZNetView component = ((Component)target).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner())
				{
					CombatFights.OnBossDeath(target, component);
				}
			}
		}
	}
	internal static class CombatCursor
	{
		private static bool _wasActive;

		internal static bool ControlsPatched;

		internal static bool Active { get; private set; }

		internal static void Tick()
		{
			Refresh();
			Apply();
		}

		internal static void OnBeforeRender()
		{
			try
			{
				Apply();
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("CombatCursorRender", ex);
			}
		}

		internal static bool Update()
		{
			Refresh();
			if (Active)
			{
				Apply();
				return true;
			}
			return false;
		}

		private static void Refresh()
		{
			Active = (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)) && Compute();
			if (_wasActive && !Active)
			{
				Release();
			}
			_wasActive = Active;
		}

		private static void Apply()
		{
			if (Active)
			{
				try
				{
					ZCursor.Show();
				}
				catch
				{
				}
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private static void Release()
		{
			if (!GameOwnsCursor() && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				try
				{
					ZCursor.Hide();
				}
				catch
				{
				}
				Cursor.lockState = (CursorLockMode)1;
				Cursor.visible = false;
			}
		}

		private static bool Compute()
		{
			if (!Plugin.CombatEnabled.Value || !Plugin.CombatAltCursor.Value)
			{
				return false;
			}
			if (!Application.isFocused || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			return !GameOwnsCursor();
		}

		internal static bool GameOwnsCursor()
		{
			if (MapAccess.AnyWindowOpen() || MapAccess.IsMapOpen())
			{
				return true;
			}
			if (Hud.IsPieceSelectionVisible())
			{
				return true;
			}
			if (Console.IsVisible())
			{
				return true;
			}
			Chat instance = Chat.instance;
			if ((Object)(object)instance != (Object)null)
			{
				return instance.HasFocus();
			}
			return false;
		}

		internal static bool Typing()
		{
			if (UiUtil.TypingInField() || TextInput.IsVisible() || Console.IsVisible())
			{
				return true;
			}
			Chat instance = Chat.instance;
			if ((Object)(object)instance != (Object)null)
			{
				return instance.HasFocus();
			}
			return false;
		}
	}
	internal static class CombatDot
	{
		internal struct Share
		{
			public long Id;

			public string Name;

			public float Frac;
		}

		private sealed class Entry
		{
			public long Id;

			public string Name;

			public float Fire;

			public float Spirit;
		}

		private sealed class Ledger
		{
			public readonly List<Entry> Entries = new List<Entry>();

			public long PoisonId;

			public string PoisonName;

			public float PoisonLeft;

			public float Touched;
		}

		private const float StaleSeconds = 60f;

		private const float Epsilon = 0.0001f;

		private static readonly Dictionary<string, Ledger> Ledgers = new Dictionary<string, Ledger>(StringComparer.Ordinal);

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

		private static Character _ctxTarget;

		private static HitData _ctxHit;

		internal static bool HasSplit;

		internal static readonly List<Share> FireShares = new List<Share>();

		internal static readonly List<Share> SpiritShares = new List<Share>();

		internal static readonly List<Share> PoisonShares = new List<Share>();

		internal static void Reset()
		{
			Ledgers.Clear();
			_ctxTarget = null;
			_ctxHit = null;
			ClearSplit();
		}

		internal static void EnterHit(Character target, HitData hit)
		{
			_ctxTarget = target;
			_ctxHit = hit;
		}

		internal static void ExitHit()
		{
			_ctxTarget = null;
			_ctxHit = null;
		}

		internal static void ClearSplit()
		{
			HasSplit = false;
			FireShares.Clear();
			SpiritShares.Clear();
			PoisonShares.Clear();
		}

		private static Ledger Get(string zkey, bool create, float now)
		{
			if (Ledgers.TryGetValue(zkey, out var value))
			{
				if (now - value.Touched <= 60f)
				{
					return value;
				}
				Ledgers.Remove(zkey);
			}
			if (!create)
			{
				return null;
			}
			value = new Ledger
			{
				Touched = now
			};
			Ledgers[zkey] = value;
			return value;
		}

		private static string TargetKey(Character target, out bool owner)
		{
			owner = false;
			ZNetView component = ((Component)target).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
			{
				return null;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			owner = true;
			return ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
		}

		internal static void PoolAdd(Character target, int channel, float damage)
		{
			if (!(damage > 0f) || (Object)(object)target == (Object)null || !Plugin.CombatEnabled.Value || _ctxTarget != target || _ctxHit == null || target.IsPlayer() || target.IsTamed())
			{
				return;
			}
			bool owner;
			string zkey = TargetKey(target, out owner);
			if (!owner)
			{
				return;
			}
			string name;
			long num = CombatOwner.Resolve(_ctxHit.GetAttacker(), out name);
			float time = Time.time;
			Ledger ledger = Get(zkey, create: true, time);
			ledger.Touched = time;
			if (channel == 9)
			{
				if (damage >= ledger.PoisonLeft)
				{
					ledger.PoisonId = num;
					ledger.PoisonName = name;
					ledger.PoisonLeft = damage;
				}
				return;
			}
			Entry entry = null;
			for (int i = 0; i < ledger.Entries.Count; i++)
			{
				if (ledger.Entries[i].Id == num)
				{
					entry = ledger.Entries[i];
					break;
				}
			}
			if (entry == null)
			{
				entry = new Entry
				{
					Id = num,
					Name = name
				};
				ledger.Entries.Add(entry);
			}
			if (!string.IsNullOrEmpty(name))
			{
				entry.Name = name;
			}
			switch (channel)
			{
			case 6:
				entry.Fire += damage;
				break;
			case 10:
				entry.Spirit += damage;
				break;
			}
		}

		internal static void ConsumeTick(Character target, HitData hit)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			ClearSplit();
			if (hit == null)
			{
				return;
			}
			HitType hitType = hit.m_hitType;
			if ((int)hitType != 5 && (int)hitType != 7)
			{
				return;
			}
			HasSplit = true;
			bool owner;
			string zkey = TargetKey(target, out owner);
			if (!owner)
			{
				return;
			}
			float time = Time.time;
			Ledger ledger = Get(zkey, create: false, time);
			if (ledger == null)
			{
				return;
			}
			ledger.Touched = time;
			ConsumeSum(ledger, hit.m_damage.m_fire, fire: true, FireShares);
			ConsumeSum(ledger, hit.m_damage.m_spirit, fire: false, SpiritShares);
			float poison = hit.m_damage.m_poison;
			if (poison > 0f && ledger.PoisonLeft > 0.0001f)
			{
				float num = Math.Min(poison, ledger.PoisonLeft);
				PoisonShares.Add(new Share
				{
					Id = ledger.PoisonId,
					Name = ledger.PoisonName,
					Frac = num / poison
				});
				ledger.PoisonLeft -= num;
				if (ledger.PoisonLeft < 0.0001f)
				{
					ledger.PoisonLeft = 0f;
				}
			}
			for (int num2 = ledger.Entries.Count - 1; num2 >= 0; num2--)
			{
				if (ledger.Entries[num2].Fire < 0.0001f && ledger.Entries[num2].Spirit < 0.0001f)
				{
					ledger.Entries.RemoveAt(num2);
				}
			}
		}

		private static void ConsumeSum(Ledger l, float tick, bool fire, List<Share> shares)
		{
			if (!(tick > 0f))
			{
				return;
			}
			float num = 0f;
			for (int i = 0; i < l.Entries.Count; i++)
			{
				num += (fire ? l.Entries[i].Fire : l.Entries[i].Spirit);
			}
			if (num < 0.0001f)
			{
				return;
			}
			float num2 = Math.Min(tick, num);
			for (int j = 0; j < l.Entries.Count; j++)
			{
				Entry entry = l.Entries[j];
				float num3 = (fire ? entry.Fire : entry.Spirit);
				if (!(num3 <= 0f))
				{
					float num4 = num2 * (num3 / num);
					shares.Add(new Share
					{
						Id = entry.Id,
						Name = entry.Name,
						Frac = num4 / tick
					});
					if (fire)
					{
						entry.Fire = Math.Max(0f, entry.Fire - num4);
					}
					else
					{
						entry.Spirit = Math.Max(0f, entry.Spirit - num4);
					}
				}
			}
		}

		internal static void Prune(float now)
		{
			if (Ledgers.Count == 0)
			{
				return;
			}
			Stale.Clear();
			foreach (KeyValuePair<string, Ledger> ledger in Ledgers)
			{
				if (now - ledger.Value.Touched > 60f)
				{
					Stale.Add(ledger.Key);
				}
			}
			for (int i = 0; i < Stale.Count; i++)
			{
				Ledgers.Remove(Stale[i]);
			}
		}
	}
	internal sealed class DpsMeter
	{
		private struct Sample
		{
			public float T;

			public float Amount;
		}

		internal const float WindowSeconds = 5f;

		internal const float MaxWindowSeconds = 30f;

		internal const float LeaveSeconds = 20f;

		private readonly Queue<Sample> _window = new Queue<Sample>();

		private double _windowSum;

		private readonly Queue<Sample> _peaks = new Queue<Sample>();

		private float _maxDps;

		private double _total;

		private float _start;

		private float _lastCombat;

		private float _burstStart;

		private bool _session;

		internal double Total => _total;

		internal bool HasData => _session;

		internal double MaxDps => _maxDps;

		internal void Touch(float now)
		{
			if (!_session)
			{
				_session = true;
				_start = now;
				_lastCombat = now;
			}
			else if (now > _lastCombat)
			{
				_lastCombat = now;
			}
		}

		internal void Add(float amount, float now)
		{
			if (!float.IsNaN(amount) && !float.IsInfinity(amount) && !(amount <= 0f))
			{
				Touch(now);
				Prune(now);
				if (_window.Count == 0)
				{
					_burstStart = now;
				}
				_total += amount;
				_window.Enqueue(new Sample
				{
					T = now,
					Amount = amount
				});
				_windowSum += amount;
				RecordPeak(now);
			}
		}

		internal void Tick(float now)
		{
			if (_session && now - _lastCombat > 20f)
			{
				Reset();
			}
		}

		private void Prune(float now)
		{
			while (_window.Count > 0 && now - _window.Peek().T > 5f)
			{
				_windowSum -= _window.Dequeue().Amount;
			}
			if (_window.Count == 0 || _windowSum < 0.0)
			{
				_windowSum = 0.0;
			}
		}

		private void RecordPeak(float now)
		{
			while (_peaks.Count > 0 && now - _peaks.Peek().T > 30f)
			{
				_peaks.Dequeue();
			}
			_peaks.Enqueue(new Sample
			{
				T = now,
				Amount = (float)Current(now)
			});
			float num = 0f;
			foreach (Sample peak in _peaks)
			{
				if (peak.Amount > num)
				{
					num = peak.Amount;
				}
			}
			_maxDps = num;
		}

		internal double Current(float now)
		{
			if (!_session)
			{
				return 0.0;
			}
			Prune(now);
			if (_windowSum <= 0.0)
			{
				return 0.0;
			}
			float num = now - _burstStart;
			if (num < 1f)
			{
				num = 1f;
			}
			if (num > 5f)
			{
				num = 5f;
			}
			return _windowSum / (double)num;
		}

		internal void Reset()
		{
			_window.Clear();
			_windowSum = 0.0;
			_total = 0.0;
			_start = 0f;
			_lastCombat = 0f;
			_burstStart = 0f;
			_session = false;
		}
	}
	internal static class CombatFights
	{
		private sealed class Active
		{
			public FightRecord Rec;

			public string Written;

			public float NextWrite;

			public Character Boss;
		}

		private const string StateKey = "Iconsee_cfight";

		private const float WriteInterval = 1f;

		private const int MaxPlayers = 64;

		private static readonly Dictionary<string, Active> Cache = new Dictionary<string, Active>(StringComparer.Ordinal);

		private static float _nextScan;

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

		internal static void Reset()
		{
			Cache.Clear();
		}

		private static double NetNow()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return -1.0;
			}
			return instance.GetTimeSeconds();
		}

		private static string PrefabName(GameObject go)
		{
			string text = ((Object)go).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num > 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		private static Active Get(Character boss, ZDO zdo, string zkey, double now, bool create)
		{
			string text = zdo.GetString("Iconsee_cfight", "");
			if (Cache.TryGetValue(zkey, out var value))
			{
				if (value.Written == text)
				{
					return value;
				}
				Cache.Remove(zkey);
			}
			FightRecord rec = null;
			if (text.Length > 0 && (!FightCodec.TryParse(text, out rec) || rec.Finished || rec.WorldUid != CombatStore.WorldUid))
			{
				rec = null;
			}
			if (rec == null)
			{
				if (!create)
				{
					return null;
				}
				rec = new FightRecord();
				rec.WorldUid = CombatStore.WorldUid;
				rec.Key = rec.WorldUid.ToString(CultureInfo.InvariantCulture) + ":" + zkey + ":" + ((long)(now * 1000.0)).ToString(CultureInfo.InvariantCulture);
				rec.BossPrefab = PrefabName(((Component)boss).gameObject);
				rec.BossName = boss.m_name;
				rec.MaxHealth = boss.GetMaxHealth();
				rec.StartTime = now;
				rec.EndTime = now;
				text = "";
			}
			value = new Active
			{
				Rec = rec,
				Written = text,
				NextWrite = 0f,
				Boss = boss
			};
			Cache[zkey] = value;
			return value;
		}

		private static void Write(ZDO zdo, Active a)
		{
			string text = FightCodec.Serialize(a.Rec);
			zdo.Set("Iconsee_cfight", text);
			a.Written = text;
		}

		internal static void Tick(float now)
		{
			if (!CombatStore.IsOpen || now < _nextScan)
			{
				return;
			}
			_nextScan = now + 1f;
			List<Character> allCharacters = Character.GetAllCharacters();
			if (allCharacters != null)
			{
				for (int i = 0; i < allCharacters.Count; i++)
				{
					Character val = allCharacters[i];
					if ((Object)(object)val == (Object)null || !CombatRules.IsMainBoss(val) || val.IsDead())
					{
						continue;
					}
					BaseAI component = ((Component)val).GetComponent<BaseAI>();
					if (!((Object)(object)component == (Object)null) && component.IsAlerted())
					{
						ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
						if (!((Object)(object)component2 == (Object)null) && component2.IsValid() && component2.IsOwner())
						{
							Begin(val, component2);
						}
					}
				}
			}
			Gone.Clear();
			foreach (KeyValuePair<string, Active> item in Cache)
			{
				Active value = item.Value;
				if ((Object)(object)value.Boss == (Object)null)
				{
					Gone.Add(item.Key);
				}
				else if (!(Time.time < value.NextWrite))
				{
					ZDO val2 = (((Object)(object)((Component)value.Boss).GetComponent<ZNetView>() != (Object)null) ? ((Component)value.Boss).GetComponent<ZNetView>().GetZDO() : null);
					if (val2 != null)
					{
						value.NextWrite = Time.time + 5f;
						Write(val2, value);
						CombatSync.PublishLive(value.Rec);
					}
				}
			}
			for (int j = 0; j < Gone.Count; j++)
			{
				Cache.Remove(Gone[j]);
			}
		}

		private static void Begin(Character boss, ZNetView nview)
		{
			ZDO zDO = nview.GetZDO();
			double num = NetNow();
			if (zDO != null && !(num < 0.0))
			{
				Active active = Get(boss, zDO, ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString(), num, create: true);
				active.Boss = boss;
				if (active.Written.Length == 0)
				{
					active.NextWrite = Time.time + 5f;
					Write(zDO, active);
					CombatSync.PublishLive(active.Rec);
				}
			}
		}

		internal static void OnBossHit(Character boss, ZNetView nview, float[] types, List<CombatPart> parts)
		{
			if (!CombatStore.IsOpen)
			{
				return;
			}
			ZDO zDO = nview.GetZDO();
			double num = NetNow();
			if (zDO == null || num < 0.0)
			{
				return;
			}
			string zkey = ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
			Active active = Get(boss, zDO, zkey, num, create: true);
			active.Boss = boss;
			FightRecord rec = active.Rec;
			rec.Hits++;
			rec.Revision++;
			rec.EndTime = num;
			for (int i = 0; i < types.Length && i < rec.Types.Length; i++)
			{
				rec.Types[i] += types[i];
			}
			for (int j = 0; j < parts.Count; j++)
			{
				CombatPart combatPart = parts[j];
				FightPlayer fightPlayer = null;
				if (combatPart.Id != 0L)
				{
					for (int k = 0; k < rec.Players.Count; k++)
					{
						if (rec.Players[k].Id == combatPart.Id)
						{
							fightPlayer = rec.Players[k];
							break;
						}
					}
					if (fightPlayer == null && rec.Players.Count < 64)
					{
						fightPlayer = new FightPlayer
						{
							Id = combatPart.Id,
							Name = (combatPart.Name ?? "")
						};
						rec.Players.Add(fightPlayer);
					}
				}
				if (fightPlayer != null)
				{
					fightPlayer.Damage += combatPart.Amount;
					if (!string.IsNullOrEmpty(combatPart.Name))
					{
						fightPlayer.Name = combatPart.Name;
					}
				}
				else
				{
					rec.UnknownDamage += combatPart.Amount;
				}
			}
			if (Time.time >= active.NextWrite)
			{
				active.NextWrite = Time.time + 1f;
				Write(zDO, active);
				CombatSync.PublishLive(rec);
			}
		}

		internal static void OnBossDeath(Character boss, ZNetView nview)
		{
			if (!CombatStore.IsOpen)
			{
				return;
			}
			ZDO zDO = nview.GetZDO();
			double num = NetNow();
			if (zDO != null && !(num < 0.0))
			{
				string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
				Active active = Get(boss, zDO, text, num, create: false);
				if (active != null)
				{
					FightRecord rec = active.Rec;
					rec.Finished = true;
					rec.EndTime = num;
					rec.FinishedUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
					rec.Revision++;
					Cache.Remove(text);
					zDO.Set("Iconsee_cfight", "");
					CombatSync.PublishFinished(rec);
				}
			}
		}
	}
	internal static class CombatTypes
	{
		internal const int General = 0;

		internal const int Blunt = 1;

		internal const int Slash = 2;

		internal const int Pierce = 3;

		internal const int Chop = 4;

		internal const int Pickaxe = 5;

		internal const int Fire = 6;

		internal const int Frost = 7;

		internal const int Lightning = 8;

		internal const int Poison = 9;

		internal const int Spirit = 10;

		internal const int Unknown = 11;

		internal const int Count = 12;

		internal static string LocKey(int type)
		{
			return "combat.type." + type.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal sealed class FightPlayer
	{
		public long Id;

		public string Name;

		public double Damage;
	}
	internal sealed class FightRecord
	{
		public string Key;

		public long WorldUid;

		public string BossPrefab;

		public string BossName;

		public double MaxHealth;

		public double StartTime;

		public double EndTime;

		public long FinishedUtc;

		public bool Finished;

		public int Revision;

		public int Hits;

		public double UnknownDamage;

		public readonly List<FightPlayer> Players = new List<FightPlayer>();

		public readonly double[] Types = new double[12];

		public double Duration => EndTime - StartTime;

		public double GroupDamage
		{
			get
			{
				double num = 0.0;
				for (int i = 0; i < Players.Count; i++)
				{
					num += Players[i].Damage;
				}
				return num;
			}
		}

		public double TypesTotal
		{
			get
			{
				double num = 0.0;
				for (int i = 0; i < Types.Length; i++)
				{
					num += Types[i];
				}
				return num;
			}
		}
	}
	internal static class FightCodec
	{
		private const string Tag = "F1";

		private const int FieldCount = 15;

		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		private static string N(double v)
		{
			return v.ToString("R", Inv);
		}

		private static string Esc(string s)
		{
			return Uri.EscapeDataString(s ?? "");
		}

		internal static string Serialize(FightRecord r)
		{
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("F1").Append('|');
			stringBuilder.Append(Esc(r.Key)).Append('|');
			stringBuilder.Append(r.WorldUid.ToString(Inv)).Append('|');
			stringBuilder.Append(Esc(r.BossPrefab)).Append('|');
			stringBuilder.Append(Esc(r.BossName)).Append('|');
			stringBuilder.Append(N(r.MaxHealth)).Append('|');
			stringBuilder.Append(N(r.StartTime)).Append('|');
			stringBuilder.Append(N(r.EndTime)).Append('|');
			stringBuilder.Append(r.FinishedUtc.ToString(Inv)).Append('|');
			stringBuilder.Append(r.Finished ? '1' : '0').Append('|');
			stringBuilder.Append(r.Revision.ToString(Inv)).Append('|');
			stringBuilder.Append(r.Hits.ToString(Inv)).Append('|');
			stringBuilder.Append(N(r.UnknownDamage)).Append('|');
			for (int i = 0; i < r.Types.Length; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(N(r.Types[i]));
			}
			stringBuilder.Append('|');
			for (int j = 0; j < r.Players.Count; j++)
			{
				if (j > 0)
				{
					stringBuilder.Append(';');
				}
				FightPlayer fightPlayer = r.Players[j];
				stringBuilder.Append(fightPlayer.Id.ToString(Inv)).Append(':').Append(Esc(fightPlayer.Name))
					.Append(':')
					.Append(N(fightPlayer.Damage));
			}
			return stringBuilder.ToString();
		}

		private static bool Num(string s, out double v)
		{
			if (double.TryParse(s, NumberStyles.Float, Inv, out v) && !double.IsNaN(v))
			{
				return !double.IsInfinity(v);
			}
			return false;
		}

		internal static bool TryParse(string line, out FightRecord rec)
		{
			rec = null;
			if (string.IsNullOrEmpty(line) || !line.StartsWith("F1|", StringComparison.Ordinal))
			{
				return false;
			}
			string[] array = line.Split(new char[1] { '|' });
			if (array.Length < 15)
			{
				return false;
			}
			try
			{
				FightRecord fightRecord = new FightRecord();
				fightRecord.Key = Uri.UnescapeDataString(array[1]);
				if (!long.TryParse(array[2], NumberStyles.Integer, Inv, out var result))
				{
					return false;
				}
				if (!Num(array[5], out var v) || !Num(array[6], out var v2) || !Num(array[7], out var v3))
				{
					return false;
				}
				if (!long.TryParse(array[8], NumberStyles.Integer, Inv, out var result2))
				{
					return false;
				}
				if (!int.TryParse(array[10], NumberStyles.Integer, Inv, out var result3))
				{
					return false;
				}
				if (!int.TryParse(array[11], NumberStyles.Integer, Inv, out var result4))
				{
					return false;
				}
				if (!Num(array[12], out var v4))
				{
					return false;
				}
				fightRecord.WorldUid = result;
				fightRecord.BossPrefab = Uri.UnescapeDataString(array[3]);
				fightRecord.BossName = Uri.UnescapeDataString(array[4]);
				fightRecord.MaxHealth = v;
				fightRecord.StartTime = v2;
				fightRecord.EndTime = v3;
				fightRecord.FinishedUtc = result2;
				fightRecord.Finished = array[9] == "1";
				fightRecord.Revision = result3;
				fightRecord.Hits = result4;
				fightRecord.UnknownDamage = v4;
				string[] array2 = array[13].Split(new char[1] { ',' });
				for (int i = 0; i < array2.Length && i < fightRecord.Types.Length; i++)
				{
					if (!Num(array2[i], out var v5))
					{
						return false;
					}
					fightRecord.Types[i] = v5;
				}
				if (array[14].Length > 0)
				{
					string[] array3 = array[14].Split(new char[1] { ';' });
					for (int j = 0; j < array3.Length; j++)
					{
						string[] array4 = array3[j].Split(new char[1] { ':' });
						if (array4.Length != 3)
						{
							return false;
						}
						if (!long.TryParse(array4[0], NumberStyles.Integer, Inv, out var result5) || !Num(array4[2], out var v6))
						{
							return false;
						}
						fightRecord.Players.Add(new FightPlayer
						{
							Id = result5,
							Name = Uri.UnescapeDataString(array4[1]),
							Damage = v6
						});
					}
				}
				rec = fightRecord;
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	internal static class CombatOwner
	{
		private struct Mem
		{
			public long Id;

			public string Name;
		}

		private const string FollowKey = "follow";

		private const int MaxMemory = 512;

		private static readonly Dictionary<string, Mem> Memory = new Dictionary<string, Mem>(StringComparer.Ordinal);

		internal static void Reset()
		{
			Memory.Clear();
		}

		internal static long Resolve(Character attacker, out string name)
		{
			name = null;
			if ((Object)(object)attacker == (Object)null)
			{
				return 0L;
			}
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if ((Object)(object)val != (Object)null)
			{
				long playerID = val.GetPlayerID();
				if (playerID == 0L)
				{
					return 0L;
				}
				name = val.GetPlayerName();
				return playerID;
			}
			return ResolveCreature(attacker, out name);
		}

		private static long ResolveCreature(Character creature, out string name)
		{
			name = null;
			ZNetView component = ((Component)creature).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return 0L;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return 0L;
			}
			string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString();
			Mem value;
			bool flag = Memory.TryGetValue(text, out value);
			string text2 = zDO.GetString("follow", "");
			if (text2.Length > 0)
			{
				List<Player> allPlayers = Player.GetAllPlayers();
				Player val = null;
				int num = 0;
				bool flag2 = false;
				for (int i = 0; i < allPlayers.Count; i++)
				{
					Player val2 = allPlayers[i];
					if (!((Object)(object)val2 == (Object)null) && string.Equals(val2.GetPlayerName(), text2, StringComparison.Ordinal))
					{
						num++;
						val = val2;
						if (flag && val2.GetPlayerID() == value.Id)
						{
							flag2 = true;
						}
					}
				}
				if (num == 1)
				{
					long playerID = val.GetPlayerID();
					if (playerID != 0L)
					{
						name = val.GetPlayerName();
						Remember(text, playerID, name);
						return playerID;
					}
				}
				else if (num > 1 && !flag2)
				{
					return 0L;
				}
			}
			if (flag)
			{
				name = value.Name;
				return value.Id;
			}
			return 0L;
		}

		private static void Remember(string zkey, long id, string name)
		{
			if (Memory.Count >= 512)
			{
				Memory.Clear();
			}
			Memory[zkey] = new Mem
			{
				Id = id,
				Name = name
			};
		}

		internal static Player FindPlayer(long id)
		{
			if (id == 0L)
			{
				return null;
			}
			List<Player> allPlayers = Player.GetAllPlayers();
			for (int i = 0; i < allPlayers.Count; i++)
			{
				Player val = allPlayers[i];
				if ((Object)(object)val != (Object)null && val.GetPlayerID() == id)
				{
					return val;
				}
			}
			return null;
		}
	}
	internal struct RankedPlayer
	{
		public long Id;

		public string Name;

		public double Damage;

		public int Rank;

		public int Points;
	}
	internal sealed class LeaderEntry
	{
		public long Id;

		public string Name;

		public int Points;

		public int Fights;

		public int Place;

		public long LastUtc;
	}
	internal static class CombatRules
	{
		internal const int MaxValidFightSeconds = 86400;

		private static readonly HashSet<string> MainBosses = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Eikthyr", "gd_king", "Bonemass", "Dragon", "GoblinKing", "SeekerQueen", "Fader", "FrozenKing", "FrozenKing_p2", "FrozenKing_p3" };

		private static double Q(double v)
		{
			return Math.Round(v, 2, MidpointRounding.AwayFromZero);
		}

		internal static double LandedFraction(double healthBefore, double appliedTotal)
		{
			if (double.IsNaN(appliedTotal) || double.IsNaN(healthBefore))
			{
				return 1.0;
			}
			if (appliedTotal <= 0.0)
			{
				return 1.0;
			}
			if (healthBefore <= 0.0)
			{
				return 0.0;
			}
			if (!(healthBefore >= appliedTotal))
			{
				return healthBefore / appliedTotal;
			}
			return 1.0;
		}

		internal static int PointsFor(int rank, int participants)
		{
			int num = participants - rank + 1;
			if (num >= 1)
			{
				return num;
			}
			return 1;
		}

		internal static bool IsValidKill(FightRecord r)
		{
			if (r == null || !r.Finished)
			{
				return false;
			}
			double duration = r.Duration;
			if (double.IsNaN(duration) || double.IsInfinity(duration))
			{
				return false;
			}
			if (duration <= 0.0 || duration > 86400.0)
			{
				return false;
			}
			if (r.FinishedUtc <= 0)
			{
				return false;
			}
			return CountParticipants(r) > 0;
		}

		internal static int CountParticipants(FightRecord r)
		{
			int num = 0;
			for (int i = 0; i < r.Players.Count; i++)
			{
				if (Q(r.Players[i].Damage) > 0.0)
				{
					num++;
				}
			}
			return num;
		}

		internal static List<RankedPlayer> RankFight(FightRecord r)
		{
			List<RankedPlayer> list = new List<RankedPlayer>();
			if (r == null)
			{
				return list;
			}
			for (int i = 0; i < r.Players.Count; i++)
			{
				FightPlayer fightPlayer = r.Players[i];
				if (!(Q(fightPlayer.Damage) <= 0.0))
				{
					list.Add(new RankedPlayer
					{
						Id = fightPlayer.Id,
						Name = fightPlayer.Name,
						Damage = fightPlayer.Damage
					});
				}
			}
			list.Sort(delegate(RankedPlayer a, RankedPlayer b)
			{
				int num2 = Q(b.Damage).CompareTo(Q(a.Damage));
				return (num2 == 0) ? a.Id.CompareTo(b.Id) : num2;
			});
			int count = list.Count;
			int rank = 0;
			for (int num = 0; num < count; num++)
			{
				RankedPlayer value = list[num];
				if (num == 0 || Q(list[num - 1].Damage) != Q(value.Damage))
				{
					rank = num + 1;
				}
				value.Rank = rank;
				value.Points = PointsFor(rank, count);
				list[num] = value;
			}
			return list;
		}

		internal static List<LeaderEntry> Leaderboard(IEnumerable<FightRecord> fights, string bossPrefab)
		{
			Dictionary<long, LeaderEntry> dictionary = new Dictionary<long, LeaderEntry>();
			foreach (FightRecord fight in fights)
			{
				if (fight == null || !fight.Finished || (bossPrefab != null && !string.Equals(fight.BossPrefab, bossPrefab, StringComparison.Ordinal)))
				{
					continue;
				}
				List<RankedPlayer> list = RankFight(fight);
				for (int i = 0; i < list.Count; i++)
				{
					RankedPlayer rankedPlayer = list[i];
					if (!dictionary.TryGetValue(rankedPlayer.Id, out var value))
					{
						value = new LeaderEntry
						{
							Id = rankedPlayer.Id,
							Name = rankedPlayer.Name
						};
						dictionary[rankedPlayer.Id] = value;
					}
					value.Points += rankedPlayer.Points;
					value.Fights++;
					if (fight.FinishedUtc >= value.LastUtc)
					{
						value.LastUtc = fight.FinishedUtc;
						value.Name = rankedPlayer.Name;
					}
				}
			}
			List<LeaderEntry> list2 = new List<LeaderEntry>(dictionary.Values);
			list2.Sort(delegate(LeaderEntry a, LeaderEntry b)
			{
				int num2 = b.Points.CompareTo(a.Points);
				if (num2 != 0)
				{
					return num2;
				}
				num2 = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase);
				return (num2 == 0) ? a.Id.CompareTo(b.Id) : num2;
			});
			int place = 0;
			for (int num = 0; num < list2.Count; num++)
			{
				if (num == 0 || list2[num - 1].Points != list2[num].Points)
				{
					place = num + 1;
				}
				list2[num].Place = place;
			}
			return list2;
		}

		internal static bool IsMainBoss(Character c)
		{
			if ((Object)(object)c == (Object)null || !c.IsBoss())
			{
				return false;
			}
			string text = ((Object)((Component)c).gameObject).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num > 0)
			{
				text = text.Substring(0, num);
			}
			text = text.Trim();
			if (MainBosses.Contains(text))
			{
				return true;
			}
			string text2 = ((Plugin.CombatExtraBosses != null) ? Plugin.CombatExtraBosses.Value : null);
			if (string.IsNullOrEmpty(text2))
			{
				return false;
			}
			string[] array = text2.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (string.Equals(array[i].Trim(), text, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal static List<string> BossTypes(IEnumerable<FightRecord> fights)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (FightRecord fight in fights)
			{
				if (fight != null && fight.Finished && !string.IsNullOrEmpty(fight.BossPrefab))
				{
					hashSet.Add(fight.BossPrefab);
				}
			}
			List<string> list = new List<string>(hashSet);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return list;
		}

		internal static Dictionary<string, FightRecord> FastestKills(IEnumerable<FightRecord> fights)
		{
			Dictionary<string, FightRecord> dictionary = new Dictionary<string, FightRecord>(StringComparer.Ordinal);
			foreach (FightRecord fight in fights)
			{
				if (IsValidKill(fight) && !string.IsNullOrEmpty(fight.BossPrefab) && (!dictionary.TryGetValue(fight.BossPrefab, out var value) || IsBetter(fight, value)))
				{
					dictionary[fight.BossPrefab] = fight;
				}
			}
			return dictionary;
		}

		private static bool IsBetter(FightRecord a, FightRecord b)
		{
			int num = a.Duration.CompareTo(b.Duration);
			if (num != 0)
			{
				return num < 0;
			}
			num = a.FinishedUtc.CompareTo(b.FinishedUtc);
			if (num != 0)
			{
				return num < 0;
			}
			return string.CompareOrdinal(a.Key, b.Key) < 0;
		}
	}
	internal static class CombatState
	{
		private const float ScanInterval = 0.5f;

		private const float AggroRange = 80f;

		private static readonly MethodInfo MGetTarget = AccessTools.Method(typeof(MonsterAI), "GetTargetCreature", (Type[])null, (Type[])null);

		private static readonly FieldInfo FTarget = AccessTools.Field(typeof(MonsterAI), "m_targetCreature");

		private static float _nextScan;

		private static bool _aggro;

		internal static void Tick(float now)
		{
			DpsMeter meter = CombatCollector.Meter;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead())
			{
				if (now >= _nextScan)
				{
					_nextScan = now + 0.5f;
					_aggro = ScanAggro(localPlayer);
				}
				if (_aggro)
				{
					meter.Touch(now);
				}
				float lastAttacked = TeamHud.LastAttacked;
				if (lastAttacked > 0f && now - lastAttacked <= 20f)
				{
					meter.Touch(lastAttacked);
				}
			}
			else
			{
				_aggro = false;
			}
			meter.Tick(now);
		}

		private static Character TargetOf(MonsterAI ai)
		{
			if (MGetTarget != null)
			{
				object? obj = MGetTarget.Invoke(ai, null);
				return (Character)((obj is Character) ? obj : null);
			}
			if (FTarget != null)
			{
				object? value = FTarget.GetValue(ai);
				return (Character)((value is Character) ? value : null);
			}
			return null;
		}

		private static bool ScanAggro(Player local)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			List<Character> allCharacters = Character.GetAllCharacters();
			if (allCharacters == null)
			{
				return false;
			}
			Vector3 position = ((Component)local).transform.position;
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character val = allCharacters[i];
				if ((Object)(object)val == (Object)null || val.IsPlayer() || val.IsTamed() || val.IsDead())
				{
					continue;
				}
				Vector3 val2 = ((Component)val).transform.position - position;
				if (!(((Vector3)(ref val2)).sqrMagnitude > 6400f))
				{
					MonsterAI component = ((Component)val).GetComponent<MonsterAI>();
					if (!((Object)(object)component == (Object)null) && (Object)(object)TargetOf(component) == (Object)(object)local)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal static class CombatStore
	{
		private const float SaveDelaySeconds = 2f;

		private const int MaxPlayersPerFight = 64;

		private const string HeaderPrefix = "# Iconsee combat v1 world=";

		private static readonly List<FightRecord> Fights = new List<FightRecord>();

		private static readonly Dictionary<string, FightRecord> ByKey = new Dictionary<string, FightRecord>(StringComparer.Ordinal);

		private static long _world;

		private static bool _open;

		private static bool _dirty;

		private static float _nextSave;

		private static string _path;

		internal static int Version { get; private set; }

		internal static bool IsOpen => _open;

		internal static long WorldUid => _world;

		internal static IList<FightRecord> All => Fights;

		internal static bool Contains(string key)
		{
			if (key != null)
			{
				return ByKey.ContainsKey(key);
			}
			return false;
		}

		internal static void Open(long worldUid)
		{
			if (!_open || _world != worldUid)
			{
				Close();
				_world = worldUid;
				_path = MapAccess.DataPath("combat_" + worldUid.ToString(CultureInfo.InvariantCulture), ".txt");
				_open = true;
				Load();
				Version++;
			}
		}

		internal static void Close()
		{
			if (_open)
			{
				Flush();
				Fights.Clear();
				ByKey.Clear();
				_open = false;
				_dirty = false;
				Version++;
			}
		}

		private static void Load()
		{
			Fights.Clear();
			ByKey.Clear();
			if (!File.Exists(_path))
			{
				return;
			}
			try
			{
				int num = 0;
				string[] array = File.ReadAllLines(_path);
				foreach (string text in array)
				{
					if (text.Length != 0 && text[0] != '#')
					{
						if (!FightCodec.TryParse(text, out var rec) || !Accept(rec))
						{
							num++;
							continue;
						}
						Fights.Add(rec);
						ByKey[rec.Key] = rec;
					}
				}
				if (num > 0)
				{
					Plugin.Log.LogWarning((object)(Loc.T("log.combatLoadFailed") + num));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)(Loc.T("log.combatLoadFailed") + ex.Message));
			}
		}

		private static bool Accept(FightRecord r)
		{
			if (r == null || !r.Finished)
			{
				return false;
			}
			if (string.IsNullOrEmpty(r.Key) || r.WorldUid != _world)
			{
				return false;
			}
			if (r.Players.Count > 64)
			{
				return false;
			}
			if (r.EndTime < r.StartTime)
			{
				return false;
			}
			return true;
		}

		internal static bool Add(FightRecord r)
		{
			if (!_open || !Accept(r) || ByKey.ContainsKey(r.Key))
			{
				return false;
			}
			Fights.Add(r);
			ByKey[r.Key] = r;
			_dirty = true;
			_nextSave = 0f;
			Version++;
			return true;
		}

		internal static void Tick(float now)
		{
			if (_open && _dirty)
			{
				if (_nextSave <= 0f)
				{
					_nextSave = now + 2f;
				}
				else if (!(now < _nextSave))
				{
					Flush();
				}
			}
		}

		internal static void Flush()
		{
			if (!_open || !_dirty)
			{
				return;
			}
			_dirty = false;
			_nextSave = 0f;
			try
			{
				string directoryName = Path.GetDirectoryName(_path);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				List<string> list = new List<string>(Fights.Count + 1);
				list.Add("# Iconsee combat v1 world=" + _world.ToString(CultureInfo.InvariantCulture));
				for (int i = 0; i < Fights.Count; i++)
				{
					list.Add(FightCodec.Serialize(Fights[i]));
				}
				string text = _path + ".tmp";
				File.WriteAllLines(text, list.ToArray());
				if (File.Exists(_path))
				{
					File.Delete(_path);
				}
				File.Move(text, _path);
			}
			catch (Exception ex)
			{
				_dirty = true;
				Plugin.ReportOnce("CombatStore.Save", ex);
				Plugin.Log.LogWarning((object)(Loc.T("log.combatSaveFailed") + ex.Message));
			}
		}
	}
	internal static class CombatSync
	{
		private const string RpcHit = "Iconsee_CHit1";

		private const string RpcLive = "Iconsee_CLive1";

		private const string RpcFight = "Iconsee_CFight1";

		private const string RpcHello = "Iconsee_CHello1";

		private const string RpcWant = "Iconsee_CWant1";

		private const float HitFlushInterval = 0.25f;

		private const float HelloDelay = 5f;

		private const float LiveStaleSeconds = 900f;

		private const float HelloReplyGap = 20f;

		private const int MaxManifestKeys = 2000;

		private const int ChunkRecords = 15;

		private const int ChunkChars = 40000;

		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		internal static readonly Dictionary<string, FightRecord> Live = new Dictionary<string, FightRecord>(StringComparer.Ordinal);

		private static readonly Dictionary<string, float> LiveStamp = new Dictionary<string, float>(StringComparer.Ordinal);

		private static readonly Dictionary<long, float> PendingHits = new Dictionary<long, float>();

		private static readonly List<long> PendingKeys = new List<long>();

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

		private static readonly Dictionary<long, float> HelloReplied = new Dictionary<long, float>();

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

		private static int _seq;

		private static float _nextFlush;

		private static float _nextPrune;

		private static float _helloAt;

		private static bool _helloSent;

		internal static void Register(ZRoutedRpc rpc)
		{
			CombatCollector.Meter.Reset();
			CombatFights.Reset();
			CombatDot.Reset();
			CombatOwner.Reset();
			Live.Clear();
			LiveStamp.Clear();
			PendingHits.Clear();
			LastSeq.Clear();
			HelloReplied.Clear();
			_helloSent = false;
			_helloAt = Time.time + 5f;
			rpc.Register<string>("Iconsee_CHit1", (Action<long, string>)OnHit);
			rpc.Register<string>("Iconsee_CLive1", (Action<long, string>)OnLive);
			rpc.Register<string>("Iconsee_CFight1", (Action<long, string>)OnFights);
			rpc.Register<string>("Iconsee_CHello1", (Action<long, string>)OnHello);
			rpc.Register<string>("Iconsee_CWant1", (Action<long, string>)OnWant);
		}

		internal static void Tick()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || instance.GetWorld() == null)
			{
				if (CombatStore.IsOpen)
				{
					CombatStore.Close();
				}
			}
			else if (Plugin.CombatEnabled.Value)
			{
				if (!CombatStore.IsOpen)
				{
					CombatStore.Open(instance.GetWorldUID());
				}
				float time = Time.time;
				CombatStore.Tick(time);
				CombatState.Tick(time);
				CombatFights.Tick(time);
				if (time >= _nextFlush)
				{
					_nextFlush = time + 0.25f;
					FlushHits();
				}
				if (time >= _nextPrune)
				{
					_nextPrune = time + 5f;
					PruneLive(time);
					CombatDot.Prune(time);
				}
				if (!_helloSent && time >= _helloAt && CombatStore.IsOpen)
				{
					_helloSent = true;
					SendHello();
				}
			}
		}

		internal static void QueueHit(Player owner, float amount)
		{
			ZNetView component = ((Component)owner).GetComponent<ZNetView>();
			ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null);
			if (val != null)
			{
				long owner2 = val.GetOwner();
				if (owner2 != 0L && owner2 != ZNet.GetUID())
				{
					PendingHits.TryGetValue(owner2, out var value);
					PendingHits[owner2] = value + amount;
				}
			}
		}

		private static void FlushHits()
		{
			if (PendingHits.Count == 0)
			{
				return;
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			PendingKeys.Clear();
			foreach (KeyValuePair<long, float> pendingHit in PendingHits)
			{
				PendingKeys.Add(pendingHit.Key);
			}
			for (int i = 0; i < PendingKeys.Count; i++)
			{
				long num = PendingKeys[i];
				float num2 = PendingHits[num];
				if (instance != null && num2 > 0f)
				{
					object[] array = new object[1];
					int num3 = ++_seq;
					array[0] = "1;" + num3.ToString(Inv) + ";" + num2.ToString("R", Inv);
					instance.InvokeRoutedRPC(num, "Iconsee_CHit1", array);
				}
			}
			PendingHits.Clear();
		}

		private static void OnHit(long sender, string payload)
		{
			if (Plugin.CombatEnabled.Value && !string.IsNullOrEmpty(payload) && sender != ZNet.GetUID())
			{
				string[] array = payload.Split(new char[1] { ';' });
				if (array.Length >= 3 && int.TryParse(array[0], NumberStyles.Integer, Inv, out var result) && result == 1 && int.TryParse(array[1], NumberStyles.Integer, Inv, out var result2) && float.TryParse(array[2], NumberStyles.Float, Inv, out var result3) && !float.IsNaN(result3) && !(result3 <= 0f) && !(result3 > 10000000f) && (!LastSeq.TryGetValue(sender, out var value) || result2 > value))
				{
					LastSeq[sender] = result2;
					CombatCollector.Meter.Add(result3, Time.time);
				}
			}
		}

		internal static void PublishLive(FightRecord rec)
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC(0L, "Iconsee_CLive1", new object[1] { FightCodec.Serialize(rec) });
			}
			StoreLive(rec);
		}

		internal static void PublishFinished(FightRecord rec)
		{
			bool flag = CombatStore.Add(rec);
			Plugin.Log.LogInfo((object)("Combat Analyzer: fight finished: " + rec.BossPrefab + ", players=" + rec.Players.Count + ", stored=" + flag));
			RemoveLive(rec.Key);
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC(0L, "Iconsee_CFight1", new object[1] { FightCodec.Serialize(rec) });
			}
		}

		private static void StoreLive(FightRecord rec)
		{
			if (rec == null || rec.Finished || rec.WorldUid != CombatStore.WorldUid || CombatStore.Contains(rec.Key))
			{
				return;
			}
			if (Live.TryGetValue(rec.Key, out var value))
			{
				if (value == rec)
				{
					LiveStamp[rec.Key] = Time.time;
					return;
				}
				if (value.Revision >= rec.Revision)
				{
					return;
				}
			}
			Live[rec.Key] = rec;
			LiveStamp[rec.Key] = Time.time;
		}

		private static void RemoveLive(string key)
		{
			Live.Remove(key);
			LiveStamp.Remove(key);
		}

		private static void PruneLive(float now)
		{
			StaleKeys.Clear();
			foreach (KeyValuePair<string, float> item in LiveStamp)
			{
				if (now - item.Value > 900f)
				{
					StaleKeys.Add(item.Key);
				}
			}
			for (int i = 0; i < StaleKeys.Count; i++)
			{
				RemoveLive(StaleKeys[i]);
			}
			if (HelloReplied.Count > 64)
			{
				HelloReplied.Clear();
			}
		}

		private static void OnLive(long sender, string line)
		{
			if (Plugin.CombatEnabled.Value && sender != ZNet.GetUID() && CombatStore.IsOpen && FightCodec.TryParse(line, out var rec))
			{
				StoreLive(rec);
			}
		}

		private static void OnFights(long sender, string payload)
		{
			if (!Plugin.CombatEnabled.Value || string.IsNullOrEmpty(payload) || !CombatStore.IsOpen)
			{
				return;
			}
			string[] array = payload.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				if (FightCodec.TryParse(array[i], out var rec) && CombatStore.Add(rec))
				{
					RemoveLive(rec.Key);
				}
			}
		}

		private static void SendHello()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC(0L, "Iconsee_CHello1", new object[1] { CombatStore.WorldUid.ToString(Inv) + ";" + JoinKeys(CombatStore.All, 2000) });
			}
		}

		private static string JoinKeys(IList<FightRecord> list, int max)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = ((list.Count > max) ? (list.Count - max) : 0); i < list.Count; i++)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(list[i].Key);
			}
			return stringBuilder.ToString();
		}

		private static HashSet<string> ParseKeys(string csv)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (string.IsNullOrEmpty(csv))
			{
				return hashSet;
			}
			string[] array = csv.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length && i < 4000; i++)
			{
				if (array[i].Length > 0)
				{
					hashSet.Add(array[i]);
				}
			}
			return hashSet;
		}

		private static bool SplitWorld(string payload, out long world, out string keys)
		{
			world = 0L;
			keys = "";
			if (string.IsNullOrEmpty(payload))
			{
				return false;
			}
			int num = payload.IndexOf(';');
			if (num <= 0)
			{
				return false;
			}
			if (!long.TryParse(payload.Substring(0, num), NumberStyles.Integer, Inv, out world))
			{
				return false;
			}
			keys = payload.Substring(num + 1);
			return true;
		}

		private static void OnHello(long sender, string payload)
		{
			if (!Plugin.CombatEnabled.Value || sender == ZNet.GetUID() || !CombatStore.IsOpen || !SplitWorld(payload, out var world, out var keys) || world != CombatStore.WorldUid)
			{
				return;
			}
			float time = Time.time;
			if (HelloReplied.TryGetValue(sender, out var value) && time - value < 20f)
			{
				return;
			}
			HelloReplied[sender] = time;
			HashSet<string> hashSet = ParseKeys(keys);
			List<FightRecord> list = new List<FightRecord>();
			IList<FightRecord> all = CombatStore.All;
			for (int i = 0; i < all.Count; i++)
			{
				if (!hashSet.Contains(all[i].Key))
				{
					list.Add(all[i]);
				}
			}
			SendRecords(sender, list);
			StringBuilder stringBuilder = new StringBuilder();
			foreach (string item in hashSet)
			{
				if (!CombatStore.Contains(item))
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append(item);
				}
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (stringBuilder.Length > 0 && instance != null)
			{
				instance.InvokeRoutedRPC(sender, "Iconsee_CWant1", new object[1] { CombatStore.WorldUid.ToString(Inv) + ";" + stringBuilder });
			}
		}

		private static void OnWant(long sender, string payload)
		{
			if (!Plugin.CombatEnabled.Value || sender == ZNet.GetUID() || !CombatStore.IsOpen || !SplitWorld(payload, out var world, out var keys) || world != CombatStore.WorldUid)
			{
				return;
			}
			HashSet<string> hashSet = ParseKeys(keys);
			List<FightRecord> list = new List<FightRecord>();
			IList<FightRecord> all = CombatStore.All;
			for (int i = 0; i < all.Count; i++)
			{
				if (hashSet.Contains(all[i].Key))
				{
					list.Add(all[i]);
				}
			}
			SendRecords(sender, list);
		}

		private static void SendRecords(long peer, List<FightRecord> list)
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null || list.Count == 0)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (num > 0)
				{
					stringBuilder.Append('\n');
				}
				stringBuilder.Append(FightCodec.Serialize(list[i]));
				num++;
				if (num >= 15 || stringBuilder.Length >= 40000)
				{
					instance.InvokeRoutedRPC(peer, "Iconsee_CFight1", new object[1] { stringBuilder.ToString() });
					stringBuilder.Length = 0;
					num = 0;
				}
			}
			if (num > 0)
			{
				instance.InvokeRoutedRPC(peer, "Iconsee_CFight1", new object[1] { stringBuilder.ToString() });
			}
		}
	}
	internal static class DeathMessages
	{
		private const string RpcName = "Icons_Death";

		private const float HitMemorySeconds = 8f;

		private const float TreeCheckRadius = 3.5f;

		private const float TreeMinSpeed = 1.5f;

		private static string lastKind;

		private static string lastValue;

		private static float lastTime = -100f;

		internal static void Register(ZRoutedRpc rpc)
		{
			lastTime = -100f;
			rpc.Register<string, string>("Icons_Death", (Action<long, string, string>)RpcReceive);
		}

		private static bool NearFallingTree(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(pos, 3.5f);
			foreach (Collider val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Transform val2 = ((Component)val).transform;
				bool flag = false;
				int num = 0;
				while (num < 5 && (Object)(object)val2 != (Object)null)
				{
					string name = ((Object)val2).name;
					if (name.IndexOf("Tree", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Log", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						flag = true;
						break;
					}
					num++;
					val2 = val2.parent;
				}
				if (!flag)
				{
					continue;
				}
				Rigidbody attachedRigidbody = val.attachedRigidbody;
				if ((Object)(object)attachedRigidbody != (Object)null && !attachedRigidbody.IsSleeping())
				{
					Vector3 linearVelocity = attachedRigidbody.linearVelocity;
					if (((Vector3)(ref linearVelocity)).sqrMagnitude >= 2.25f)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static void OnDamage(Character target, HitData hit)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.DeathChatMessages.Value || (Object)(object)target == (Object)null || hit == null || (Object)(object)target != (Object)(object)Player.m_localPlayer || hit.GetTotalDamage() <= 0f)
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker != (Object)null)
			{
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if ((Object)(object)val != (Object)null)
				{
					lastKind = "player";
					lastValue = val.GetPlayerName();
				}
				else
				{
					lastKind = "mob";
					lastValue = attacker.m_name;
				}
			}
			else if (NearFallingTree(((Component)target).transform.position))
			{
				lastKind = "tree";
				lastValue = "";
			}
			else
			{
				lastKind = "hit";
				lastValue = ((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString();
			}
			lastTime = Time.time;
		}

		internal static void OnPlayerDeath(Player player)
		{
			if (Plugin.DeathChatMessages.Value && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				string text = "hit";
				string text2 = "Undefined";
				if (Time.time - lastTime <= 8f && lastKind != null)
				{
					text = lastKind;
					text2 = lastValue;
				}
				lastTime = -100f;
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(0L, "Icons_Death", new object[2]
					{
						player.GetPlayerName(),
						text + "|" + text2
					});
				}
			}
		}

		private static void RpcReceive(long sender, string playerName, string payload)
		{
			if (string.IsNullOrEmpty(playerName) || string.IsNullOrEmpty(payload) || payload.Length > 200)
			{
				return;
			}
			int num = payload.IndexOf('|');
			if (num <= 0)
			{
				return;
			}
			string text = payload.Substring(0, num);
			string text2 = payload.Substring(num + 1);
			if (text == "tree")
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					((Terminal)Chat.instance).AddString(string.Format(Loc.T("death.tree"), playerName));
				}
				return;
			}
			string text3 = "m";
			string arg;
			if (text == "mob")
			{
				arg = ((Localization.instance != null) ? Localization.instance.Localize(text2) : text2);
			}
			else if (text == "player")
			{
				arg = text2;
			}
			else
			{
				if (!Loc.TryGet("death.hit." + text2, out var value))
				{
					value = Loc.T("death.hit.Undefined");
				}
				string[] array = value.Split(new char[1] { '|' });
				arg = array[0];
				if (array.Length > 1)
				{
					text3 = array[1];
				}
			}
			string arg2 = Loc.T("death.verb." + text3);
			string text4 = string.Format(Loc.T("death.fmt"), arg, arg2, playerName);
			if ((Object)(object)Chat.instance != (Object)null)
			{
				((Terminal)Chat.instance).AddString(text4);
			}
		}
	}
	internal sealed class HudContext
	{
		public Camera Cam;

		public Player Player;

		public Vector3 PlayerPos;

		public Rect PlayerRect;

		public bool HasPlayerRect;
	}
	internal static class HudMarkers
	{
		private sealed class Marker
		{
			public RectTransform Rt;

			public Image Icon;

			public Text Label;

			public Outline Outline;

			public string LastLabel;
		}

		private static readonly HudContext Shared = new HudContext();

		private static readonly List<Marker> Pool = new List<Marker>();

		private static GameObject overlayGo;

		private static RectTransform overlayRoot;

		private static GameObject depthGo;

		private static Canvas depthCanvas;

		private static RectTransform depthRoot;

		private static int used;

		internal static HudContext Ctx;

		internal static void Tick()
		{
			Ctx = BuildContext();
			if (Ctx == null)
			{
				HideFrom(0);
				return;
			}
			EnsureCanvas();
			if (Plugin.BehindPlayer.Value)
			{
				Camera cam = Ctx.Cam;
				float num = FarthestPlayerZ(cam, Ctx.Player);
				depthCanvas.worldCamera = cam;
				depthCanvas.planeDistance = Mathf.Clamp(num + 0.15f, cam.nearClipPlane + 0.05f, cam.farClipPlane - 1f);
			}
			used = 0;
			TrackedPins.Collect(Ctx);
			TempMarks.Collect(Ctx);
			TeamHud.Collect(Ctx);
			HideFrom(used);
		}

		private static float FarthestPlayerZ(Camera cam, Player player)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_007e: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			Collider component = ((Component)player).GetComponent<Collider>();
			Bounds bounds = default(Bounds);
			if ((Object)(object)component != (Object)null)
			{
				bounds = component.bounds;
			}
			else
			{
				((Bounds)(ref bounds))..ctor(((Component)player).transform.position + Vector3.up * 0.9f, new Vector3(0.8f, 1.8f, 0.8f));
			}
			((Bounds)(ref bounds)).Expand(0.5f);
			Vector3 position = ((Component)cam).transform.position;
			Vector3 forward = ((Component)cam).transform.forward;
			Vector3 min = ((Bounds)(ref bounds)).min;
			Vector3 max = ((Bounds)(ref bounds)).max;
			float num = float.MinValue;
			for (int i = 0; i < 8; i++)
			{
				float num2 = Vector3.Dot(new Vector3(((i & 1) == 0) ? min.x : max.x, ((i & 2) == 0) ? min.y : max.y, ((i & 4) == 0) ? min.z : max.z) - position, forward);
				if (num2 > num)
				{
					num = num2;
				}
			}
			return num;
		}

		private static HudContext BuildContext()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Camera main = Camera.main;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)main == (Object)null)
			{
				return null;
			}
			if (MapAccess.IsMapOpen() || MapAccess.HudHiddenByUser() || ((Character)localPlayer).IsDead() || MapAccess.AnyWindowOpen())
			{
				return null;
			}
			if (CinematicsManager.IsStartedPlaying() || ((Character)localPlayer).InCutscene())
			{
				return null;
			}
			HudContext shared = Shared;
			shared.Cam = main;
			shared.Player = localPlayer;
			shared.PlayerPos = ((Component)localPlayer).transform.position;
			shared.HasPlayerRect = PlayerScreenRect(main, shared.PlayerPos, out shared.PlayerRect);
			return shared;
		}

		private static bool PlayerScreenRect(Camera cam, Vector3 playerPos, out Rect rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to inval