Decompiled source of SkeletonCommander v1.9.1

plugins/SkeletonCommander.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SkeletonCommander")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ba1fdd6e7a3b796f906e04e7c1e23455c53972ec")]
[assembly: AssemblyProduct("SkeletonCommander")]
[assembly: AssemblyTitle("SkeletonCommander")]
[assembly: AssemblyVersion("1.0.0.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 SkeletonCommander
{
	internal static class HotkeyInputBlocker
	{
		private delegate bool KeyCodeToKey(KeyCode keyCode, out Key key);

		private static readonly KeyCodeToKey TryKeyCodeToKey = AccessTools.MethodDelegate<KeyCodeToKey>(AccessTools.Method(typeof(ZInput), "TryKeyCodeToKey", (Type[])null, (Type[])null), (object)null, true);

		private static readonly HashSet<Key> Blocked = new HashSet<Key>();

		private static int _blockedFrame = -1;

		public static bool IsBlocked(string buttonName)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			UpdateBlockedKeys();
			if (Blocked.Count == 0 || ZInput.instance == null)
			{
				return false;
			}
			ButtonDef buttonDef = ZInput.instance.GetButtonDef(buttonName);
			if (buttonDef == null)
			{
				return false;
			}
			Enumerator<InputControl> enumerator = buttonDef.ButtonAction.controls.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					InputControl current = enumerator.Current;
					KeyControl val = (KeyControl)(object)((current is KeyControl) ? current : null);
					if (val != null && Blocked.Contains(val.keyCode))
					{
						return true;
					}
				}
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			return false;
		}

		private static void UpdateBlockedKeys()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (_blockedFrame == Time.frameCount)
			{
				return;
			}
			_blockedFrame = Time.frameCount;
			Blocked.Clear();
			foreach (ConfigEntry<KeyboardShortcut> hotkey in SkeletonCommanderPlugin.Hotkeys)
			{
				KeyboardShortcut value = hotkey.Value;
				if (((KeyboardShortcut)(ref value)).Modifiers.Any() && ((KeyboardShortcut)(ref value)).Modifiers.All((Func<KeyCode, bool>)UnityInput.Current.GetKey) && TryKeyCodeToKey(((KeyboardShortcut)(ref value)).MainKey, out var key))
				{
					Blocked.Add(key);
				}
			}
		}
	}
	internal enum HudStyle
	{
		Bars,
		Text
	}
	[BepInPlugin("xmordax.valheim.skeletoncommander", "Skeleton Commander", "1.9.1")]
	public class SkeletonCommanderPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "xmordax.valheim.skeletoncommander";

		public const string PluginName = "Skeleton Commander";

		public const string PluginVersion = "1.9.1";

		private static readonly string[] OldConfigFileNames = new string[2] { "michal.valheim.skeletoncommander.cfg", "michal.valheim.skeletontracker.cfg" };

		private const float ScanInterval = 0.5f;

		private const string PinIconItem = "TrophySkeleton";

		private const string PinNameFollowing = "Skeleton";

		private const string PinNameStaying = "Skeleton (staying)";

		private const string GlowObjectName = "SkeletonCommander_Glow";

		private static readonly FieldRef<Minimap, bool> PinUpdateRequired = AccessTools.FieldRefAccess<Minimap, bool>("m_pinUpdateRequired");

		private static readonly FieldRef<Chat, float> ChatHideTimer = AccessTools.FieldRefAccess<Chat, float>("m_hideTimer");

		private static readonly Func<Player, bool> PlayerTakesInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true);

		internal static ConfigEntry<KeyboardShortcut> FollowKey;

		internal static ConfigEntry<KeyboardShortcut> StayKey;

		internal static ConfigEntry<KeyboardShortcut> DismissKey;

		internal static ConfigEntry<KeyboardShortcut> ListKey;

		internal static ConfigEntry<bool> ShowCounter;

		internal static ConfigEntry<bool> ShowDetails;

		internal static ConfigEntry<bool> ShowStateSummary;

		internal static ConfigEntry<bool> ShowTeam;

		internal static ConfigEntry<bool> ShowName;

		internal static ConfigEntry<bool> ShowWeapon;

		internal static ConfigEntry<bool> ShowHealth;

		internal static ConfigEntry<bool> ShowDistance;

		internal static ConfigEntry<bool> ShowState;

		internal static ConfigEntry<bool> ShowShield;

		internal static ConfigEntry<float> CounterPositionY;

		internal static ConfigEntry<float> HudFontSize;

		internal static ConfigEntry<HudStyle> Style;

		internal static ConfigEntry<bool> HealthBars;

		internal static ConfigEntry<float> HealthBarDistance;

		internal static ConfigEntry<bool> HealthBarColors;

		internal static ConfigEntry<bool> GlowEnabled;

		internal static ConfigEntry<Color> GlowColor;

		internal static ConfigEntry<float> GlowIntensity;

		internal static ConfigEntry<float> GlowRange;

		internal static ConfigEntry<bool> DeathNotifications;

		internal static ConfigEntry<bool> TeamEnabled;

		internal static ConfigEntry<TeamSlot>[] TeamSlots;

		internal static ConfigEntry<bool> DismissWeakest;

		internal static ConfigEntry<float> DismissBeyondDistance;

		internal static ConfigEntry<bool> KeepNearOtherPlayers;

		private static readonly HashSet<Character> Owned = new HashSet<Character>();

		private readonly List<Character> _skeletons = new List<Character>();

		private readonly Dictionary<Character, PinData> _pins = new Dictionary<Character, PinData>();

		private readonly List<Character> _stale = new List<Character>();

		private readonly HashSet<Character> _deathHooked = new HashSet<Character>();

		private readonly SkeletonHud _hud = new SkeletonHud();

		private Harmony _harmony;

		private Minimap _minimap;

		private Sprite _pinIcon;

		private float _timer;

		internal static ManualLogSource Log;

		internal static IEnumerable<ConfigEntry<KeyboardShortcut>> Hotkeys => new ConfigEntry<KeyboardShortcut>[4] { FollowKey, StayKey, DismissKey, ListKey };

		internal static bool IsOwned(Character character)
		{
			if ((Object)(object)character != (Object)null)
			{
				return Owned.Contains(character);
			}
			return false;
		}

		private void Awake()
		{
			//IL_0031: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Expected O, but got Unknown
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fc: Expected O, but got Unknown
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Expected O, but got Unknown
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ee: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			MigrateOldConfigFile();
			FollowKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Follow", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Skeletons follow you (like /sk follow). When aiming at one of your skeletons, affects only that one.");
			StayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Stay", new KeyboardShortcut((KeyCode)103, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Skeletons stay in place (like /sk stay). When aiming at one of your skeletons, affects only that one.");
			DismissKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Dismiss", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Dismisses skeletons (like /sk dismiss). When aiming at one of your skeletons, affects only that one.");
			ListKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "List", new KeyboardShortcut((KeyCode)106, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Lists your skeletons in chat (like /sk list).");
			TeamEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Summon", "TeamEnabled", true, "Gives new summons the weapons set by TeamSlot1-4. Off: skeletons get random weapons as in the base game.");
			TeamSlots = new TeamSlot[4]
			{
				TeamSlot.SwordAndShield,
				TeamSlot.Bow,
				TeamSlot.SwordAndShield,
				TeamSlot.Bow
			}.Select((TeamSlot value, int i) => ((BaseUnityPlugin)this).Config.Bind<TeamSlot>("Summon", $"TeamSlot{i + 1}", value, $"Weapons of team member {i + 1}, used from a summon limit of {i + 1} (Dead Raiser upgrade level). " + "New summons get the weapon the team is missing; Random accepts any weapon. Already summoned skeletons keep their weapons.")).ToArray();
			DismissWeakest = ((BaseUnityPlugin)this).Config.Bind<bool>("Summon", "DismissWeakest", true, "Over the summon limit, the skeleton with the least HP is dismissed (the oldest on a tie), so a new summon replaces it with the weapon its team slot needs. Off: the oldest is dismissed as in the base game.");
			DismissBeyondDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Summon", "DismissBeyondDistance", 100f, new ConfigDescription("Skeletons farther away than this (in meters) from you, and from other players with KeepNearOtherPlayers, are dismissed, also when you teleport or die far from them. Skeletons left in an unloaded area would otherwise not count toward the summon limit. Skeletons leaving the area the game simulates around you (which depends on the Simulation distance graphics setting) are dismissed even when closer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 120f), Array.Empty<object>()));
			KeepNearOtherPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Summon", "KeepNearOtherPlayers", true, "Far skeletons are kept while another player is near them, so they keep fighting alongside that player (e.g. after you die). They don't count toward your summon limit while you are away; when you come back to more skeletons than your limit, the extra ones are dismissed. Off: far skeletons are dismissed even when other players are near them.");
			DeathNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "Death", true, "On-screen message when one of your skeletons dies.");
			ShowCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowCounter", true, "Shows the skeleton counter below the minimap.");
			ShowDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowDetails", true, "Lists each skeleton below the counter: name, weapon, health, distance and whether it follows you.");
			ShowStateSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowStateSummary", true, "Shows how many skeletons are following you and how many are staying next to the counter.");
			ShowTeam = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowTeam", true, "Shows how many skeletons of each type your team (Summon.TeamSlot1-4) has and wants.");
			ShowName = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowName", true, "Shows each skeleton's name in the detailed list.");
			ShowWeapon = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowWeapon", true, "Shows each skeleton's weapon in the detailed list.");
			ShowHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowHealth", true, "Shows each skeleton's health in the detailed list.");
			ShowDistance = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowDistance", true, "Shows each skeleton's distance from you in the detailed list.");
			ShowState = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowState", true, "Shows whether each skeleton is following you or staying in the detailed list.");
			ShowShield = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowShield", true, "Shows the barrier from the Staff of Protection (how much is left and for how long) in the detailed list.");
			CounterPositionY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "CounterPositionY", 0.3f, new ConfigDescription("Vertical counter position as a fraction of screen height (0 = top, 1 = bottom).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			HudFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "FontSize", 18f, new ConfigDescription("Font size of the skeleton counter and list; the whole box scales with it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 40f), Array.Empty<object>()));
			Style = ((BaseUnityPlugin)this).Config.Bind<HudStyle>("HUD", "Style", HudStyle.Bars, "How the detailed list shows each skeleton: Bars (a panel with an HP bar per skeleton) or Text (one line each).");
			HealthBars = ((BaseUnityPlugin)this).Config.Bind<bool>("HealthBars", "AlwaysShow", true, "Health bars above your skeletons' heads are always visible.");
			HealthBarDistance = ((BaseUnityPlugin)this).Config.Bind<float>("HealthBars", "MaxDistance", 40f, new ConfigDescription("Maximum distance (m) at which skeleton health bars are shown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f), Array.Empty<object>()));
			HealthBarColors = ((BaseUnityPlugin)this).Config.Bind<bool>("HealthBars", "ColorByHealth", true, "Colors skeleton health bars by HP: green > 60%, yellow > 30%, red below.");
			GlowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "Enabled", true, "Subtle glow around your skeletons.");
			GlowColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Glow", "Color", new Color(0.4f, 0.85f, 1f, 1f), "Glow color (RGBA, hex).");
			GlowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "Intensity", 1.2f, new ConfigDescription("Glow brightness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			GlowRange = ((BaseUnityPlugin)this).Config.Bind<float>("Glow", "Range", 3f, new ConfigDescription("Glow range (m).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
			SkeletonCommands.Register();
			_harmony = Harmony.CreateAndPatchAll(typeof(SkeletonPatches), "xmordax.valheim.skeletoncommander");
		}

		private void Update()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			HandleHotkeys();
			_timer += Time.deltaTime;
			if (_timer < 0.5f)
			{
				return;
			}
			_timer = 0f;
			Minimap instance = Minimap.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance != (Object)(object)_minimap)
			{
				RemoveAllPins();
				_minimap = instance;
			}
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				RemoveAllPins();
				_skeletons.Clear();
				Owned.Clear();
				_hud.Hide();
				return;
			}
			SummonedSkeletons.FindOwned(localPlayer, _skeletons);
			if (!((Character)localPlayer).IsTeleporting())
			{
				DismissFarSkeletons(localPlayer, ((Component)localPlayer).transform.position, _skeletons);
			}
			DismissOverLimit(localPlayer, _skeletons);
			Owned.Clear();
			Owned.UnionWith(_skeletons);
			_deathHooked.RemoveWhere((Character c) => (Object)(object)c == (Object)null);
			int num = 0;
			foreach (Character skeleton in _skeletons)
			{
				bool flag = SummonedSkeletons.IsFollowing(skeleton, localPlayer);
				if (flag)
				{
					num++;
				}
				UpdatePin(instance, skeleton, flag);
				UpdateGlow(skeleton);
				if (_deathHooked.Add(skeleton))
				{
					Character obj = skeleton;
					obj.m_onDeath = (Action)Delegate.Combine(obj.m_onDeath, (Action)delegate
					{
						OnSkeletonDied(skeleton);
					});
				}
			}
			RemoveStalePins(instance);
			PinUpdateRequired.Invoke(instance) = true;
			UpdateHud(localPlayer, num);
		}

		private void HandleHotkeys()
		{
			//IL_0036: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && PlayerTakesInput(localPlayer))
			{
				Character hoverCreature = localPlayer.GetHoverCreature();
				Character only = (IsOwned(hoverCreature) ? hoverCreature : null);
				if (IsPressed(FollowKey.Value))
				{
					ShowMessage(localPlayer, SkeletonCommands.Follow(localPlayer, only));
				}
				else if (IsPressed(StayKey.Value))
				{
					ShowMessage(localPlayer, SkeletonCommands.Stay(localPlayer, only));
				}
				else if (IsPressed(DismissKey.Value))
				{
					ShowMessage(localPlayer, SkeletonCommands.Dismiss(localPlayer, only));
				}
				else if (IsPressed(ListKey.Value) && (Object)(object)Chat.instance != (Object)null)
				{
					SkeletonCommands.List((Terminal)(object)Chat.instance, localPlayer);
					ChatHideTimer.Invoke(Chat.instance) = 0f;
				}
			}
		}

		private static bool IsPressed(KeyboardShortcut shortcut)
		{
			//IL_0002: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && UnityInput.Current.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey))
			{
				return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)UnityInput.Current.GetKey);
			}
			return false;
		}

		private void MigrateOldConfigFile()
		{
			if (File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				return;
			}
			string[] oldConfigFileNames = OldConfigFileNames;
			foreach (string path in oldConfigFileNames)
			{
				string text = Path.Combine(Paths.ConfigPath, path);
				if (File.Exists(text))
				{
					File.Move(text, ((BaseUnityPlugin)this).Config.ConfigFilePath);
					((BaseUnityPlugin)this).Config.Reload();
					break;
				}
			}
		}

		private static void ShowMessage(Player player, string text)
		{
			((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
		}

		internal static string GetHoverHints()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			return HoverHint(FollowKey.Value, "Follow") + HoverHint(StayKey.Value, "Stay") + HoverHint(DismissKey.Value, "Dismiss");
		}

		private static string HoverHint(KeyboardShortcut key, string action)
		{
			//IL_0002: 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_002b: Expected I4, but got Unknown
			if ((int)((KeyboardShortcut)(ref key)).MainKey == 0)
			{
				return "";
			}
			string text = string.Join(" + ", ((KeyboardShortcut)(ref key)).Modifiers.Concat((IEnumerable<KeyCode>)(object)new KeyCode[1] { (KeyCode)(int)((KeyboardShortcut)(ref key)).MainKey }));
			return "\n[<color=yellow><b>" + text + "</b></color>] " + action;
		}

		private void UpdateHud(Player player, int following)
		{
			if (!ShowCounter.Value || _skeletons.Count == 0)
			{
				_hud.Hide();
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"<align=center>Skeletons: {_skeletons.Count}");
			if (ShowStateSummary.Value)
			{
				stringBuilder.Append($"  ({following} following, {_skeletons.Count - following} staying)");
			}
			stringBuilder.Append("</align>");
			if (ShowTeam.Value && TeamEnabled.Value)
			{
				stringBuilder.Append("\n<align=center><size=80%>" + SkeletonLoadouts.DescribeTeam(_skeletons) + "</size></align>");
			}
			List<SkeletonFrameData> frames = null;
			if (ShowDetails.Value && Style.Value == HudStyle.Bars)
			{
				frames = _skeletons.Select((Character skeleton, int i) => DescribeFrame(skeleton, i, player)).ToList();
			}
			else if (ShowDetails.Value)
			{
				for (int num = 0; num < _skeletons.Count; num++)
				{
					stringBuilder.Append($"\n<size=80%>#{num + 1}  ").Append(SummonedSkeletons.Describe(_skeletons[num], player, ShowName.Value, ShowWeapon.Value, ShowHealth.Value, ShowDistance.Value, ShowState.Value, padded: true, ShowShield.Value)).Append("</size>");
				}
			}
			_hud.Show(stringBuilder.ToString(), CounterPositionY.Value, HudFontSize.Value, frames);
		}

		private static SkeletonFrameData DescribeFrame(Character skeleton, int index, Player player)
		{
			//IL_00ac: 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)
			string arg = (ShowName.Value ? (" " + SummonedSkeletons.GetName(skeleton)) : "");
			string arg2 = SummonedSkeletons.Describe(skeleton, player, name: false, ShowWeapon.Value, health: false, ShowDistance.Value, ShowState.Value, padded: false, shield: false);
			float remaining;
			float total;
			float seconds;
			bool flag = SummonedSkeletons.TryGetShield(skeleton, out remaining, out total, out seconds);
			return new SkeletonFrameData
			{
				Header = $"<color=#FFC74D>#{index + 1}{arg}</color>  <size=90%>{arg2}</size>",
				ShowBar = ShowHealth.Value,
				Health = skeleton.GetHealth(),
				MaxHealth = skeleton.GetMaxHealth(),
				BarColor = SummonedSkeletons.GetHealthColor(skeleton),
				ShowShield = (ShowShield.Value && flag),
				Shield = remaining,
				MaxShield = total,
				ShieldSeconds = seconds
			};
		}

		internal static void DismissFarSkeletons(Player player, Vector3 center, List<Character> skeletons)
		{
			//IL_0007: 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)
			float maxDistance = DismissBeyondDistance.Value;
			int num = skeletons.RemoveAll(delegate(Character skeleton)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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_007c: 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)
				Vector3 position = ((Component)skeleton).transform.position;
				if (IsNear(position, center, maxDistance))
				{
					return false;
				}
				ZNetView component = ((Component)skeleton).GetComponent<ZNetView>();
				if (KeepNearOtherPlayers.Value && ((component.GetZDO().HasOwner() && !component.IsOwner()) || Player.GetAllPlayers().Any((Player other) => (Object)(object)other != (Object)(object)player && IsNear(position, ((Component)other).transform.position, maxDistance))))
				{
					return false;
				}
				float num2 = SummonedSkeletons.HorizontalDistance(position, center);
				Log.LogInfo((object)($"Dismissing {SummonedSkeletons.GetName(skeleton)} at {num2:0} m " + "(owner: " + (component.IsOwner() ? "you" : (component.GetZDO().HasOwner() ? "another player" : "none")) + ")"));
				SummonedSkeletons.Dismiss(skeleton);
				return true;
			});
			if (num > 0)
			{
				((Character)player).Message((MessageType)1, (num == 1) ? "Skeleton too far away - dismissed" : $"{num} skeletons too far away - dismissed", 0, (Sprite)null, false);
			}
		}

		private static bool IsNear(Vector3 position, Vector3 center, float maxDistance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (SummonedSkeletons.HorizontalDistance(position, center) <= maxDistance)
			{
				return ZNetScene.InActiveArea(position, center);
			}
			return false;
		}

		private static void DismissOverLimit(Player player, List<Character> skeletons)
		{
			if (skeletons.Count == 0)
			{
				return;
			}
			int maxInstances = SummonedSkeletons.GetMaxInstances(skeletons[0]);
			if (maxInstances > 0)
			{
				int num = SummonedSkeletons.DismissExcess(skeletons, maxInstances);
				if (num > 0)
				{
					((Character)player).Message((MessageType)1, (num == 1) ? "Skeleton over the summon limit - dismissed" : $"{num} skeletons over the summon limit - dismissed", 0, (Sprite)null, false);
				}
			}
		}

		internal static void DismissFarSkeletons(Player player, Vector3 center)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			List<Character> list = new List<Character>();
			SummonedSkeletons.FindOwned(player, list);
			DismissFarSkeletons(player, center, list);
		}

		private void OnSkeletonDied(Character skeleton)
		{
			Player localPlayer = Player.m_localPlayer;
			if (DeathNotifications.Value && !((Object)(object)localPlayer == (Object)null))
			{
				string name = SummonedSkeletons.GetName(skeleton);
				((Character)localPlayer).Message((MessageType)1, name + " (" + SummonedSkeletons.GetWeaponName(skeleton) + ") has died", 0, GetPinIcon(), false);
			}
		}

		private void UpdatePin(Minimap minimap, Character skeleton, bool following)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)skeleton).transform.position;
			string text = (following ? "Skeleton" : "Skeleton (staying)");
			if (_pins.TryGetValue(skeleton, out var value))
			{
				if (value.m_name == text)
				{
					value.m_pos = position;
					return;
				}
				minimap.RemovePin(value);
			}
			value = minimap.AddPin(position, (PinType)3, text, false, false, 0L, default(PlatformUserID));
			Sprite pinIcon = GetPinIcon();
			if ((Object)(object)pinIcon != (Object)null)
			{
				value.m_icon = pinIcon;
			}
			_pins[skeleton] = value;
		}

		private void RemoveStalePins(Minimap minimap)
		{
			_stale.Clear();
			foreach (KeyValuePair<Character, PinData> pin in _pins)
			{
				if (!Owned.Contains(pin.Key))
				{
					_stale.Add(pin.Key);
				}
			}
			foreach (Character item in _stale)
			{
				minimap.RemovePin(_pins[item]);
				_pins.Remove(item);
			}
		}

		private static void UpdateGlow(Character skeleton)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)skeleton).transform.Find("SkeletonCommander_Glow");
			if (!GlowEnabled.Value)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
				return;
			}
			if ((Object)(object)val == (Object)null)
			{
				val = new GameObject("SkeletonCommander_Glow").transform;
				val.SetParent(((Component)skeleton).transform, false);
				val.localPosition = Vector3.up * 1.2f;
				Light obj = ((Component)val).gameObject.AddComponent<Light>();
				obj.type = (LightType)2;
				obj.shadows = (LightShadows)0;
			}
			Light component = ((Component)val).GetComponent<Light>();
			component.color = GlowColor.Value;
			component.intensity = GlowIntensity.Value;
			component.range = GlowRange.Value;
		}

		private Sprite GetPinIcon()
		{
			if ((Object)(object)_pinIcon == (Object)null && (Object)(object)ObjectDB.instance != (Object)null)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("TrophySkeleton");
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null)
				{
					_pinIcon = val.m_itemData.GetIcon();
				}
			}
			return _pinIcon;
		}

		private void RemoveAllPins()
		{
			if ((Object)(object)_minimap != (Object)null)
			{
				foreach (PinData value in _pins.Values)
				{
					_minimap.RemovePin(value);
				}
			}
			_pins.Clear();
		}

		private void OnDestroy()
		{
			RemoveAllPins();
			_hud.Destroy();
			foreach (Character item in Owned)
			{
				Transform val = (((Object)(object)item != (Object)null) ? ((Component)item).transform.Find("SkeletonCommander_Glow") : null);
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
			Owned.Clear();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class SkeletonCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Run;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleOptionsFetcher <>9__1_0;

			internal List<string> <Register>b__1_0()
			{
				return Subcommands;
			}
		}

		private static readonly List<string> Subcommands = new List<string> { "list", "follow", "stay", "dismiss" };

		public static void Register()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			object obj = <>O.<0>__Run;
			if (obj == null)
			{
				ConsoleEvent val = Run;
				<>O.<0>__Run = val;
				obj = (object)val;
			}
			object obj2 = <>c.<>9__1_0;
			if (obj2 == null)
			{
				ConsoleOptionsFetcher val2 = () => Subcommands;
				<>c.<>9__1_0 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("sk", "[list|follow|stay|dismiss] - manage your Dead Raiser skeletons", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
		}

		private static void Run(ConsoleEventArgs args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No player in the world.");
				return;
			}
			switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "list")
			{
			case "list":
				List(args.Context, localPlayer);
				break;
			case "follow":
				args.Context.AddString(Follow(localPlayer));
				break;
			case "stay":
				args.Context.AddString(Stay(localPlayer));
				break;
			case "dismiss":
				args.Context.AddString(Dismiss(localPlayer));
				break;
			default:
				args.Context.AddString("Usage: sk [list|follow|stay|dismiss]");
				break;
			}
		}

		public static string Follow(Player player, Character only = null)
		{
			List<Character> list = Targets(player, only);
			foreach (Character item in list)
			{
				SummonedSkeletons.SetFollowing(item, player, follow: true);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return $"Skeletons following you: {list.Count}";
			}
			return SummonedSkeletons.GetName(only) + " is following you";
		}

		public static string Stay(Player player, Character only = null)
		{
			List<Character> list = Targets(player, only);
			foreach (Character item in list)
			{
				SummonedSkeletons.SetFollowing(item, player, follow: false);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return $"Skeletons staying: {list.Count}";
			}
			return SummonedSkeletons.GetName(only) + " is staying";
		}

		public static string Dismiss(Player player, Character only = null)
		{
			List<Character> list = Targets(player, only);
			foreach (Character item in list)
			{
				SummonedSkeletons.Dismiss(item);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return $"Skeletons dismissed: {list.Count}";
			}
			return "Dismissed: " + SummonedSkeletons.GetName(only);
		}

		public static void List(Terminal terminal, Player player)
		{
			List<Character> list = Targets(player, null);
			terminal.AddString($"Your skeletons: {list.Count}");
			for (int i = 0; i < list.Count; i++)
			{
				terminal.AddString($"  #{i + 1}  {SummonedSkeletons.Describe(list[i], player)}");
			}
		}

		private static List<Character> Targets(Player player, Character only)
		{
			if ((Object)(object)only != (Object)null)
			{
				return new List<Character> { only };
			}
			List<Character> result = new List<Character>();
			SummonedSkeletons.FindOwned(player, result);
			return result;
		}
	}
	internal struct SkeletonFrameData
	{
		public string Header;

		public bool ShowBar;

		public float Health;

		public float MaxHealth;

		public Color BarColor;

		public bool ShowShield;

		public float Shield;

		public float MaxShield;

		public float ShieldSeconds;
	}
	internal class SkeletonFrames
	{
		private class Bar
		{
			public RectTransform Back;

			public RectTransform Fill;

			public Image FillImage;

			public TextMeshProUGUI Text;
		}

		private class Frame
		{
			public RectTransform Root;

			public TextMeshProUGUI Header;

			public Bar Health;

			public Bar Shield;
		}

		private const float Gap = 6f;

		private static readonly Color PanelColor = new Color(0f, 0f, 0f, 0.45f);

		private static readonly Color BarBackColor = new Color(0.08f, 0.08f, 0.08f, 0.85f);

		private static readonly Color ShieldColor = new Color(0.45f, 0.75f, 1f);

		private readonly List<Frame> _frames = new List<Frame>();

		private RectTransform _container;

		public void Show(List<SkeletonFrameData> data, Vector2 anchor, float rightMargin, float top, float fontSize)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_container == (Object)null && !CreateContainer())
			{
				return;
			}
			float num = fontSize * 22f;
			float num2 = fontSize * 0.35f;
			float num3 = fontSize * 1.45f;
			float num4 = fontSize * 1.2f;
			float num5 = fontSize * 0.95f;
			RectTransform container = _container;
			Vector2 anchorMin = (_container.anchorMax = anchor);
			container.anchorMin = anchorMin;
			_container.anchoredPosition = new Vector2(0f - rightMargin, 0f - top);
			((Component)_container).gameObject.SetActive(true);
			float num6 = 0f;
			for (int i = 0; i < data.Count; i++)
			{
				if (i == _frames.Count)
				{
					_frames.Add(CreateFrame((Transform)(object)_container));
				}
				Frame frame = _frames[i];
				SkeletonFrameData skeletonFrameData = data[i];
				((Component)frame.Root).gameObject.SetActive(true);
				frame.Root.anchoredPosition = new Vector2(0f, 0f - num6);
				((TMP_Text)frame.Header).text = skeletonFrameData.Header;
				((TMP_Text)frame.Header).fontSize = fontSize * 1.05f;
				((TMP_Text)frame.Header).rectTransform.anchoredPosition = new Vector2(num2, (0f - num2) * 0.5f);
				((TMP_Text)frame.Header).rectTransform.sizeDelta = new Vector2(num - num2 * 2f, num3);
				float num7 = num2 * 0.5f + num3;
				if (SetBar(frame.Health, skeletonFrameData.ShowBar, skeletonFrameData.Health, skeletonFrameData.MaxHealth, skeletonFrameData.BarColor, $"{skeletonFrameData.Health:0} / {skeletonFrameData.MaxHealth:0}", num7, num, num2, num4, fontSize * 0.9f))
				{
					num7 += num4 + num2 * 0.5f;
				}
				if (SetBar(frame.Shield, skeletonFrameData.ShowShield, skeletonFrameData.Shield, skeletonFrameData.MaxShield, ShieldColor, $"shield {skeletonFrameData.Shield:0} / {skeletonFrameData.MaxShield:0} · {skeletonFrameData.ShieldSeconds:0} s", num7, num, num2, num5, fontSize * 0.75f))
				{
					num7 += num5 + num2 * 0.5f;
				}
				float num8 = num7 + num2 * 0.5f;
				frame.Root.sizeDelta = new Vector2(num, num8);
				num6 += num8 + 6f;
			}
			for (int j = data.Count; j < _frames.Count; j++)
			{
				((Component)_frames[j].Root).gameObject.SetActive(false);
			}
		}

		private static bool SetBar(Bar bar, bool show, float value, float max, Color color, string text, float top, float width, float padding, float height, float fontSize)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			((Component)bar.Back).gameObject.SetActive(show);
			if (!show)
			{
				return false;
			}
			bar.Back.anchoredPosition = new Vector2(padding, 0f - top);
			bar.Back.sizeDelta = new Vector2(width - padding * 2f, height);
			bar.Fill.anchorMax = new Vector2((max > 0f) ? Mathf.Clamp01(value / max) : 0f, 1f);
			((Graphic)bar.FillImage).color = color;
			((TMP_Text)bar.Text).text = text;
			((TMP_Text)bar.Text).fontSize = fontSize;
			return true;
		}

		public void Hide()
		{
			if ((Object)(object)_container != (Object)null)
			{
				((Component)_container).gameObject.SetActive(false);
			}
		}

		public void Destroy()
		{
			if ((Object)(object)_container != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_container).gameObject);
			}
			_container = null;
			_frames.Clear();
		}

		private bool CreateContainer()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			_frames.Clear();
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_rootObject == (Object)null)
			{
				return false;
			}
			_container = CreateRect("SkeletonCommanderFrames", instance.m_rootObject.transform, new Vector2(1f, 1f));
			((Transform)_container).SetAsFirstSibling();
			return true;
		}

		private static Frame CreateFrame(Transform parent)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			Frame frame = new Frame();
			frame.Root = CreateRect("Frame", parent, new Vector2(1f, 1f));
			RectTransform root = frame.Root;
			RectTransform root2 = frame.Root;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(1f, 1f);
			root2.anchorMax = val;
			root.anchorMin = val;
			AddImage(frame.Root, null, PanelColor);
			frame.Header = SkeletonHud.CreateText("Header", (Transform)(object)frame.Root, (TextAlignmentOptions)513, new Vector2(0f, 1f), Vector2.zero);
			RectTransform rectTransform = ((TMP_Text)frame.Header).rectTransform;
			RectTransform rectTransform2 = ((TMP_Text)frame.Header).rectTransform;
			((Vector2)(ref val))..ctor(0f, 1f);
			rectTransform2.anchorMax = val;
			rectTransform.anchorMin = val;
			((TMP_Text)frame.Header).overflowMode = (TextOverflowModes)1;
			frame.Health = CreateBar("HealthBar", (Transform)(object)frame.Root);
			frame.Shield = CreateBar("ShieldBar", (Transform)(object)frame.Root);
			return frame;
		}

		private static Bar CreateBar(string name, Transform parent)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			Sprite barSprite = GetBarSprite();
			Bar bar = new Bar();
			bar.Back = CreateRect(name, parent, new Vector2(0f, 1f));
			RectTransform back = bar.Back;
			RectTransform back2 = bar.Back;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			back2.anchorMax = val;
			back.anchorMin = val;
			AddImage(bar.Back, barSprite, BarBackColor);
			bar.Fill = CreateRect("Fill", (Transform)(object)bar.Back, new Vector2(0f, 0.5f));
			bar.Fill.anchorMin = Vector2.zero;
			val = (bar.Fill.offsetMin = (bar.Fill.offsetMax = Vector2.zero));
			bar.FillImage = AddImage(bar.Fill, barSprite, Color.white);
			bar.Text = SkeletonHud.CreateText("Text", (Transform)(object)bar.Back, (TextAlignmentOptions)514, new Vector2(0.5f, 0.5f), Vector2.zero);
			RectTransform rectTransform = ((TMP_Text)bar.Text).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			val = (rectTransform.offsetMin = (rectTransform.offsetMax = Vector2.zero));
			return bar;
		}

		private static Sprite GetBarSprite()
		{
			GuiBar val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_healthBarFast : null);
			Image val2 = (((Object)(object)val != (Object)null && (Object)(object)val.m_bar != (Object)null) ? ((Component)val.m_bar).GetComponent<Image>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return val2.sprite;
		}

		private static RectTransform CreateRect(string name, Transform parent, Vector2 pivot)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.pivot = pivot;
			return val2;
		}

		private static Image AddImage(RectTransform rect, Sprite sprite, Color color)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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)
			Image obj = ((Component)rect).gameObject.AddComponent<Image>();
			obj.sprite = sprite;
			obj.type = (Type)(((Object)(object)sprite != (Object)null && sprite.border != Vector4.zero) ? 1 : 0);
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}
	}
	internal class SkeletonHud
	{
		private const float RightMargin = 20f;

		private const float FramesGap = 6f;

		private readonly SkeletonFrames _frames = new SkeletonFrames();

		private TextMeshProUGUI _list;

		public void Show(string text, float positionY, float fontSize, List<SkeletonFrameData> frames)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_list == (Object)null)
			{
				_list = CreateHudText("SkeletonCommanderHud", (TextAlignmentOptions)257, new Vector2(1f, 1f), new Vector2(-20f, 0f));
			}
			if (!((Object)(object)_list == (Object)null))
			{
				((TMP_Text)_list).text = text;
				((TMP_Text)_list).fontSize = fontSize;
				RectTransform rectTransform = ((TMP_Text)_list).rectTransform;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(1f, 1f - positionY);
				Vector2 anchorMin = (rectTransform.anchorMax = val);
				rectTransform.anchorMin = anchorMin;
				Vector2 preferredValues = ((TMP_Text)_list).GetPreferredValues(text);
				rectTransform.sizeDelta = new Vector2(Mathf.Ceil(preferredValues.x), Mathf.Ceil(preferredValues.y));
				((Component)_list).gameObject.SetActive(true);
				if (frames != null && frames.Count > 0)
				{
					_frames.Show(frames, val, 20f, rectTransform.sizeDelta.y + 6f, fontSize);
				}
				else
				{
					_frames.Hide();
				}
			}
		}

		public void Hide()
		{
			if ((Object)(object)_list != (Object)null)
			{
				((Component)_list).gameObject.SetActive(false);
			}
			_frames.Hide();
		}

		public void Destroy()
		{
			if ((Object)(object)_list != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_list).gameObject);
			}
			_list = null;
			_frames.Destroy();
		}

		private static TextMeshProUGUI CreateHudText(string name, TextAlignmentOptions alignment, Vector2 pivot, Vector2 offset)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_rootObject == (Object)null)
			{
				return null;
			}
			TextMeshProUGUI obj = CreateText(name, instance.m_rootObject.transform, alignment, pivot, offset);
			((TMP_Text)obj).transform.SetAsFirstSibling();
			return obj;
		}

		public static TextMeshProUGUI CreateText(string name, Transform parent, TextAlignmentOptions alignment, Vector2 pivot, Vector2 offset)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			val.SetActive(false);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = instance.m_healthText.font;
			((TMP_Text)obj).fontSharedMaterial = instance.m_healthText.fontSharedMaterial;
			((TMP_Text)obj).fontSize = 18f;
			((TMP_Text)obj).alignment = alignment;
			((TMP_Text)obj).richText = true;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
			RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
			rectTransform.pivot = pivot;
			rectTransform.sizeDelta = new Vector2(700f, 400f);
			rectTransform.anchoredPosition = offset;
			val.SetActive(true);
			return obj;
		}
	}
	internal enum SkeletonLoadout
	{
		Random,
		SwordAndShield,
		Bow
	}
	internal enum TeamSlot
	{
		Random,
		SwordAndShield,
		Bow
	}
	internal static class SkeletonLoadouts
	{
		private const double NewSpawnSeconds = 5.0;

		private const int MaxAttempts = 200;

		private static readonly FieldRef<Humanoid, int> Seed = AccessTools.FieldRefAccess<Humanoid, int>("m_seed");

		private static bool _rerolling;

		public static void Apply(Humanoid humanoid)
		{
			if (_rerolling || !SkeletonCommanderPlugin.TeamEnabled.Value || !SummonedSkeletons.IsSummonedSkeleton((Component)(object)humanoid))
			{
				return;
			}
			ZNetView component = ((Component)humanoid).GetComponent<ZNetView>();
			BaseAI baseAI = ((Character)humanoid).GetBaseAI();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)baseAI == (Object)null || baseAI.GetTimeSinceSpawned().TotalSeconds > 5.0)
			{
				return;
			}
			SkeletonLoadout skeletonLoadout = ChooseForTeam(humanoid, component.GetZDO().GetInt(ZDOVars.s_maxInstances, 0));
			if (skeletonLoadout == SkeletonLoadout.Random)
			{
				return;
			}
			int num = Seed.Invoke(humanoid);
			Inventory inventory = humanoid.GetInventory();
			_rerolling = true;
			try
			{
				for (int i = 1; i < 200; i++)
				{
					if (Matches(skeletonLoadout, inventory.GetAllItems()))
					{
						break;
					}
					int num2 = num + i * 7919;
					if (num2 != 0)
					{
						humanoid.UnequipAllItems();
						inventory.RemoveAll();
						Seed.Invoke(humanoid) = num2;
						component.GetZDO().Set(ZDOVars.s_seed, num2, false);
						humanoid.GiveDefaultItems();
					}
				}
			}
			finally
			{
				_rerolling = false;
			}
		}

		private static bool Matches(SkeletonLoadout loadout, List<ItemData> items)
		{
			SummonedSkeletons.Classify(items, out var bow, out var melee, out var shield);
			if (loadout != SkeletonLoadout.Bow)
			{
				return !bow && melee && shield;
			}
			return bow;
		}

		private static SkeletonLoadout ChooseForTeam(Humanoid newSkeleton, int maxInstances)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return SkeletonLoadout.Random;
			}
			List<Character> list = new List<Character>();
			SummonedSkeletons.FindOwned(localPlayer, list);
			list.Remove((Character)(object)newSkeleton);
			List<SkeletonLoadout> teamTarget = GetTeamTarget(maxInstances);
			int num = FillSlots(teamTarget, list).IndexOf(item: false);
			if (num < 0)
			{
				return teamTarget[0];
			}
			return teamTarget[num];
		}

		public static List<SkeletonLoadout> GetTeamTarget(int maxInstances)
		{
			IEnumerable<SkeletonLoadout> enumerable = SkeletonCommanderPlugin.TeamSlots.Select((ConfigEntry<TeamSlot> slot) => ToLoadout(slot.Value));
			return ((maxInstances > 0) ? enumerable.Take(maxInstances) : enumerable).ToList();
		}

		private static List<bool> FillSlots(List<SkeletonLoadout> target, List<Character> skeletons)
		{
			List<bool> list = target.Select((SkeletonLoadout _) => false).ToList();
			List<SkeletonLoadout> list2 = new List<SkeletonLoadout>();
			foreach (SkeletonLoadout item in skeletons.Select(SummonedSkeletons.GetLoadout))
			{
				int num = ((item == SkeletonLoadout.Random) ? (-1) : FindSlot(target, list, item));
				if (num >= 0)
				{
					list[num] = true;
				}
				else
				{
					list2.Add(item);
				}
			}
			foreach (SkeletonLoadout item2 in list2)
			{
				_ = item2;
				int num2 = FindSlot(target, list, SkeletonLoadout.Random);
				if (num2 >= 0)
				{
					list[num2] = true;
				}
			}
			return list;
		}

		private static int FindSlot(List<SkeletonLoadout> target, List<bool> filled, SkeletonLoadout type)
		{
			for (int i = 0; i < target.Count; i++)
			{
				if (!filled[i] && target[i] == type)
				{
					return i;
				}
			}
			return -1;
		}

		private static SkeletonLoadout ToLoadout(TeamSlot slot)
		{
			return slot switch
			{
				TeamSlot.SwordAndShield => SkeletonLoadout.SwordAndShield, 
				TeamSlot.Bow => SkeletonLoadout.Bow, 
				_ => SkeletonLoadout.Random, 
			};
		}

		public static string DescribeTeam(List<Character> skeletons)
		{
			int maxInstances = ((skeletons.Count > 0) ? SummonedSkeletons.GetMaxInstances(skeletons[0]) : 0);
			List<SkeletonLoadout> target = GetTeamTarget(maxInstances);
			List<bool> filled = FillSlots(target, skeletons);
			IEnumerable<string> values = from type in new SkeletonLoadout[3]
				{
					SkeletonLoadout.SwordAndShield,
					SkeletonLoadout.Bow,
					SkeletonLoadout.Random
				}
				select (type: type, count: Enumerable.Range(0, target.Count).Count((int i) => target[i] == type && filled[i]), wanted: target.Count((SkeletonLoadout t) => t == type)) into entry
				where entry.wanted > 0
				select $"{DescribeSlot(entry.type)} {entry.count}/{entry.wanted}";
			return "Team: " + string.Join(" · ", values);
		}

		private static string DescribeSlot(SkeletonLoadout type)
		{
			return type switch
			{
				SkeletonLoadout.SwordAndShield => "sword & shield", 
				SkeletonLoadout.Bow => "bow", 
				_ => "any", 
			};
		}
	}
	[HarmonyPatch]
	internal static class SkeletonPatches
	{
		private static readonly Type HudDataType = AccessTools.Inner(typeof(EnemyHud), "HudData");

		private static readonly FieldInfo HudHoverTimer = AccessTools.Field(HudDataType, "m_hoverTimer");

		private static readonly FieldInfo HudHealthFast = AccessTools.Field(HudDataType, "m_healthFast");

		private static readonly FieldInfo HudHealthFastFriendly = AccessTools.Field(HudDataType, "m_healthFastFriendly");

		[HarmonyPatch(typeof(EnemyHud), "TestShow")]
		[HarmonyPostfix]
		private static void ExtendHealthBarRange(Character c, Vector3 ___m_refPoint, ref bool __result)
		{
			//IL_002b: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!__result && SkeletonCommanderPlugin.HealthBars.Value && SkeletonCommanderPlugin.IsOwned(c))
			{
				float value = SkeletonCommanderPlugin.HealthBarDistance.Value;
				Vector3 val = ((Component)c).transform.position - ___m_refPoint;
				__result = ((Vector3)(ref val)).sqrMagnitude < value * value;
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		[HarmonyPrefix]
		private static void UpdateOwnedHealthBars(object ___m_huds)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			foreach (DictionaryEntry item in (IDictionary)___m_huds)
			{
				Character character = (Character)item.Key;
				if (SkeletonCommanderPlugin.IsOwned(character))
				{
					if (SkeletonCommanderPlugin.HealthBars.Value)
					{
						HudHoverTimer.SetValue(item.Value, 0f);
					}
					SetBarColor((GuiBar)HudHealthFast.GetValue(item.Value), character);
					SetBarColor((GuiBar)HudHealthFastFriendly.GetValue(item.Value), character);
				}
			}
		}

		private static void SetBarColor(GuiBar bar, Character character)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)bar == (Object)null))
			{
				if (SkeletonCommanderPlugin.HealthBarColors.Value)
				{
					bar.SetColor(SummonedSkeletons.GetHealthColor(character));
				}
				else
				{
					bar.ResetColor();
				}
			}
		}

		[HarmonyPatch(typeof(Tameable), "UnsummonMaxInstances")]
		[HarmonyPrefix]
		private static bool EnforceSummonLimit(Tameable __instance, int maxInstances, ZNetView ___m_nview, MonsterAI ___m_monsterAI)
		{
			if (!SummonedSkeletons.IsSummonedSkeleton((Component)(object)__instance))
			{
				return true;
			}
			if ((Object)(object)___m_nview == (Object)null || !___m_nview.IsValid() || !___m_nview.IsOwner() || (Object)(object)___m_monsterAI == (Object)null)
			{
				return false;
			}
			GameObject followTarget = ___m_monsterAI.GetFollowTarget();
			Player val = (((Object)(object)followTarget != (Object)null) ? followTarget.GetComponent<Player>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			List<Character> list = new List<Character>();
			SummonedSkeletons.FindOwned(val, list);
			if (SummonedSkeletons.DismissExcess(list, maxInstances) > 0 && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, __instance.m_maxSummonReached, 0, (Sprite)null, false);
			}
			return false;
		}

		[HarmonyPatch(typeof(Player), "TeleportTo")]
		[HarmonyPostfix]
		private static void DismissSkeletonsLeftByTeleport(Player __instance, Vector3 pos, bool __result)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				SkeletonCommanderPlugin.DismissFarSkeletons(__instance, pos);
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		[HarmonyPostfix]
		private static void DismissSkeletonsLeftByDeath(Player __instance)
		{
			//IL_003d: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && val != null)
			{
				Vector3 center = (val.HaveCustomSpawnPoint() ? val.GetCustomSpawnPoint() : val.GetHomePoint());
				SkeletonCommanderPlugin.DismissFarSkeletons(__instance, center);
			}
		}

		[HarmonyPatch(typeof(ZInput), "GetButtonDown")]
		[HarmonyPrefix]
		private static bool BlockButtonDown(string name, ref bool __result)
		{
			return AllowButton(name, ref __result);
		}

		[HarmonyPatch(typeof(ZInput), "GetButton")]
		[HarmonyPrefix]
		private static bool BlockButton(string name, ref bool __result)
		{
			return AllowButton(name, ref __result);
		}

		[HarmonyPatch(typeof(ZInput), "GetButtonUp")]
		[HarmonyPrefix]
		private static bool BlockButtonUp(string name, ref bool __result)
		{
			return AllowButton(name, ref __result);
		}

		private static bool AllowButton(string name, ref bool __result)
		{
			if (!HotkeyInputBlocker.IsBlocked(name))
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Humanoid), "GiveDefaultItems")]
		[HarmonyPostfix]
		private static void ChooseSummonLoadout(Humanoid __instance)
		{
			SkeletonLoadouts.Apply(__instance);
		}

		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		[HarmonyPostfix]
		private static void AddHotkeyHints(Tameable __instance, ref string __result)
		{
			if (SkeletonCommanderPlugin.IsOwned(((Component)__instance).GetComponent<Character>()))
			{
				__result += SkeletonCommanderPlugin.GetHoverHints();
			}
		}
	}
	internal static class SummonedSkeletons
	{
		private const string Prefab = "Skeleton_Friendly";

		private const string DigitWidth = "0.55em";

		private const string OwnerKey = "SkeletonTracker_owner";

		private static readonly FieldRef<SE_Shield, float> ShieldTotal = AccessTools.FieldRefAccess<SE_Shield, float>("m_totalAbsorbDamage");

		private static readonly FieldRef<SE_Shield, float> ShieldDamage = AccessTools.FieldRefAccess<SE_Shield, float>("m_damage");

		public static bool IsSummonedSkeleton(Component component)
		{
			return Utils.GetPrefabName(component.gameObject) == "Skeleton_Friendly";
		}

		public static void FindOwned(Player player, List<Character> result)
		{
			result.Clear();
			long playerID = player.GetPlayerID();
			string playerName = player.GetPlayerName();
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter == (Object)null || allCharacter.IsDead() || !IsSummonedSkeleton((Component)(object)allCharacter))
				{
					continue;
				}
				ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					ZDO zDO = component.GetZDO();
					bool num = zDO.GetString(ZDOVars.s_follow, "") == playerName;
					if (num && component.IsOwner() && zDO.GetLong("SkeletonTracker_owner", 0L) != playerID)
					{
						zDO.Set("SkeletonTracker_owner", playerID);
					}
					if (num || zDO.GetLong("SkeletonTracker_owner", 0L) == playerID)
					{
						result.Add(allCharacter);
					}
				}
			}
			result.Sort((Character a, Character b) => GetSpawnTime(b).CompareTo(GetSpawnTime(a)));
		}

		private static long GetSpawnTime(Character skeleton)
		{
			return ((Component)skeleton).GetComponent<ZNetView>().GetZDO().GetLong(ZDOVars.s_spawnTime, 0L);
		}

		public static float HorizontalDistance(Vector3 a, Vector3 b)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			a.y = 0f;
			b.y = 0f;
			return Vector3.Distance(a, b);
		}

		public static bool IsFollowing(Character skeleton, Player player)
		{
			ZNetView component = ((Component)skeleton).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				return component.GetZDO().GetString(ZDOVars.s_follow, "") == player.GetPlayerName();
			}
			return false;
		}

		public static void SetFollowing(Character skeleton, Player player, bool follow)
		{
			if (IsFollowing(skeleton, player) != follow)
			{
				Tameable component = ((Component)skeleton).GetComponent<Tameable>();
				if ((Object)(object)component != (Object)null)
				{
					component.Command((Humanoid)(object)player, false);
				}
			}
		}

		public static void Dismiss(Character skeleton)
		{
			ZNetView component = ((Component)skeleton).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				if (!component.IsOwner() && !component.GetZDO().HasOwner())
				{
					component.ClaimOwnership();
				}
				component.InvokeRPC(ZNetView.Everybody, "RPC_UnSummon", Array.Empty<object>());
			}
		}

		public static int DismissExcess(List<Character> skeletons, int maxInstances)
		{
			int num = skeletons.Count - maxInstances;
			for (int i = 0; i < num; i++)
			{
				int num2 = skeletons.Count - 1;
				if (SkeletonCommanderPlugin.DismissWeakest.Value)
				{
					for (int num3 = num2 - 1; num3 > 0; num3--)
					{
						if (skeletons[num3].GetHealth() < skeletons[num2].GetHealth())
						{
							num2 = num3;
						}
					}
				}
				Dismiss(skeletons[num2]);
				skeletons.RemoveAt(num2);
			}
			return Math.Max(num, 0);
		}

		public static int GetMaxInstances(Character skeleton)
		{
			return ((Component)skeleton).GetComponent<ZNetView>().GetZDO().GetInt(ZDOVars.s_maxInstances, 0);
		}

		public static string GetName(Character skeleton)
		{
			return Localization.instance.Localize(skeleton.GetHoverName());
		}

		public static string GetWeaponName(Character skeleton)
		{
			Humanoid val = (Humanoid)(object)((skeleton is Humanoid) ? skeleton : null);
			if (val == null)
			{
				return "unarmed";
			}
			Classify(val.GetInventory().GetAllItems(), out var bow, out var melee, out var shield);
			if (bow)
			{
				return "bow";
			}
			if (!melee)
			{
				return "unarmed";
			}
			if (!shield)
			{
				return "sword";
			}
			return "sword & shield";
		}

		public static SkeletonLoadout GetLoadout(Character skeleton)
		{
			Humanoid val = (Humanoid)(object)((skeleton is Humanoid) ? skeleton : null);
			if (val == null)
			{
				return SkeletonLoadout.Random;
			}
			Classify(val.GetInventory().GetAllItems(), out var bow, out var melee, out var _);
			if (bow)
			{
				return SkeletonLoadout.Bow;
			}
			if (!melee)
			{
				return SkeletonLoadout.Random;
			}
			return SkeletonLoadout.SwordAndShield;
		}

		public static void Classify(List<ItemData> items, out bool bow, out bool melee, out bool shield)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			bow = (melee = (shield = false));
			foreach (ItemData item in items)
			{
				if (IsRangedWeapon(item))
				{
					bow = true;
				}
				else if (item.IsWeapon())
				{
					melee = true;
				}
				else if ((int)item.m_shared.m_itemType == 5)
				{
					shield = true;
				}
			}
		}

		public static bool IsRangedWeapon(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			if ((int)item.m_shared.m_itemType != 4)
			{
				if (item.IsWeapon() && item.m_shared.m_attack != null)
				{
					return (int)item.m_shared.m_attack.m_attackType == 2;
				}
				return false;
			}
			return true;
		}

		public static Color GetHealthColor(Character skeleton)
		{
			//IL_001e: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			float healthPercentage = skeleton.GetHealthPercentage();
			if (healthPercentage > 0.6f)
			{
				return new Color(0.3f, 0.9f, 0.3f);
			}
			if (healthPercentage > 0.3f)
			{
				return new Color(1f, 0.85f, 0.2f);
			}
			return new Color(1f, 0.25f, 0.2f);
		}

		public static string Describe(Character skeleton, Player player, bool name = true, bool weapon = true, bool health = true, bool distance = true, bool state = true, bool padded = true, bool shield = true)
		{
			//IL_0029: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			if (name)
			{
				list.Add(GetName(skeleton));
			}
			if (weapon)
			{
				list.Add(GetWeaponName(skeleton));
			}
			if (health)
			{
				string text = ColorUtility.ToHtmlStringRGB(GetHealthColor(skeleton));
				list.Add(string.Format("<color=#{0}>HP <mspace={1}>{2,3:0}/{3,-3:0}</mspace></color>", text, "0.55em", skeleton.GetHealth(), skeleton.GetMaxHealth()));
			}
			if (distance)
			{
				float num = Vector3.Distance(((Component)player).transform.position, ((Component)skeleton).transform.position);
				list.Add(padded ? string.Format("<mspace={0}>{1,3:0}</mspace> m", "0.55em", num) : $"{num:0} m");
			}
			if (shield && TryGetShield(skeleton, out var remaining, out var total, out var seconds))
			{
				list.Add($"<color=#73BFFF>shield {remaining:0}/{total:0} ({seconds:0} s)</color>");
			}
			if (state)
			{
				list.Add(IsFollowing(skeleton, player) ? "following" : "staying");
			}
			return string.Join(" · ", list);
		}

		public static bool TryGetShield(Character skeleton, out float remaining, out float total, out float seconds)
		{
			foreach (StatusEffect statusEffect in skeleton.GetSEMan().GetStatusEffects())
			{
				SE_Shield val = (SE_Shield)(object)((statusEffect is SE_Shield) ? statusEffect : null);
				if (val != null)
				{
					total = ShieldTotal.Invoke(val);
					remaining = Mathf.Max(0f, total - ShieldDamage.Invoke(val));
					seconds = Mathf.Max(0f, ((StatusEffect)val).GetRemaningTime());
					return true;
				}
			}
			remaining = (total = (seconds = 0f));
			return false;
		}
	}
}