Decompiled source of SpiritAnimism v0.5.0

plugins/SpiritAnimism.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Brruid")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0+b484c870d91cfd57226fb8b24318bf034cb58e23")]
[assembly: AssemblyProduct("SpiritAnimism")]
[assembly: AssemblyTitle("SpiritAnimism")]
[assembly: AssemblyVersion("0.5.0.0")]
namespace SpiritAnimism;

internal static class Dismiss
{
	public static void MySummons()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		int num = 0;
		bool flag = false;
		foreach (Summon item in Summons.Mine())
		{
			if (!Saddles.DismissWhileRiding.Value && Saddles.IsRiding(localPlayer, item))
			{
				flag = true;
				continue;
			}
			if (!item.View.HasOwner())
			{
				item.View.ClaimOwnership();
			}
			item.View.InvokeRPC(ZNetView.Everybody, "RPC_UnSummon", Array.Empty<object>());
			num++;
		}
		Plugin.Log.LogInfo((object)$"Dismissed {num} summon(s)");
		string text = ((num > 0) ? string.Format("Dismissed {0} spirit{1}", num, (num == 1) ? "" : "s") : "");
		if (flag)
		{
			text = ((text == "") ? "" : (text + ". ")) + "Dismount to dismiss your mount";
		}
		if (text != "")
		{
			((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
		}
	}

	public static bool InputIsFree()
	{
		if (!Console.IsVisible() && !Menu.IsVisible())
		{
			if (!((Object)(object)Chat.instance == (Object)null))
			{
				return !Chat.instance.HasFocus();
			}
			return true;
		}
		return false;
	}
}
internal readonly struct Slot
{
	public readonly float Right;

	public readonly float Forward;

	public readonly SummonType Kind;

	public Slot(float right, float forward, SummonType kind = null)
	{
		Right = right;
		Forward = forward;
		Kind = kind;
	}

	public Vector3 WorldPosition(Vector3 origin, Vector3 forward)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: 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_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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = Vector3.Cross(Vector3.up, forward);
		return origin + val * Right + forward * Forward;
	}
}
internal abstract class Formation
{
	public const float RowSpacing = 3f;

	public static readonly Formation Leader = new LeaderFormation();

	public static readonly Formation Defense = new DefenseFormation();

	public static readonly Formation Bubble = new BubbleFormation();

	public static readonly Formation[] BuiltIn = new Formation[3] { Leader, Defense, Bubble };

	public abstract string Name { get; }

	public abstract List<Slot> Layout(int count, float distance, float gap);

	public static Formation BuiltInNamed(string name)
	{
		Formation[] builtIn = BuiltIn;
		foreach (Formation formation in builtIn)
		{
			if (string.Equals(formation.Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return formation;
			}
		}
		return null;
	}

	protected static void Ring(List<Slot> slots, int n, float r)
	{
		for (int i = 0; i < n; i++)
		{
			float num = (float)i * 2f * (float)Math.PI / (float)n;
			slots.Add(new Slot(r * Mathf.Sin(num), r * Mathf.Cos(num)));
		}
	}
}
internal sealed class LeaderFormation : Formation
{
	private const float ArcRadians = 2.7925267f;

	public override string Name => "Leader";

	public override List<Slot> Layout(int count, float distance, float gap)
	{
		List<Slot> list = new List<Slot>(count);
		int num = 0;
		while (list.Count < count)
		{
			float num2 = distance + (float)num * 3f;
			int num3 = Mathf.Max(1, Mathf.FloorToInt(num2 * 2.7925267f / gap) + 1);
			int num4 = Mathf.Min(count - list.Count, num3);
			float num5 = gap / num2;
			List<float> list2 = new List<float>(num4);
			for (int i = 0; i < num4; i++)
			{
				list2.Add(((float)i - (float)(num4 - 1) / 2f) * num5);
			}
			list2.Sort((float a, float b) => Mathf.Abs(a).CompareTo(Mathf.Abs(b)));
			foreach (float item in list2)
			{
				list.Add(new Slot(num2 * Mathf.Sin(item), (0f - num2) * Mathf.Cos(item)));
			}
			num++;
		}
		return list;
	}
}
internal sealed class DefenseFormation : Formation
{
	public override string Name => "Defense";

	public override List<Slot> Layout(int count, float distance, float gap)
	{
		List<Slot> list = new List<Slot>(count);
		int num = 0;
		while (list.Count < count)
		{
			float num2 = distance + (float)num * 3f;
			int num3 = Mathf.Max(1, Mathf.FloorToInt((float)Math.PI * 2f * num2 / gap));
			Formation.Ring(list, Mathf.Min(count - list.Count, num3), num2);
			num++;
		}
		return list;
	}
}
internal sealed class BubbleFormation : Formation
{
	public override string Name => "Bubble";

	public override List<Slot> Layout(int count, float distance, float gap)
	{
		List<Slot> list = new List<Slot>(count);
		Formation.Ring(list, count, Mathf.Max(distance, (float)count * gap / ((float)Math.PI * 2f)));
		return list;
	}
}
internal sealed class CustomFormation : Formation
{
	private readonly string _name;

	public readonly List<Slot> Slots;

	public override string Name => _name;

	public CustomFormation(string name, List<Slot> slots)
	{
		_name = name;
		Slots = slots;
	}

	public override List<Slot> Layout(int count, float distance, float gap)
	{
		List<Slot> list = new List<Slot>(Slots);
		if (count > list.Count)
		{
			float num = 0f;
			foreach (Slot slot in Slots)
			{
				if (!(slot.Forward > gap))
				{
					num = Mathf.Max(num, Mathf.Sqrt(slot.Right * slot.Right + slot.Forward * slot.Forward));
				}
			}
			float distance2 = Mathf.Max(distance, num + Mathf.Max(3f, gap));
			list.AddRange(Formation.Leader.Layout(count - list.Count, distance2, gap));
		}
		return list;
	}
}
internal static class Formations
{
	private sealed class FollowState
	{
		public Vector3 LastPosition;

		public float StillSince;

		public bool Settled;

		public Vector3 AnchorPosition;

		public Vector3 AnchorForward;

		public float NextRefresh;

		public readonly Dictionary<ZDOID, Vector3> Targets = new Dictionary<ZDOID, Vector3>();
	}

	[HarmonyPatch(typeof(BaseAI), "Follow")]
	private static class FollowPatch
	{
		private static readonly Dictionary<ZDOID, float> _passSide = new Dictionary<ZDOID, float>();

		private const float UndecidedAcross = 0.5f;

		private static bool Prefix(BaseAI __instance, GameObject go, float dt, Character ___m_character)
		{
			//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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)___m_character == (Object)null || (Object)(object)go == (Object)null)
			{
				return true;
			}
			SummonType summonType = Summons.SpeciesOf(((Object)((Component)___m_character).gameObject).name);
			if (summonType == null)
			{
				return true;
			}
			Player component = go.GetComponent<Player>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			ZNetView component2 = ((Component)___m_character).GetComponent<ZNetView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return true;
			}
			Summon summon = new Summon(___m_character, summonType, component2);
			Vector3? val = TargetFor(summon, component);
			if (!val.HasValue)
			{
				return true;
			}
			Vector3 position = ((Component)___m_character).transform.position;
			if (!((Character)component).IsAttached() && InFrontOf(component, position) && !AheadOf(component, val.Value))
			{
				__instance.StopMoving();
				return false;
			}
			float num = Utils.DistanceXZ(val.Value, position);
			if (num < 2f)
			{
				__instance.StopMoving();
				return false;
			}
			Vector3 arg = ((Buffer.Value > 0f) ? AroundOthers(summon, position, val.Value) : val.Value);
			MoveTo(__instance, dt, arg, 1f, num > 10f);
			return false;
		}

		private static bool InFrontOf(Player player, Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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)
			if (Vector3.Distance(point, ((Component)player).transform.position) < 5f)
			{
				return AheadOf(player, point);
			}
			return false;
		}

