Decompiled source of SummonCommander v1.0.1

plugins/SummonCommander.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.Bootstrap;
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("SummonCommander")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1aa43078b29f224a16c4d5ef2ff5c870e5312612")]
[assembly: AssemblyProduct("SummonCommander")]
[assembly: AssemblyTitle("SummonCommander")]
[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 SummonCommander
{
	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 SummonCommanderPlugin.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 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 || !SummonCommanderPlugin.SkeletonTeamEnabled.Value || !Summons.IsSkeleton((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)
		{
			Summons.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>();
			Summons.FindOwned(localPlayer, list);
			List<Character> list2 = Summons.OfKind(list, SummonKind.Skeleton);
			list2.Remove((Character)(object)newSkeleton);
			List<SkeletonLoadout> teamTarget = GetTeamTarget(maxInstances);
			int num = FillSlots(teamTarget, list2).IndexOf(item: false);
			if (num < 0)
			{
				return teamTarget[0];
			}
			return teamTarget[num];
		}

		public static List<SkeletonLoadout> GetTeamTarget(int maxInstances)
		{
			IEnumerable<SkeletonLoadout> enumerable = SummonCommanderPlugin.SkeletonTeamSlots.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(Summons.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) ? Summons.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 Texts.Get("Team: ") + string.Join(" · ", values);
		}

		private static string DescribeSlot(SkeletonLoadout type)
		{
			return type switch
			{
				SkeletonLoadout.SwordAndShield => Texts.Get("sword & shield"), 
				SkeletonLoadout.Bow => Texts.Get("bow"), 
				_ => Texts.Get("any"), 
			};
		}
	}
	internal enum HudStyle
	{
		Bars,
		Text
	}
	[BepInPlugin("xmordax.valheim.summoncommander", "Summon Commander", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class SummonCommanderPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "xmordax.valheim.summoncommander";

		public const string PluginName = "Summon Commander";

		public const string PluginVersion = "1.0.1";

		private const string SkeletonCommanderGuid = "xmordax.valheim.skeletoncommander";

		private const string SkeletonCommanderConfigFileName = "xmordax.valheim.skeletoncommander.cfg";

		private const float ScanInterval = 0.5f;

		private const string GlowObjectName = "SummonCommander_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<KeyboardShortcut> NextSpiritKey;

		internal static ConfigEntry<SpiritSpecies> NextSpirit;

		internal static ConfigEntry<bool> ShowCounter;

		internal static ConfigEntry<bool> ShowDetails;

		internal static ConfigEntry<bool> ShowStateSummary;

		internal static ConfigEntry<bool> ShowSkeletonTeam;

		internal static ConfigEntry<bool> ShowSpiritSpecies;

		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<float> PanelWidth;

		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> SkeletonTeamEnabled;

		internal static ConfigEntry<TeamSlot>[] SkeletonTeamSlots;

		internal static ConfigEntry<bool> DismissWeakest;

		internal static ConfigEntry<float> DismissBeyondDistance;

		internal static ConfigEntry<bool> KeepNearOtherPlayers;

		internal static ConfigEntry<bool> PassThroughSpirits;

		internal static ConfigEntry<bool> PassThroughSkeletons;

		internal static ConfigEntry<bool> ImprovedFollow;

		internal static ConfigEntry<float> FollowDistance;

		internal static ConfigEntry<float> PersonalSpace;

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

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

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

		internal static readonly ConditionalWeakTable<PinData, object> PinColors = new ConditionalWeakTable<PinData, object>();

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

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

		private readonly Dictionary<SummonKind, Sprite> _pinIcons = new Dictionary<SummonKind, Sprite>();

		private readonly SummonHud _hud = new SummonHud();

		private Harmony _harmony;

		private Minimap _minimap;

		private float _timer;

		internal static ManualLogSource Log;

		private static readonly (ConfigDefinition Old, ConfigDefinition New)[] RenamedSettings = (from names in new(string, string, string, string)[1] { ("Summon", "TeamEnabled", "SkeletonTeam", "Enabled") }.Concat(from i in Enumerable.Range(1, 4)
				select ("Summon", $"TeamSlot{i}", "SkeletonTeam", $"Slot{i}")).Concat(new(string, string, string, string)[2]
			{
				("HUD", "ShowTeam", "HUD", "ShowSkeletonTeam"),
				("HUD", "ShowSpecies", "HUD", "ShowSpiritSpecies")
			})
			select ((ConfigDefinition, ConfigDefinition))(new ConfigDefinition(names.Item1, names.Item2), new ConfigDefinition(names.Item3, names.Item4))).ToArray();

		private static readonly PropertyInfo OrphanedEntries = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");

		private const string StayingColor = "#FFA040";

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

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

		private void Awake()
		{
			//IL_0037: 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_009f: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Expected O, but got Unknown
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Expected O, but got Unknown
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Expected O, but got Unknown
			//IL_0506: Unknown result type (might be due to invalid IL or missing references)
			//IL_0510: Expected O, but got Unknown
			//IL_0583: Unknown result type (might be due to invalid IL or missing references)
			//IL_058d: Expected O, but got Unknown
			//IL_05f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0638: Unknown result type (might be due to invalid IL or missing references)
			//IL_0642: Expected O, but got Unknown
			//IL_0675: Unknown result type (might be due to invalid IL or missing references)
			//IL_067f: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			DisableSkeletonCommander();
			CopySkeletonCommanderConfig();
			FollowKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Follow", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Summons follow you (like /sc follow). When aiming at one of your summons, affects only that one.");
			StayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Stay", new KeyboardShortcut((KeyCode)103, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Summons stay in place (like /sc stay). When aiming at one of your summons, affects only that one.");
			DismissKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Dismiss", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Dismisses summons (like /sc dismiss). When aiming at one of your summons, 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 summons in chat (like /sc list).");
			NextSpiritKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "NextSpirit", new KeyboardShortcut((KeyCode)107, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Switches the species of the next Spirit Caller spirit (Summon.NextSpirit): random, wolf, boar, moose, bear.");
			PassThroughSpirits = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "PassThroughSpirits", true, "You walk through Spirit Caller spirits (yours and other players') instead of being blocked by them. Only the collision between you and the spirits is removed: they still fight and can be hit.");
			PassThroughSkeletons = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "PassThroughSkeletons", false, "You walk through Dead Raiser skeletons (yours and other players') instead of being blocked by them.");
			ImprovedFollow = ((BaseUnityPlugin)this).Config.Bind<bool>("Movement", "ImprovedFollow", true, "Following summons stop FollowDistance away from you (measured from their edge, so big spirits keep more room) and step back when closer than PersonalSpace, instead of pushing to within 3 m of you as in the base game. Off: the base game's following.");
			FollowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "FollowDistance", 4f, new ConfigDescription("How far (m) from you following summons stop, measured from the summon's edge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 15f), Array.Empty<object>()));
			PersonalSpace = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "PersonalSpace", 1.5f, new ConfigDescription("Summons closer than this (m, from their edge) step away from you, e.g. after you walk into the group. Kept at least 1 m below FollowDistance. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			DismissWeakest = ((BaseUnityPlugin)this).Config.Bind<bool>("Summon", "DismissWeakest", true, "Over the summon limit, the summon of that type with the least HP is dismissed (the oldest on a tie), so a new skeleton replaces it with the weapon its team slot needs. Off: the oldest is dismissed as in the base game.");
			NextSpirit = ((BaseUnityPlugin)this).Config.Bind<SpiritSpecies>("Summon", "NextSpirit", SpiritSpecies.Random, "The species the Spirit Caller summons: Random (as in the base game) or always Wolf, Boar, Moose or Bear. At the species' limit the new spirit replaces one of that species. Hotkeys.NextSpirit switches it in game.");
			DismissBeyondDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Summon", "DismissBeyondDistance", 100f, new ConfigDescription("Summons 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. Summons left in an unloaded area would otherwise not count toward the summon limit. Summons 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 summons 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 summons than your limit, the extra ones are dismissed. Off: far summons are dismissed even when other players are near them.");
			SkeletonTeamEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("SkeletonTeam", "Enabled", true, "Dead Raiser skeletons only (spirits have no weapons). Gives new skeletons the weapons set by Slot1-4. Off: skeletons get random weapons as in the base game.");
			SkeletonTeamSlots = new TeamSlot[4]
			{
				TeamSlot.SwordAndShield,
				TeamSlot.Bow,
				TeamSlot.SwordAndShield,
				TeamSlot.Bow
			}.Select((TeamSlot value, int i) => ((BaseUnityPlugin)this).Config.Bind<TeamSlot>("SkeletonTeam", $"Slot{i + 1}", value, "Dead Raiser skeletons only (spirits have no weapons). " + $"Weapons of skeleton {i + 1} of your team, used from a summon limit of {i + 1} (Dead " + "Raiser upgrade level). New skeletons get the weapon the team is missing; Random accepts any weapon. Already summoned skeletons keep their weapons.")).ToArray();
			DeathNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("Notifications", "Death", true, "On-screen message when one of your summons dies.");
			ShowCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowCounter", true, "Shows the summon counter below the minimap.");
			ShowDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowDetails", true, "Lists your summons below the counter, grouped by species (skeletons, wolves, boars, moose, bears), each species in its own color: name, weapon, health, distance and whether it follows you.");
			ShowStateSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowStateSummary", false, "Shows how many summons are following you and how many are staying next to the counter, and how many of a species stay in its panel header.");
			ShowSkeletonTeam = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowSkeletonTeam", true, "Shows how many Dead Raiser skeletons of each weapon your team (SkeletonTeam.Slot1-4) has and wants.");
			ShowSpiritSpecies = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowSpiritSpecies", true, "Shows how many Spirit Caller spirits of each species you have and the limit per species, e.g. wolf 2/3 · boar 3/3; species at the limit are highlighted.");
			ShowName = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowName", true, "Shows each summon's name in the detailed list.");
			ShowWeapon = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowWeapon", true, "Shows the weapon of each skeleton, and the species of each spirit you renamed, in the detailed list.");
			ShowHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowHealth", true, "Shows each summon's health in the detailed list.");
			ShowDistance = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowDistance", true, "Shows each summon's distance from you in the detailed list.");
			ShowState = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowState", false, "Shows whether each summon is following you or staying in the detailed list (the panels mark only the ones that stay, in a column the HP bars give up). Off: the HP bars are longer.");
			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 summon counter and list; the whole box scales with it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 40f), Array.Empty<object>()));
			PanelWidth = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PanelWidth", 0f, new ConfigDescription("Width of the species panels (Style = Bars) in pixels of the HUD. 0: as wide as the longest name needs (it scales with FontSize). In a set width the HP bars and names share the space; text that does not fit shrinks a little, then is cut with an ellipsis.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			Style = ((BaseUnityPlugin)this).Config.Bind<HudStyle>("HUD", "Style", HudStyle.Bars, "How the detailed list shows your summons: Bars (a panel per species with a row and an HP bar per summon; it shrinks when it would not fit on the screen) or Text (one line each).");
			HealthBars = ((BaseUnityPlugin)this).Config.Bind<bool>("HealthBars", "AlwaysShow", true, "Health bars above your summons' heads are always visible (within MaxDistance). Off: shown as in the base game.");
			HealthBarDistance = ((BaseUnityPlugin)this).Config.Bind<float>("HealthBars", "MaxDistance", 40f, new ConfigDescription("Maximum distance (m) at which summon health bars are shown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f), Array.Empty<object>()));
			HealthBarColors = ((BaseUnityPlugin)this).Config.Bind<bool>("HealthBars", "ColorByHealth", true, "Colors summon health bars by HP: green > 60%, yellow > 30%, red below.");
			GlowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Glow", "Enabled", true, "Subtle glow around your summons.");
			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>()));
			MigrateRenamedSettings();
			SummonCommands.Register();
			_harmony = Harmony.CreateAndPatchAll(typeof(SummonPatches), "xmordax.valheim.summoncommander");
		}

		private void Update()
		{
			//IL_00b2: 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();
				_summons.Clear();
				Owned.Clear();
				_hud.Hide();
				return;
			}
			SummonMovement.UpdateCollisions(localPlayer);
			Summons.FindOwned(localPlayer, _summons);
			if (!((Character)localPlayer).IsTeleporting())
			{
				DismissFarSummons(localPlayer, ((Component)localPlayer).transform.position, _summons);
			}
			DismissOverLimit(localPlayer, _summons);
			Owned.Clear();
			Owned.UnionWith(_summons);
			_deathHooked.RemoveWhere((Character c) => (Object)(object)c == (Object)null);
			int num = 0;
			foreach (Character summon in _summons)
			{
				bool flag = Summons.IsFollowing(summon, localPlayer);
				if (flag)
				{
					num++;
				}
				UpdatePin(instance, summon, flag);
				UpdateGlow(summon);
				if (_deathHooked.Add(summon))
				{
					Character obj = summon;
					obj.m_onDeath = (Action)Delegate.Combine(obj.m_onDeath, (Action)delegate
					{
						OnSummonDied(summon);
					});
				}
			}
			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_0056: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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, SummonCommands.Follow(localPlayer, only));
				}
				else if (IsPressed(StayKey.Value))
				{
					ShowMessage(localPlayer, SummonCommands.Stay(localPlayer, only));
				}
				else if (IsPressed(DismissKey.Value))
				{
					ShowMessage(localPlayer, SummonCommands.Dismiss(localPlayer, only));
				}
				else if (IsPressed(NextSpiritKey.Value))
				{
					NextSpirit.Value = (SpiritSpecies)((int)(NextSpirit.Value + 1) % Enum.GetValues(typeof(SpiritSpecies)).Length);
					ShowMessage(localPlayer, Texts.Format("Next spirit: {0}", Summons.GetSpiritSpeciesName(NextSpirit.Value)));
				}
				else if (IsPressed(ListKey.Value) && (Object)(object)Chat.instance != (Object)null)
				{
					SummonCommands.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 DisableSkeletonCommander()
		{
			if (Chainloader.PluginInfos.TryGetValue("xmordax.valheim.skeletoncommander", out var value) && !((Object)(object)value.Instance == (Object)null))
			{
				Log.LogWarning((object)($"Skeleton Commander {value.Metadata.Version} is installed; Summon Commander replaces it, " + "so it is turned off. Remove BepInEx/plugins/SkeletonCommander."));
				Object.Destroy((Object)(object)value.Instance);
			}
		}

		private void MigrateRenamedSettings()
		{
			Dictionary<ConfigDefinition, string> dictionary = (Dictionary<ConfigDefinition, string>)OrphanedEntries.GetValue(((BaseUnityPlugin)this).Config);
			bool flag = false;
			(ConfigDefinition, ConfigDefinition)[] renamedSettings = RenamedSettings;
			for (int i = 0; i < renamedSettings.Length; i++)
			{
				var (val, val2) = renamedSettings[i];
				if (dictionary.TryGetValue(val, out var value))
				{
					((BaseUnityPlugin)this).Config[val2].SetSerializedValue(value);
					dictionary.Remove(val);
					flag = true;
					Log.LogInfo((object)("Setting " + val.Section + "." + val.Key + " moved to " + val2.Section + "." + val2.Key + " = " + value));
				}
			}
			if (flag)
			{
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void CopySkeletonCommanderConfig()
		{
			string text = Path.Combine(Paths.ConfigPath, "xmordax.valheim.skeletoncommander.cfg");
			if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath) && File.Exists(text))
			{
				File.Copy(text, ((BaseUnityPlugin)this).Config.ConfigFilePath);
				((BaseUnityPlugin)this).Config.Reload();
			}
		}

		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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			return HoverHint(FollowKey.Value, Texts.Get("Follow")) + HoverHint(StayKey.Value, Texts.Get("Stay")) + HoverHint(DismissKey.Value, Texts.Get("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 || _summons.Count == 0)
			{
				_hud.Hide();
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<align=center>" + DescribeCount(_summons));
			if (ShowStateSummary.Value)
			{
				stringBuilder.Append("  " + Texts.Format("({0} following, {1} staying)", following, _summons.Count - following));
			}
			stringBuilder.Append("</align>");
			List<Character> list = Summons.OfKind(_summons, SummonKind.Skeleton);
			if (ShowSkeletonTeam.Value && SkeletonTeamEnabled.Value && list.Count > 0)
			{
				stringBuilder.Append("\n<align=center><size=80%>" + SkeletonLoadouts.DescribeTeam(list) + "</size></align>");
			}
			List<Character> list2 = Summons.OfKind(_summons, SummonKind.Spirit);
			if (ShowSpiritSpecies.Value && list2.Count > 0)
			{
				stringBuilder.Append("\n<align=center><size=80%>" + Summons.DescribeSpecies(list2, NextSpirit.Value) + "</size></align>");
			}
			List<SummonGroupData> frames = null;
			if (ShowDetails.Value && Style.Value == HudStyle.Bars)
			{
				frames = DescribePanels(player);
			}
			else if (ShowDetails.Value)
			{
				for (int i = 0; i < _summons.Count; i++)
				{
					stringBuilder.Append($"\n<size=80%>#{i + 1}  ").Append(Summons.Describe(_summons[i], 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, PanelInfoWidth(), PanelWidth.Value);
		}

		private static string DescribeCount(List<Character> summons)
		{
			int num = summons.Count(Summons.IsSkeleton);
			int num2 = summons.Count - num;
			if (num2 == 0)
			{
				return Texts.Format("Skeletons: {0}", num);
			}
			if (num == 0)
			{
				return Texts.Format("Spirits: {0}", num2);
			}
			return Texts.Format("Summons: {0} ({1} skeletons, {2} spirits)", summons.Count, num, num2);
		}

		private List<SummonGroupData> DescribePanels(Player player)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			List<SummonGroupData> list = new List<SummonGroupData>();
			int number = 0;
			foreach (List<Character> item in Summons.LimitGroups(_summons))
			{
				Character summon = item[0];
				int maxInstances = Summons.GetMaxInstances(summon);
				int num = item.Count((Character summon2) => !Summons.IsFollowing(summon2, player));
				string text = Summons.Colored(Summons.GetGroupName(summon), Summons.GetSpeciesColor(summon)) + $"  {item.Count}" + ((maxInstances > 0) ? $"/{maxInstances}" : "");
				if (ShowStateSummary.Value && num > 0)
				{
					text = text + "  <size=85%><color=#FFA040>" + Texts.Format("{0} staying", num) + "</color></size>";
				}
				list.Add(new SummonGroupData
				{
					Header = text,
					Rows = item.Select((Character summon2) => DescribeRow(summon2, ++number, player)).ToList()
				});
			}
			return list;
		}

		private static float PanelInfoWidth()
		{
			return (ShowDistance.Value ? 2.5f : 0f) + (ShowState.Value ? 3.2f : 0f);
		}

		private static SummonRowData DescribeRow(Character summon, int number, Player player)
		{
			//IL_0039: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			string rowDetail = Summons.GetRowDetail(summon, ShowName.Value, ShowWeapon.Value);
			List<string> list = new List<string>();
			if (ShowDistance.Value)
			{
				list.Add($"{Vector3.Distance(((Component)player).transform.position, ((Component)summon).transform.position):0} m");
			}
			if (ShowState.Value && !Summons.IsFollowing(summon, player))
			{
				list.Add("<color=#FFA040>" + Texts.Get("staying") + "</color>");
			}
			float remaining;
			float total;
			float seconds;
			bool flag = Summons.TryGetShield(summon, out remaining, out total, out seconds);
			return new SummonRowData
			{
				Label = $"<color=#FFC74D>#{number}</color>" + ((rowDetail.Length > 0) ? (" " + rowDetail) : ""),
				Info = string.Join(" · ", list),
				SpeciesColor = Summons.GetSpeciesColor(summon),
				ShowBar = ShowHealth.Value,
				Health = summon.GetHealth(),
				MaxHealth = summon.GetMaxHealth(),
				BarColor = Summons.GetHealthColor(summon),
				ShowShield = (ShowShield.Value && flag),
				Shield = remaining,
				MaxShield = total,
				ShieldSeconds = seconds
			};
		}

		internal static void DismissFarSummons(Player player, Vector3 center, List<Character> summons)
		{
			//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 = summons.RemoveAll(delegate(Character summon)
			{
				//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)summon).transform.position;
				if (IsNear(position, center, maxDistance))
				{
					return false;
				}
				ZNetView component = ((Component)summon).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 = Summons.HorizontalDistance(position, center);
				Log.LogInfo((object)($"Dismissing {Summons.GetName(summon)} at {num2:0} m " + "(owner: " + (component.IsOwner() ? "you" : (component.GetZDO().HasOwner() ? "another player" : "none")) + ")"));
				Summons.Dismiss(summon);
				return true;
			});
			if (num > 0)
			{
				((Character)player).Message((MessageType)1, (num == 1) ? Texts.Get("Summon too far away - dismissed") : Texts.Format("{0} summons too far away - dismissed", num), 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 (Summons.HorizontalDistance(position, center) <= maxDistance)
			{
				return ZNetScene.InActiveArea(position, center);
			}
			return false;
		}

		private static void DismissOverLimit(Player player, List<Character> summons)
		{
			List<List<Character>> list = Summons.LimitGroups(summons).ToList();
			int num = 0;
			foreach (List<Character> item in list)
			{
				int maxInstances = Summons.GetMaxInstances(item[0]);
				if (maxInstances > 0)
				{
					num += Summons.DismissExcess(item, maxInstances);
				}
			}
			if (num != 0)
			{
				HashSet<Character> kept = new HashSet<Character>(list.SelectMany((List<Character> group) => group));
				summons.RemoveAll((Character summon) => !kept.Contains(summon));
				((Character)player).Message((MessageType)1, (num == 1) ? Texts.Get("Summon over the summon limit - dismissed") : Texts.Format("{0} summons over the summon limit - dismissed", num), 0, (Sprite)null, false);
			}
		}

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

		private void OnSummonDied(Character summon)
		{
			Player localPlayer = Player.m_localPlayer;
			if (DeathNotifications.Value && !((Object)(object)localPlayer == (Object)null))
			{
				((Character)localPlayer).Message((MessageType)1, Texts.Format("{0} has died", Summons.GetTitle(summon)), 0, GetPinIcon(Summons.GetKind((Component)(object)summon)), false);
			}
		}

		private void UpdatePin(Minimap minimap, Character summon, 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_0071: 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)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)summon).transform.position;
			SummonKind kind = Summons.GetKind((Component)(object)summon);
			string text = ((kind == SummonKind.Spirit) ? Summons.GetSpeciesName(summon) : Texts.Get("Skeleton")) + (following ? "" : Texts.Get(" (staying)"));
			if (_pins.TryGetValue(summon, 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(kind);
			if ((Object)(object)pinIcon != (Object)null)
			{
				value.m_icon = pinIcon;
			}
			if (kind == SummonKind.Spirit)
			{
				PinColors.Add(value, Summons.GetSpeciesColor(summon));
			}
			_pins[summon] = 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 summon)
		{
			//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)summon).transform.Find("SummonCommander_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("SummonCommander_Glow").transform;
				val.SetParent(((Component)summon).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(SummonKind kind)
		{
			if (_pinIcons.TryGetValue(kind, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab((kind == SummonKind.Spirit) ? "StaffSpiritCaller" : "TrophySkeleton");
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			value = (((Object)(object)val != (Object)null) ? val.m_itemData.GetIcon() : null);
			_pinIcons[kind] = value;
			return value;
		}

		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();
			SummonMovement.RestoreCollisions(Player.m_localPlayer);
			foreach (Character item in Owned)
			{
				Transform val = (((Object)(object)item != (Object)null) ? ((Component)item).transform.Find("SummonCommander_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 SummonCommands
	{
		[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__2_0;

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

		private const string Syntax = "sc [list|follow|stay|dismiss] [all|skeletons|spirits]";

		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__2_0;
			if (obj2 == null)
			{
				ConsoleOptionsFetcher val2 = () => Subcommands;
				<>c.<>9__2_0 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("sc", "[list|follow|stay|dismiss] [all|skeletons|spirits] - manage your Dead Raiser skeletons and Spirit Caller spirits", (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(Texts.Get("No player in the world."));
				return;
			}
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "list");
			if (!TryParseKind((args.Length > 2) ? args[2].ToLowerInvariant() : "all", out var kind))
			{
				args.Context.AddString(Texts.Format("Usage: {0}", "sc [list|follow|stay|dismiss] [all|skeletons|spirits]"));
				return;
			}
			switch (text)
			{
			case "list":
				List(args.Context, localPlayer, kind);
				break;
			case "follow":
				args.Context.AddString(Follow(localPlayer, null, kind));
				break;
			case "stay":
				args.Context.AddString(Stay(localPlayer, null, kind));
				break;
			case "dismiss":
				args.Context.AddString(Dismiss(localPlayer, null, kind));
				break;
			default:
				args.Context.AddString(Texts.Format("Usage: {0}", "sc [list|follow|stay|dismiss] [all|skeletons|spirits]"));
				break;
			}
		}

		private static bool TryParseKind(string text, out SummonKind kind)
		{
			switch (text)
			{
			case "all":
				kind = SummonKind.None;
				return true;
			case "skeletons":
			case "skeleton":
			case "sk":
				kind = SummonKind.Skeleton;
				return true;
			case "spirits":
			case "spirit":
				kind = SummonKind.Spirit;
				return true;
			default:
				kind = SummonKind.None;
				return false;
			}
		}

		public static string Follow(Player player, Character only = null, SummonKind kind = SummonKind.None)
		{
			List<Character> list = Targets(player, only, kind);
			foreach (Character item in list)
			{
				Summons.SetFollowing(item, player, follow: true);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return Texts.Format("{0} following you: {1}", Label(kind), list.Count);
			}
			return Texts.Format("{0} is following you", Summons.GetName(only));
		}

		public static string Stay(Player player, Character only = null, SummonKind kind = SummonKind.None)
		{
			List<Character> list = Targets(player, only, kind);
			foreach (Character item in list)
			{
				Summons.SetFollowing(item, player, follow: false);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return Texts.Format("{0} staying: {1}", Label(kind), list.Count);
			}
			return Texts.Format("{0} is staying", Summons.GetName(only));
		}

		public static string Dismiss(Player player, Character only = null, SummonKind kind = SummonKind.None)
		{
			List<Character> list = Targets(player, only, kind);
			foreach (Character item in list)
			{
				Summons.Dismiss(item);
			}
			if (!((Object)(object)only != (Object)null))
			{
				return Texts.Format("{0} dismissed: {1}", Label(kind), list.Count);
			}
			return Texts.Format("Dismissed: {0}", Summons.GetName(only));
		}

		public static void List(Terminal terminal, Player player, SummonKind kind = SummonKind.None)
		{
			List<Character> list = Targets(player, null, kind);
			terminal.AddString(Texts.Format("Your {0}: {1}", Label(kind).ToLowerInvariant(), list.Count));
			for (int i = 0; i < list.Count; i++)
			{
				terminal.AddString($"  #{i + 1}  {Summons.Describe(list[i], player)}");
			}
		}

		private static string Label(SummonKind kind)
		{
			return kind switch
			{
				SummonKind.Skeleton => Texts.Get("Skeletons"), 
				SummonKind.Spirit => Texts.Get("Spirits"), 
				_ => Texts.Get("Summons"), 
			};
		}

		private static List<Character> Targets(Player player, Character only, SummonKind kind)
		{
			if ((Object)(object)only != (Object)null)
			{
				return new List<Character> { only };
			}
			List<Character> list = new List<Character>();
			Summons.FindOwned(player, list);
			if (kind != SummonKind.None)
			{
				return Summons.OfKind(list, kind);
			}
			return list;
		}
	}
	internal struct SummonRowData
	{
		public string Label;

		public string Info;

		public Color SpeciesColor;

		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 SummonGroupData
	{
		public string Header;

		public List<SummonRowData> Rows;
	}
	internal class SummonFrames
	{
		private class Bar
		{
			public RectTransform Back;

			public RectTransform Fill;

			public Image FillImage;

			public TextMeshProUGUI Text;
		}

		private class Row
		{
			public RectTransform Root;

			public Image Stripe;

			public TextMeshProUGUI Label;

			public TextMeshProUGUI Info;

			public Bar Health;

			public Bar Shield;
		}

		private class Panel
		{
			public RectTransform Root;

			public TextMeshProUGUI Header;

			public readonly List<Row> Rows = new List<Row>();
		}

		private readonly struct Sizes
		{
			public readonly float Font;

			public readonly float Padding;

			public readonly float HeaderHeight;

			public readonly float RowHeight;

			public readonly float StripeWidth;

			public readonly float MinBarWidth;

			public readonly float RowChrome;

			public readonly float InfoWidth;

			public readonly float ShieldHeight;

			public Sizes(float font, float infoWidth)
			{
				Font = font;
				Padding = font * 0.35f;
				HeaderHeight = font * 1.4f;
				RowHeight = font * 1.15f;
				StripeWidth = font * 0.3f;
				InfoWidth = font * infoWidth;
				MinBarWidth = font * 9f;
				RowChrome = Padding * 4f + StripeWidth + InfoWidth;
				ShieldHeight = font * 0.6f;
			}
		}

		private const float Gap = 6f;

		private const float BottomReserve = 0.08f;

		private const float MinScale = 0.4f;

		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<Panel> _panels = new List<Panel>();

		private RectTransform _container;

		private TextMeshProUGUI _measure;

		private const float LabelFont = 0.9f;

		private const float HeaderFont = 1.05f;

		private const float BarFont = 0.8f;

		private const float MinFontScale = 0.7f;

		public void Show(List<SummonGroupData> groups, Vector2 anchor, float rightMargin, float top, float fontSize, float infoWidth, float panelWidth)
		{
			//IL_0152: 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_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_container == (Object)null && !CreateContainer())
			{
				return;
			}
			Sizes size = new Sizes(fontSize, infoWidth);
			List<float> list = groups.Select((SummonGroupData group) => (group.Rows.Count != 0) ? (group.Rows.Max((SummonRowData row) => Measure(row.Label, size.Font * 0.9f)) + size.Font * 0.2f) : 0f).ToList();
			float num = ((panelWidth > 0f) ? Mathf.Max(panelWidth, size.RowChrome + size.Font * 4f) : Mathf.Max(list.Max() + size.RowChrome + size.MinBarWidth, groups.Max((SummonGroupData group) => Measure(group.Header, size.Font * 1.05f)) + size.Padding * 2f + size.Font * 0.2f));
			float num2 = num - size.RowChrome;
			float num3 = groups.Max((SummonGroupData group) => (group.Rows.Count != 0) ? group.Rows.Max((SummonRowData row) => Measure(HealthText(row.MaxHealth, row.MaxHealth), size.Font * 0.8f)) : 0f) + size.Font * 0.6f;
			float num4 = num2 - Mathf.Min(num3, num2 * 0.5f);
			for (int num5 = 0; num5 < list.Count; num5++)
			{
				list[num5] = Mathf.Min(list[num5], num4);
			}
			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 num7 = 0; num7 < groups.Count; num7++)
			{
				if (num7 == _panels.Count)
				{
					_panels.Add(CreatePanel((Transform)(object)_container));
				}
				Panel panel = _panels[num7];
				((Component)panel.Root).gameObject.SetActive(true);
				panel.Root.anchoredPosition = new Vector2(0f, 0f - num6);
				float num8 = LayoutPanel(panel, groups[num7], size, num, list[num7]);
				panel.Root.sizeDelta = new Vector2(num, num8);
				num6 += num8 + 6f;
			}
			for (int num9 = groups.Count; num9 < _panels.Count; num9++)
			{
				((Component)_panels[num9].Root).gameObject.SetActive(false);
			}
			FitOnScreen(anchor, top, num6);
		}

		private static string HealthText(float health, float maxHealth)
		{
			return $"{health:0} / {maxHealth:0}";
		}

		private float Measure(string text, float fontSize)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)_measure).fontSize = fontSize;
			return ((TMP_Text)_measure).GetPreferredValues(text).x;
		}

		private static float LayoutPanel(Panel panel, SummonGroupData group, Sizes size, float panelWidth, float labelWidth)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			SetText(panel.Header, group.Header, size.Padding, size.Padding * 0.5f, panelWidth - size.Padding * 2f, size.HeaderHeight, size.Font * 1.05f);
			float num = size.Padding * 0.5f + size.HeaderHeight;
			for (int i = 0; i < group.Rows.Count; i++)
			{
				if (i == panel.Rows.Count)
				{
					panel.Rows.Add(CreateRow((Transform)(object)panel.Root));
				}
				Row row = panel.Rows[i];
				((Component)row.Root).gameObject.SetActive(true);
				row.Root.anchoredPosition = new Vector2(size.Padding, 0f - num);
				float num2 = LayoutRow(row, group.Rows[i], size, panelWidth, labelWidth);
				num += num2 + size.Padding * 0.4f;
			}
			for (int j = group.Rows.Count; j < panel.Rows.Count; j++)
			{
				((Component)panel.Rows[j].Root).gameObject.SetActive(false);
			}
			return num + size.Padding * 0.6f;
		}

		private static float LayoutRow(Row row, SummonRowData item, Sizes size, float panelWidth, float labelWidth)
		{
			//IL_0041: 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)
			//IL_0068: 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_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			float num = panelWidth - size.Padding * 2f;
			float num2 = size.RowHeight + (item.ShowShield ? (size.ShieldHeight + size.Padding * 0.25f) : 0f);
			row.Root.sizeDelta = new Vector2(num, num2);
			RectTransform rectTransform = ((Graphic)row.Stripe).rectTransform;
			rectTransform.anchoredPosition = Vector2.zero;
			rectTransform.sizeDelta = new Vector2(size.StripeWidth, num2);
			((Graphic)row.Stripe).color = item.SpeciesColor;
			float num3 = size.StripeWidth + size.Padding;
			SetText(row.Label, item.Label, num3, 0f, labelWidth, size.RowHeight, size.Font * 0.9f);
			SetText(row.Info, item.Info, num - size.InfoWidth, 0f, size.InfoWidth, size.RowHeight, size.Font * 0.85f);
			float num4 = num3 + labelWidth + size.Padding * 0.5f;
			float width = num - size.InfoWidth - size.Padding * 0.5f - num4;
			float num5 = size.RowHeight * 0.85f;
			float y = (size.RowHeight - num5) * 0.5f;
			SetBar(row.Health, item.ShowBar, item.Health, item.MaxHealth, item.BarColor, HealthText(item.Health, item.MaxHealth), num4, y, width, num5, size.Font * 0.8f);
			SetBar(row.Shield, item.ShowShield, item.Shield, item.MaxShield, ShieldColor, $"{item.Shield:0} · {item.ShieldSeconds:0} s", num4, size.RowHeight + size.Padding * 0.25f, width, size.ShieldHeight, size.Font * 0.55f);
			return num2;
		}

		private static void SetText(TextMeshProUGUI text, string value, float x, float y, float width, float height, float fontSize)
		{
			//IL_0021: 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)
			((TMP_Text)text).text = value;
			SetFontSize(text, fontSize);
			((TMP_Text)text).rectTransform.anchoredPosition = new Vector2(x, 0f - (y + height * 0.5f));
			((TMP_Text)text).rectTransform.sizeDelta = new Vector2(width, height * 3f);
		}

		private static void SetBar(Bar bar, bool show, float value, float max, Color color, string text, float x, float y, float width, float height, float fontSize)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			((Component)bar.Back).gameObject.SetActive(show);
			if (show)
			{
				bar.Back.anchoredPosition = new Vector2(x, 0f - y);
				bar.Back.sizeDelta = new Vector2(width, 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;
				SetFontSize(bar.Text, fontSize);
				((TMP_Text)bar.Text).rectTransform.offsetMin = new Vector2(0f, 0f - height);
				((TMP_Text)bar.Text).rectTransform.offsetMax = new Vector2(0f, height);
			}
		}

		private static void SetFontSize(TextMeshProUGUI text, float fontSize)
		{
			((TMP_Text)text).enableAutoSizing = true;
			((TMP_Text)text).fontSizeMax = fontSize;
			((TMP_Text)text).fontSizeMin = fontSize * 0.7f;
			((TMP_Text)text).fontSize = fontSize;
		}

		private void FitOnScreen(Vector2 anchor, float top, float height)
		{
			//IL_000b: 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_0015: 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)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = ((RectTransform)((Transform)_container).parent).rect;
			float num = ((Rect)(ref rect)).height * (anchor.y - 0.08f) - top;
			float num2 = ((height > num && num > 0f) ? Mathf.Max(0.4f, num / height) : 1f);
			((Transform)_container).localScale = new Vector3(num2, num2, 1f);
		}

		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;
			_panels.Clear();
		}

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

		private static Panel CreatePanel(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)
			Panel panel = new Panel();
			panel.Root = CreateRect("Panel", parent, new Vector2(1f, 1f));
			RectTransform root = panel.Root;
			RectTransform root2 = panel.Root;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(1f, 1f);
			root2.anchorMax = val;
			root.anchorMin = val;
			AddImage(panel.Root, null, PanelColor);
			panel.Header = CreateLeftText("Header", (Transform)(object)panel.Root, (TextAlignmentOptions)513);
			return panel;
		}

		private static Row CreateRow(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_0064: 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_0088: 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)
			Row row = new Row();
			row.Root = CreateRect("Row", parent, new Vector2(0f, 1f));
			RectTransform root = row.Root;
			RectTransform root2 = row.Root;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			root2.anchorMax = val;
			root.anchorMin = val;
			RectTransform val2 = CreateRect("Stripe", (Transform)(object)row.Root, new Vector2(0f, 1f));
			((Vector2)(ref val))..ctor(0f, 1f);
			val2.anchorMax = val;
			val2.anchorMin = val;
			row.Stripe = AddImage(val2, null, Color.white);
			row.Label = CreateLeftText("Label", (Transform)(object)row.Root, (TextAlignmentOptions)513);
			row.Info = CreateLeftText("Info", (Transform)(object)row.Root, (TextAlignmentOptions)516);
			row.Health = CreateBar("HealthBar", (Transform)(object)row.Root);
			row.Shield = CreateBar("ShieldBar", (Transform)(object)row.Root);
			return row;
		}

		private static TextMeshProUGUI CreateLeftText(string name, Transform parent, TextAlignmentOptions alignment)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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)
			TextMeshProUGUI val = SummonHud.CreateText(name, parent, alignment, new Vector2(0f, 0.5f), Vector2.zero);
			RectTransform rectTransform = ((TMP_Text)val).rectTransform;
			RectTransform rectTransform2 = ((TMP_Text)val).rectTransform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			rectTransform2.anchorMax = val2;
			rectTransform.anchorMin = val2;
			((TMP_Text)val).overflowMode = (TextOverflowModes)1;
			return val;
		}

		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 = SummonHud.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 SummonHud
	{
		private const float RightMargin = 20f;

		private const float FramesGap = 6f;

		private readonly SummonFrames _frames = new SummonFrames();

		private TextMeshProUGUI _list;

		public void Show(string text, float positionY, float fontSize, List<SummonGroupData> frames, float infoWidth, float panelWidth)
		{
			//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("SummonCommanderHud", (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, infoWidth, panelWidth);
				}
				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 static class SummonMovement
	{
		private const float RunBeyondStop = 7f;

		private const float MinBackOffGap = 1f;

		private static readonly Func<BaseAI, float, Vector3, float, bool, bool> MoveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", (Type[])null, (Type[])null), (object)null, true);

		private static readonly List<Collider> PlayerColliders = new List<Collider>();

		private static readonly List<Collider> SummonColliders = new List<Collider>();

		public static void UpdateCollisions(Player player)
		{
			SetCollisions(player, IsPassThrough);
		}

		public static void RestoreCollisions(Player player)
		{
			SetCollisions(player, (SummonKind _) => false);
		}

		private static void SetCollisions(Player player, Func<SummonKind, bool> ignore)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			GetSolidColliders((Component)(object)player, PlayerColliders);
			if (PlayerColliders.Count == 0)
			{
				return;
			}
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player))
				{
					SummonKind kind = Summons.GetKind((Component)(object)allCharacter);
					if (kind != SummonKind.None)
					{
						SetCollisionIgnored(allCharacter, ignore(kind));
					}
				}
			}
		}

		private static bool IsPassThrough(SummonKind kind)
		{
			if (kind != SummonKind.Spirit)
			{
				return SummonCommanderPlugin.PassThroughSkeletons.Value;
			}
			return SummonCommanderPlugin.PassThroughSpirits.Value;
		}

		private static void SetCollisionIgnored(Character summon, bool ignore)
		{
			GetSolidColliders((Component)(object)summon, SummonColliders);
			foreach (Collider summonCollider in SummonColliders)
			{
				foreach (Collider playerCollider in PlayerColliders)
				{
					Physics.IgnoreCollision(playerCollider, summonCollider, ignore);
				}
			}
		}

		private static void GetSolidColliders(Component root, List<Collider> result)
		{
			root.GetComponentsInChildren<Collider>(false, result);
			result.RemoveAll((Collider collider) => collider.isTrigger || !collider.enabled);
		}

		public static void Follow(BaseAI ai, Character summon, Vector3 target, float dt)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			float radius = summon.GetRadius();
			float value = SummonCommanderPlugin.FollowDistance.Value;
			float num = value + radius;
			Vector3 val = ((Component)summon).transform.position - target;
			float magnitude = ((Vector3)(ref val)).magnitude;
			float num2 = Mathf.Min(SummonCommanderPlugin.PersonalSpace.Value, value - 1f);
			if (num2 > 0f && magnitude < num2 + radius)
			{
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
				{
					val = -((Component)summon).transform.forward;
				}
				ai.MoveTowards(val, false);
			}
			else if (magnitude <= num)
			{
				ai.StopMoving();
			}
			else
			{
				MoveTo(ai, dt, target, num, magnitude > num + 7f);
			}
		}
	}
	[HarmonyPatch]
	internal static class SummonPatches
	{
		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(SpawnAbility), "Setup")]
		[HarmonyPrefix]
		private static void ChooseSpiritSpecies(SpawnAbility __instance, Character owner)
		{
			string spiritPrefab = Summons.GetSpiritPrefab(SummonCommanderPlugin.NextSpirit.Value);
			if (spiritPrefab == null || (Object)(object)owner == (Object)null || (Object)(object)owner != (Object)(object)Player.m_localPlayer || __instance.m_spawnPrefab == null)
			{
				return;
			}
			GameObject[] spawnPrefab = __instance.m_spawnPrefab;
			foreach (GameObject val in spawnPrefab)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name == spiritPrefab)
				{
					__instance.m_spawnPrefab = (GameObject[])(object)new GameObject[1] { val };
					break;
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "UpdatePins")]
		[HarmonyPostfix]
		private static void ColorSummonPins(List<PinData> ___m_pins)
		{
			//IL_0035: 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)
			foreach (PinData ___m_pin in ___m_pins)
			{
				if (!((Object)(object)___m_pin.m_iconElement == (Object)null) && SummonCommanderPlugin.PinColors.TryGetValue(___m_pin, out var value))
				{
					((Graphic)___m_pin.m_iconElement).color = (Color)value;
					PinNameData namePinData = ___m_pin.m_NamePinData;
					if ((Object)(object)((namePinData != null) ? namePinData.PinNameText : null) != (Object)null)
					{
						((Graphic)___m_pin.m_NamePinData.PinNameText).color = (Color)value;
					}
				}
			}
		}

		[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 && SummonCommanderPlugin.HealthBars.Value && SummonCommanderPlugin.IsOwned(c))
			{
				float value = SummonCommanderPlugin.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 (SummonCommanderPlugin.IsOwned(character))
				{
					if (SummonCommanderPlugin.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 (SummonCommanderPlugin.HealthBarColors.Value)
				{
					bar.SetColor(Summons.GetHealthColor(character));
				}
				else
				{
					bar.ResetColor();
				}
			}
		}

		[HarmonyPatch(typeof(Tameable), "UnsummonMaxInstances")]
		[HarmonyPrefix]
		private static bool EnforceSummonLimit(Tameable __instance, int maxInstances, ZNetView ___m_nview, MonsterAI ___m_monsterAI, Character ___m_character)
		{
			if (!Summons.IsSummon((Component)(object)__instance))
			{
				return true;
			}
			if ((Object)(object)___m_character == (Object)null)
			{
				return false;
			}
			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>();
			Summons.FindOwned(val, list);
			if (Summons.DismissExcess(Summons.SameLimitGroup(list, ___m_character), 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 DismissSummonsLeftByTeleport(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)
			{
				SummonCommanderPlugin.DismissFarSummons(__instance, pos);
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		[HarmonyPostfix]
		private static void DismissSummonsLeftByDeath(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());
				SummonCommanderPlugin.DismissFarSummons(__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(BaseAI), "Follow")]
		[HarmonyPrefix]
		private static bool FollowAtDistance(BaseAI __instance, GameObject go, float dt, Character ___m_character)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!SummonCommanderPlugin.ImprovedFollow.Value || (Object)(object)go == (Object)null || (Object)(object)___m_character == (Object)null || !Summons.IsSummon((Component)(object)___m_character))
			{
				return true;
			}
			SummonMovement.Follow(__instance, ___m_character, go.transform.position, dt);
			return false;
		}

		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		[HarmonyPostfix]
		private static void AddHotkeyHints(Tameable __instance, ref string __result)
		{
			if (SummonCommanderPlugin.IsOwned(((Component)__instance).GetComponent<Character>()))
			{
				__result += SummonCommanderPlugin.GetHoverHints();
			}
		}
	}
	internal enum SpiritSpecies
	{
		Random,
		Wolf,
		Boar,
		Moose,
		Bear
	}
	internal enum SummonKind
	{
		None,
		Skeleton,
		Spirit
	}
	internal static class Summons
	{
		private const string SkeletonPrefab = "Skeleton_Friendly";

		private const string SpiritPrefabSuffix = "_spiritcaller";

		private static readonly string[] SpiritPrefabs = new string[4] { "Wolf_spiritcaller", "Boar_spiritcaller", "Moose_spiritcaller", "Bjorn_spiritcaller" };

		private static readonly string[] DisplayOrder = new string[1] { "Skeleton_Friendly" }.Concat(SpiritPrefabs).ToArray();

		private static readonly string[] GroupNames = new string[5] { "Skeletons", "Wolves", "Boars", "Moose", "Bears" };

		private static readonly Color[] SpeciesColors = (Color[])(object)new Color[5]
		{
			new Color(0.91f, 0.88f, 0.78f),
			new Color(0.55f, 0.78f, 1f),
			new Color(0.85f, 0.63f, 0.4f),
			new Color(0.56f, 0.83f, 0.42f),
			new Color(0.78f, 0.61f, 1f)
		};

		private static readonly Color OtherSpeciesColor = new Color(1f, 0.62f, 0.81f);

		private const string FullSpeciesColor = "#FFE066";

		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 SummonKind GetKind(Component component)
		{
			string prefabName = Utils.GetPrefabName(component.gameObject);
			if (prefabName == "Skeleton_Friendly")
			{
				return SummonKind.Skeleton;
			}
			if (prefabName.EndsWith("_spiritcaller", StringComparison.OrdinalIgnoreCase))
			{
				return SummonKind.Spirit;
			}
			return SummonKind.None;
		}

		public static bool IsSummon(Component component)
		{
			return GetKind(component) != SummonKind.None;
		}

		public static bool IsSkeleton(Component component)
		{
			return GetKind(component) == SummonKind.Skeleton;
		}

		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() || !IsSummon((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(delegate(Character a, Character b)
			{
				int num2 = GetDisplayRank(a).CompareTo(GetDisplayRank(b));
				if (num2 == 0)
				{
					num2 = string.CompareOrdinal(a.m_name, b.m_name);
				}
				return (num2 == 0) ? GetSpawnTime(b).CompareTo(GetSpawnTime(a)) : num2;
			});
		}

		private static int GetDisplayRank(Character summon)
		{
			int num = Array.IndexOf(DisplayOrder, Utils.GetPrefabName(((Component)summon).gameObject));
			if (num < 0)
			{
				return DisplayOrder.Length;
			}
			return num;
		}

		public static Color GetSpeciesColor(Character summon)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			return GetSpeciesColor(Utils.GetPrefabName(((Component)summon).gameObject));
		}

		private static Color GetSpeciesColor(string prefabName)
		{
			//IL_001c: 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)
			int num = Array.IndexOf(DisplayOrder, prefabName);
			if (num < 0)
			{
				return OtherSpeciesColor;
			}
			return SpeciesColors[num];
		}

		public static string Colored(string text, Color color)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return "<color=#" + ColorUtility.ToHtmlStringRGB(color) + ">" + text + "</color>";
		}

		public static List<Character> OfKind(IEnumerable<Character> summons, SummonKind kind)
		{
			return summons.Where((Character summon) => GetKind((Component)(object)summon) == kind).ToList();
		}

		public static IEnumerable<List<Character>> LimitGroups(IEnumerable<Character> summons)
		{
			return from summon in summons
				group summon by summon.m_name into @group
				select @group.ToList();
		}

		public static List<Character> SameLimitGroup(IEnumerable<Character> summons, Character summon)
		{
			return summons.Where((Character other) => other.m_name == summon.m_name).ToList();
		}

		public static string DescribeSpecies(List<Character> spirits, SpiritSpecies next)
		{
			int maxInstances = GetMaxInstances(spirits[0]);
			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				string[] spiritPrefabs = SpiritPrefabs;
				foreach (string text in spiritPrefabs)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(text);
					Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Character>() : null);
					if ((Object)(object)val != (Object)null)
					{
						list.Add(new KeyValuePair<string, string>(val.m_name, text));
					}
				}
			}
			foreach (Character spirit in spirits)
			{
				if (list.All((KeyValuePair<string, string> entry) => entry.Key != spirit.m_name))
				{
					list.Add(new KeyValuePair<string, string>(spirit.m_name, Utils.GetPrefabName(((Component)spirit).gameObject)));
				}
			}
			IEnumerable<string> values = list.Select(delegate(KeyValuePair<string, string> entry)
			{
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				int num = spirits.Count((Character val2) => val2.m_name == entry.Key);
				string text3 = Colored(GetSpeciesName(entry.Key, entry.Value).ToLowerInvariant(), GetSpeciesColor(entry.Value));
				if (maxInstances <= 0)
				{
					return $"{text3} {num}";
				}
				string text4 = $"{num}/{maxInstances}";
				return (num < maxInstances) ? (text3 + " " + text4) : (text3 + " <color=#FFE066><b>" + text4 + "</b></color>");
			});
			string text2 = Texts.Get("Spirits: ") + string.Join(" · ", values);
			if (next != SpiritSpecies.Random)
			{
				text2 = text2 + "  (" + Texts.Format("next: {0}", GetSpiritSpeciesName(next)) + ")";
			}
			return text2;
		}

		private static long GetSpawnTime(Character summon)
		{
			return ((Component)summon).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 summon, Player player)
		{
			ZNetView component = ((Component)summon).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 summon, Player player, bool follow)
		{
			if (IsFollowing(summon, player) != follow)
			{
				Tameable component = ((Component)summon).GetComponent<Tameable>();
				if ((Object)(object)component != (Object)null)
				{
					component.Command((Humanoid)(object)player, false);
				}
			}
		}

		public static void Dismiss(Character summon)
		{
			ZNetView component = ((Component)summon).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> group, int maxInstances)
		{
			int num = group.Count - maxInstances;
			for (int i = 0; i < num; i++)
			{
				int num2 = group.Count - 1;
				if (SummonCommanderPlugin.DismissWeakest.Value)
				{
					for (int num3 = num2 - 1; num3 > 0; num3--)
					{
						if (group[num3].GetHealth() < group[num2].GetHealth())
						{
							num2 = num3;
						}
					}
				}
				Dismiss(group[num2]);
				group.RemoveAt(num2);
			}
			return Math.Max(num, 0);
		}

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

		public static string GetName(Character summon)
		{
			string text = Localization.instance.Localize(summon.GetHoverName());
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return GetSpeciesName(summon);
		}

		public static string GetGroupName(Character summon)
		{
			int num = Array.IndexOf(DisplayOrder, Utils.GetPrefabName(((Component)summon).gameObject));
			if (num < 0)
			{
				return GetSpeciesName(summon);
			}
			return Texts.Get(GroupNames[num]);
		}

		public static string GetSpeciesName(Character summon)
		{
			return GetSpeciesName(summon.m_name, Utils.GetPrefabName(((Component)summon).gameObject));
		}

		public static string GetSpiritPrefab(SpiritSpecies species)
		{
			if (species != SpiritSpecies.Random)
			{
				return SpiritPrefabs[(int)(species - 1)];
			}
			return null;
		}

		public static string GetSpiritSpeciesName(SpiritSpecies species)
		{
			string spiritPrefab = GetSpiritPrefab(species);
			if (spiritPrefab == null)
			{
				return Texts.Get("random");
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(spiritPrefab) : null);
			Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
			return GetSpeciesName(((Object)(object)val2 != (Object)null) ? val2.m_name : "", spiritPrefab).ToLowerInvariant();
		}

		private static string GetSpeciesName(string nameToken, string prefabName)
		{
			string text = Localization.instance.Localize(nameToken);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			string text2 = (prefabName.EndsWith("_spiritcaller", StringComparison.OrdinalIgnoreCase) ? prefabName.Substring(0, prefabName.Length - "_spiritcaller".Length) : prefabName);
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(text2) : null);
			Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
			text = (((Object)(object)val2 != (Object)null) ? Localization.instance.Localize(val2.m_name) : "");
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return text2;
		}

		public static string GetRowDetail(Character summon, bool name, bool weapon)
		{
			List<string> list = new List<string>();
			if (name && GetName(summon) != GetSpeciesName(summon))
			{
				list.Add(GetName(summon));
			}
			if (weapon && IsSkeleton((Component)(object)summon))
			{
				list.Add(GetWeaponName(summon));
			}
			return string.Join(" · ", list);
		}

		public static string GetTitle(Character summon)
		{
			string detail = GetDetail(summon);
			if (detail == null)
			{
				return GetName(summon);
			}
			return GetName(summon) + " (" + detail + ")";
		}

		private static string GetDetail(Character summon)
		{
			if (IsSkeleton((Component)(object)summon))
			{
				return GetWeaponName(summon);
			}
			string speciesName = GetSpeciesName(summon);
			if (!(GetName(summon) == speciesName))
			{
				return speciesName;
			}
			return null;
		}

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

		public static SkeletonLoadout GetLoadout(Character skeleton)
		{
			Humanoid val = (Humanoid)(object)((skeleton is Humanoid) ? skeleton : null);
			if (val == null)
			{
				return SkeletonLoadout.Random;
			}
			Classify(val.GetInv