Decompiled source of CompanionTraining v0.6.7

BepInEx/plugins/Goblinheim - Companion/CompanionTraining.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GoblinHeim.CoreStorage;
using GoblinHeim.ValheimCore;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CompanionTraining")]
[assembly: AssemblyDescription("Persistent trained companions for Valheim")]
[assembly: AssemblyCompany("GoblinHeim")]
[assembly: AssemblyProduct("CompanionTraining")]
[assembly: AssemblyFileVersion("0.6.7.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.6.7.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 GoblinHeim.CompanionTraining
{
	internal static class CharmBinding
	{
		private sealed class PendingName : TextReceiver
		{
			internal readonly Player Player;

			internal readonly Inventory Inventory;

			internal readonly ItemData Item;

			internal readonly Character Target;

			internal readonly CompanionTrainingPlugin.Species Species;

			internal PendingName(Player p, Inventory i, ItemData item, Character target, CompanionTrainingPlugin.Species species)
			{
				Player = p;
				Inventory = i;
				Item = item;
				Target = target;
				Species = species;
			}

			public string GetText()
			{
				return "";
			}

			public void SetText(string text)
			{
				Commit(this, text);
			}
		}

		private sealed class ColliderShape
		{
			internal CapsuleCollider Collider;

			internal float Radius;

			internal float Height;

			internal Vector3 Center;
		}

		private const float Range = 5f;

		internal const string AlphaKey = "GoblinHeim.Companion.Alpha";

		internal const string OwnerKey = "GoblinHeim.Companion.Owner";

		internal const string SpeciesKey = "GoblinHeim.Companion.Species";

		internal const string RetiredKey = "GoblinHeim.Companion.Retired";

		private const string AlphaScaleAppliedKey = "GoblinHeim.Companion.AlphaScaleApplied";

		private const string WoundedKey = "GoblinHeim.Companion.Wounded";

		private const string HealKey = "GoblinHeim.Companion.Healing";

		private const string FeedUntilKey = "GoblinHeim.Companion.FeedUntil";

		private const string FeedCooldownKey = "GoblinHeim.Companion.FeedCooldown";

		private const string WasFollowKey = "GoblinHeim.Companion.WasFollow";

		private const string AttackSkillKey = "GoblinHeim.Companion.AttackSkill";

		private const string RobustSkillKey = "GoblinHeim.Companion.RobustSkill";

		private static readonly Dictionary<Character, float> NextRegen = new Dictionary<Character, float>();

		private static readonly Dictionary<Character, float> LastCombat = new Dictionary<Character, float>();

		private static readonly Dictionary<Character, Vector2> SavedSpeeds = new Dictionary<Character, Vector2>();

		private static readonly Dictionary<Character, Vector2> NamingSpeeds = new Dictionary<Character, Vector2>();

		private static readonly Dictionary<Character, float> SavedFleeThresholds = new Dictionary<Character, float>();

		private static readonly Dictionary<Character, Vector3> OriginalScales = new Dictionary<Character, Vector3>();

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

		private static PendingName _pending;

		private static readonly Dictionary<ZDOID, PinData> PersonalPins = new Dictionary<ZDOID, PinData>();

		private static readonly Dictionary<Character, ColliderShape> OriginalColliders = new Dictionary<Character, ColliderShape>();

		internal static bool HasPendingName => _pending != null;

		internal static bool CanBecomeCompanion(Character c)
		{
			if ((Object)(object)c == (Object)null || c.IsDead() || !c.IsTamed() || c.GetLevel() != 3 || IsAlpha(c))
			{
				return false;
			}
			return CompanionTrainingPlugin.GetSpecies((Component)(object)c) != CompanionTrainingPlugin.Species.None;
		}

		internal static void BeginDamage(Character victim, HitData hit)
		{
			if ((Object)(object)victim == (Object)null || hit == null)
			{
				return;
			}
			if (IsAlpha(victim))
			{
				LastCombat[victim] = Time.time;
			}
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker != (Object)null && IsAlpha(attacker))
			{
				LastCombat[attacker] = Time.time;
			}
			if (IsAlpha(victim))
			{
				Character attacker2 = hit.GetAttacker();
				Character obj = ((attacker2 is Player) ? attacker2 : null);
				ItemData val = ((obj != null) ? ((Humanoid)obj).GetCurrentWeapon() : null);
				string obj2 = (((Object)(object)val?.m_dropPrefab == (Object)null) ? "" : ((Object)val.m_dropPrefab).name);
				string text = val?.m_shared?.m_name ?? "";
				if (obj2.IndexOf("Butcher", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("butcher", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("boucher", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					ButcherKills.Add(victim);
				}
			}
		}

		internal static void EndDamage(Character victim)
		{
			if ((Object)(object)victim != (Object)null)
			{
				ButcherKills.Remove(victim);
			}
		}

		internal static bool IsButcherKill(Character victim)
		{
			if ((Object)(object)victim != (Object)null)
			{
				return ButcherKills.Contains(victim);
			}
			return false;
		}

		internal static bool IsAlpha(Character c)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if ((Object)c == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if ((Object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				return zDO.GetBool("GoblinHeim.Companion.Alpha", false);
			}
			return false;
		}

		internal static bool IsTrained(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
			if (val != null)
			{
				if (!val.GetBool("GoblinHeim.Companion.Alpha", false))
				{
					return val.GetBool("GoblinHeim.Companion.Retired", false);
				}
				return true;
			}
			return false;
		}

		internal static float Skill(Character c, string key)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			ZNetView val = (((Object)(object)c != (Object)null) ? ((Component)c).GetComponent<ZNetView>() : null);
			ZDO val2 = (((Object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 != null)
			{
				return Mathf.Clamp(val2.GetFloat(key, 0f), 0f, 100f);
			}
			return 0f;
		}

		internal static float CompanionDamage(Character c)
		{
			float num = 1f + Skill(c, "GoblinHeim.Companion.AttackSkill") * 0.01f;
			return CompanionTrainingPlugin.GetSpecies((Component)(object)c) switch
			{
				CompanionTrainingPlugin.Species.Wolf => 200f * num, 
				CompanionTrainingPlugin.Species.Lox => 300f * num, 
				CompanionTrainingPlugin.Species.Asksvin => 220f * num, 
				CompanionTrainingPlugin.Species.Moose => 180f * num, 
				_ => 32f * num, 
			};
		}

		internal static float CompanionMaxHealth(Character c)
		{
			float num = Skill(c, "GoblinHeim.Companion.RobustSkill");
			return CompanionTrainingPlugin.GetSpecies((Component)(object)c) switch
			{
				CompanionTrainingPlugin.Species.Wolf => 300f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Lox => 3500f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Asksvin => 1800f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Moose => 1200f * (1f + num * 0.01f), 
				_ => 60f * (1f + num * 0.01f), 
			};
		}

		internal static float CompanionArmor(Character c)
		{
			float num = Skill(c, "GoblinHeim.Companion.RobustSkill");
			return CompanionTrainingPlugin.GetSpecies((Component)(object)c) switch
			{
				CompanionTrainingPlugin.Species.Lox => 40f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Wolf => 10f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Asksvin => 30f * (1f + num * 0.01f), 
				CompanionTrainingPlugin.Species.Moose => 25f * (1f + num * 0.01f), 
				_ => 10f * (1f + num * 0.01f), 
			};
		}

		internal static void AddAttackXp(Character c, float xp)
		{
			AddSkill(c, "GoblinHeim.Companion.AttackSkill", xp, "Attack");
		}

		internal static void AddRobustXp(Character c, float xp)
		{
			AddSkill(c, "GoblinHeim.Companion.RobustSkill", xp, "Robustness");
		}

		private static void AddSkill(Character c, string key, float xp, string label)
		{
			if (!IsAlpha(c))
			{
				return;
			}
			ZDO zDO = ((Component)c).GetComponent<ZNetView>().GetZDO();
			if (zDO != null)
			{
				float num = Mathf.Clamp(zDO.GetFloat(key, 0f), 0f, 100f);
				float num2 = Mathf.Clamp(num + Mathf.Max(0f, xp), 0f, 100f);
				if (num2 != num)
				{
					zDO.Set(key, num2);
					CompanionTrainingPlugin.Log.LogInfo((object)$"[COMPANION-SKILL] {label} {num:0.##}->{num2:0.##}");
				}
			}
		}

		internal static bool AreEnemies(Character a, Character b)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if ((Object)a == (Object)null || (Object)b == (Object)null)
			{
				return false;
			}
			return BaseAI.IsEnemy(a, b);
		}

		internal static bool IsWounded(Character c)
		{
			if (!IsAlpha(c))
			{
				return false;
			}
			ZDO zDO = ((Component)c).GetComponent<ZNetView>().GetZDO();
			if (zDO != null)
			{
				return zDO.GetBool("GoblinHeim.Companion.Wounded", false);
			}
			return false;
		}

		internal static void EnterWounded(Character c)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			if (!IsAlpha(c))
			{
				return;
			}
			ZDO zDO = ((Component)c).GetComponent<ZNetView>().GetZDO();
			if (zDO == null || zDO.GetBool("GoblinHeim.Companion.Wounded", false))
			{
				return;
			}
			zDO.Set("GoblinHeim.Companion.Wounded", true);
			zDO.Set("GoblinHeim.Companion.Healing", false);
			MonsterAI component = ((Component)c).GetComponent<MonsterAI>();
			bool flag = (Object)component != (Object)null && (Object)component.GetFollowTarget() != (Object)null;
			if ((Object)(object)component != (Object)null && !SavedFleeThresholds.ContainsKey(c))
			{
				SavedFleeThresholds[c] = component.m_fleeIfLowHealth;
				component.m_fleeIfLowHealth = 1f;
			}
			zDO.Set("GoblinHeim.Companion.WasFollow", flag);
			TryStagger(c);
			SetHalfSpeed(c, half: true);
			BaseAI component2 = ((Component)c).GetComponent<BaseAI>();
			if ((Object)component2 != (Object)null)
			{
				MethodInfo methodInfo = AccessTools.Method(((object)component2).GetType(), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null);
				if (methodInfo != null)
				{
					try
					{
						methodInfo.Invoke(component2, new object[1] { true });
					}
					catch
					{
					}
				}
			}
			CompanionTrainingPlugin.Log.LogInfo((object)("[COMPANION] WOUNDED: " + Utils.GetPrefabName(((Component)c).gameObject) + " intercepted at 1 HP; skills preserved, vanilla flee enabled, speed 50%, previous=" + (flag ? "Follow" : "Stay")));
		}

		private static void TryStagger(Character c)
		{
			//IL_0070: 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)
			try
			{
				MethodInfo methodInfo = AccessTools.Method(((object)c).GetType(), "Stagger", new Type[1] { typeof(Vector3) }, (Type[])null);
				if (methodInfo == null)
				{
					methodInfo = AccessTools.Method(typeof(Character), "Stagger", new Type[1] { typeof(Vector3) }, (Type[])null);
				}
				if (methodInfo != null)
				{
					methodInfo.Invoke(c, new object[1] { -((Component)c).transform.forward });
				}
			}
			catch
			{
			}
		}

		private static void SetHalfSpeed(Character c, bool half)
		{
			//IL_00eb: 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)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo fieldInfo = AccessTools.Field(((object)c).GetType(), "m_runSpeed") ?? AccessTools.Field(typeof(Character), "m_runSpeed");
			FieldInfo fieldInfo2 = AccessTools.Field(((object)c).GetType(), "m_walkSpeed") ?? AccessTools.Field(typeof(Character), "m_walkSpeed");
			if (fieldInfo == null || fieldInfo2 == null)
			{
				return;
			}
			Vector2 value;
			if (half)
			{
				if (!SavedSpeeds.ContainsKey(c))
				{
					SavedSpeeds[c] = new Vector2((float)fieldInfo2.GetValue(c), (float)fieldInfo.GetValue(c));
				}
				Vector2 val = SavedSpeeds[c];
				fieldInfo2.SetValue(c, val.x * 0.5f);
				fieldInfo.SetValue(c, val.y * 0.5f);
			}
			else if (SavedSpeeds.TryGetValue(c, out value))
			{
				fieldInfo2.SetValue(c, value.x);
				fieldInfo.SetValue(c, value.y);
				SavedSpeeds.Remove(c);
			}
		}

		private static void FreezeForNaming(Character c, bool frozen)
		{
			//IL_00db: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c == (Object)null)
			{
				return;
			}
			FieldInfo fieldInfo = AccessTools.Field(((object)c).GetType(), "m_runSpeed") ?? AccessTools.Field(typeof(Character), "m_runSpeed");
			FieldInfo fieldInfo2 = AccessTools.Field(((object)c).GetType(), "m_walkSpeed") ?? AccessTools.Field(typeof(Character), "m_walkSpeed");
			if (fieldInfo == null || fieldInfo2 == null)
			{
				return;
			}
			Vector2 value;
			if (frozen)
			{
				if (!NamingSpeeds.ContainsKey(c))
				{
					NamingSpeeds[c] = new Vector2((float)fieldInfo2.GetValue(c), (float)fieldInfo.GetValue(c));
				}
				fieldInfo2.SetValue(c, 0f);
				fieldInfo.SetValue(c, 0f);
			}
			else if (NamingSpeeds.TryGetValue(c, out value))
			{
				fieldInfo2.SetValue(c, value.x);
				fieldInfo.SetValue(c, value.y);
				NamingSpeeds.Remove(c);
			}
		}

		internal static void Feed(Character c, Player player)
		{
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Expected O, but got Unknown
			if (!IsAlpha(c) || c.IsDead() || (Object)(object)player == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
			if (val == null)
			{
				return;
			}
			long ticks = DateTime.UtcNow.Ticks;
			long num = val.GetLong("GoblinHeim.Companion.FeedCooldown", 0L);
			if (ticks < num)
			{
				((Character)player).Message((MessageType)2, "Ce compagnon pourra être nourri dans " + Mathf.CeilToInt((float)(num - ticks) / 10000000f) + " s.", 0, (Sprite)null, false);
				return;
			}
			if (c.GetHealth() >= CompanionMaxHealth(c) - 0.01f)
			{
				((Character)player).Message((MessageType)2, "Ce compagnon est déjà en pleine santé.", 0, (Sprite)null, false);
				return;
			}
			MonsterAI ai = ((Component)c).GetComponent<MonsterAI>();
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData val2 = ((inventory != null) ? ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => (Object)(object)ai != (Object)null && ai.m_consumeItems != null && ai.m_consumeItems.Any((ItemDrop accepted) => (Object)(object)accepted != (Object)null && item != null && item.m_shared != null && (accepted.m_itemData.m_shared.m_name == item.m_shared.m_name || ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)((Component)accepted).gameObject).name == ((Object)item.m_dropPrefab).name))))) : null);
			if (val2 == null && (Object)(object)ai != (Object)null && ai.m_consumeItems != null)
			{
				float num2 = 0f;
				if (StoragePolicy.CraftFromContainers)
				{
					num2 = Mathf.Max(num2, StoragePolicy.CraftRange);
				}
				if (StoragePolicy.BuildFromContainers)
				{
					num2 = Mathf.Max(num2, StoragePolicy.BuildRange);
				}
				foreach (ItemDrop consumeItem in ai.m_consumeItems)
				{
					string acceptedName = consumeItem?.m_itemData?.m_shared?.m_name;
					if (string.IsNullOrEmpty(acceptedName) || num2 <= 0f)
					{
						continue;
					}
					List<ResourceCost> list = new List<ResourceCost>
					{
						new ResourceCost
						{
							Name = acceptedName,
							Amount = 1
						}
					};
					if (ResourceLedger.Prepare(player, list, num2, true))
					{
						val2 = ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => item?.m_shared?.m_name == acceptedName));
						if (val2 != null)
						{
							break;
						}
					}
				}
			}
			if (val2 == null)
			{
				((Character)player).Message((MessageType)2, "Vous n'avez aucune nourriture compatible pour soigner ce compagnon.", 0, (Sprite)null, false);
				return;
			}
			inventory.RemoveOneItem(val2);
			val.Set("GoblinHeim.Companion.FeedUntil", ticks + 50000000);
			val.Set("GoblinHeim.Companion.FeedCooldown", ticks + 600000000);
			NextRegen.Remove(c);
			CompanionTrainingPlugin.Log.LogInfo((object)$"[COMPANION] FED: hp={c.GetHealth():0.##}/{CompanionMaxHealth(c):0.##}; +10% max HP/s for 5 seconds.");
		}

		internal static void UpdateCompanion(Character c)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			if (!IsAlpha(c) || c.IsDead())
			{
				return;
			}
			float num = CompanionMaxHealth(c);
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if ((Object)component == (Object)null || !component.IsValid() || !component.IsOwner())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				return;
			}
			if (zDO.GetBool("GoblinHeim.Companion.Wounded", false) || zDO.GetBool("GoblinHeim.Companion.Healing", false))
			{
				zDO.Set("GoblinHeim.Companion.Wounded", false);
				zDO.Set("GoblinHeim.Companion.Healing", false);
				SetHalfSpeed(c, half: false);
			}
			if (!(c.GetHealth() < num - 0.01f))
			{
				return;
			}
			float time = Time.time;
			if (DateTime.UtcNow.Ticks < zDO.GetLong("GoblinHeim.Companion.FeedUntil", 0L))
			{
				if (!NextRegen.TryGetValue(c, out var value) || time >= value)
				{
					NextRegen[c] = time + 1f;
					c.Heal(Mathf.Max(0.1f, num * 0.1f), true);
				}
				return;
			}
			BaseAI baseAI = c.GetBaseAI();
			float value2;
			float value3;
			if ((Object)(object)baseAI != (Object)null && (baseAI.IsAlerted() || (Object)(object)baseAI.GetTargetCreature() != (Object)null))
			{
				LastCombat[c] = time;
			}
			else if (!LastCombat.TryGetValue(c, out value2))
			{
				LastCombat[c] = time;
			}
			else if (!(time - value2 < 60f) && (!NextRegen.TryGetValue(c, out value3) || !(time < value3)))
			{
				NextRegen[c] = time + 1f;
				c.Heal(Mathf.Max(0.1f, num * CompanionTrainingPlugin.CompanionRegen.Value / 100f), true);
			}
		}

		private static void FinishWounded(Character c)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO val = (((Object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
			if (val == null)
			{
				return;
			}
			bool flag = val.GetBool("GoblinHeim.Companion.WasFollow", false);
			val.Set("GoblinHeim.Companion.Wounded", false);
			val.Set("GoblinHeim.Companion.Healing", false);
			SetHalfSpeed(c, half: false);
			NextRegen.Remove(c);
			BaseAI component2 = ((Component)c).GetComponent<BaseAI>();
			if ((Object)component2 != (Object)null)
			{
				MethodInfo methodInfo = AccessTools.Method(((object)component2).GetType(), "SetAlerted", new Type[1] { typeof(bool) }, (Type[])null);
				if (methodInfo != null)
				{
					try
					{
						methodInfo.Invoke(component2, new object[1] { false });
					}
					catch
					{
					}
				}
			}
			if (flag)
			{
				Player val2 = FindOwner(val.GetLong("GoblinHeim.Companion.Owner", 0L));
				if ((Object)val2 != (Object)null)
				{
					MonsterAI component3 = ((Component)c).GetComponent<MonsterAI>();
					if ((Object)component3 != (Object)null)
					{
						component3.SetFollowTarget(((Component)val2).gameObject);
						((BaseAI)component3).ResetPatrolPoint();
					}
				}
			}
			MonsterAI component4 = ((Component)c).GetComponent<MonsterAI>();
			if ((Object)(object)component4 != (Object)null && SavedFleeThresholds.TryGetValue(c, out var value))
			{
				component4.m_fleeIfLowHealth = value;
				SavedFleeThresholds.Remove(c);
			}
			CompanionTrainingPlugin.Log.LogInfo((object)("[COMPANION] RECOVERED 100%: resumes " + (flag ? "Follow" : "Stay") + "."));
		}

		private static Player FindOwner(long id)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				Player val = (Player)((allCharacter is Player) ? allCharacter : null);
				if ((Object)(object)val != (Object)null && val.GetPlayerID() == id)
				{
					return val;
				}
			}
			return null;
		}

		private static void TryCommand(Tameable tame, Humanoid user, bool desiredFollow)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if ((Object)tame == (Object)null || (Object)user == (Object)null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(Tameable), "m_follow");
				if (fieldInfo != null && fieldInfo.FieldType == typeof(bool) && (bool)fieldInfo.GetValue(tame) == desiredFollow)
				{
					return;
				}
				MethodInfo methodInfo = typeof(Tameable).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo x) => x.Name == "Command" && x.GetParameters().Length >= 1 && typeof(Humanoid).IsAssignableFrom(x.GetParameters()[0].ParameterType));
				if (!(methodInfo == null))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					object[] array = new object[parameters.Length];
					array[0] = user;
					for (int num = 1; num < parameters.Length; num++)
					{
						array[num] = ((parameters[num].ParameterType == typeof(bool)) ? ((object)true) : (parameters[num].HasDefaultValue ? parameters[num].DefaultValue : null));
					}
					methodInfo.Invoke(tame, array);
				}
			}
			catch
			{
			}
		}

		internal static bool IsOwnedAlpha(Tameable tame, Player player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			if ((Object)tame == (Object)null || (Object)player == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)tame).GetComponent<ZNetView>();
			if ((Object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			ZDO zDO = component.GetZDO();
			if (zDO != null && zDO.GetBool("GoblinHeim.Companion.Alpha", false))
			{
				return zDO.GetLong("GoblinHeim.Companion.Owner", 0L) == player.GetPlayerID();
			}
			return false;
		}

		internal static bool IsFollowing(Character character)
		{
			MonsterAI val = (((Object)(object)character == (Object)null) ? null : ((Component)character).GetComponent<MonsterAI>());
			if ((Object)(object)val != (Object)null)
			{
				return (Object)(object)val.GetFollowTarget() != (Object)null;
			}
			return false;
		}

		internal static void StopOtherOwnedCompanions(Player owner, Character selected)
		{
			if ((Object)(object)owner == (Object)null)
			{
				return;
			}
			long playerID = owner.GetPlayerID();
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)selected || !IsAlpha(allCharacter))
				{
					continue;
				}
				ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
				ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
				if (val != null && val.GetLong("GoblinHeim.Companion.Owner", 0L) == playerID)
				{
					MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
					if (!((Object)(object)component2 == (Object)null) && !((Object)(object)component2.GetFollowTarget() == (Object)null))
					{
						component2.SetFollowTarget((GameObject)null);
						((BaseAI)component2).SetPatrolPoint();
						CompanionTrainingPlugin.Log.LogInfo((object)"[COMPANION] other active follower switched to Stay.");
					}
				}
			}
		}

		internal static void ApplyAlphaVisual(Character character)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0077: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)character == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)character).GetComponent<ZNetView>();
			if ((Object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null || (!zDO.GetBool("GoblinHeim.Companion.Alpha", false) && !zDO.GetBool("GoblinHeim.Companion.Retired", false)))
			{
				return;
			}
			if (!OriginalScales.ContainsKey(character))
			{
				OriginalScales[character] = ((Component)character).transform.localScale;
			}
			float num = CompanionTrainingPlugin.GetSpecies((Component)(object)character) switch
			{
				CompanionTrainingPlugin.Species.Asksvin => 1.08f + Skill(character, "GoblinHeim.Companion.RobustSkill") * 0.00075f, 
				CompanionTrainingPlugin.Species.Lox => 1.03f + Skill(character, "GoblinHeim.Companion.RobustSkill") * 0.0005f, 
				_ => 1.1f + Skill(character, "GoblinHeim.Companion.RobustSkill") * 0.001f, 
			};
			((Component)character).transform.localScale = OriginalScales[character] * num;
			CapsuleCollider component2 = ((Component)character).GetComponent<CapsuleCollider>();
			if ((Object)(object)component2 != (Object)null)
			{
				if (!OriginalColliders.TryGetValue(character, out var value))
				{
					value = new ColliderShape
					{
						Collider = component2,
						Radius = component2.radius,
						Height = component2.height,
						Center = component2.center
					};
					OriginalColliders[character] = value;
				}
				component2.radius = value.Radius / num;
				component2.height = value.Height / num;
				component2.center = value.Center / num;
			}
			BaseAI baseAI = character.GetBaseAI();
			if ((Object)(object)baseAI != (Object)null)
			{
				baseAI.m_avoidFire = true;
				baseAI.m_avoidLava = true;
				MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
				baseAI.m_avoidWater = val == null || (Object)(object)val.GetFollowTarget() == (Object)null;
			}
		}

		private static void RestoreAlphaVisual(Character character)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null)
			{
				return;
			}
			if (OriginalScales.TryGetValue(character, out var value))
			{
				((Component)character).transform.localScale = value;
				OriginalScales.Remove(character);
			}
			if (OriginalColliders.TryGetValue(character, out var value2))
			{
				if ((Object)(object)value2.Collider != (Object)null)
				{
					value2.Collider.radius = value2.Radius;
					value2.Collider.height = value2.Height;
					value2.Collider.center = value2.Center;
				}
				OriginalColliders.Remove(character);
			}
		}

		internal static void RestoreReleasedVisual(Character character)
		{
			RestoreAlphaVisual(character);
		}

		internal static void UpdatePersonalCompanionPin()
		{
			//IL_008a: 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_005d: 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_00c3: 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_00cf: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				ZDOID companionId;
				string name;
				Vector3 position;
				bool flag = CompanionRecovery.TryGetActiveBackup(out companionId, out name, out position);
				KeyValuePair<ZDOID, PinData>[] array = PersonalPins.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					KeyValuePair<ZDOID, PinData> keyValuePair = array[i];
					if (CompanionTrainingPlugin.ShowPins.Value && flag)
					{
						ZDOID key = keyValuePair.Key;
						if (((ZDOID)(ref key)).Equals(companionId))
						{
							continue;
						}
					}
					if (keyValuePair.Value != null)
					{
						Minimap.instance.RemovePin(keyValuePair.Value);
					}
					PersonalPins.Remove(keyValuePair.Key);
				}
				if (!CompanionTrainingPlugin.ShowPins.Value || !flag)
				{
					return;
				}
				ZDOMan instance = ZDOMan.instance;
				ZDO val = ((instance != null) ? instance.GetZDO(companionId) : null);
				Vector3 val2 = ((val != null) ? val.GetPosition() : position);
				string name2 = (CompanionTrainingPlugin.ShowPinNames.Value ? name : "");
				if (!PersonalPins.TryGetValue(companionId, out var value) || value == null)
				{
					value = AddPin(val2, (PinType)3, name2);
					if (value != null)
					{
						PersonalPins[companionId] = value;
					}
				}
				if (value != null)
				{
					value.m_pos = val2;
					value.m_name = name2;
				}
			}
			catch (Exception ex)
			{
				CompanionTrainingPlugin.Log.LogWarning((object)("[COMPANION-PIN] update failed: " + ex.Message));
			}
		}

		private static PinData AddPin(Vector3 position, PinType type, string name)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo methodInfo = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddPin" && m.GetParameters().Length >= 5);
			if (methodInfo == null)
			{
				return null;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			object[] array = new object[parameters.Length];
			array[0] = position;
			array[1] = type;
			array[2] = name;
			array[3] = false;
			array[4] = false;
			for (int num = 5; num < array.Length; num++)
			{
				array[num] = (parameters[num].HasDefaultValue ? parameters[num].DefaultValue : (parameters[num].ParameterType.IsValueType ? Activator.CreateInstance(parameters[num].ParameterType) : null));
			}
			object? obj = methodInfo.Invoke(Minimap.instance, array);
			return (PinData)((obj is PinData) ? obj : null);
		}

		internal static string GetCompanionName(Character c)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			try
			{
				ZNetView component = ((Component)c).GetComponent<ZNetView>();
				ZDO val = (((Object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
				string text = ((val != null) ? val.GetString(ZDOVars.s_tamedName, "") : null);
				return string.IsNullOrWhiteSpace(text) ? SpeciesName(CompanionTrainingPlugin.GetSpecies((Component)c)) : text;
			}
			catch
			{
				return "Companion";
			}
		}

		internal static bool TryBegin(Player player, Inventory inventory, ItemData item, CompanionTrainingPlugin.Species species)
		{
			//IL_0055: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			Character val = (from candidate in Character.GetAllCharacters()
				where (Object)(object)candidate != (Object)null && !candidate.IsDead() && !IsAlpha(candidate) && candidate.IsTamed() && candidate.GetLevel() == 3 && CompanionTrainingPlugin.GetSpecies((Component)(object)candidate) == species && Vector3.Distance(((Component)player).transform.position, ((Component)candidate).transform.position) <= 5f
				orderby Vector3.Distance(((Component)player).transform.position, ((Component)candidate).transform.position)
				select candidate).FirstOrDefault();
			float num = (((Object)(object)val == (Object)null) ? 5.001f : Vector3.Distance(((Component)player).transform.position, ((Component)val).transform.position));
			if ((Object)val == (Object)null)
			{
				((Character)player).Message((MessageType)2, "Trouvez un compagnon digne", 0, (Sprite)null, false);
				CompanionTrainingPlugin.Log.LogInfo((object)$"[CHARM] use refused: no tame 2-star {species} within 5m");
				return false;
			}
			if ((Object)TextInput.instance == (Object)null)
			{
				((Character)player).Message((MessageType)2, "Fenêtre de nom indisponible.", 0, (Sprite)null, false);
				return false;
			}
			FreezeForNaming(val, frozen: true);
			_pending = new PendingName(player, inventory, item, val, species);
			TextInput.instance.RequestText((TextReceiver)_pending, "Choisissez un nom à votre compagnon", 20);
			CompanionTrainingPlugin.Log.LogInfo((object)$"[CHARM] binding pending: {species}, target={Utils.GetPrefabName(((Component)val).gameObject)}, distance={num:0.00}m");
			return true;
		}

		internal static bool TryBeginDirect(Player player, Character target)
		{
			//IL_0017: 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)
			if ((Object)(object)player == (Object)null || !CanBecomeCompanion(target) || Vector3.Distance(((Component)player).transform.position, ((Component)target).transform.position) > 5f)
			{
				return false;
			}
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				((Character)player).Message((MessageType)2, "Fenêtre de nom indisponible.", 0, (Sprite)null, false);
				return false;
			}
			CompanionTrainingPlugin.Species species = CompanionTrainingPlugin.GetSpecies((Component)(object)target);
			FreezeForNaming(target, frozen: true);
			_pending = new PendingName(player, null, null, target, species);
			CompanionCoatPalette.ResetSelection();
			TextInput.instance.RequestText((TextReceiver)(object)_pending, "Choisissez un nom à votre compagnon", 20);
			CompanionTrainingPlugin.Log.LogInfo((object)$"[COMPANION] direct binding pending: {species}, target={Utils.GetPrefabName(((Component)target).gameObject)}");
			return true;
		}

		private static void Commit(PendingName p, string raw)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Expected O, but got Unknown
			if (_pending != p)
			{
				return;
			}
			string text = (raw ?? "").Trim();
			if (text.Length == 0)
			{
				((Character)p.Player).Message((MessageType)2, "Le compagnon doit avoir un nom.", 0, (Sprite)null, false);
				TextInput instance = TextInput.instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.RequestText((TextReceiver)p, "Choisissez un nom à votre compagnon", 20);
				}
				return;
			}
			if ((Object)p.Target == (Object)null || p.Target.IsDead() || !p.Target.IsTamed() || p.Target.GetLevel() != 3 || CompanionTrainingPlugin.GetSpecies((Component)p.Target) != p.Species || Vector3.Distance(((Component)p.Player).transform.position, ((Component)p.Target).transform.position) > 5f)
			{
				((Character)p.Player).Message((MessageType)2, "La liaison a été annulée : cible invalide ou hors de portée.", 0, (Sprite)null, false);
				FreezeForNaming(p.Target, frozen: false);
				_pending = null;
				CompanionCoatPalette.ResetSelection();
				return;
			}
			if (p.Item != null && p.Item.m_stack <= 0)
			{
				((Character)p.Player).Message((MessageType)2, "Le charme n'est plus disponible.", 0, (Sprite)null, false);
				FreezeForNaming(p.Target, frozen: false);
				_pending = null;
				CompanionCoatPalette.ResetSelection();
				return;
			}
			long playerID = p.Player.GetPlayerID();
			CompanionRecovery.ReleasePrevious(p.Player, p.Species, p.Target);
			if ((Object)((Component)p.Target).GetComponent<ZNetView>() == (Object)null || !((Component)p.Target).GetComponent<ZNetView>().IsValid())
			{
				((Character)p.Player).Message((MessageType)2, "La liaison a échoué : ZNetView invalide.", 0, (Sprite)null, false);
				FreezeForNaming(p.Target, frozen: false);
				_pending = null;
				CompanionCoatPalette.ResetSelection();
				return;
			}
			ZNetView component = ((Component)p.Target).GetComponent<ZNetView>();
			component.ClaimOwnership();
			ZDO zDO = component.GetZDO();
			if (zDO == null)
			{
				FreezeForNaming(p.Target, frozen: false);
				_pending = null;
				CompanionCoatPalette.ResetSelection();
				return;
			}
			bool flag = zDO.GetBool("GoblinHeim.Companion.Retired", false);
			zDO.Set("GoblinHeim.Companion.Alpha", true);
			zDO.Set("GoblinHeim.Companion.Retired", false);
			zDO.Set("GoblinHeim.Companion.Owner", playerID);
			zDO.Set("GoblinHeim.Companion.Species", p.Species.ToString());
			if (!flag)
			{
				zDO.Set("GoblinHeim.Companion.AttackSkill", 0f);
				zDO.Set("GoblinHeim.Companion.RobustSkill", 0f);
			}
			zDO.Set("GoblinHeim.Companion.Wounded", false);
			zDO.Set("GoblinHeim.Companion.Healing", false);
			CompanionCoatPalette.SaveSelected(zDO);
			ApplyAlphaVisual(p.Target);
			CompanionCoatPalette.Apply(p.Target);
			p.Target.SetHealth(CompanionMaxHealth(p.Target));
			CompanionTrainingPlugin.Log.LogInfo((object)$"[COMPANION] Alpha visual/stats applied: {p.Species} scale={((Component)p.Target).transform.localScale}, HP={CompanionMaxHealth(p.Target):0.#}, damage={CompanionDamage(p.Target):0.#}, armor={CompanionArmor(p.Target):0.#}.");
			Tameable component2 = ((Component)p.Target).GetComponent<Tameable>();
			if ((Object)component2 != (Object)null)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Tameable), "RPC_SetName", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					try
					{
						methodInfo.Invoke(component2, new object[2] { 0L, text });
					}
					catch
					{
						zDO.Set(ZDOVars.s_tamedName, text);
					}
				}
				else
				{
					zDO.Set(ZDOVars.s_tamedName, text);
				}
			}
			else
			{
				zDO.Set(ZDOVars.s_tamedName, text);
			}
			if (p.Item != null && p.Inventory != null)
			{
				p.Inventory.RemoveOneItem(p.Item);
			}
			((Character)p.Player).Message((MessageType)2, text + " est désormais votre compagnon.", 0, (Sprite)null, false);
			CompanionTrainingPlugin.Log.LogInfo((object)$"[COMPANION] binding committed: species={p.Species}, name='{text}', owner={playerID}.");
			CompanionRecovery.SaveBackup(p.Target);
			FreezeForNaming(p.Target, frozen: false);
			_pending = null;
			CompanionCoatPalette.ResetSelection();
		}

		private static string SpeciesName(CompanionTrainingPlugin.Species s)
		{
			return s switch
			{
				CompanionTrainingPlugin.Species.Wolf => "loup", 
				CompanionTrainingPlugin.Species.Boar => "sanglier", 
				CompanionTrainingPlugin.Species.Asksvin => "asksvin", 
				CompanionTrainingPlugin.Species.Moose => "élan", 
				_ => "lox", 
			};
		}
	}
	internal static class CharmRegistry
	{
		internal sealed class Def
		{
			internal string Prefab;

			internal string Name;

			internal string Icon;

			internal string Trophy;

			internal string Hide;

			internal string Metal;

			internal string Mead;

			internal string RecipeLabel;

			internal CompanionTrainingPlugin.Species Species;

			internal int StationLevel;
		}

		internal static readonly Def[] Defs = new Def[3]
		{
			new Def
			{
				Prefab = "GoblinHeim_BoarCharm",
				Name = "Charme du Sanglier",
				RecipeLabel = "Boar",
				Icon = "BoarCharm.png",
				Trophy = "TrophyBoar",
				Hide = "LeatherScraps",
				Metal = "Bronze",
				Mead = "MeadHealthMinor",
				Species = CompanionTrainingPlugin.Species.Boar,
				StationLevel = 1
			},
			new Def
			{
				Prefab = "GoblinHeim_WolfCharm",
				Name = "Charme du Loup",
				RecipeLabel = "Wolf",
				Icon = "WolfCharm.png",
				Trophy = "TrophyWolf",
				Hide = "WolfPelt",
				Metal = "Silver",
				Mead = "MeadBzerker",
				Species = CompanionTrainingPlugin.Species.Wolf,
				StationLevel = 3
			},
			new Def
			{
				Prefab = "GoblinHeim_LoxCharm",
				Name = "Charme du Lox",
				RecipeLabel = "Lox",
				Icon = "LoxCharm.png",
				Trophy = "TrophyLox",
				Hide = "LoxPelt",
				Metal = "BlackMetal",
				Mead = "MeadStrength",
				Species = CompanionTrainingPlugin.Species.Lox,
				StationLevel = 4
			}
		};

		private static readonly Dictionary<string, GameObject> Prefabs = new Dictionary<string, GameObject>();

		private static readonly Dictionary<CompanionTrainingPlugin.Species, Sprite> UiIcons = new Dictionary<CompanionTrainingPlugin.Species, Sprite>();

		private static bool _itemsRegistered;

		private static bool _recipesRegistered;

		private static bool _registrationInProgress;

		private static bool _completeLogged;

		internal static void TryRegisterContent(string trigger)
		{
			if (_registrationInProgress)
			{
				return;
			}
			ObjectDB db = ObjectDB.instance;
			if ((Object)(object)db == (Object)null || db.m_items == null)
			{
				CompanionTrainingPlugin.Log.LogDebug((object)("[CHARM] waiting (" + trigger + "): ObjectDB unavailable"));
				return;
			}
			_registrationInProgress = true;
			try
			{
				EnsureItems(db);
				if ((Object)(object)ZNetScene.instance != (Object)null)
				{
					EnsureNetworkPrefabs(ZNetScene.instance, db);
					EnsureRecipes(db, ZNetScene.instance);
				}
				else
				{
					CompanionTrainingPlugin.Log.LogDebug((object)("[CHARM] items ready (" + trigger + "); recipes waiting for ZNetScene"));
				}
				_itemsRegistered = Defs.All((Def d) => db.m_items.Any((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == d.Prefab));
				_recipesRegistered = db.m_recipes != null && Defs.All((Def d) => db.m_recipes.Any((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == "Recipe_" + d.Prefab));
				if (_itemsRegistered && _recipesRegistered && !_completeLogged)
				{
					_completeLogged = true;
					CompanionTrainingPlugin.Log.LogInfo((object)"Vanilla dependencies ready.");
					CompanionTrainingPlugin.Log.LogInfo((object)"[CHARM] Charms registered: 3.");
					CompanionTrainingPlugin.Log.LogInfo((object)"[CHARM] registration complete: items=3 recipes=3");
					CompanionTrainingPlugin.Log.LogInfo((object)"Companion systems ready.");
				}
			}
			catch (Exception ex)
			{
				CompanionTrainingPlugin.Log.LogError((object)("[CHARM] content registration error: " + ex));
			}
			finally
			{
				_registrationInProgress = false;
			}
		}

		private static void EnsureItems(ObjectDB db)
		{
			bool flag = false;
			Def[] defs = Defs;
			foreach (Def def in defs)
			{
				GameObject val = Create(def, db);
				if (!((Object)(object)val == (Object)null) && !db.m_items.Any((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == def.Prefab))
				{
					db.m_items.Add(val);
					flag = true;
					CompanionTrainingPlugin.Log.LogInfo((object)("[CHARM] " + def.Prefab + " registered"));
				}
			}
			if (flag)
			{
				MethodInfo methodInfo = AccessTools.Method(((object)db).GetType(), "UpdateRegisters", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					throw new MissingMethodException(((object)db).GetType().FullName, "UpdateRegisters");
				}
				methodInfo.Invoke(db, null);
			}
			defs = Defs;
			foreach (Def def2 in defs)
			{
				if ((Object)(object)db.GetItemPrefab(def2.Prefab) == (Object)null)
				{
					throw new InvalidOperationException("ObjectDB hash register missing " + def2.Prefab);
				}
			}
		}

		private static GameObject Create(Def def, ObjectDB db)
		{
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			if (Prefabs.TryGetValue(def.Prefab, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			GameObject val = FindItemPrefab(db, "Amber") ?? FindItemPrefab(db, "AmberPearl") ?? FindItemPrefab(db, "Coins") ?? FindItemPrefab(db, "CryptKey") ?? ((IEnumerable<GameObject>)db.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && (Object)(object)x.GetComponent<ItemDrop>() != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val);
			((Object)val2).name = def.Prefab;
			Object.DontDestroyOnLoad((Object)(object)val2);
			ZSyncTransform[] componentsInChildren = val2.GetComponentsInChildren<ZSyncTransform>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[num]);
			}
			WearNTear[] componentsInChildren2 = val2.GetComponentsInChildren<WearNTear>(true);
			for (int num = 0; num < componentsInChildren2.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[num]);
			}
			ZNetView[] componentsInChildren3 = val2.GetComponentsInChildren<ZNetView>(true);
			for (int num = 0; num < componentsInChildren3.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[num]);
			}
			ItemDrop component = val2.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
			component.m_itemData.m_shared = (SharedData)method.Invoke(component.m_itemData.m_shared, null);
			component.m_itemData.m_shared.m_name = def.Name;
			component.m_itemData.m_shared.m_description = "Lie votre âme à celle de votre companion.";
			component.m_itemData.m_shared.m_itemType = (ItemType)2;
			component.m_itemData.m_shared.m_maxStackSize = 20;
			component.m_itemData.m_shared.m_weight = 0.1f;
			AccessTools.Field(typeof(SharedData), "m_consumeStatusEffect")?.SetValue(component.m_itemData.m_shared, null);
			Sprite val3 = LoadEmbeddedIcon(def.Icon);
			if ((Object)(object)val3 != (Object)null)
			{
				component.m_itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { val3 };
			}
			component.m_itemData.m_dropPrefab = val2;
			Prefabs[def.Prefab] = val2;
			return val2;
		}

		private static GameObject FindItemPrefab(ObjectDB db, string name)
		{
			object obj = db.m_items?.FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == name));
			if (obj == null)
			{
				ZNetScene instance = ZNetScene.instance;
				if (instance == null)
				{
					return null;
				}
				obj = instance.GetPrefab(name);
			}
			return (GameObject)obj;
		}

		private static void EnsureNetworkPrefabs(ZNetScene scene, ObjectDB db)
		{
			Def[] defs = Defs;
			foreach (Def def in defs)
			{
				GameObject val = Create(def, db);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (AccessTools.Field(((object)scene).GetType(), "m_prefabs")?.GetValue(scene) is IList list)
				{
					bool flag = false;
					foreach (object item in list)
					{
						GameObject val2 = (GameObject)((item is GameObject) ? item : null);
						if (val2 != null && ((Object)val2).name == def.Prefab)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						list.Add(val);
					}
				}
				if (AccessTools.Field(((object)scene).GetType(), "m_namedPrefabs")?.GetValue(scene) is IDictionary dictionary)
				{
					int stableHashCode = StringExtensionMethods.GetStableHashCode(def.Prefab);
					if (!dictionary.Contains(stableHashCode))
					{
						dictionary.Add(stableHashCode, val);
					}
				}
			}
		}

		private static void EnsureRecipes(ObjectDB db, ZNetScene scene)
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			if (db.m_recipes == null)
			{
				return;
			}
			GameObject prefab = scene.GetPrefab("piece_workbench");
			CraftingStation val = ((prefab != null) ? prefab.GetComponent<CraftingStation>() : null);
			if ((Object)(object)val == (Object)null)
			{
				CompanionTrainingPlugin.Log.LogDebug((object)"[CHARM] recipes waiting for piece_workbench");
				return;
			}
			Def[] defs = Defs;
			foreach (Def def in defs)
			{
				string recipeName = "Recipe_" + def.Prefab;
				if (db.m_recipes.Any((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == recipeName))
				{
					continue;
				}
				GameObject val2 = Create(def, db);
				(string, int)[] obj = new(string, int)[4]
				{
					(def.Trophy, 1),
					(def.Metal, 4),
					(def.Hide, 4),
					(def.Mead, 1)
				};
				List<Requirement> list = new List<Requirement>();
				bool flag = (Object)(object)val2 != (Object)null;
				(string, int)[] array = obj;
				for (int num = 0; num < array.Length; num++)
				{
					(string, int) tuple = array[num];
					GameObject obj2 = FindItemPrefab(db, tuple.Item1);
					ItemDrop val3 = ((obj2 != null) ? obj2.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val3 == (Object)null)
					{
						flag = false;
						break;
					}
					list.Add(new Requirement
					{
						m_resItem = val3,
						m_amount = tuple.Item2,
						m_amountPerLevel = 0,
						m_recover = true
					});
				}
				if (flag)
				{
					Recipe val4 = ScriptableObject.CreateInstance<Recipe>();
					((Object)val4).name = recipeName;
					val4.m_item = val2.GetComponent<ItemDrop>();
					val4.m_amount = 1;
					val4.m_minStationLevel = def.StationLevel;
					val4.m_resources = list.ToArray();
					val4.m_craftingStation = val;
					db.m_recipes.Add(val4);
					CompanionTrainingPlugin.Log.LogInfo((object)("[CHARM] " + def.RecipeLabel + " recipe registered"));
				}
			}
		}

		internal static bool IsCharm(ItemData item, out CompanionTrainingPlugin.Species species)
		{
			species = CompanionTrainingPlugin.Species.None;
			if (item == null)
			{
				return false;
			}
			string prefab = (((Object)(object)item.m_dropPrefab == (Object)null) ? "" : ((Object)item.m_dropPrefab).name);
			Def def = Defs.FirstOrDefault((Def d) => d.Prefab == prefab || item.m_shared?.m_name == d.Name);
			if (def == null)
			{
				return false;
			}
			species = def.Species;
			return true;
		}

		internal static Sprite GetIcon(CompanionTrainingPlugin.Species species)
		{
			if (UiIcons.TryGetValue(species, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Def def = Defs.FirstOrDefault((Def d) => d.Species == species);
			GameObject value2;
			GameObject obj = ((def == null) ? null : (Prefabs.TryGetValue(def.Prefab, out value2) ? value2 : null));
			Sprite[] array = ((obj == null) ? null : obj.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons);
			Sprite val = ((array != null && array.Length != 0) ? array[0] : ((def == null) ? null : LoadEmbeddedIcon(def.Icon)));
			if ((Object)(object)val == (Object)null && species == CompanionTrainingPlugin.Species.Asksvin)
			{
				ObjectDB instance = ObjectDB.instance;
				object obj2 = ((instance != null) ? instance.GetItemPrefab("TrophyAsksvin") : null);
				if (obj2 == null)
				{
					ObjectDB instance2 = ObjectDB.instance;
					obj2 = ((instance2 != null) ? instance2.GetItemPrefab("TrophyAsksvinQueen") : null);
				}
				Sprite[] array2 = ((obj2 == null) ? null : ((GameObject)obj2).GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons);
				if (array2 != null && array2.Length != 0)
				{
					val = array2[0];
				}
			}
			if ((Object)(object)val == (Object)null && species == CompanionTrainingPlugin.Species.Moose)
			{
				ObjectDB instance3 = ObjectDB.instance;
				GameObject obj3 = ((instance3 != null) ? instance3.GetItemPrefab("TrophyMoose") : null);
				Sprite[] array3 = ((obj3 == null) ? null : obj3.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons);
				if (array3 != null && array3.Length != 0)
				{
					val = array3[0];
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				UiIcons[species] = val;
			}
			return val;
		}

		internal static Sprite GetTrophyIcon(CompanionTrainingPlugin.Species species)
		{
			string text = Defs.FirstOrDefault((Def d) => d.Species == species)?.Trophy;
			if (species == CompanionTrainingPlugin.Species.Asksvin)
			{
				text = "TrophyAsksvin";
			}
			else if (species == CompanionTrainingPlugin.Species.Moose)
			{
				text = "TrophyMoose";
			}
			object obj;
			if (!string.IsNullOrEmpty(text))
			{
				ObjectDB instance = ObjectDB.instance;
				obj = ((instance != null) ? instance.GetItemPrefab(text) : null);
			}
			else
			{
				obj = null;
			}
			Sprite[] array = ((obj == null) ? null : ((GameObject)obj).GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons);
			if (array == null || array.Length == 0)
			{
				return GetIcon(species);
			}
			return array[0];
		}

		private static Sprite LoadEmbeddedIcon(string file)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			string text = Assembly.GetExecutingAssembly().GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("." + file, StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				return null;
			}
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			MethodInfo methodInfo = (Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule") ?? (from a in AppDomain.CurrentDomain.GetAssemblies()
				select a.GetType("UnityEngine.ImageConversion")).FirstOrDefault((Type t) => t != null))?.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "LoadImage" && m.GetParameters().Length >= 2 && m.GetParameters()[0].ParameterType == typeof(Texture2D) && m.GetParameters()[1].ParameterType == typeof(byte[]));
			if (methodInfo == null)
			{
				CompanionTrainingPlugin.Log.LogError((object)("[CHARM] UnityEngine.ImageConversion.LoadImage unavailable for " + file));
				return null;
			}
			object[] parameters = ((methodInfo.GetParameters().Length != 2) ? new object[3]
			{
				val,
				memoryStream.ToArray(),
				false
			} : new object[2]
			{
				val,
				memoryStream.ToArray()
			});
			if (!(bool)methodInfo.Invoke(null, parameters))
			{
				CompanionTrainingPlugin.Log.LogError((object)("[CHARM] PNG decoding failed for " + file));
				return null;
			}
			((Object)val).name = file;
			CompanionTrainingPlugin.Log.LogInfo((object)("[CHARM] icon loaded: " + file));
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}
	}
	internal static class CompanionAlliances
	{
		internal static bool IsProtected(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if (character.IsPlayer() || character.IsTamed())
			{
				return true;
			}
			string text = ((Object)((Component)character).gameObject).name.Replace("(Clone)", "");
			if (!text.StartsWith("Dverger", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Dvergr", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			BaseAI baseAI = character.GetBaseAI();
			if (!((Object)(object)baseAI == (Object)null))
			{
				return !baseAI.IsAggravated();
			}
			return true;
		}

		internal static bool IsPlayerBuilding(Component component)
		{
			Piece val = (((Object)(object)component == (Object)null) ? null : component.GetComponentInParent<Piece>());
			if ((Object)(object)val != (Object)null)
			{
				return val.IsPlacedByPlayer();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
	{
		typeof(Character),
		typeof(Character)
	})]
	internal static class CompanionAlliancePatch
	{
		private static void Postfix(Character a, Character b, ref bool __result)
		{
			if (__result && !((Object)(object)a == (Object)null) && !((Object)(object)b == (Object)null) && ((CharmBinding.IsAlpha(a) && CompanionAlliances.IsProtected(b)) || (CharmBinding.IsAlpha(b) && CompanionAlliances.IsProtected(a))))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(StaticTarget) })]
	internal static class CompanionBuildingSelectionPatch
	{
		private static bool Prefix(BaseAI __instance, StaticTarget __0, ref bool __result)
		{
			if (!CharmBinding.IsAlpha(((Component)__instance).GetComponent<Character>()) || !CompanionAlliances.IsPlayerBuilding((Component)(object)__0))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
	internal static class CompanionBuildingTargetPatch
	{
		private static void Prefix(MonsterAI __instance, ref StaticTarget ___m_targetStatic)
		{
			if (CharmBinding.IsAlpha(((Component)__instance).GetComponent<Character>()) && CompanionAlliances.IsPlayerBuilding((Component)(object)___m_targetStatic))
			{
				___m_targetStatic = null;
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Damage")]
	internal static class CompanionBuildingDamagePatch
	{
		private static bool Prefix(WearNTear __instance, HitData hit)
		{
			if (hit != null && CharmBinding.IsAlpha(hit.GetAttacker()))
			{
				return !CompanionAlliances.IsPlayerBuilding((Component)(object)__instance);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
	internal static class CompanionBuildingRpcDamagePatch
	{
		private static bool Prefix(WearNTear __instance, HitData hit)
		{
			if (hit != null && CharmBinding.IsAlpha(hit.GetAttacker()))
			{
				return !CompanionAlliances.IsPlayerBuilding((Component)(object)__instance);
			}
			return true;
		}
	}
	internal sealed class CompanionBeacon : MonoBehaviour, Hoverable, Interactable
	{
		internal const string PrefabName = "piece_companion_beacon";

		internal static readonly HashSet<CompanionBeacon> Instances = new HashSet<CompanionBeacon>();

		private Piece piece;

		private void Awake()
		{
			piece = ((Component)this).GetComponent<Piece>();
			Instances.Add(this);
		}

		private void OnDestroy()
		{
			Instances.Remove(this);
		}

		internal bool IsOwnedBy(Player player)
		{
			if (Object.op_Implicit((Object)(object)player) && Object.op_Implicit((Object)(object)piece))
			{
				return piece.GetCreator() == player.GetPlayerID();
			}
			return false;
		}

		public string GetHoverName()
		{
			return "Balise de Compagnon";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!IsOwnedBy(localPlayer))
			{
				return "Balise de Compagnon\n<color=grey>Personnelle</color>";
			}
			return "Balise de Compagnon\n[<color=yellow><b>$KEY_Use</b></color>] Rappeler";
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (!hold)
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && IsOwnedBy(val))
				{
					CompanionBeaconUI.Open(this);
					return true;
				}
			}
			return false;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal static class CompanionBeaconPrefab
	{
		private const string SourceName = "guard_stone";

		private static GameObject root;

		private static GameObject prefab;

		internal static void Register(ZNetScene scene)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)scene))
			{
				return;
			}
			GameObject val = ((IEnumerable<GameObject>)scene.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject go) => Object.op_Implicit((Object)(object)go) && ((Object)go).name == "guard_stone"));
			if (!Object.op_Implicit((Object)(object)val))
			{
				CompanionTrainingPlugin.Log.LogError((object)"[BEACON] Vanilla guardstone prefab not found.");
				return;
			}
			root = new GameObject("CompanionBeacon_PrefabRoot");
			Object.DontDestroyOnLoad((Object)(object)root);
			root.SetActive(false);
			prefab = Object.Instantiate<GameObject>(val, root.transform, false);
			((Object)prefab).name = "piece_companion_beacon";
			PrivateArea[] componentsInChildren = prefab.GetComponentsInChildren<PrivateArea>(true);
			foreach (PrivateArea val2 in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val2.m_enabledEffect))
				{
					val2.m_enabledEffect.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)val2.m_areaMarker))
				{
					((Component)val2.m_areaMarker).gameObject.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)val2.m_connectEffect))
				{
					val2.m_connectEffect.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)val2.m_inRangeEffect))
				{
					val2.m_inRangeEffect.SetActive(false);
				}
				Object.DestroyImmediate((Object)(object)val2);
			}
			Piece component = prefab.GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				Object.Destroy((Object)(object)prefab);
				prefab = null;
				return;
			}
			component.m_name = "Balise de Compagnon";
			component.m_description = "Rappelle uniquement vos compagnons personnels.";
			prefab.AddComponent<CompanionBeacon>();
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.38f, 0.43f, 0.42f, 1f);
			Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val4 in componentsInChildren2)
			{
				Material[] materials = val4.materials;
				Material[] array = materials;
				foreach (Material val5 in array)
				{
					if (Object.op_Implicit((Object)(object)val5) && val5.HasProperty("_Color"))
					{
						val5.color = Color.Lerp(val5.color, val3, 0.68f);
					}
				}
				val4.materials = materials;
			}
			scene.m_prefabs.Add(prefab);
			CompanionTrainingPlugin.Log.LogInfo((object)"[BEACON] Personal Companion beacon registered from Vanilla guardstone.");
		}

		internal static void AddToHammer()
		{
			if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer");
			ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			PieceTable val2 = (Object.op_Implicit((Object)(object)val) ? val.m_itemData.m_shared.m_buildPieces : null);
			if (Object.op_Implicit((Object)(object)val2) && !val2.m_pieces.Contains(prefab))
			{
				int num = val2.m_pieces.FindIndex((GameObject go) => Object.op_Implicit((Object)(object)go) && ((Object)go).name == "guard_stone");
				if (num < 0)
				{
					val2.m_pieces.Add(prefab);
				}
				else
				{
					val2.m_pieces.Insert(num + 1, prefab);
				}
				CompanionTrainingPlugin.Log.LogInfo((object)"[BEACON] Added beside the Vanilla guardstone in the hammer table.");
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class CompanionBeaconScenePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetScene __instance)
		{
			CompanionBeaconPrefab.Register(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix()
		{
			CompanionBeaconPrefab.AddToHammer();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class CompanionBeaconObjectDbPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			CompanionBeaconPrefab.AddToHammer();
		}
	}
	internal sealed class CompanionBeaconUI : MonoBehaviour
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__CloseCurrent;

			public static Action <1>__CloseCurrent;
		}

		private static CompanionBeaconUI current;

		private Component anchor;

		private Image vanillaBackground;

		private Image windowBackground;

		internal static void Open(CompanionBeacon source)
		{
			OpenWindow((Component)(object)source, delegate(CompanionTrainingPlugin.Species species)
			{
				CompanionRecovery.RecallFromBeacon(species, source);
			});
		}

		internal static void OpenStation(CraftingStation source)
		{
			OpenWindow((Component)(object)source, delegate(CompanionTrainingPlugin.Species species)
			{
				CompanionRecovery.RecallFromStation(species, source);
			});
		}

		private static void OpenWindow(Component source, Action<CompanionTrainingPlugin.Species> recall)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Expected O, but got Unknown
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Expected O, but got Unknown
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0406: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_0445: Unknown result type (might be due to invalid IL or missing references)
			//IL_0459: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04da: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			CloseCurrent();
			InventoryGui instance = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.m_container) || !Object.op_Implicit((Object)(object)instance.m_takeAllButton))
			{
				return;
			}
			Canvas componentInParent = ((Component)instance).GetComponentInParent<Canvas>(true);
			Transform val = (Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).transform : ((Component)instance).transform);
			Transform obj = ((Transform)instance.m_container).Find("Bkg");
			RectTransform val2 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)((Component)val2).GetComponent<Image>()))
			{
				return;
			}
			GameObject obj2 = Object.Instantiate<GameObject>(((Component)val2).gameObject, val, false);
			((Object)obj2).name = "CompanionBeaconWindow";
			CompanionBeaconUI companionBeaconUI = obj2.AddComponent<CompanionBeaconUI>();
			companionBeaconUI.anchor = source;
			current = companionBeaconUI;
			RectTransform val3 = (RectTransform)obj2.transform;
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 0.5f);
			val3.pivot = val4;
			Vector2 val5 = (val3.anchorMin = (val3.anchorMax = val4));
			List<CompanionTrainingPlugin.Species> list = (from CompanionTrainingPlugin.Species s in Enum.GetValues(typeof(CompanionTrainingPlugin.Species))
				where s != CompanionTrainingPlugin.Species.None && CompanionRecovery.HasBackup(s)
				select s).ToList();
			int num = Mathf.Clamp(list.Count, 1, 5);
			int num2 = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / 5f));
			val3.sizeDelta = new Vector2((float)Mathf.Max(290, num * 88 + 30), (float)(105 + num2 * 100));
			Image component = ((Component)val2).GetComponent<Image>();
			Image component2 = obj2.GetComponent<Image>();
			companionBeaconUI.vanillaBackground = component;
			companionBeaconUI.windowBackground = component2;
			companionBeaconUI.SyncBackground();
			TMP_Text obj3 = Object.Instantiate<TMP_Text>(instance.m_containerName, (Transform)(object)val3);
			obj3.text = "BALISE DE COMPAGNON";
			RectTransform rectTransform = obj3.rectTransform;
			((Vector2)(ref val5))..ctor(0.5f, 1f);
			rectTransform.anchorMax = val5;
			rectTransform.anchorMin = val5;
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.anchoredPosition = new Vector2(0f, -12f);
			rectTransform.sizeDelta = new Vector2(val3.sizeDelta.x - 70f, 28f);
			Button obj4 = Object.Instantiate<Button>(instance.m_takeAllButton, (Transform)(object)val3);
			((UnityEventBase)obj4.onClick).RemoveAllListeners();
			ButtonClickedEvent onClick = obj4.onClick;
			object obj5 = <>O.<0>__CloseCurrent;
			if (obj5 == null)
			{
				UnityAction val8 = CloseCurrent;
				<>O.<0>__CloseCurrent = val8;
				obj5 = (object)val8;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj5);
			((Component)obj4).GetComponentInChildren<TMP_Text>(true).text = "×";
			RectTransform component3 = ((Component)obj4).GetComponent<RectTransform>();
			((Vector2)(ref val5))..ctor(1f, 1f);
			component3.anchorMax = val5;
			component3.anchorMin = val5;
			component3.pivot = new Vector2(1f, 1f);
			component3.anchoredPosition = new Vector2(-8f, -8f);
			component3.sizeDelta = new Vector2(34f, 30f);
			for (int num3 = 0; num3 < list.Count; num3++)
			{
				CompanionTrainingPlugin.Species species = list[num3];
				Button val9 = Object.Instantiate<Button>(instance.m_takeAllButton, (Transform)(object)val3);
				((UnityEventBase)val9.onClick).RemoveAllListeners();
				((UnityEvent)val9.onClick).AddListener((UnityAction)delegate
				{
					recall(species);
					CloseCurrent();
				});
				RectTransform component4 = ((Component)val9).GetComponent<RectTransform>();
				((Vector2)(ref val5))..ctor(0.5f, 1f);
				component4.anchorMax = val5;
				component4.anchorMin = val5;
				component4.pivot = new Vector2(0.5f, 1f);
				component4.sizeDelta = new Vector2(80f, 92f);
				component4.anchoredPosition = new Vector2(((float)(num3 % 5) - (float)(num - 1) * 0.5f) * 88f, -52f - (float)(num3 / 5) * 100f);
				TMP_Text componentInChildren = ((Component)val9).GetComponentInChildren<TMP_Text>(true);
				if (Object.op_Implicit((Object)(object)componentInChildren))
				{
					componentInChildren.text = CompanionRecovery.GetBackupName(species);
					RectTransform rectTransform2 = componentInChildren.rectTransform;
					rectTransform2.anchorMin = new Vector2(0f, 0f);
					rectTransform2.anchorMax = new Vector2(1f, 0f);
					rectTransform2.pivot = new Vector2(0.5f, 0f);
					rectTransform2.anchoredPosition = new Vector2(0f, 3f);
					rectTransform2.sizeDelta = new Vector2(-6f, 19f);
				}
				Sprite trophyIcon = CharmRegistry.GetTrophyIcon(species);
				if (Object.op_Implicit((Object)(object)trophyIcon))
				{
					GameObject val10 = new GameObject("Portrait", new Type[2]
					{
						typeof(RectTransform),
						typeof(Image)
					});
					val10.transform.SetParent(((Component)val9).transform, false);
					Image component5 = val10.GetComponent<Image>();
					component5.sprite = trophyIcon;
					component5.preserveAspect = true;
					((Graphic)component5).raycastTarget = false;
					RectTransform val11 = (RectTransform)val10.transform;
					val11.anchorMin = Vector2.zero;
					val11.anchorMax = Vector2.one;
					val11.offsetMin = new Vector2(8f, 25f);
					val11.offsetMax = new Vector2(-8f, -5f);
				}
			}
			CoreUiWindows.Open((object)companionBeaconUI, (Action)CloseCurrent);
		}

		private void Update()
		{
			//IL_002a: 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)
			SyncBackground();
			if (!Object.op_Implicit((Object)(object)anchor) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || Vector3.Distance(anchor.transform.position, ((Component)Player.m_localPlayer).transform.position) > 6f)
			{
				CloseCurrent();
			}
		}

		private void SyncBackground()
		{
			//IL_0053: 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)
			if (Object.op_Implicit((Object)(object)vanillaBackground) && Object.op_Implicit((Object)(object)windowBackground))
			{
				windowBackground.sprite = vanillaBackground.sprite;
				windowBackground.overrideSprite = vanillaBackground.overrideSprite;
				((Graphic)windowBackground).color = ((Graphic)vanillaBackground).color;
				((Graphic)windowBackground).material = ((Graphic)vanillaBackground).material;
				windowBackground.type = vanillaBackground.type;
				windowBackground.preserveAspect = vanillaBackground.preserveAspect;
				windowBackground.fillCenter = vanillaBackground.fillCenter;
				windowBackground.pixelsPerUnitMultiplier = vanillaBackground.pixelsPerUnitMultiplier;
			}
		}

		internal static void CloseCurrent()
		{
			if (Object.op_Implicit((Object)(object)current))
			{
				CompanionBeaconUI companionBeaconUI = current;
				current = null;
				CoreUiWindows.CloseRegistration((object)companionBeaconUI);
				Object.Destroy((Object)(object)((Component)companionBeaconUI).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Interact")]
	internal static class CompanionWorkshopRecallPatch
	{
		[HarmonyPostfix]
		private static void Postfix(CraftingStation __instance, Humanoid user, bool repeat, bool __result)
		{
			if (!repeat && !((Object)(object)user != (Object)(object)Player.m_localPlayer) && __instance.InUseDistance(user))
			{
				CompanionRecovery.RememberStation(__instance);
				if ((Object)(object)Player.m_localPlayer.GetCurrentCraftingStation() != (Object)(object)__instance)
				{
					CompanionBeaconUI.OpenStation(__instance);
				}
			}
		}
	}
	internal static class CompanionBoat
	{
		private sealed class DeckState
		{
			internal Ship Ship;

			internal Transform Anchor;

			internal int AnchorIndex;

			internal Vector3 FloorLocal;

			internal Quaternion RotationLocal;

			internal bool AiWasEnabled;

			internal bool BodyWasKinematic;

			internal readonly Dictionary<Collider, bool> Colliders = new Dictionary<Collider, bool>();

			internal readonly Dictionary<Renderer, bool> Renderers = new Dictionary<Renderer, bool>();

			internal GameObject PassengerCard;
		}

		private const string DeckedKey = "GoblinHeim.Companion.Boat.Decked";

		private const string BoatUserKey = "GoblinHeim.Companion.Boat.User";

		private const string BoatIdKey = "GoblinHeim.Companion.Boat.Id";

		private const string AnchorIndexKey = "GoblinHeim.Companion.Boat.Anchor";

		private static readonly Dictionary<Character, DeckState> Decked = new Dictionary<Character, DeckState>();

		private static readonly Dictionary<Character, float> SwimSpeeds = new Dictionary<Character, float>();

		internal static void Update()
		{
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			SynchronizeNetworkPassengers();
			foreach (Character item in Decked.Keys.ToList())
			{
				if ((Object)(object)item == (Object)null)
				{
					Decked.Remove(item);
					continue;
				}
				ZDO zdo = GetZdo(item);
				if (item.IsDead() || !CharmBinding.IsAlpha(item) || zdo == null || !zdo.GetBool("GoblinHeim.Companion.Boat.Decked", false))
				{
					ZNetView component = ((Component)item).GetComponent<ZNetView>();
					bool clearNetwork = zdo != null && zdo.GetBool("GoblinHeim.Companion.Boat.Decked", false) && (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner();
					Detach(item, null, placeByPlayer: false, 0, clearNetwork);
				}
				else
				{
					KeepOnDeck(item, Decked[item]);
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Ship localShip = Ship.GetLocalShip();
			if ((Object)(object)localShip == (Object)null || !localShip.IsPlayerInBoat(localPlayer))
			{
				DisembarkOwned(localPlayer);
				RestoreAllSwim();
			}
			else
			{
				if (!((Object)(object)localPlayer.GetControlledShip() == (Object)(object)localShip) && !((Character)localPlayer).IsAttached())
				{
					return;
				}
				List<Transform> anchors = GetAnchors(localShip);
				if (anchors.Count == 0)
				{
					return;
				}
				foreach (KeyValuePair<Character, int> item2 in BuildAssignments(localShip, anchors))
				{
					Character key = item2.Key;
					ZDO zdo2 = GetZdo(key);
					if (zdo2 == null || zdo2.GetLong("GoblinHeim.Companion.Owner", 0L) != localPlayer.GetPlayerID() || (Object)(object)key == (Object)null)
					{
						continue;
					}
					if (Decked.TryGetValue(key, out var value))
					{
						if ((Object)(object)value.Ship == (Object)(object)localShip && value.AnchorIndex == item2.Value)
						{
							continue;
						}
						Detach(key, null, placeByPlayer: false, 0, clearNetwork: true);
					}
					Transform val = anchors[item2.Value];
					if (Vector3.Distance(((Component)key).transform.position, val.position) > 14f)
					{
						BoostSwim(key);
					}
					else
					{
						Board(key, localShip, val, item2.Value);
					}
				}
			}
		}

		private static void Board(Character companion, Ship ship, Transform anchor, int anchorIndex)
		{
			ZNetView component = ((Component)companion).GetComponent<ZNetView>();
			ZNetView component2 = ((Component)ship).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid() && !((Object)(object)component2 == (Object)null) && component2.IsValid())
			{
				component.ClaimOwnership();
				ZDO zDO = component.GetZDO();
				ZDO zDO2 = component2.GetZDO();
				if (zDO != null && zDO2 != null)
				{
					zDO.Set("GoblinHeim.Companion.Boat.Decked", true);
					zDO.Set("GoblinHeim.Companion.Boat.User", ((ZDOID)(ref zDO2.m_uid)).UserID);
					zDO.Set("GoblinHeim.Companion.Boat.Id", (long)((ZDOID)(ref zDO2.m_uid)).ID);
					zDO.Set("GoblinHeim.Companion.Boat.Anchor", anchorIndex);
					AttachLocalRepresentation(companion, ship, anchor, anchorIndex);
					CompanionTrainingPlugin.Log.LogInfo((object)("[COMPANION-BOAT] " + CharmBinding.GetCompanionName(companion) + " boarded on network slot " + anchorIndex + "."));
				}
			}
		}

		private static void AttachLocalRepresentation(Character companion, Ship ship, Transform anchor, int anchorIndex)
		{
			//IL_0060: 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_0087: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)companion == (Object)null || (Object)(object)ship == (Object)null || (Object)(object)anchor == (Object)null || Decked.ContainsKey(companion))
			{
				return;
			}
			ZNetView component = ((Component)companion).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner())
			{
				BoostSwim(companion);
			}
			MonsterAI component2 = ((Component)companion).GetComponent<MonsterAI>();
			Rigidbody component3 = ((Component)companion).GetComponent<Rigidbody>();
			Vector3 val = FindDeckSurface(ship, anchor, companion);
			DeckState deckState = new DeckState
			{
				Ship = ship,
				Anchor = anchor,
				AnchorIndex = anchorIndex,
				FloorLocal = ((Component)ship).transform.InverseTransformPoint(val),
				RotationLocal = Quaternion.Inverse(((Component)ship).transform.rotation) * anchor.rotation,
				AiWasEnabled = ((Object)(object)component2 != (Object)null && ((Behaviour)component2).enabled),
				BodyWasKinematic = ((Object)(object)component3 != (Object)null && component3.isKinematic)
			};
			Decked[companion] = deckState;
			if ((Object)(object)component2 != (Object)null)
			{
				((Behaviour)component2).enabled = false;
			}
			if ((Object)(object)component3 != (Object)null)
			{
				component3.linearVelocity = Vector3.zero;
				component3.angularVelocity = Vector3.zero;
				component3.isKinematic = true;
			}
			Collider[] componentsInChildren = ((Component)companion).GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					deckState.Colliders[val2] = val2.enabled;
					val2.enabled = false;
				}
			}
			Sprite animalIcon = GetAnimalIcon(companion);
			if ((Object)(object)animalIcon != (Object)null)
			{
				Renderer[] componentsInChildren2 = ((Component)companion).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val3 in componentsInChildren2)
				{
					if (!((Object)(object)val3 == (Object)null))
					{
						deckState.Renderers[val3] = val3.enabled;
						val3.enabled = false;
					}
				}
				deckState.PassengerCard = CreatePassengerCard(companion, deckState, animalIcon);
			}
			KeepOnDeck(companion, deckState);
		}

		private static void KeepOnDeck(Character companion, DeckState state)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: 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_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)companion == (Object)null || (Object)(object)state?.Ship == (Object)null || (Object)(object)state.Anchor == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)companion).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner())
			{
				SetNetworkPosition(companion, ((Component)state.Ship).transform.TransformPoint(state.FloorLocal), ((Component)state.Ship).transform.rotation * state.RotationLocal);
			}
			if (!((Object)(object)state.PassengerCard != (Object)null))
			{
				return;
			}
			float num = ((CompanionTrainingPlugin.GetSpecies((Component)(object)companion) == CompanionTrainingPlugin.Species.Lox) ? 1.25f : 0.95f);
			state.PassengerCard.transform.position = ((Component)state.Ship).transform.TransformPoint(state.FloorLocal) + ((Component)state.Ship).transform.up * (num * 0.5f + 0.08f);
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				Vector3 val = ((Component)main).transform.position - state.PassengerCard.transform.position;
				if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
				{
					state.PassengerCard.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, ((Component)state.Ship).transform.up);
				}
			}
		}

		private static void SynchronizeNetworkPassengers()
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			ZDOID val = default(ZDOID);
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter == (Object)null)
				{
					continue;
				}
				ZDO zdo = GetZdo(allCharacter);
				if (zdo != null && zdo.GetBool("GoblinHeim.Companion.Boat.Decked", false) && !CharmBinding.IsAlpha(allCharacter))
				{
					ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
					bool clearNetwork = (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner();
					if (Decked.ContainsKey(allCharacter))
					{
						Detach(allCharacter, null, placeByPlayer: false, 0, clearNetwork);
					}
					continue;
				}
				if (zdo == null || !zdo.GetBool("GoblinHeim.Companion.Boat.Decked", false))
				{
					if (Decked.ContainsKey(allCharacter))
					{
						Detach(allCharacter, null, placeByPlayer: false, 0, clearNetwork: false);
					}
					continue;
				}
				((ZDOID)(ref val))..ctor(zdo.GetLong("GoblinHeim.Companion.Boat.User", 0L), (uint)zdo.GetLong("GoblinHeim.Companion.Boat.Id", 0L));
				ZNetScene instance = ZNetScene.instance;
				GameObject val2 = ((instance != null) ? instance.FindInstance(val) : null);
				Ship val3 = (((Object)(object)val2 == (Object)null) ? null : val2.GetComponent<Ship>());
				List<Transform> list = (((Object)(object)val3 == (Object)null) ? null : GetAnchors(val3));
				int num = zdo.GetInt("GoblinHeim.Companion.Boat.Anchor", -1);
				if ((Object)(object)val3 == (Object)null || list == null || num < 0 || num >= list.Count)
				{
					continue;
				}
				if (Decked.TryGetValue(allCharacter, out var value))
				{
					if ((Object)(object)value.Ship == (Object)(object)val3 && value.AnchorIndex == num)
					{
						continue;
					}
					Detach(allCharacter, null, placeByPlayer: false, 0, clearNetwork: false);
				}
				AttachLocalRepresentation(allCharacter, val3, list[num], num);
			}
		}

		private static Dictionary<Character, int> BuildAssignments(Ship ship, List<Transform> anchors)
		{
			Dictionary<Character, int> dictionary = new Dictionary<Character, int>();
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < anchors.Count; i++)
			{
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					if ((Object)(object)allPlayer != (Object)null && (Object)(object)allPlayer.GetAttachPoint() == (Object)(object)anchors[i])
					{
						hashSet.Add(i);
					}
				}
			}
			ZNetView component = ((Component)ship).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return dictionary;
			}
			List<Character> list = (from character in Character.GetAllCharacters()
				where (Object)(object)character != (Object)null && CharmBinding.IsAlpha(character) && GetZdo(character) != null
				orderby GetZdo(character).GetLong("GoblinHeim.Companion.Owner", 0L), ((ZDOID)(ref GetZdo(character).m_uid)).UserID, ((ZDOID)(ref GetZdo(character).m_uid)).ID
				select character).ToList();
			foreach (Character item in list)
			{
				ZDO zdo = GetZdo(item);
				if (zdo.GetBool("GoblinHeim.Companion.Boat.Decked", false) && zdo.GetLong("GoblinHeim.Companion.Boat.User", 0L) == ((ZDOID)(ref val.m_uid)).UserID && (uint)zdo.GetLong("GoblinHeim.Companion.Boat.Id", 0L) == ((ZDOID)(ref val.m_uid)).ID)
				{
					int num = zdo.GetInt("GoblinHeim.Companion.Boat.Anchor", -1);
					int num2 = ((num >= 0 && num < anchors.Count && !hashSet.Contains(num)) ? num : FirstFree(anchors.Count, hashSet));
					if (num2 >= 0)
					{
						dictionary[item] = num2;
						hashSet.Add(num2);
					}
				}
			}
			foreach (Character item2 in list)
			{
				if (dictionary.ContainsKey(item2) || (CompanionTrainingPlugin.GetSpecies((Component)(object)item2) == CompanionTrainingPlugin.Species.Lox && !AllowsLox(ship)))
				{
					continue;
				}
				MonsterAI component2 = ((Component)item2).GetComponent<MonsterAI>();
				object obj;
				if (component2 == null)
				{
					obj = null;
				}
				else
				{
					GameObject followTarget = component2.GetFollowTarget();
					obj = ((followTarget != null) ? followTarget.GetComponent<Player>() : null);
				}
				Player val2 = (Player)obj;
				if (!((Object)(object)val2 == (Object)null) && ship.IsPlayerInBoat(val2) && ((Object)(object)val2.GetControlledShip() == (Object)(object)ship || ((Character)val2).IsAttached()))
				{
					int num3 = FirstFree(anchors.Count, hashSet);
					if (num3 < 0)
					{
						break;
					}
					dictionary[item2] = num3;
					hashSet.Add(num3);
				}
			}
			return dictionary;
		}

		private static int FirstFree(int count, HashSet<int> unavailable)
		{
			for (int i = 0; i < count; i++)
			{
				if (!unavailable.Contains(i))
				{
					return i;
				}
			}
			return -1;
		}

		private static List<Transform> GetAnchors(Ship ship)
		{
			List<Transform> list = new List<Transform>();
			Chair[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Chair>(true);
			foreach (Chair val in componentsInChildren)
			{
				Transform val2 = (((Object)(object)val == (Object)null) ? null : val.m_attachPoint);
				if ((Object)(object)val2 != (Object)null && !list.Contains(val2))
				{
					list.Add(val2);
				}
			}
			return list.OrderBy(StablePath).ToList();
		}

		private static string StablePath(Transform transform)
		{
			string text = "";
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				text = val.GetSiblingIndex().ToString("D4") + "/" + ((Object)val).name + "/" + text;
				val = val.parent;
			}
			return text;
		}

		private static Vector3 FindDeckSurface(Ship ship, Transform anchor, Character companion)
		{
			//IL_004c: 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_0061: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_002c: 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_00a0: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			float num = 0.05f;
			Collider component = ((Component)companion).GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				float y = ((Component)companion).transform.position.y;
				Bounds bounds = component.bounds;
				num = Mathf.Clamp(y - ((Bounds)(ref bounds)).min.y, 0.05f, 2.5f);
			}
			RaycastHit[] array = Physics.RaycastAll(anchor.position + ((Component)ship).transform.up * 2f, -((Component)ship).transform.up, 6f, -1, (QueryTriggerInteraction)1);
			RaycastHit? val = null;
			float num2 = float.MaxValue;
			RaycastHit[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				RaycastHit value = array2[i];
				if (!((Object)(object)((RaycastHit)(ref value)).collider == (Object)null) && ((Component)((RaycastHit)(ref value)).collider).transform.IsChildOf(((Component)ship).transform) && ((RaycastHit)(ref value)).distance < num2)
				{
					val = value;
					num2 = ((RaycastHit)(ref value)).distance;
				}
			}
			Vector3 val2;
			if (!val.HasValue)
			{
				val2 = anchor.position;
			}
			else
			{
				RaycastHit value2 = val.Value;
				val2 = ((RaycastHit)(ref value2)).point;
			}
			return val2 + ((Component)ship).transform.up * num;
		}

		private static bool AllowsLox(Ship ship)
		{
			string text = Utils.GetPrefabName(((Component)ship).gameObject).ToLowerInvariant();
			if (!text.Contains("viking") && !text.Contains("long") && !text.Contains("ashland"))
			{
				return text.Contains("drakkar");
			}
			return true;
		}

		private static Sprite GetAnimalIcon(Character compani