		private static bool AheadOf(Player player, Vector3 point)
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//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)
			return Vector3.Dot(Flat(((Component)player).transform.forward), Flat(point - ((Component)player).transform.position)) > 0.8f;
		}

		private static Vector3 AroundOthers(Summon summon, Vector3 from, Vector3 target)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			float num = Utils.DistanceXZ(target, from);
			if (num < 0.01f)
			{
				return target;
			}
			Vector3 val = Flat(target - from);
			Vector3 val2 = Vector3.Cross(Vector3.up, val);
			Character character = summon.Character;
			Character val3 = null;
			float num2 = num;
			float num3 = 0f;
			float num4 = 0f;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter == (Object)(object)character || allCharacter.IsDead())
				{
					continue;
				}
				Vector3 val4 = ((Component)allCharacter).transform.position - from;
				val4.y = 0f;
				float num5 = Vector3.Dot(val4, val);
				if (!(num5 <= 0f) && !(num5 >= num))
				{
					float num6 = Vector3.Dot(val4, val2);
					float num7 = character.GetRadius() + allCharacter.GetRadius() + Buffer.Value;
					if (!(Mathf.Abs(num6) >= num7) && !(num5 - num7 >= num2))
					{
						val3 = allCharacter;
						num2 = num5 - num7;
						num3 = num6;
						num4 = num7;
					}
				}
			}
			if ((Object)(object)val3 == (Object)null)
			{
				return target;
			}
			ZDOID uid = summon.Zdo.m_uid;
			if (Mathf.Abs(num3) >= 0.5f || !_passSide.TryGetValue(uid, out var value))
			{
				value = ((num3 >= 0f) ? (-1f) : 1f);
			}
			_passSide[uid] = value;
			return ((Component)val3).transform.position + val2 * (value * num4);
		}
	}

	private const string FileName = "Brruid.SpiritAnimism.Formations.cfg";

	internal static readonly int FormationKey = StringExtensionMethods.GetStableHashCode("SpiritAnimism.Formation");

	private const string SpecVersion = "1";

	private const float RefreshSeconds = 0.25f;

	private const float FilePollSeconds = 1f;

	private const float DeadZone = 2f;

	private const float ArriveWithin = 1f;

	private const float RunBeyond = 10f;

	private const float SettleAfterSeconds = 1f;

	private const float SettleBreakMargin = 3f;

	private const float FreezeDistance = 5f;

	private const float FreezeFacingDot = 0.8f;

	internal static ConfigEntry<string> Chosen;

	internal static ConfigEntry<float> Distance;

	internal static ConfigEntry<float> Buffer;

	private static string _filePath;

	private static DateTime _fileLastWrite;

	private static float _nextFilePoll;

	private static readonly Dictionary<string, CustomFormation> _custom = new Dictionary<string, CustomFormation>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<string> _warnedUnknown = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, FollowState> _states = new Dictionary<string, FollowState>();

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

	private const string DefaultFile = "## SpiritAnimism formations. Each [Name] below is a formation you can pick with\n## Formation = Name in Brruid.SpiritAnimism.cfg. One line per slot:\n##   SlotN = right, forward          metres from you; negative = left / behind\n##   SlotN = Kind, right, forward    reserved for one kind: Bear, Boar, Moose or Wolf\n## You stand at 0, 0 and the shape turns with you. A reserved slot stays empty when\n## that kind is not out. Summons beyond the listed slots form a Leader arc behind you.\n## Leader, Defense and Bubble are built in and cannot be redefined here.\n## Edits apply while you play.\n\n[Escort]\nSlot1 = Bear, 3, 10\nSlot2 = Bear, -3, 10\nSlot3 = Wolf, 3, -2\nSlot4 = Wolf, -3, -2\nSlot5 = 0, -5\nSlot6 = 2.5, -6\nSlot7 = -2.5, -6\n";

	public static void Init(ConfigFile config)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Expected O, but got Unknown
		Chosen = config.Bind<string>("Formations", "Formation", "Leader", "How your summons arrange themselves around you while following. Leader = an arc behind you. Defense = a ring around you, more rings when full. Bubble = one ring that widens to fit. Or the name of a formation from Brruid.SpiritAnimism.Formations.cfg beside this file.");
		Distance = config.Bind<float>("Formations", "Distance", 5f, new ConfigDescription("How far the nearest row or ring stands from you, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 20f), Array.Empty<object>()));
		Buffer = config.Bind<float>("Formations", "Buffer", 1f, new ConfigDescription("Air between animals, in metres: the gap between their spots in formation, and how wide a walking animal steers around anyone standing in its way, you and your mount included. 0 = bodies touch and nobody steers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
		_filePath = Path.Combine(Path.GetDirectoryName(config.ConfigFilePath), "Brruid.SpiritAnimism.Formations.cfg");
		if (!File.Exists(_filePath))
		{
			File.WriteAllText(_filePath, "## SpiritAnimism formations. Each [Name] below is a formation you can pick with\n## Formation = Name in Brruid.SpiritAnimism.cfg. One line per slot:\n##   SlotN = right, forward          metres from you; negative = left / behind\n##   SlotN = Kind, right, forward    reserved for one kind: Bear, Boar, Moose or Wolf\n## You stand at 0, 0 and the shape turns with you. A reserved slot stays empty when\n## that kind is not out. Summons beyond the listed slots form a Leader arc behind you.\n## Leader, Defense and Bubble are built in and cannot be redefined here.\n## Edits apply while you play.\n\n[Escort]\nSlot1 = Bear, 3, 10\nSlot2 = Bear, -3, 10\nSlot3 = Wolf, 3, -2\nSlot4 = Wolf, -3, -2\nSlot5 = 0, -5\nSlot6 = 2.5, -6\nSlot7 = -2.5, -6\n");
			Plugin.Log.LogInfo((object)("Formations file created at " + _filePath));
		}
		LoadFile();
	}

	public static void Update()
	{
		if (!(Time.unscaledTime < _nextFilePoll))
		{
			_nextFilePoll = Time.unscaledTime + 1f;
			if (!File.Exists(_filePath))
			{
				File.WriteAllText(_filePath, "## SpiritAnimism formations. Each [Name] below is a formation you can pick with\n## Formation = Name in Brruid.SpiritAnimism.cfg. One line per slot:\n##   SlotN = right, forward          metres from you; negative = left / behind\n##   SlotN = Kind, right, forward    reserved for one kind: Bear, Boar, Moose or Wolf\n## You stand at 0, 0 and the shape turns with you. A reserved slot stays empty when\n## that kind is not out. Summons beyond the listed slots form a Leader arc behind you.\n## Leader, Defense and Bubble are built in and cannot be redefined here.\n## Edits apply while you play.\n\n[Escort]\nSlot1 = Bear, 3, 10\nSlot2 = Bear, -3, 10\nSlot3 = Wolf, 3, -2\nSlot4 = Wolf, -3, -2\nSlot5 = 0, -5\nSlot6 = 2.5, -6\nSlot7 = -2.5, -6\n");
				LoadFile();
				Plugin.Log.LogInfo((object)"Formations file recreated");
			}
			else if (File.GetLastWriteTimeUtc(_filePath) != _fileLastWrite)
			{
				LoadFile();
			}
		}
	}

	private static void LoadFile()
	{
		string[] array;
		try
		{
			array = File.ReadAllLines(_filePath);
		}
		catch (IOException ex)
		{
			Plugin.Log.LogWarning((object)("Formations file not read yet: " + ex.Message));
			return;
		}
		_fileLastWrite = File.GetLastWriteTimeUtc(_filePath);
		_custom.Clear();
		_warnedUnknown.Clear();
		string text = null;
		List<Slot> list = null;
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim();
			if (text2.Length == 0 || text2.StartsWith("#"))
			{
				continue;
			}
			if (text2.StartsWith("[") && text2.EndsWith("]"))
			{
				text = text2.Substring(1, text2.Length - 2).Trim();
				if (Formation.BuiltInNamed(text) != null)
				{
					Plugin.Log.LogWarning((object)$"Formations file line {i + 1}: {text} is built in and cannot be redefined");
					text = null;
					list = null;
				}
				else
				{
					list = new List<Slot>();
					_custom[text] = new CustomFormation(text, list);
				}
			}
			else
			{
				if (list == null)
				{
					continue;
				}
				int num = text2.IndexOf('=');
				if (num < 0)
				{
					continue;
				}
				string[] array2 = text2.Substring(num + 1).Split(new char[1] { ',' });
				SummonType summonType = null;
				int num2 = 0;
				if (array2.Length == 3)
				{
					summonType = Summons.Named(array2[0].Trim());
					if (summonType == null)
					{
						Plugin.Log.LogWarning((object)$"Formations file line {i + 1}: unknown kind {array2[0].Trim()}; use Bear, Boar, Moose or Wolf");
						continue;
					}
					num2 = 1;
				}
				else if (array2.Length != 2)
				{
					Plugin.Log.LogWarning((object)$"Formations file line {i + 1}: expected 'right, forward' or 'Kind, right, forward'");
					continue;
				}
				if (!TryParseMetres(array2[num2], out var value) || !TryParseMetres(array2[num2 + 1], out var value2))
				{
					Plugin.Log.LogWarning((object)$"Formations file line {i + 1}: coordinates must be numbers in metres");
				}
				else
				{
					list.Add(new Slot(value, value2, summonType));
				}
			}
		}
		Plugin.Log.LogInfo((object)$"Formations file loaded: {_custom.Count} custom formation(s)");
	}

	private static bool TryParseMetres(string text, out float value)
	{
		if (float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value))
		{
			return !float.IsInfinity(value);
		}
		return false;
	}

	private static Formation Named(string name)
	{
		Formation formation = Formation.BuiltInNamed(name);
		if (formation != null)
		{
			return formation;
		}
		if (_custom.TryGetValue(name, out var value))
		{
			return value;
		}
		if (_warnedUnknown.Add(name))
		{
			Plugin.Log.LogWarning((object)("Unknown formation " + name + "; using Leader"));
		}
		return Formation.Leader;
	}

	private static bool SpecFor(Summon summon, Player summoner, out Formation formation, out float distance)
	{
		ZDO zdo = summon.Zdo;
		if ((Object)(object)summoner == (Object)(object)Player.m_localPlayer)
		{
			formation = Named(Chosen.Value);
			distance = Distance.Value;
			string text = "1;" + formation.Name + ";" + distance.ToString("0.#", CultureInfo.InvariantCulture);
			if (summon.View.IsOwner() && zdo.GetString(FormationKey, "") != text)
			{
				zdo.Set(FormationKey, text);
			}
			return true;
		}
		string[] array = zdo.GetString(FormationKey, "").Split(new char[1] { ';' });
		if (array.Length != 3 || array[0] != "1" || !TryParseMetres(array[2], out distance))
		{
			formation = null;
			distance = 0f;
			return false;
		}
		formation = Named(array[1]);
		distance = Mathf.Clamp(distance, 3f, 20f);
		return true;
	}

	private static Vector3? TargetFor(Summon summon, Player summoner)
	{
		//IL_003a: 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_0090: 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)
		if (!SpecFor(summon, summoner, out var formation, out var distance))
		{
			return null;
		}
		string playerName = summoner.GetPlayerName();
		if (!_states.TryGetValue(playerName, out var value))
		{
			value = new FollowState
			{
				LastPosition = ((Component)summoner).transform.position,
				StillSince = Time.time
			};
			_states[playerName] = value;
		}
		if (Time.time >= value.NextRefresh)
		{
			value.NextRefresh = Time.time + 0.25f;
			Refresh(value, summoner, formation, distance);
		}
		if (!value.Targets.TryGetValue(summon.Zdo.m_uid, out var value2))
		{
			return null;
		}
		return value2;
	}

	private static void Refresh(FollowState state, Player summoner, Formation formation, float distance)
	{
		//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_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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_0032: 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_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: 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_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = ((Component)summoner).transform.position;
		Vector3 val = Flat(((Component)summoner).transform.forward);
		if (Vector3.Distance(position, state.LastPosition) > 0.2f)
		{
			state.LastPosition = position;
			state.StillSince = Time.time;
		}
		if (!state.Settled && Time.time - state.StillSince >= 1f)
		{
			state.Settled = true;
			state.AnchorPosition = position;
			state.AnchorForward = val;
		}
		else if (state.Settled && Vector3.Distance(position, state.AnchorPosition) > distance + 3f)
		{
			state.Settled = false;
		}
		Vector3 origin = (state.Settled ? state.AnchorPosition : position);
		Vector3 forward = (state.Settled ? state.AnchorForward : val);
		List<Summon> list = new List<Summon>();
		string playerName = summoner.GetPlayerName();
		float num = 0f;
		foreach (Summon item in Summons.Standing())
		{
			if (item.CalledBy(playerName) && item.Zdo.GetLong(ZDOVars.s_user, 0L) == 0L)
			{
				list.Add(item);
				num = Mathf.Max(num, item.Character.GetRadius());
			}
		}
		list.Sort((Summon a, Summon b) => Age(b).CompareTo(Age(a)));
		float gap = 2f * num + Buffer.Value;
		state.Targets.Clear();
		List<Slot> slots = formation.Layout(list.Count, distance, gap);
		foreach (KeyValuePair<Summon, Slot> item2 in Assign(list, slots))
		{
			state.Targets[item2.Key.Zdo.m_uid] = item2.Value.WorldPosition(origin, forward);
		}
	}

	private static TimeSpan Age(Summon summon)
	{
		BaseAI component = ((Component)summon.Character).GetComponent<BaseAI>();
		if (!((Object)(object)component != (Object)null))
		{
			return TimeSpan.Zero;
		}
		return component.GetTimeSinceSpawned();
	}

	private static List<KeyValuePair<Summon, Slot>> Assign(List<Summon> followers, List<Slot> slots)
	{
		List<KeyValuePair<Summon, Slot>> list = new List<KeyValuePair<Summon, Slot>>(followers.Count);
		List<Summon> list2 = new List<Summon>(followers);
		List<Slot> list3 = new List<Slot>();
		foreach (Slot slot in slots)
		{
			if (slot.Kind == null)
			{
				list3.Add(slot);
				continue;
			}
			int num = list2.FindIndex((Summon s) => s.Species == slot.Kind);
			if (num >= 0)
			{
				list.Add(new KeyValuePair<Summon, Slot>(list2[num], slot));
				list2.RemoveAt(num);
			}
		}
		for (int num2 = 0; num2 < list2.Count && num2 < list3.Count; num2++)
		{
			list.Add(new KeyValuePair<Summon, Slot>(list2[num2], list3[num2]));
		}
		return list;
	}

	private static Vector3 Flat(Vector3 direction)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		direction.y = 0f;
		if (!(((Vector3)(ref direction)).sqrMagnitude < 0.001f))
		{
			return ((Vector3)(ref direction)).normalized;
		}
		return Vector3.forward;
	}
}
internal static class Picker
{
	public const string Random = "Random";

	private const string StaffItemName = "$item_staff_spiritcaller";

	private const string StaffPrefabName = "StaffSpiritCaller";

	private const string OtherPickerGuid = "algo7.pickyourspirit";

	private const string PickKey = "SpiritAnimism.Pick";

	internal static ConfigEntry<string> Order;

	internal static ConfigEntry<bool> SkipFull;

	private static bool _disabled;

	public static bool Enabled => !_disabled;

	public static void Init(ConfigFile config)
	{
		Order = config.Bind<string>("Pick", "Order", "Random, Bear, Moose, Boar, Wolf", "What the secondary attack cycles through with the Spirit Caller in hand, in this order. Random is the game's own roll. The current pick is remembered with your character.");
		SkipFull = config.Bind<bool>("Pick", "SkipFull", true, "Skip animals you already have at your staff's limit, both when cycling and in the Random roll, so a cast never sends your oldest one home. When every kind is full the game's own roll applies.");
		if (Chainloader.PluginInfos.ContainsKey("algo7.pickyourspirit"))
		{
			_disabled = true;
			Plugin.Log.LogWarning((object)"PickYourSpirit is loaded; SpiritAnimism's own picker is switched off to avoid fighting over the cast");
		}
	}

	public static bool IsSpiritCaller(ItemData item)
	{
		if (item == null)
		{
			return false;
		}
		if (item.m_shared != null && item.m_shared.m_name == "$item_staff_spiritcaller")
		{
			return true;
		}
		if ((Object)(object)item.m_dropPrefab != (Object)null)
		{
			return ((Object)item.m_dropPrefab).name == "StaffSpiritCaller";
		}
		return false;
	}

	public static SummonType CurrentType()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer != (Object)null) || !localPlayer.m_customData.TryGetValue("SpiritAnimism.Pick", out var value))
		{
			return null;
		}
		return Summons.Named(value);
	}

	public static void Cycle()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		List<string> list = Steps();
		if (list.Count == 0)
		{
			return;
		}
		SummonType summonType = CurrentType();
		string currentName = ((summonType != null) ? summonType.Name : "Random");
		int num = list.FindIndex((string step) => string.Equals(step, currentName, StringComparison.OrdinalIgnoreCase));
		for (int num2 = 1; num2 <= list.Count; num2++)
		{
			SummonType summonType2 = Summons.Named(list[(num + num2) % list.Count]);
			if (summonType2 == null || !SkipFull.Value || !IsAtCap(summonType2))
			{
				localPlayer.m_customData["SpiritAnimism.Pick"] = ((summonType2 != null) ? summonType2.Name : "Random");
				break;
			}
		}
		((Character)localPlayer).Message((MessageType)2, "Spirit: " + DisplayName(CurrentType()), 0, (Sprite)null, false);
	}

	public static string DisplayName(SummonType type)
	{
		if (type == null)
		{
			return "Random";
		}
		GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(type.WildPrefabName) : null);
		Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
		if ((Object)(object)val2 == (Object)null || string.IsNullOrEmpty(val2.m_name))
		{
			return type.Name;
		}
		return Localization.instance.Localize(val2.m_name);
	}

	public static bool IsAtCap(SummonType type)
	{
		int num = 0;
		int num2 = 0;
		foreach (Summon item in Summons.Mine())
		{
			if (item.Species == type)
			{
				num++;
				if (num2 == 0)
				{
					num2 = item.Zdo.GetInt(ZDOVars.s_maxInstances, 0);
				}
			}
		}
		if (num2 > 0)
		{
			return num >= num2;
		}
		return false;
	}

	public static GameObject[] WithRoom(GameObject[] spawnList)
	{
		List<GameObject> list = new List<GameObject>();
		foreach (GameObject val in spawnList)
		{
			if (!((Object)(object)val == (Object)null))
			{
				SummonType summonType = Summons.SpeciesOf(((Object)val).name);
				if (summonType == null || !IsAtCap(summonType))
				{
					list.Add(val);
				}
			}
		}
		if (list.Count <= 0 || list.Count >= spawnList.Length)
		{
			return spawnList;
		}
		return list.ToArray();
	}

	private static List<string> Steps()
	{
		List<string> list = new List<string>();
		string[] array = Order.Value.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length != 0)
			{
				if (!string.Equals(text, "Random", StringComparison.OrdinalIgnoreCase) && Summons.Named(text) == null)
				{
					Plugin.Log.LogWarning((object)("Pick order entry '" + text + "' is not Random, Bear, Moose, Boar or Wolf; ignored"));
				}
				else
				{
					list.Add(text);
				}
			}
		}
		return list;
	}
}
[HarmonyPatch(typeof(SpawnAbility), "Setup")]
internal static class SpawnAbilitySetupPatch
{
	private static void Prefix(SpawnAbility __instance, Character owner, ItemData item)
	{
		if (!Picker.Enabled || (Object)(object)owner == (Object)null || (Object)(object)owner != (Object)(object)Player.m_localPlayer || !Picker.IsSpiritCaller(item) || __instance.m_spawnPrefab == null)
		{
			return;
		}
		SummonType summonType = Picker.CurrentType();
		if (summonType == null)
		{
			if (Picker.SkipFull.Value)
			{
				__instance.m_spawnPrefab = Picker.WithRoom(__instance.m_spawnPrefab);
			}
			return;
		}
		GameObject[] spawnPrefab = __instance.m_spawnPrefab;
		foreach (GameObject val in spawnPrefab)
		{
			if (!((Object)(object)val == (Object)null) && !(((Object)val).name != summonType.PrefabName))
			{
				__instance.m_spawnPrefab = (GameObject[])(object)new GameObject[1] { val };
				return;
			}
		}
		Plugin.Log.LogWarning((object)(summonType.PrefabName + " is not in the staff's spawn list; this cast stays random"));
	}
}
[HarmonyPatch(typeof(Player), "SetControls")]
internal static class PlayerSetControlsPatch
{
	private static void Postfix(Player __instance, bool secondaryAttack)
	{
		if (secondaryAttack && Picker.Enabled && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Picker.IsSpiritCaller(((Humanoid)__instance).GetCurrentWeapon()))
		{
			Picker.Cycle();
		}
	}
}
[BepInPlugin("Brruid.SpiritAnimism", "SpiritAnimism", "0.5.0")]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "Brruid.SpiritAnimism";

	public const string Name = "SpiritAnimism";

	public const string Version = "0.5.0";

	internal static ManualLogSource Log;

	internal static ConfigFile Settings;

	internal static ConfigEntry<KeyboardShortcut> DismissKey;

	private const float ConfigPollSeconds = 1f;

	private float _nextConfigPoll;

	private DateTime _configLastWrite;

	private void Awake()
	{
		//IL_001b: 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)
		Log = ((BaseUnityPlugin)this).Logger;
		Settings = ((BaseUnityPlugin)this).Config;
		new Harmony("Brruid.SpiritAnimism").PatchAll();
		BindLooks();
		DismissKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Dismiss", "Key", new KeyboardShortcut((KeyCode)120, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Dismisses all spirit summons you called, with the vanilla despawn effect.");
		Saddles.Init(((BaseUnityPlugin)this).Config);
		Formations.Init(((BaseUnityPlugin)this).Config);
		Picker.Init(((BaseUnityPlugin)this).Config);
		SummonPanel.Init(((BaseUnityPlugin)this).Config);
		_configLastWrite = File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
		Log.LogInfo((object)"SpiritAnimism 0.5.0 loaded");
	}

	private void BindLooks()
	{
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Expected O, but got Unknown
		Color32 val = default(Color32);
		((Color32)(ref val))..ctor((byte)64, (byte)200, byte.MaxValue, byte.MaxValue);
		SummonType[] types = Summons.Types;
		foreach (SummonType summonType in types)
		{
			bool flag = summonType == Summons.Types[0];
			summonType.Look = ((BaseUnityPlugin)this).Config.Bind<Look>(summonType.Name, "Look", Look.Spirit, flag ? "Spirit = the vanilla translucent ghost in your tint. Natural = the wild animal's own opaque body with a glow in your tint. Every animal's section takes the same settings." : "");
			summonType.Tint = ((BaseUnityPlugin)this).Config.Bind<Color>(summonType.Name, "Tint", Color32.op_Implicit(val), flag ? "Body colour of the Spirit look, glow colour of the Natural look. Hex RRGGBBAA; alpha is ignored." : "");
			summonType.Glow = ((BaseUnityPlugin)this).Config.Bind<float>(summonType.Name, "Glow", 0.3f, new ConfigDescription(flag ? "Body glow strength of the Natural look. 0 = plain wild animal." : "", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			if (summonType.HasEyeMask)
			{
				summonType.EyeGlow = ((BaseUnityPlugin)this).Config.Bind<float>(summonType.Name, "EyeGlow", 0f, new ConfigDescription("Eye glow strength of the Natural look, drawn from the moose's own eye mask. Combine with Glow: body only, eyes only, or both at their own strengths.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			}
		}
	}

	private void Update()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		SummonLooks.Update();
		SummonPanel.Update();
		KeyboardShortcut value = DismissKey.Value;
		if (((KeyboardShortcut)(ref value)).IsDown() && Dismiss.InputIsFree())
		{
			Dismiss.MySummons();
		}
		Saddles.Update();
		Formations.Update();
		ReloadConfigIfEdited();
	}

	private void ReloadConfigIfEdited()
	{
		if (Time.unscaledTime < _nextConfigPoll)
		{
			return;
		}
		_nextConfigPoll = Time.unscaledTime + 1f;
		string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
		if (!File.Exists(configFilePath))
		{
			((BaseUnityPlugin)this).Config.Save();
			_configLastWrite = File.GetLastWriteTimeUtc(configFilePath);
			Log.LogInfo((object)"Config file recreated with the current settings");
		}
		else if (!(File.GetLastWriteTimeUtc(configFilePath) == _configLastWrite))
		{
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			catch (IOException ex)
			{
				Log.LogWarning((object)("Config not reloaded yet: " + ex.Message));
				return;
			}
			_configLastWrite = File.GetLastWriteTimeUtc(configFilePath);
			Log.LogInfo((object)"Config reloaded");
		}
	}
}
[HarmonyPatch(typeof(ConfigFile), "Save")]
internal static class ConfigFileSavePatch
{
	private static void Postfix(ConfigFile __instance)
	{
		if (__instance != Plugin.Settings)
		{
			return;
		}
		List<string> list = new List<string>();
		foreach (ConfigDefinition key in __instance.Keys)
		{
			if (!list.Contains(key.Section))
			{
				list.Add(key.Section);
			}
		}
		List<string> list2 = new List<string>();
		Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
		List<string> value = list2;
		string[] array = File.ReadAllLines(__instance.ConfigFilePath);
		foreach (string text in array)
		{
			string text2 = text.Trim();
			if (text2.StartsWith("[") && text2.EndsWith("]"))
			{
				string text3 = text2.Substring(1, text2.Length - 2);
				if (!dictionary.TryGetValue(text3, out value))
				{
					value = (dictionary[text3] = new List<string>());
				}
				if (!list.Contains(text3))
				{
					list.Add(text3);
				}
			}
			value.Add(text);
		}
		List<string> list4 = new List<string>(list2);
		foreach (string item in list)
		{
			if (dictionary.TryGetValue(item, out var value2))
			{
				list4.AddRange(value2);
			}
		}
		File.WriteAllLines(__instance.ConfigFilePath, list4, Utility.UTF8NoBom);
	}
}
internal static class Saddles
{
	private sealed class Pending
	{
		public long Id;

		public string PrefabName;

		public Vector3 Position;

		public Queue<string> Recipients;

		public float Deadline;
	}

	[HarmonyPatch(typeof(Tameable), "RPC_AddSaddle")]
	private static class AddSaddlePatch
	{
		private static void Prefix(Tameable __instance, ZNetView ___m_nview, out bool __state)
		{
			__state = ___m_nview.IsValid() && HasSaddle(__instance, ___m_nview);
		}

		private static void Postfix(Tameable __instance, ZNetView ___m_nview, long sender, bool __state)
		{
			if (!__state && ___m_nview.IsValid() && ___m_nview.IsOwner() && Summons.SpeciesOf(((Object)((Component)__instance).gameObject).name) != null && HasSaddle(__instance, ___m_nview))
			{
				string text = SenderName(sender);
				___m_nview.GetZDO().Set("SpiritAnimism.Fitter", text);
				Plugin.Log.LogInfo((object)("Saddle fitted by " + ((text == "") ? "an unknown player" : text)));
			}
		}
	}

	[HarmonyPatch(typeof(Tameable), "RPC_UnSummon")]
	private static class UnSummonPatch
	{
		private static void Prefix(Tameable __instance, ZNetView ___m_nview)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (ReturnSaddle.Value && !((Object)(object)___m_nview == (Object)null) && ___m_nview.IsValid() && ___m_nview.IsOwner() && Summons.SpeciesOf(((Object)((Component)__instance).gameObject).name) != null && HasSaddle(__instance, ___m_nview))
			{
				ZDO zDO = ___m_nview.GetZDO();
				Return(((Object)((Component)__instance.m_saddleItem).gameObject).name, ((Component)__instance).transform.position, zDO.GetString("SpiritAnimism.Fitter", ""), zDO.GetString(ZDOVars.s_follow, ""));
			}
		}
	}

	private const string FitterKey = "SpiritAnimism.Fitter";

	private const string TakeRpc = "SpiritAnimism_TakeSaddle";

	private const string TakenRpc = "SpiritAnimism_SaddleTaken";

	private const float ReplySeconds = 2f;

	internal static ConfigEntry<bool> ReturnSaddle;

	internal static ConfigEntry<bool> DismissWhileRiding;

	private static readonly List<Pending> _pending = new List<Pending>();

	private static ZRoutedRpc _registeredOn;

	private static long _nextId = 1L;

	public static void Init(ConfigFile config)
	{
		ReturnSaddle = config.Bind<bool>("Dismiss", "ReturnSaddle", true, "A saddle on a dismissed or unsummoned animal goes back to the player who fitted it, failing that to the summoner, failing that it drops where the animal stood. Off = the saddle is lost, as in the unmodded game.");
		DismissWhileRiding = config.Bind<bool>("Dismiss", "DismissWhileRiding", true, "Whether Dismiss also removes the animal you are riding. Off = your mount stays and the others go.");
	}

	public static void Update()
	{
		RegisterRpcs();
		if (_pending.Count == 0)
		{
			return;
		}
		for (int num = _pending.Count - 1; num >= 0; num--)
		{
			if (Time.unscaledTime >= _pending[num].Deadline)
			{
				Advance(_pending[num]);
			}
		}
	}

	private static void RegisterRpcs()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && instance != _registeredOn)
		{
			instance.Register<string, string, long>("SpiritAnimism_TakeSaddle", (Action<long, string, string, long>)OnTakeSaddle);
			instance.Register<long>("SpiritAnimism_SaddleTaken", (Action<long, long>)OnSaddleTaken);
			_registeredOn = instance;
			_pending.Clear();
		}
	}

	public static void Return(string prefabName, Vector3 position, string fitter, string summoner)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		Queue<string> queue = new Queue<string>();
		if (!string.IsNullOrEmpty(fitter))
		{
			queue.Enqueue(fitter);
		}
		if (!string.IsNullOrEmpty(summoner) && summoner != fitter)
		{
			queue.Enqueue(summoner);
		}
		Pending pending = new Pending
		{
			Id = _nextId++,
			PrefabName = prefabName,
			Position = position,
			Recipients = queue
		};
		_pending.Add(pending);
		Advance(pending);
	}

	private static void Advance(Pending pending)
	{
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		while (pending.Recipients.Count > 0)
		{
			string text = pending.Recipients.Dequeue();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && localPlayer.GetPlayerName() == text)
			{
				Take(localPlayer, pending.PrefabName);
				_pending.Remove(pending);
				return;
			}
			if (IsConnected(text))
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SpiritAnimism_TakeSaddle", new object[3] { text, pending.PrefabName, pending.Id });
				pending.Deadline = Time.unscaledTime + 2f;
				Plugin.Log.LogInfo((object)("Asked " + text + " to take a " + pending.PrefabName));
				return;
			}
		}
		Drop(pending.PrefabName, pending.Position);
		_pending.Remove(pending);
	}

	private static void OnTakeSaddle(long sender, string recipient, string prefabName, long id)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null) && !(localPlayer.GetPlayerName() != recipient))
		{
			Take(localPlayer, prefabName);
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "SpiritAnimism_SaddleTaken", new object[1] { id });
		}
	}

	private static void OnSaddleTaken(long sender, long id)
	{
		if (_pending.RemoveAll((Pending p) => p.Id == id) > 0)
		{
			Plugin.Log.LogInfo((object)"Saddle taken by its recipient");
		}
	}

	private static void Take(Player player, string prefabName)
	{
		//IL_0032: 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)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: 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)
		GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)("Unknown saddle prefab " + prefabName));
			return;
		}
		GameObject val = Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position + Vector3.up * 0.5f, Quaternion.identity);
		((Humanoid)player).Pickup(val, false, false);
	}

	private static void Drop(string prefabName, Vector3 position)
	{
		//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)
		//IL_0032: 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)
		//IL_0051: 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)
		GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)("Unknown saddle prefab " + prefabName));
			return;
		}
		Rigidbody component = Object.Instantiate<GameObject>(prefab, position + Vector3.up, Quaternion.identity).GetComponent<Rigidbody>();
		if ((Object)(object)component != (Object)null)
		{
			component.AddForce(Vector3.up * 5f, (ForceMode)2);
		}
		Plugin.Log.LogInfo((object)("Dropped a " + prefabName + " where the summon stood"));
	}

	private static bool IsConnected(string playerName)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
		{
			if (player.m_name == playerName)
			{
				return true;
			}
		}
		return false;
	}

	private static string SenderName(long sender)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		if (sender == ZNet.GetUID())
		{
			if (!((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return "";
			}
			return Player.m_localPlayer.GetPlayerName();
		}
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return "";
		}
		foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
		{
			ZDOID characterID = player.m_characterID;
			if (((ZDOID)(ref characterID)).UserID == sender)
			{
				return player.m_name;
			}
		}
		return "";
	}

	public static bool HasSaddle(Tameable tameable, ZNetView view)
	{
		if ((Object)(object)tameable.m_saddle != (Object)null && (Object)(object)tameable.m_saddleItem != (Object)null)
		{
			return view.GetZDO().GetBool(ZDOVars.s_haveSaddleHash, false);
		}
		return false;
	}

	public static bool IsRiding(Player player, Summon summon)
	{
		IDoodadController doodadController = player.GetDoodadController();
		Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		Tameable component = ((Component)summon.Character).GetComponent<Tameable>();
		if ((Object)(object)component != (Object)null)
		{
			return (Object)(object)component.m_saddle == (Object)(object)val;
		}
		return false;
	}
}
internal struct LookSpec
{
	public const float MaxGlow = 5f;

	private const string Version = "2";

	public Look Look;

	public Color Tint;

	public float Glow;

	public float EyeGlow;

	public static LookSpec FromConfig(SummonType type)
	{
		//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)
		return new LookSpec
		{
			Look = type.Look.Value,
			Tint = type.Tint.Value,
			Glow = type.Glow.Value,
			EyeGlow = ((type.EyeGlow != null) ? type.EyeGlow.Value : 0f)
		};
	}

	public string Serialize()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		return string.Format("{0};{1};{2};{3};{4}", "2", Look, ColorUtility.ToHtmlStringRGB(Tint), Strength(Glow), Strength(EyeGlow));
	}

	private static string Strength(float value)
	{
		return value.ToString("0.###", CultureInfo.InvariantCulture);
	}

	public static bool TryParse(string text, out LookSpec spec)
	{
		spec = default(LookSpec);
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		string[] array = text.Split(new char[1] { ';' });
		if (array.Length != 5 || array[0] != "2")
		{
			return false;
		}
		if (!Enum.TryParse<Look>(array[1], out spec.Look) || !Enum.IsDefined(typeof(Look), spec.Look))
		{
			return false;
		}
		if (!ColorUtility.TryParseHtmlString("#" + array[2], ref spec.Tint))
		{
			return false;
		}
		if (TryParseStrength(array[3], out spec.Glow))
		{
			return TryParseStrength(array[4], out spec.EyeGlow);
		}
		return false;
	}

	private static bool TryParseStrength(string text, out float value)
	{
		if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value))
		{
			return false;
		}
		if (float.IsNaN(value) || float.IsInfinity(value))
		{
			return false;
		}
		value = Mathf.Clamp(value, 0f, 5f);
		return true;
	}
}
internal static class SummonLooks
{
	internal static readonly int TintColorId = Shader.PropertyToID("_TintColor");

	internal static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor");

	internal static readonly int EmissionMapId = Shader.PropertyToID("_EmissionMap");

	private static readonly int MainTexId = Shader.PropertyToID("_MainTex");

	internal static readonly int LookKey = StringExtensionMethods.GetStableHashCode("SpiritAnimism.Look");

	private const float RefreshSeconds = 0.25f;

	private static float _nextRefresh;

	public static void Update()
	{
		if (Time.unscaledTime < _nextRefresh)
		{
			return;
		}
		_nextRefresh = Time.unscaledTime + 0.25f;
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		string playerName = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : null);
		foreach (Summon item in Summons.Standing())
		{
			if (!LookUpAssets(item.Species))
			{
				continue;
			}
			ZDO zdo = item.Zdo;
			string text = zdo.GetString(LookKey, "");
			LookSpec? spec;
			string text2;
			LookSpec spec2;
			if (item.CalledBy(playerName))
			{
				LookSpec value = LookSpec.FromConfig(item.Species);
				spec = value;
				text2 = value.Serialize();
				if (item.View.IsOwner() && text != text2)
				{
					zdo.Set(LookKey, text2);
				}
			}
			else if (LookSpec.TryParse(text, out spec2))
			{
				spec = spec2;
				text2 = text;
			}
			else
			{
				spec = null;
				text2 = "vanilla";
			}
			(((Component)item.Character).GetComponent<SummonLook>() ?? ((Component)item.Character).gameObject.AddComponent<SummonLook>()).Apply(item.Species, spec, text2);
		}
	}

	private static bool LookUpAssets(SummonType species)
	{
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		if (species.AssetsLookedUp)
		{
			return (Object)(object)species.SpiritMaterial != (Object)null;
		}
		species.AssetsLookedUp = true;
		GameObject prefab = ZNetScene.instance.GetPrefab(species.PrefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)("Prefab " + species.PrefabName + " not found"));
			return false;
		}
		Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
			foreach (Material val in sharedMaterials)
			{
				if (!((Object)(object)val == (Object)null) && val.HasProperty(TintColorId) && species.OwnsMaterial(val))
				{
					species.SpiritMaterial = val;
					species.VanillaTint = val.GetColor(TintColorId);
					break;
				}
			}
			if ((Object)(object)species.SpiritMaterial != (Object)null)
			{
				break;
			}
		}
		if ((Object)(object)species.SpiritMaterial == (Object)null)
		{
			Plugin.Log.LogWarning((object)("No spirit body material on " + species.PrefabName));
			return false;
		}
		GameObject prefab2 = ZNetScene.instance.GetPrefab(species.WildPrefabName);
		Texture texture = species.SpiritMaterial.GetTexture(MainTexId);
		string text = (((Object)(object)texture != (Object)null) ? ((Object)texture).name : null);
		if ((Object)(object)prefab2 != (Object)null)
		{
			componentsInChildren = prefab2.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if (!((Object)(object)val2 == (Object)null) && !val2.HasProperty(TintColorId) && val2.HasProperty(MainTexId))
					{
						Texture texture2 = val2.GetTexture(MainTexId);
						if (!((Object)(object)texture2 == (Object)null) && !(((Object)texture2).name != text))
						{
							species.WildMaterial = val2;
							species.VanillaEmissionMap = (val2.HasProperty(EmissionMapId) ? val2.GetTexture(EmissionMapId) : null);
							species.VanillaEmissionColor = val2.GetColor(EmissionColorId);
							break;
						}
					}
				}
				if ((Object)(object)species.WildMaterial != (Object)null)
				{
					break;
				}
			}
		}
		if ((Object)(object)species.WildMaterial == (Object)null)
		{
			Plugin.Log.LogWarning((object)("No wild body material for " + species.Name + "; its Natural look falls back to Spirit"));
		}
		Plugin.Log.LogInfo((object)(species.Name + ": spirit material " + ((Object)species.SpiritMaterial).name + ", wild material " + (((Object)(object)species.WildMaterial != (Object)null) ? ((Object)species.WildMaterial).name : "none")));
		return true;
	}

	public static Color MatchIntensity(Color tint, Color original)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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_0079: 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)
		float num = Mathf.Max(new float[3] { original.r, original.g, original.b });
		float num2 = Mathf.Max(new float[3] { tint.r, tint.g, tint.b });
		float num3 = ((num2 > 0f) ? (num / num2) : 1f);
		return new Color(tint.r * num3, tint.g * num3, tint.b * num3, original.a);
	}
}
internal sealed class SummonLook : MonoBehaviour
{
	private struct BodySlot
	{
		public Renderer Renderer;

		public int Index;

		public Material Vanilla;
	}

	public const string VanillaKey = "vanilla";

	private SummonType _species;

	private readonly List<BodySlot> _slots = new List<BodySlot>();

	private string _applied;

	private Material _spirit;

	private Material _natural;

	private Texture2D _mixedGlow;

	private float _mixedBody;

	private float _mixedEyes;

	public void Apply(SummonType species, LookSpec? spec, string key)
	{
		if (key == _applied)
		{
			return;
		}
		if (_species == null)
		{
			_species = species;
			FindBodySlots();
		}
		_applied = key;
		foreach (BodySlot slot in _slots)
		{
			if ((Object)(object)slot.Renderer == (Object)null)
			{
				continue;
			}
			Material[] sharedMaterials = slot.Renderer.sharedMaterials;
			if (slot.Index < sharedMaterials.Length)
			{
				Material val = (spec.HasValue ? Styled(spec.Value, slot.Vanilla) : slot.Vanilla);
				if (!((Object)(object)sharedMaterials[slot.Index] == (Object)(object)val))
				{
					sharedMaterials[slot.Index] = val;
					slot.Renderer.sharedMaterials = sharedMaterials;
				}
			}
		}
	}

	private void FindBodySlots()
	{
		string name = ((Object)_species.SpiritMaterial).name;
		Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			Material[] sharedMaterials = val.sharedMaterials;
			for (int j = 0; j < sharedMaterials.Length; j++)
			{
				if (!((Object)(object)sharedMaterials[j] == (Object)null) && ((Object)sharedMaterials[j]).name.StartsWith(name, StringComparison.Ordinal))
				{
					_slots.Add(new BodySlot
					{
						Renderer = val,
						Index = j,
						Vanilla = sharedMaterials[j]
					});
				}
			}
		}
		if (_slots.Count == 0)
		{
			Plugin.Log.LogWarning((object)(_species.Name + " summon " + ((Object)this).name + " has no body material slot; it stays vanilla"));
		}
	}

	private Material Styled(LookSpec spec, Material vanilla)
	{
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Expected O, but got Unknown
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		if (spec.Look == Look.Natural && (Object)(object)_species.WildMaterial != (Object)null)
		{
			if ((Object)(object)_natural == (Object)null)
			{
				_natural = new Material(_species.WildMaterial)
				{
					name = ((Object)_species.WildMaterial).name + "_spiritanimism"
				};
				_natural.EnableKeyword("_EMISSION");
			}
			float glow = spec.Glow;
			float num = (((Object)(object)_species.VanillaEmissionMap != (Object)null) ? spec.EyeGlow : 0f);
			if (glow <= 0f && num <= 0f)
			{
				_natural.SetTexture(SummonLooks.EmissionMapId, _species.VanillaEmissionMap);
				_natural.SetColor(SummonLooks.EmissionColorId, _species.VanillaEmissionColor);
			}
			else
			{
				float num2 = Mathf.Max(glow, num);
				_natural.SetTexture(SummonLooks.EmissionMapId, GlowMap(glow / num2, num / num2));
				_natural.SetColor(SummonLooks.EmissionColorId, new Color(spec.Tint.r, spec.Tint.g, spec.Tint.b, 1f) * num2);
			}
			return _natural;
		}
		if ((Object)(object)_spirit == (Object)null)
		{
			_spirit = new Material(vanilla)
			{
				name = ((Object)_species.SpiritMaterial).name + "_spiritanimism"
			};
		}
		_spirit.SetColor(SummonLooks.TintColorId, SummonLooks.MatchIntensity(spec.Tint, _species.VanillaTint));
		return _spirit;
	}

	private Texture GlowMap(float body, float eyes)
	{
		//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_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Expected O, but got Unknown
		if (eyes <= 0f)
		{
			return (Texture)(object)Texture2D.whiteTexture;
		}
		if (body <= 0f)
		{
			return _species.VanillaEmissionMap;
		}
		if ((Object)(object)_mixedGlow != (Object)null && body == _mixedBody && eyes == _mixedEyes)
		{
			return (Texture)(object)_mixedGlow;
		}
		Texture vanillaEmissionMap = _species.VanillaEmissionMap;
		if ((Object)(object)_species.ReadableEyeMask == (Object)null)
		{
			_species.ReadableEyeMask = Textures.ReadableCopy(vanillaEmissionMap);
		}
		Color32[] pixels = _species.ReadableEyeMask.GetPixels32();
		for (int i = 0; i < pixels.Length; i++)
		{
			float num = (float)Mathf.Max(new int[3]
			{
				pixels[i].r,
				pixels[i].g,
				pixels[i].b
			}) / 255f * eyes;
			byte b = (byte)Mathf.RoundToInt(255f * Mathf.Max(num, body));
			pixels[i] = new Color32(b, b, b, byte.MaxValue);
		}
		if ((Object)(object)_mixedGlow == (Object)null)
		{
			_mixedGlow = new Texture2D(vanillaEmissionMap.width, vanillaEmissionMap.height, (TextureFormat)4, true)
			{
				name = ((Object)vanillaEmissionMap).name + "_spiritanimism",
				wrapMode = vanillaEmissionMap.wrapMode,
				filterMode = vanillaEmissionMap.filterMode
			};
		}
		_mixedGlow.SetPixels32(pixels);
		_mixedGlow.Apply();
		_mixedBody = body;
		_mixedEyes = eyes;
		return (Texture)(object)_mixedGlow;
	}

	private void OnDestroy()
	{
		if ((Object)(object)_spirit != (Object)null)
		{
			Object.Destroy((Object)(object)_spirit);
		}
		if ((Object)(object)_natural != (Object)null)
		{
			Object.Destroy((Object)(object)_natural);
		}
		if ((Object)(object)_mixedGlow != (Object)null)
		{
			Object.Destroy((Object)(object)_mixedGlow);
		}
	}
}
internal static class Textures
{
	public static Texture2D ReadableCopy(Texture source)
	{
		//IL_0035: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		RenderTexture temporary = RenderTexture.GetTemporary(source.width, source.height, 0, (RenderTextureFormat)0);
		RenderTexture active = RenderTexture.active;
		try
		{
			Graphics.Blit(source, temporary);
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D(source.width, source.height, (TextureFormat)4, false);
			val.ReadPixels(new Rect(0f, 0f, (float)source.width, (float)source.height), 0, 0);
			val.Apply();
			return val;
		}
		finally
		{
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
		}
	}
}
internal static class SummonPanel
{
	private sealed class Row
	{
		public GameObject Root;

		public TMP_Text Text;

		public RectTransform Fill;
	}

	internal static ConfigEntry<bool> Show;

	internal static ConfigEntry<float> OffsetX;

	internal static ConfigEntry<float> OffsetY;

	internal static ConfigEntry<float> Scale;

	private const float RefreshSeconds = 0.25f;

	private const float RowHeight = 32f;

	private const float RowIndent = 32f;

	private const float BarWidth = 160f;

	private const float BarHeight = 6f;

	private const string PreferredFont = "Valheim-AveriaSansLibre";

	private static readonly Color HeaderColor = new Color(0.94f, 0.76f, 0.35f);

	private static readonly Color NameColor = new Color(0.9f, 0.9f, 0.9f);

	private static readonly Color ActivityColor = new Color(0.65f, 0.65f, 0.65f);

	private static readonly Color BarBackground = new Color(0f, 0f, 0f, 0.5f);

	private static readonly Color BarFill = new Color(0.4f, 0.8f, 0.25f);

	private static GameObject _root;

	private static TMP_Text _header;

	private static TMP_FontAsset _font;

	private static Material _fontMaterial;

	private static bool _fontMissing;

	private static readonly List<Row> Rows = new List<Row>();

	private static float _nextRefresh;

	public static void Init(ConfigFile config)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		Show = config.Bind<bool>("Panel", "Show", true, "Show the list of your standing summons at the top left.");
		OffsetX = config.Bind<float>("Panel", "OffsetX", 20f, "Distance from the left edge of the screen.");
		OffsetY = config.Bind<float>("Panel", "OffsetY", 220f, "Distance from the top edge of the screen.");
		Scale = config.Bind<float>("Panel", "Scale", 1f, new ConfigDescription("Size of the panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
	}

	public static void Update()
	{
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: 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_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		if (Time.unscaledTime < _nextRefresh)
		{
			return;
		}
		_nextRefresh = Time.unscaledTime + 0.25f;
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)Hud.instance == (Object)null || (Object)(object)localPlayer == (Object)null)
		{
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
		}
		else
		{
			if ((Object)(object)_root == (Object)null && !Build())
			{
				return;
			}
			List<Summon> list = new List<Summon>(Summons.Mine());
			bool flag = Show.Value && Hud.instance.IsVisible() && (list.Count > 0 || Picker.IsSpiritCaller(((Humanoid)localPlayer).GetCurrentWeapon()));
			_root.SetActive(flag);
			if (!flag)
			{
				return;
			}
			RectTransform val = (RectTransform)_root.transform;
			val.anchoredPosition = new Vector2(OffsetX.Value, 0f - OffsetY.Value);
			((Transform)val).localScale = Vector3.one * Scale.Value;
			_header.text = $"SPIRITS [{list.Count}]  <color=#{ColorUtility.ToHtmlStringRGB(HeaderColor)}>> {Picker.DisplayName(Picker.CurrentType())}</color>";
			while (Rows.Count < list.Count)
			{
				Rows.Add(BuildRow(Rows.Count));
			}
			for (int i = 0; i < Rows.Count; i++)
			{
				Row row = Rows[i];
				if (i >= list.Count)
				{
					row.Root.SetActive(false);
					continue;
				}
				Summon summon = list[i];
				row.Root.SetActive(true);
				row.Text.text = Picker.DisplayName(summon.Species) + "   <color=#" + ColorUtility.ToHtmlStringRGB(ActivityColor) + ">" + Activity(summon.Character) + "</color>";
				row.Fill.sizeDelta = new Vector2(160f * Mathf.Clamp01(summon.Character.GetHealthPercentage()), 6f);
			}
		}
	}

	private static string Activity(Character character)
	{
		MonsterAI component = ((Component)character).GetComponent<MonsterAI>();
		if ((Object)(object)component == (Object)null)
		{
			return "";
		}
		if ((Object)(object)((BaseAI)component).GetTargetCreature() != (Object)null)
		{
			return "Fighting";
		}
		if (((BaseAI)component).IsAlerted())
		{
			return "Alert";
		}
		if ((Object)(object)component.GetFollowTarget() != (Object)null)
		{
			return "Following";
		}
		return "Idle";
	}

	private static bool Build()
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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)
		//IL_0085: 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_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		if (_fontMissing || !PickFont())
		{
			return false;
		}
		Rows.Clear();
		_root = new GameObject("SpiritAnimismPanel", new Type[1] { typeof(RectTransform) });
		_root.transform.SetParent(Hud.instance.m_rootObject.transform, false);
		RectTransform val = (RectTransform)_root.transform;
		Vector2 val2 = default(Vector2);
		((Vector2)(ref val2))..ctor(0f, 1f);
		val.pivot = val2;
		Vector2 anchorMin = (val.anchorMax = val2);
		val.anchorMin = anchorMin;
		val.sizeDelta = new Vector2(160f, 32f);
		_header = MakeText(_root.transform, "Header", 14f, NameColor, (FontStyles)1);
		((RectTransform)_header.transform).anchoredPosition = Vector2.zero;
		return true;
	}

	private static bool PickFont()
	{
		TMP_FontAsset val = null;
		TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
		foreach (TMP_FontAsset val2 in array)
		{
			if (!(((Object)val2).name != "Valheim-AveriaSansLibre"))
			{
				val = val2;
				break;
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			val = TMP_Settings.defaultFontAsset;
		}
		if ((Object)(object)val == (Object)null)
		{
			TMP_Text componentInChildren = Hud.instance.m_rootObject.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				val = componentInChildren.font;
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			_fontMissing = true;
			Plugin.Log.LogWarning((object)"No font found for the summon panel; it stays off this session");
			return false;
		}
		_font = val;
		_fontMaterial = ((TMP_Asset)val).material;
		Plugin.Log.LogInfo((object)("Summon panel font: " + ((Object)val).name));
		return true;
	}

	private static Row BuildRow(int index)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		//IL_005b: 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_0061: 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_0074: 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_007b: 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)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: 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)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		Row row = new Row();
		row.Root = new GameObject($"Row{index}", new Type[1] { typeof(RectTransform) });
		row.Root.transform.SetParent(_root.transform, false);
		RectTransform val = (RectTransform)row.Root.transform;
		Vector2 val2 = default(Vector2);
		((Vector2)(ref val2))..ctor(0f, 1f);
		val.pivot = val2;
		Vector2 anchorMin = (val.anchorMax = val2);
		val.anchorMin = anchorMin;
		val.anchoredPosition = new Vector2(32f, -32f * ((float)index + 0.7f));
		val.sizeDelta = new Vector2(160f, 32f);
		row.Text = MakeText(row.Root.transform, "Text", 13f, NameColor, (FontStyles)0);
		((RectTransform)row.Text.transform).anchoredPosition = Vector2.zero;
		RectTransform val4 = MakeImage(row.Root.transform, "Bar", BarBackground, new Vector2(160f, 6f));
		val4.anchoredPosition = new Vector2(0f, -20f);
		row.Fill = MakeImage((Transform)(object)val4, "Fill", BarFill, new Vector2(160f, 6f));
		row.Fill.anchoredPosition = Vector2.zero;
		return row;
	}

	private static TMP_Text MakeText(Transform parent, string name, float size, Color color, FontStyles style)
	{
		//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_0026: 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_0031: 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_0033: 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_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
		val.transform.SetParent(parent, false);
		RectTransform val2 = (RectTransform)val.transform;
		Vector2 val3 = default(Vector2);
		((Vector2)(ref val3))..ctor(0f, 1f);
		val2.pivot = val3;
		Vector2 anchorMin = (val2.anchorMax = val3);
		val2.anchorMin = anchorMin;
		val2.sizeDelta = new Vector2(400f, 20f);
		TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
		((TMP_Text)obj).font = _font;
		((TMP_Text)obj).fontSharedMaterial = _fontMaterial;
		((TMP_Text)obj).fontSize = size;
		((Graphic)obj).color = color;
		((TMP_Text)obj).fontStyle = style;
		((TMP_Text)obj).richText = true;
		((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
		((Graphic)obj).raycastTarget = false;
		return (TMP_Text)(object)obj;
	}

	private static RectTransform MakeImage(Transform parent, string name, Color color, Vector2 size)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_002d: 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_0033: 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_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: 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)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: 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;
		Vector2 val3 = default(Vector2);
		((Vector2)(ref val3))..ctor(0f, 1f);
		val2.pivot = val3;
		Vector2 anchorMin = (val2.anchorMax = val3);
		val2.anchorMin = anchorMin;
		val2.sizeDelta = size;
		Image obj = val.AddComponent<Image>();
		((Graphic)obj).color = color;
		((Graphic)obj).raycastTarget = false;
		return val2;
	}
}
internal enum Look
{
	Spirit,
	Natural
}
internal sealed class SummonType
{
	public readonly string Name;

	public readonly string PrefabName;

	public readonly string WildPrefabName;

	public readonly bool HasEyeMask;

	public ConfigEntry<Look> Look;

	public ConfigEntry<Color> Tint;

	public ConfigEntry<float> Glow;

	public ConfigEntry<float> EyeGlow;

	public Material SpiritMaterial;

	public Color VanillaTint;

	public Material WildMaterial;

	public Texture VanillaEmissionMap;

	public Color VanillaEmissionColor;

	public Texture2D ReadableEyeMask;

	public bool AssetsLookedUp;

	public SummonType(string name, string wildPrefabName, bool hasEyeMask = false)
	{
		Name = name;
		WildPrefabName = wildPrefabName;
		PrefabName = wildPrefabName + "_spiritcaller";
		HasEyeMask = hasEyeMask;
	}

	public bool OwnsMaterial(Material material)
	{
		return ((Object)material).name.StartsWith(WildPrefabName + "_", StringComparison.OrdinalIgnoreCase);
	}
}
internal readonly struct Summon
{
	public readonly Character Character;

	public readonly SummonType Species;

	public readonly ZNetView View;

	public ZDO Zdo => View.GetZDO();

	public Summon(Character character, SummonType species, ZNetView view)
	{
		Character = character;
		Species = species;
		View = view;
	}

	public bool CalledBy(string playerName)
	{
		return Zdo.GetString(ZDOVars.s_follow, "") == playerName;
	}
}
internal static class Summons
{
	internal static readonly SummonType[] Types = new SummonType[4]
	{
		new SummonType("Bear", "Bjorn"),
		new SummonType("Boar", "Boar"),
		new SummonType("Moose", "Moose", hasEyeMask: true),
		new SummonType("Wolf", "Wolf")
	};

	public static SummonType SpeciesOf(string objectName)
	{
		SummonType[] types = Types;
		foreach (SummonType summonType in types)
		{
			if (objectName.StartsWith(summonType.PrefabName, StringComparison.Ordinal))
			{
				return summonType;
			}
		}
		return null;
	}

	public static SummonType Named(string name)
	{
		SummonType[] types = Types;
		foreach (SummonType summonType in types)
		{
			if (string.Equals(summonType.Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return summonType;
			}
		}
		return null;
	}

	public static IEnumerable<Summon> Standing()
	{
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if ((Object)(object)allCharacter == (Object)null)
			{
				continue;
			}
			SummonType summonType = SpeciesOf(((Object)((Component)allCharacter).gameObject).name);
			if (summonType != null)
			{
				ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					yield return new Summon(allCharacter, summonType, component);
				}
			}
		}
	}

	public static IEnumerable<Summon> Mine()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			yield break;
		}
		string playerName = localPlayer.GetPlayerName();
		foreach (Summon item in Standing())
		{
			if (item.CalledBy(playerName))
			{
				yield return item;
			}
		}
	}
}