Decompiled source of DecorativePet v1.5.0

plugins/DecorativePet/DecorativePet.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("DecorativePet")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.6.0")]
[assembly: AssemblyInformationalVersion("1.2.6")]
[assembly: AssemblyProduct("DecorativePet")]
[assembly: AssemblyTitle("DecorativePet")]
[assembly: AssemblyVersion("1.2.6.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 DecorativePet
{
	[BepInPlugin("ua.bohdan.decorativepet", "Decorative Pet", "1.5.0")]
	public sealed class DecorativePetPlugin : BaseUnityPlugin
	{
		private sealed class PetRecord
		{
			public object View;

			public object GameObject;

			public object Zdo;

			public string Name;

			public string Owner;

			public string Prefab;

			public bool Follow;

			public bool IgnoreEnemies;

			public object OwnerPlayer;

			public object Character;

			public object Tameable;

			public object MonsterAI;

			public IList AttackList;

			public List<object> AttackEntries;

			public object SecondaryAttack;

			public object AttackContainer;

			public object EmptyAttackContainer;

			public Dictionary<string, bool> AttackFlags;
		}

		[HarmonyPatch]
		private static class TerminalInitPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Terminal")?.GetMethods(AccessTools.all)
					where m.Name == "InitTerminal"
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static void Postfix()
			{
				_instance?.TryRegisterCommand();
			}
		}

		[HarmonyPatch]
		private static class ChatInputPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Chat")?.GetMethods(AccessTools.all)
					where m.Name == "InputText"
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object __instance)
			{
				if (_instance == null)
				{
					return true;
				}
				string text = ReadChatInput(__instance)?.Trim();
				if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("/", StringComparison.Ordinal))
				{
					return true;
				}
				List<string> list = Tokenize(text.Substring(1));
				if (list.Count == 0 || !string.Equals(list[0], "pet", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				ClearChatInput(__instance);
				_instance.SendChatCommand(list);
				return false;
			}
		}

		[HarmonyPatch]
		private static class ChatRpcPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Chat")?.GetMethods(AccessTools.all)
					where m.Name == "RPC_ChatMessage"
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object[] __args)
			{
				try
				{
					if (!TryParseChatCommand(__args, out var tokens, out var requestId, out var ownerName, out var position))
					{
						return true;
					}
					if (_instance != null && IsServer())
					{
						_instance.HandleChatCommand(tokens, requestId, ownerName, position);
					}
					return false;
				}
				catch (Exception ex)
				{
					DecorativePetPlugin instance = _instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogError((object)("Chat command handling failed: " + ex));
					}
					return true;
				}
			}
		}

		[HarmonyPatch]
		private static class CharacterDamagePatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Character")?.GetMethods(AccessTools.all)
					where m.Name == "Damage"
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object __instance)
			{
				return !IsPetComponent(__instance);
			}
		}

		[HarmonyPatch]
		private static class TameableHoverPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				Type type = FindType("Tameable");
				if (!(type == null))
				{
					return (from m in type.GetMethods(AccessTools.all)
						where m.Name == "GetHoverText" && m.ReturnType == typeof(string)
						select m).Cast<MethodBase>();
				}
				return new MethodBase[0];
			}

			private static void Postfix(object __instance, ref string __result)
			{
				object petZdo = GetPetZdo(__instance);
				if (IsPetZdo(petZdo))
				{
					_instance?.SetHoveredPet(__instance, petZdo);
					string text = ((GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1") ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies"));
					string zdoString = GetZdoString(petZdo, "ua.bohdan.decorativepet.name");
					if ((__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0)
					{
						__result = (__result ?? zdoString ?? "Pet") + "\n" + LocalText("Режим: ", "Mode: ") + text;
					}
				}
			}
		}

		[HarmonyPatch]
		private static class CharacterHoverPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				Type type = FindType("Character");
				if (!(type == null))
				{
					return (from m in type.GetMethods(AccessTools.all)
						where m.Name == "GetHoverText" && m.ReturnType == typeof(string)
						select m).Cast<MethodBase>();
				}
				return new MethodBase[0];
			}

			private static void Postfix(object __instance, ref string __result)
			{
				object petZdo = GetPetZdo(__instance);
				if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0)
				{
					_instance?.SetHoveredPet(__instance, petZdo);
					bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1";
					GetComponent(GetMember(__instance, "gameObject"), FindType("Tameable"));
					__result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies"));
				}
			}
		}

		[HarmonyPatch]
		private static class CharacterHoverNamePatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				Type type = FindType("Character");
				if (!(type == null))
				{
					return (from m in type.GetMethods(AccessTools.all)
						where m.Name == "GetHoverName" && m.ReturnType == typeof(string)
						select m).Cast<MethodBase>();
				}
				return new MethodBase[0];
			}

			private static void Postfix(object __instance, ref string __result)
			{
				object petZdo = GetPetZdo(__instance);
				if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0)
				{
					_instance?.SetHoveredPet(__instance, petZdo);
					bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1";
					__result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies"));
				}
			}
		}

		[HarmonyPatch]
		private static class IgnorePetTargetPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return (from m in new Type[2]
					{
						FindType("BaseAI"),
						FindType("MonsterAI")
					}.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all))
					where (m.Name == "SetTarget" || m.Name == "OnDamaged") && (m.ReturnType == typeof(void) || m.ReturnType == typeof(bool))
					select m).Cast<MethodBase>().Distinct();
			}

			private static bool Prefix(object __instance)
			{
				return !IsIgnoredPetAI(__instance);
			}
		}

		[HarmonyPatch]
		private static class IgnorePetAttackMethodsPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return (from m in new Type[2]
					{
						FindType("BaseAI"),
						FindType("MonsterAI")
					}.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all))
					where (m.Name == "Attack" || m.Name == "AttackTarget" || m.Name == "DoAttack") && (m.ReturnType == typeof(void) || m.ReturnType == typeof(bool))
					select m).Cast<MethodBase>().Distinct();
			}

			private static bool Prefix(object __instance)
			{
				return !IsIgnoredPetAI(__instance);
			}
		}

		[HarmonyPatch]
		private static class TameableInteractPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Tameable")?.GetMethods(AccessTools.all)
					where m.Name == "Interact" && m.ReturnType == typeof(bool)
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object __instance, object[] __args, ref bool __result)
			{
				object zdo = GetPetZdo(__instance);
				if (!IsPetZdo(zdo))
				{
					return true;
				}
				string zdoString = GetZdoString(zdo, "ua.bohdan.decorativepet.owner");
				object localPlayer = GetLocalPlayer();
				if (string.IsNullOrWhiteSpace(zdoString) || !string.Equals(zdoString, GetPlayerName(localPlayer), StringComparison.Ordinal))
				{
					return true;
				}
				if (HasFoodInHand(localPlayer))
				{
					return true;
				}
				bool flag = GetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies") == "1";
				if (_instance != null && IsServer())
				{
					PetRecord petRecord = _instance._cachedPets.FirstOrDefault((PetRecord p) => p.Zdo == zdo);
					if (petRecord != null)
					{
						petRecord.IgnoreEnemies = !flag;
						SetZdoString(petRecord.Zdo, "ua.bohdan.decorativepet.ignore_enemies", petRecord.IgnoreEnemies ? "1" : "0");
						_instance.ApplyIgnoreMode(petRecord);
						_instance.CommandMessage("[DecorativePet] Pet aggression: " + (petRecord.IgnoreEnemies ? "disabled" : "enabled") + ".", fromChat: false);
					}
				}
				else
				{
					_instance?.SendChatCommand(new List<string>
					{
						"pet",
						flag ? "attack" : "ignore"
					});
				}
				__result = true;
				return false;
			}
		}

		[HarmonyPatch]
		private static class PetPickupBlockPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("Pickable")?.GetMethods(AccessTools.all)
					where m.Name == "Interact" && m.ReturnType == typeof(bool)
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object __instance)
			{
				return !IsPetComponent(__instance);
			}
		}

		[HarmonyPatch]
		private static class PetItemPickupBlockPatch
		{
			private static MethodBase TargetMethod()
			{
				return (from m in FindType("ItemDrop")?.GetMethods(AccessTools.all)
					where m.Name == "Interact" && m.ReturnType == typeof(bool)
					orderby m.GetParameters().Length
					select m).FirstOrDefault();
			}

			private static bool Prefix(object __instance)
			{
				return !IsPetComponent(__instance);
			}
		}

		[HarmonyPatch]
		private static class FishPickupBlockPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return (from m in new Type[3]
					{
						FindType("Fish"),
						FindType("Pickable"),
						FindType("ItemDrop")
					}.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all))
					where (m.Name == "Interact" || m.Name == "Pick" || m.Name == "Pickup") && (m.ReturnType == typeof(bool) || m.ReturnType == typeof(void))
					select m).Cast<MethodBase>().Distinct();
			}

			private static bool Prefix(object __instance)
			{
				return !IsPetComponent(__instance);
			}
		}

		[HarmonyPatch]
		private static class FishHoverPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return (from m in new Type[3]
					{
						FindType("Fish"),
						FindType("ItemDrop"),
						FindType("Pickable")
					}.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all))
					where (m.Name == "GetHoverText" || m.Name == "GetHoverName") && m.ReturnType == typeof(string)
					select m).Cast<MethodBase>().Distinct();
			}

			private static void Postfix(object __instance, ref string __result)
			{
				object petZdo = GetPetZdo(__instance);
				if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("[Decorative Pet]", StringComparison.Ordinal) < 0)
				{
					bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1";
					__result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? "Allow attacks" : "Ignore enemies");
				}
			}
		}

		[HarmonyPatch]
		private static class FishPickupMethodPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return (from m in new Type[3]
					{
						FindType("Fish"),
						FindType("Pickable"),
						FindType("ItemDrop")
					}.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all))
					where (m.Name == "Pickup" || m.Name == "Pick" || m.Name == "Take") && (m.ReturnType == typeof(bool) || m.ReturnType == typeof(void))
					select m).Cast<MethodBase>().Distinct();
			}

			private static bool Prefix(object __instance)
			{
				return !IsPetComponent(__instance);
			}
		}

		public const string PluginGuid = "ua.bohdan.decorativepet";

		public const string PluginName = "Decorative Pet";

		public const string PluginVersion = "1.5.0";

		private const string MarkerKey = "ua.bohdan.decorativepet.marker";

		private const string NameKey = "ua.bohdan.decorativepet.name";

		private const string OwnerKey = "ua.bohdan.decorativepet.owner";

		private const string PrefabKey = "ua.bohdan.decorativepet.prefab";

		private const string FollowKey = "ua.bohdan.decorativepet.follow";

		private const string IgnoreEnemiesKey = "ua.bohdan.decorativepet.ignore_enemies";

		private const string ChatCommandPrefix = "\u200b[DecorativePetCmd] ";

		private static DecorativePetPlugin _instance;

		private Harmony _harmony;

		private readonly List<PetRecord> _cachedPets = new List<PetRecord>();

		private DateTime _nextBehaviourUtc = DateTime.MinValue;

		private const double BehaviourIntervalSeconds = 0.5;

		private const int ChatRequestHistoryLimit = 256;

		private readonly Queue<string> _handledChatRequestOrder = new Queue<string>();

		private readonly HashSet<string> _handledChatRequests = new HashSet<string>(StringComparer.Ordinal);

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

		private object _hoveredPet;

		private object _hoveredPetZdo;

		private DateTime _hoveredUntilUtc;

		private ConfigEntry<string> _defaultPrefab;

		private ConfigEntry<string> _defaultName;

		private ConfigEntry<float> _followDistance;

		private ConfigEntry<float> _teleportDistance;

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			_instance = this;
			_defaultPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Pet", "DefaultPrefab", "Wolf", "Vanilla prefab name used by 'pet spawn' (Wolf, Boar, Lox, Chicken, Deer, ...). ");
			_defaultName = ((BaseUnityPlugin)this).Config.Bind<string>("Pet", "DefaultName", "Рекс", "Name used by 'pet spawn' when no name is supplied.");
			_followDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Pet", "FollowDistance", 3f, "How close the pet stays to its owner, in metres.");
			_teleportDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Pet", "TeleportDistance", 15f, "If the pet is farther than this many metres from its owner, teleport it back.");
			try
			{
				_harmony = new Harmony("ua.bohdan.decorativepet");
				_harmony.PatchAll();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Decorative Pet 1.5.0 loaded. Type '/pet help' in the Valheim chat.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to initialize Decorative Pet: " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_cachedPets.Clear();
			if (_instance == this)
			{
				_instance = null;
			}
		}

		private void Update()
		{
			if (_cachedPets.Count != 0)
			{
				DateTime utcNow = DateTime.UtcNow;
				if (!(utcNow < _nextBehaviourUtc))
				{
					_nextBehaviourUtc = utcNow.AddSeconds(0.5);
					ApplyPetBehaviour();
				}
			}
		}

		private void SetHoveredPet(object tameable, object zdo)
		{
			_hoveredPet = tameable;
			_hoveredPetZdo = zdo;
			_hoveredUntilUtc = DateTime.UtcNow.AddMilliseconds(300.0);
		}

		private void OnGUI()
		{
			if (_hoveredPetZdo == null || DateTime.UtcNow > _hoveredUntilUtc)
			{
				return;
			}
			Type type = FindType("UnityEngine.Screen");
			Type type2 = FindType("UnityEngine.Rect");
			Type type3 = FindType("UnityEngine.GUI");
			if (type == null || type2 == null || type3 == null)
			{
				return;
			}
			try
			{
				float num = Convert.ToSingle(GetStaticMember(type, "width"), CultureInfo.InvariantCulture);
				float num2 = Convert.ToSingle(GetStaticMember(type, "height"), CultureInfo.InvariantCulture);
				ConstructorInfo constructor = type2.GetConstructor(new Type[4]
				{
					typeof(float),
					typeof(float),
					typeof(float),
					typeof(float)
				});
				if (!(constructor == null))
				{
					object obj = constructor.Invoke(new object[4]
					{
						num * 0.5f - 120f,
						num2 * 0.5f + 45f,
						240f,
						44f
					});
					InvokeStaticCompatible(type3, "Box", obj, "");
					string text = ((GetZdoString(_hoveredPetZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1") ? LocalText("Ігнор", "Ignore") : LocalText("Атака", "Attack"));
					InvokeStaticCompatible(type3, "Label", constructor.Invoke(new object[4]
					{
						num * 0.5f - 110f,
						num2 * 0.5f + 51f,
						220f,
						18f
					}), LocalText("Їжа: ", "Food: ") + FoodBarText(_hoveredPet) + "   " + LocalText("Режим: ", "Mode: ") + text);
				}
			}
			catch
			{
			}
		}

		private static string FoodBarText(object tameable)
		{
			string text = FoodText(tameable);
			if (text.IndexOf("0%", StringComparison.Ordinal) >= 0)
			{
				return "---------- 0%";
			}
			if (text.IndexOf("100%", StringComparison.Ordinal) >= 0)
			{
				return "########## 100%";
			}
			return "----------";
		}

		private static bool InvokeStaticCompatible(Type type, string name, params object[] args)
		{
			if (type == null)
			{
				return false;
			}
			foreach (MethodInfo item in from m in type.GetMethods(AccessTools.all)
				where m.Name == name && m.IsStatic
				select m)
			{
				if (item.GetParameters().Length == args.Length)
				{
					try
					{
						item.Invoke(null, args);
						return true;
					}
					catch
					{
					}
				}
			}
			return false;
		}

		private void TryRegisterCommand()
		{
			try
			{
				Type type = FindType("Terminal");
				if (type == null)
				{
					return;
				}
				FieldInfo fieldInfo = FindFieldNoLog(type, "commands", isStatic: true);
				if (fieldInfo == null || fieldInfo.GetValue(null) == null)
				{
					return;
				}
				Type nestedType = type.GetNestedType("ConsoleCommand", BindingFlags.Public | BindingFlags.NonPublic);
				if (nestedType == null)
				{
					return;
				}
				ConstructorInfo constructorInfo = (from c in nestedType.GetConstructors(AccessTools.all)
					where c.GetParameters().Length >= 3
					where c.GetParameters().Any((ParameterInfo p) => typeof(Delegate).IsAssignableFrom(p.ParameterType))
					select c).OrderByDescending(delegate(ConstructorInfo c)
				{
					MethodInfo methodInfo = c.GetParameters().FirstOrDefault((ParameterInfo p) => typeof(Delegate).IsAssignableFrom(p.ParameterType))?.ParameterType.GetMethod("Invoke");
					return (methodInfo != null && methodInfo.ReturnType == typeof(void)) ? 1 : 0;
				}).FirstOrDefault();
				if (constructorInfo == null)
				{
					return;
				}
				ParameterInfo[] parameters = constructorInfo.GetParameters();
				object[] array = new object[parameters.Length];
				int num = 0;
				bool flag = false;
				for (int num2 = 0; num2 < parameters.Length; num2++)
				{
					ParameterInfo parameterInfo = parameters[num2];
					Type parameterType = parameterInfo.ParameterType;
					if (parameterType == typeof(string))
					{
						array[num2] = ((num++ == 0) ? "pet" : "Spawn and control a named, passive vanilla companion.");
					}
					else if (!flag && typeof(Delegate).IsAssignableFrom(parameterType))
					{
						array[num2] = BuildConsoleDelegate(parameterType);
						flag = array[num2] != null;
					}
					else if (parameterInfo.HasDefaultValue)
					{
						array[num2] = parameterInfo.DefaultValue;
					}
					else
					{
						array[num2] = GetDefault(parameterType);
					}
				}
				if (flag)
				{
					constructorInfo.Invoke(array);
					TryInvokeCompatible(GetStaticMember(type, "m_terminal") ?? GetStaticMember(type, "instance"), "updateCommandList");
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered console command: pet");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Decorative Pet command registration is waiting: " + ex.Message));
			}
		}

		private static Delegate BuildConsoleDelegate(Type delegateType)
		{
			MethodInfo method = delegateType.GetMethod("Invoke");
			if (method == null || method.GetParameters().Length != 1)
			{
				return null;
			}
			ParameterExpression parameterExpression = Expression.Parameter(method.GetParameters()[0].ParameterType, "args");
			Expression expression = Expression.Call(typeof(DecorativePetPlugin).GetMethod("DispatchConsole", BindingFlags.Static | BindingFlags.NonPublic), Expression.Convert(parameterExpression, typeof(object)));
			Expression body = expression;
			if (method.ReturnType != typeof(void))
			{
				body = Expression.Block(expression, Expression.Default(method.ReturnType));
			}
			return Expression.Lambda(delegateType, body, parameterExpression).Compile();
		}

		private static void DispatchConsole(object eventArgs)
		{
			_instance?.HandleConsole(eventArgs);
		}

		private void HandleConsole(object eventArgs)
		{
			string text = Convert.ToString(GetMember(eventArgs, "FullLine"), CultureInfo.InvariantCulture);
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Convert.ToString(GetMember(eventArgs, "ArgsAll"), CultureInfo.InvariantCulture);
			}
			List<string> list = Tokenize(text);
			if (list.Count == 0)
			{
				PrintHelp(fromChat: false);
				return;
			}
			if (!string.Equals(list[0], "pet", StringComparison.OrdinalIgnoreCase))
			{
				list.Insert(0, "pet");
			}
			object position = GetPosition(GetMember(GetLocalPlayer(), "transform"));
			ExecuteCommand(list, GetLocalPlayerName(), position, fromChat: false);
		}

		private void HandleChatCommand(List<string> tokens, string requestId, string ownerName, object chatPosition)
		{
			if (IsServer() && (string.IsNullOrWhiteSpace(requestId) || MarkChatRequest(requestId)))
			{
				if (string.IsNullOrWhiteSpace(ownerName))
				{
					ownerName = "Pet owner";
				}
				ExecuteCommand(tokens, ownerName, chatPosition, fromChat: true);
			}
		}

		private bool MarkChatRequest(string requestId)
		{
			if (_handledChatRequests.Contains(requestId))
			{
				return false;
			}
			_handledChatRequests.Add(requestId);
			_handledChatRequestOrder.Enqueue(requestId);
			while (_handledChatRequestOrder.Count > 256)
			{
				_handledChatRequests.Remove(_handledChatRequestOrder.Dequeue());
			}
			return true;
		}

		private void ExecuteCommand(List<string> tokens, string ownerName, object requestedPosition, bool fromChat)
		{
			if (tokens == null || tokens.Count == 0)
			{
				PrintHelp(fromChat);
				return;
			}
			if (!string.Equals(tokens[0], "pet", StringComparison.OrdinalIgnoreCase))
			{
				tokens.Insert(0, "pet");
			}
			switch ((tokens.Count > 1) ? tokens[1].ToLowerInvariant() : "help")
			{
			case "spawn":
				SpawnFromCommand(tokens, ownerName, requestedPosition, fromChat);
				break;
			case "name":
			case "rename":
				RenameFromCommand(tokens, ownerName, fromChat);
				break;
			case "follow":
				SetFollowForOwner(follow: true, ownerName, fromChat);
				break;
			case "stay":
			case "stop":
				SetFollowForOwner(follow: false, ownerName, fromChat);
				break;
			case "ignore":
			case "passive":
				SetIgnoreEnemiesForOwner(ignore: true, ownerName, fromChat);
				break;
			case "attack":
			case "aggressive":
				SetIgnoreEnemiesForOwner(ignore: false, ownerName, fromChat);
				break;
			case "remove":
			case "delete":
				RemoveOwnedPets(ownerName, fromChat);
				break;
			case "list":
				ListPets(ownerName, fromChat);
				break;
			default:
				PrintHelp(fromChat);
				break;
			}
		}

		private void SpawnFromCommand(List<string> tokens, string ownerName, object requestedPosition, bool fromChat)
		{
			string text = ((tokens.Count > 2 && !string.IsNullOrWhiteSpace(tokens[2])) ? tokens[2] : _defaultPrefab.Value);
			string text2 = ((tokens.Count > 3) ? string.Join(" ", tokens.Skip(3).ToArray()) : _defaultName.Value);
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = "Pet";
			}
			if (!IsServer())
			{
				CommandMessage("[DecorativePet] Spawn must be handled by the world host/server.", fromChat);
				return;
			}
			object obj = FindPlayerByName(ownerName);
			object obj2 = GetPosition(GetMember(obj, "transform"));
			if (obj2 == null)
			{
				obj2 = OffsetPosition(requestedPosition, 0f, -1.5f, 0f);
			}
			if (obj2 == null)
			{
				CommandMessage("[DecorativePet] The requesting player is not ready yet.", fromChat);
				return;
			}
			obj2 = OffsetPosition(obj2, 2f, 0f, 0f);
			object member = GetMember(GetMember(obj, "transform"), "rotation");
			if (CreateNetworkPet(text, text2, obj2, ownerName, obj, member) != null)
			{
				CommandMessage("[DecorativePet] Spawned " + text + " '" + text2 + "' for " + ownerName + ".", fromChat);
			}
		}

		private void RenameFromCommand(List<string> tokens, string ownerName, bool fromChat)
		{
			if (tokens.Count < 3)
			{
				CommandMessage("Usage: /pet name <new name>", fromChat);
				return;
			}
			string text = string.Join(" ", tokens.Skip(2).ToArray());
			int num = 0;
			foreach (PetRecord item in _cachedPets.ToList())
			{
				if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal))
				{
					SetZdoString(item.Zdo, "ua.bohdan.decorativepet.name", text);
					ApplyName(item.GameObject, item.Zdo, text);
					item.Name = text;
					num++;
				}
			}
			CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Renamed " + num + " pet(s) to '" + text + "'."), fromChat);
		}

		private void SetFollowForOwner(bool follow, string ownerName, bool fromChat)
		{
			int num = 0;
			foreach (PetRecord item in _cachedPets.ToList())
			{
				if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal))
				{
					SetZdoString(item.Zdo, "ua.bohdan.decorativepet.follow", follow ? "1" : "0");
					item.Follow = follow;
					ApplyNativeFollow(item, follow);
					num++;
				}
			}
			CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Pet mode: " + (follow ? "follow" : "stay") + "."), fromChat);
		}

		private void SetIgnoreEnemiesForOwner(bool ignore, string ownerName, bool fromChat)
		{
			int num = 0;
			foreach (PetRecord item in _cachedPets.ToList())
			{
				if (string.Equals(item.Owner?.Trim(), ownerName?.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					item.IgnoreEnemies = ignore;
					SetZdoString(item.Zdo, "ua.bohdan.decorativepet.ignore_enemies", ignore ? "1" : "0");
					ApplyIgnoreMode(item);
					num++;
				}
			}
			if (num == 0)
			{
				List<PetRecord> list = _cachedPets.Where((PetRecord p) => string.Equals(p.Owner?.Trim(), ownerName?.Trim(), StringComparison.OrdinalIgnoreCase)).ToList();
				if (list.Count == 0 && _cachedPets.Count == 1)
				{
					list = _cachedPets.ToList();
				}
				foreach (PetRecord item2 in list)
				{
					item2.IgnoreEnemies = ignore;
					SetZdoString(item2.Zdo, "ua.bohdan.decorativepet.ignore_enemies", ignore ? "1" : "0");
					ApplyIgnoreMode(item2);
					num++;
				}
			}
			CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Pet aggression: " + (ignore ? "disabled" : "enabled") + "."), fromChat);
		}

		private void RemoveOwnedPets(string ownerName, bool fromChat)
		{
			if (!IsServer())
			{
				CommandMessage("[DecorativePet] Remove must be handled by the world host/server.", fromChat);
				return;
			}
			int num = 0;
			foreach (PetRecord item in _cachedPets.ToList())
			{
				if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal) && DestroyNetworkPet(item))
				{
					_cachedPets.Remove(item);
					num++;
				}
			}
			CommandMessage("[DecorativePet] Removed " + num + " pet(s).", fromChat);
		}

		private void ListPets(string ownerName, bool fromChat)
		{
			List<PetRecord> list = _cachedPets.ToList();
			if (fromChat)
			{
				list = list.Where((PetRecord p) => string.Equals(p.Owner, ownerName, StringComparison.Ordinal)).ToList();
			}
			if (list.Count == 0)
			{
				CommandMessage("[DecorativePet] No decorative pets found for this player.", fromChat);
				return;
			}
			foreach (PetRecord item in list)
			{
				CommandMessage("[DecorativePet] " + item.Prefab + " '" + item.Name + "' — owner: " + item.Owner, fromChat);
			}
		}

		private void PrintHelp(bool fromChat)
		{
			CommandMessage("[DecorativePet] /pet spawn [Prefab] [Name] | /pet name <Name> | /pet follow | /pet stay | /pet ignore | /pet attack | /pet list | /pet remove", fromChat);
			CommandMessage("[DecorativePet] Default prefab: " + _defaultPrefab.Value + "; default name: " + _defaultName.Value + ".", fromChat);
		}

		private void CommandMessage(string message, bool fromChat)
		{
			if (fromChat)
			{
				SendChatNotice(message);
			}
			else
			{
				Print(message);
			}
		}

		private void SendChatCommand(List<string> tokens)
		{
			object staticMember = GetStaticMember(FindType("Chat"), "instance");
			Type type = FindType("Talker+Type") ?? FindType("Talker/Type") ?? FindType("Talker")?.GetNestedType("Type", AccessTools.all);
			if (staticMember == null || type == null)
			{
				Print("[DecorativePet] Chat is not ready yet.");
				return;
			}
			string text = Guid.NewGuid().ToString("N");
			string text2 = string.Join(" ", tokens.ToArray());
			string text3 = "\u200b[DecorativePetCmd] " + text + "|" + text2;
			object obj = Enum.ToObject(type, 2);
			if (!TryInvokeCompatible(staticMember, "SendText", obj, text3))
			{
				Print("[DecorativePet] Could not send the pet command to the host.");
			}
		}

		private void SendChatNotice(string message)
		{
			try
			{
				object staticMember = GetStaticMember(FindType("Chat"), "instance");
				Type type = FindType("Talker+Type") ?? FindType("Talker/Type") ?? FindType("Talker")?.GetNestedType("Type", AccessTools.all);
				if (staticMember != null && type != null && TryInvokeCompatible(staticMember, "SendText", Enum.ToObject(type, 2), message))
				{
					return;
				}
			}
			catch
			{
			}
			Print(message);
		}

		private static string ReadChatInput(object chat)
		{
			object member = GetMember(GetMember(GetMember(chat, "m_terminalInstance") ?? chat, "m_input") ?? GetMember(chat, "m_input"), "text");
			if (member != null)
			{
				return Convert.ToString(member, CultureInfo.InvariantCulture);
			}
			return "";
		}

		private static void ClearChatInput(object chat)
		{
			SetMember(GetMember(GetMember(chat, "m_terminalInstance") ?? chat, "m_input") ?? GetMember(chat, "m_input"), "text", "");
		}

		private static bool TryParseChatCommand(object[] arguments, out List<string> tokens, out string requestId, out string ownerName, out object position)
		{
			tokens = null;
			requestId = "";
			ownerName = "";
			position = null;
			if (arguments == null || arguments.Length < 5)
			{
				return false;
			}
			string text = arguments[4] as string;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string text2 = null;
			string[] array = new string[2] { "\u200b[DecorativePetCmd] ", "[DecorativePetCmd] " };
			foreach (string text3 in array)
			{
				if (text.StartsWith(text3, StringComparison.Ordinal))
				{
					text2 = text.Substring(text3.Length).Trim();
					break;
				}
			}
			if (text2 == null)
			{
				return false;
			}
			int num = text2.IndexOf('|');
			if (num > 0)
			{
				requestId = text2.Substring(0, num);
				text2 = text2.Substring(num + 1).Trim();
			}
			tokens = Tokenize(text2);
			if (tokens.Count == 0)
			{
				return false;
			}
			if (!string.Equals(tokens[0], "pet", StringComparison.OrdinalIgnoreCase))
			{
				tokens.Insert(0, "pet");
			}
			position = arguments[1];
			ownerName = GetChatSenderName(arguments[3]);
			return true;
		}

		private static string GetChatSenderName(object sender)
		{
			if (sender is string result)
			{
				return result;
			}
			object obj = GetMember(sender, "Name") ?? GetMember(sender, "m_name") ?? GetMember(sender, "UserName");
			if (obj != null)
			{
				return Convert.ToString(obj, CultureInfo.InvariantCulture);
			}
			return "";
		}

		private static List<string> Tokenize(string line)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrWhiteSpace(line))
			{
				return list;
			}
			foreach (Match item in Regex.Matches(line, "\"([^\"]*)\"|(\\S+)"))
			{
				list.Add(item.Groups[1].Success ? item.Groups[1].Value : item.Groups[2].Value);
			}
			return list;
		}

		private void Print(string message)
		{
			try
			{
				Type type = FindType("Terminal");
				object obj = GetStaticMember(type, "m_terminal") ?? GetStaticMember(type, "instance");
				if (obj != null)
				{
					MethodInfo methodInfo = (from m in obj.GetType().GetMethods(AccessTools.all)
						where m.Name == "AddString"
						where m.GetParameters().Length >= 1
						where m.GetParameters()[0].ParameterType == typeof(string)
						orderby m.GetParameters().Length
						select m).FirstOrDefault();
					if (methodInfo != null)
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						object[] array = new object[parameters.Length];
						array[0] = message;
						for (int num = 1; num < array.Length; num++)
						{
							array[num] = GetDefault(parameters[num].ParameterType);
						}
						methodInfo.Invoke(obj, array);
						return;
					}
				}
			}
			catch
			{
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		private object CreateNetworkPet(string prefabName, string petName, object position, string ownerName, object ownerPlayer, object rotation)
		{
			object staticMember = GetStaticMember(FindType("ZNetScene"), "instance");
			if (staticMember == null)
			{
				Print("[DecorativePet] ZNetScene is not ready.");
				return null;
			}
			object obj = InvokeCompatible(staticMember, "GetPrefab", prefabName);
			int num = StableHash(prefabName);
			if (obj == null)
			{
				obj = InvokeCompatible(staticMember, "GetPrefab", num);
			}
			if (obj == null)
			{
				Print("[DecorativePet] Vanilla prefab not found: " + prefabName + ". Try Wolf, Boar, Lox, Chicken or Deer.");
				return null;
			}
			object obj2;
			try
			{
				obj2 = InstantiatePrefab(obj, position, rotation);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to instantiate pet prefab: " + ex));
				return null;
			}
			if (obj2 == null)
			{
				Print("[DecorativePet] Valheim could not instantiate prefab: " + prefabName + ".");
				return null;
			}
			object netView = GetNetView(obj2);
			object zdo = GetZdo(netView);
			if (zdo == null)
			{
				Print("[DecorativePet] Spawned prefab has no network object: " + prefabName + ".");
				return null;
			}
			SetZdoString(zdo, "ua.bohdan.decorativepet.marker", "1");
			SetZdoString(zdo, "ua.bohdan.decorativepet.name", petName);
			SetZdoString(zdo, "ua.bohdan.decorativepet.prefab", prefabName);
			SetZdoString(zdo, "ua.bohdan.decorativepet.owner", ownerName ?? "");
			SetZdoString(zdo, "ua.bohdan.decorativepet.follow", "1");
			SetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies", "0");
			SetMember(zdo, "Persistent", false);
			TrackPetView(netView, ownerPlayer);
			return zdo;
		}

		private static object InstantiatePrefab(object prefab, object position, object rotation)
		{
			if (prefab == null || position == null)
			{
				return null;
			}
			Type type = FindType("UnityEngine.Quaternion");
			if (rotation == null)
			{
				rotation = GetStaticMember(type, "identity");
			}
			return ((from m in FindType("UnityEngine.Object")?.GetMethods(AccessTools.all)
				where m.Name == "Instantiate" && m.IsStatic
				where !m.ContainsGenericParameters
				where m.GetParameters().Length == 3
				select m).FirstOrDefault(delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return parameters[0].ParameterType.IsInstanceOfType(prefab) && parameters[1].ParameterType.IsInstanceOfType(position) && rotation != null && parameters[2].ParameterType.IsInstanceOfType(rotation);
			}))?.Invoke(null, new object[3] { prefab, position, rotation });
		}

		private void TrackPetView(object view, object ownerPlayer)
		{
			if (view == null || _cachedPets.Any((PetRecord p) => p.View == view))
			{
				return;
			}
			object zdo = GetZdo(view);
			if (!IsPetZdo(zdo))
			{
				return;
			}
			object member = GetMember(view, "gameObject");
			PetRecord pet = new PetRecord
			{
				View = view,
				GameObject = member,
				Zdo = zdo,
				Name = GetZdoString(zdo, "ua.bohdan.decorativepet.name"),
				Owner = GetZdoString(zdo, "ua.bohdan.decorativepet.owner"),
				Prefab = GetZdoString(zdo, "ua.bohdan.decorativepet.prefab"),
				Follow = (GetZdoString(zdo, "ua.bohdan.decorativepet.follow") != "0"),
				IgnoreEnemies = (GetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"),
				OwnerPlayer = ownerPlayer,
				Character = GetComponent(member, FindType("Character")),
				Tameable = GetComponent(member, FindType("Tameable")),
				MonsterAI = GetComponent(member, FindType("MonsterAI"))
			};
			if (pet.MonsterAI != null)
			{
				pet.AttackContainer = GetMember(pet.MonsterAI, "m_attacks");
				pet.AttackList = pet.AttackContainer as IList;
				if (pet.AttackList != null)
				{
					pet.AttackEntries = pet.AttackList.Cast<object>().ToList();
				}
				pet.SecondaryAttack = GetMember(pet.MonsterAI, "m_secondaryAttack");
				pet.AttackFlags = (from f in pet.MonsterAI.GetType().GetFields(AccessTools.all)
					where f.FieldType == typeof(bool) && f.Name.IndexOf("attack", StringComparison.OrdinalIgnoreCase) >= 0
					select f).ToDictionary((FieldInfo f) => f.Name, (FieldInfo f) => (bool)f.GetValue(pet.MonsterAI));
			}
			_cachedPets.Add(pet);
			string name = (string.IsNullOrWhiteSpace(pet.Name) ? "Pet" : pet.Name);
			ApplyName(pet.GameObject, pet.Zdo, name);
			MakePassiveAndImmortal(pet);
			ApplyIgnoreMode(pet);
			ApplyNativeFollow(pet, pet.Follow);
		}

		private void ApplyPetBehaviour()
		{
			foreach (PetRecord item in _cachedPets.ToList())
			{
				if (item.IgnoreEnemies)
				{
					ClearMonsterTarget(item);
				}
				if (item.Follow)
				{
					object obj = item.OwnerPlayer;
					if (obj == null)
					{
						obj = (item.OwnerPlayer = FindPlayerByName(item.Owner));
						ApplyNativeFollow(item, follow: true);
					}
					if (obj != null)
					{
						TeleportIfTooFar(item, obj);
					}
				}
			}
		}

		private void MakePassiveAndImmortal(PetRecord pet)
		{
			if (pet != null && pet.GameObject != null && pet.Character != null)
			{
				InvokeCompatible(pet.Character, "SetTamed", true);
				object member = GetMember(pet.Character, "m_maxHealth");
				if (member != null)
				{
					SetMember(pet.Character, "m_health", member);
				}
			}
		}

		private void ApplyIgnoreMode(PetRecord pet)
		{
			if (pet == null || pet.MonsterAI == null)
			{
				return;
			}
			if (pet.IgnoreEnemies)
			{
				IEnumerable<string> enumerable = pet.AttackFlags?.Keys;
				foreach (string item in enumerable ?? Enumerable.Empty<string>())
				{
					SetMember(pet.MonsterAI, item, false);
				}
				SetMember(pet.MonsterAI, "m_attackPlayerObjects", false);
				SetMember(pet.MonsterAI, "m_attackPlayerObjectsWhenAlerted", false);
				SetMember(pet.MonsterAI, "m_targetCreature", null);
				SetMember(pet.MonsterAI, "m_targetPlayer", null);
				SetMember(pet.MonsterAI, "m_targetStatic", null);
				if (pet.AttackList != null)
				{
					pet.AttackList.Clear();
				}
				else if (pet.AttackContainer != null)
				{
					Type type = pet.AttackContainer.GetType();
					if (type.IsArray)
					{
						pet.EmptyAttackContainer = Array.CreateInstance(type.GetElementType(), 0);
					}
					else
					{
						pet.EmptyAttackContainer = null;
					}
					if (pet.EmptyAttackContainer != null)
					{
						SetMember(pet.MonsterAI, "m_attacks", pet.EmptyAttackContainer);
					}
				}
				SetMember(pet.MonsterAI, "m_secondaryAttack", null);
				InvokeCompatible(pet.MonsterAI, "SetAlerted", false);
				ClearMonsterTarget(pet);
			}
			else if (pet.AttackList != null && pet.AttackEntries != null && pet.AttackList.Count == 0)
			{
				RestoreAttackFlags(pet);
				foreach (object attackEntry in pet.AttackEntries)
				{
					pet.AttackList.Add(attackEntry);
				}
				SetMember(pet.MonsterAI, "m_secondaryAttack", pet.SecondaryAttack);
			}
			else if (!pet.IgnoreEnemies && pet.AttackContainer != null)
			{
				RestoreAttackFlags(pet);
				SetMember(pet.MonsterAI, "m_attacks", pet.AttackContainer);
				SetMember(pet.MonsterAI, "m_secondaryAttack", pet.SecondaryAttack);
			}
		}

		private static void RestoreAttackFlags(PetRecord pet)
		{
			if (pet == null || pet.MonsterAI == null || pet.AttackFlags == null)
			{
				return;
			}
			foreach (KeyValuePair<string, bool> attackFlag in pet.AttackFlags)
			{
				SetMember(pet.MonsterAI, attackFlag.Key, attackFlag.Value);
			}
		}

		private static void ClearMonsterTarget(PetRecord pet)
		{
			if (pet != null && pet.MonsterAI != null)
			{
				SetMember(pet.MonsterAI, "m_targetCreature", null);
				SetMember(pet.MonsterAI, "m_targetPlayer", null);
				SetMember(pet.MonsterAI, "m_targetStatic", null);
				SetMember(pet.MonsterAI, "m_targetZDO", null);
				SetMember(pet.MonsterAI, "m_alerted", false);
				InvokeCompatible(pet.MonsterAI, "SetAlerted", false);
			}
		}

		private void ApplyName(object gameObject, object zdo, string name)
		{
			if (gameObject != null)
			{
				object component = GetComponent(gameObject, FindType("Character"));
				if (component != null)
				{
					InvokeCompatible(component, "SetTamed", true);
				}
				Type componentType = FindType("Tameable");
				object component2 = GetComponent(gameObject, componentType);
				if (component2 != null)
				{
					InvokeCompatible(component2, "Tame");
					InvokeCompatible(component2, "SetName", name);
					SetMember(component2, "m_name", name);
				}
				object staticMember = GetStaticMember(FindType("ZDOVars"), "s_tamedName");
				if (staticMember != null)
				{
					InvokeCompatible(zdo, "Set", staticMember, name);
				}
			}
		}

		private void ApplyNativeFollow(PetRecord pet, bool follow)
		{
			if (pet == null || pet.MonsterAI == null)
			{
				return;
			}
			object obj = null;
			if (follow)
			{
				if (pet.OwnerPlayer == null)
				{
					pet.OwnerPlayer = FindPlayerByName(pet.Owner);
				}
				obj = GetMember(pet.OwnerPlayer, "gameObject");
			}
			InvokeCompatible(pet.MonsterAI, "SetFollowTarget", obj);
		}

		private void TeleportIfTooFar(PetRecord pet, object owner)
		{
			if (pet != null && pet.GameObject != null && owner != null)
			{
				object member;
				object member2;
				try
				{
					member = GetMember(pet.GameObject, "transform");
					member2 = GetMember(owner, "transform");
				}
				catch
				{
					return;
				}
				object position = GetPosition(member);
				object position2 = GetPosition(member2);
				if (position != null && position2 != null && VectorDistance(position, position2) >= Math.Max(_teleportDistance.Value, _followDistance.Value + 1f))
				{
					object obj2 = OffsetPosition(position2, 1.5f, 0f, 0f);
					SetMember(member, "position", obj2);
					InvokeCompatible(pet.Zdo, "SetPosition", obj2);
				}
			}
		}

		private bool DestroyNetworkPet(PetRecord pet)
		{
			object netView = GetNetView(pet.GameObject);
			if (netView != null && TryInvokeCompatible(netView, "Destroy"))
			{
				return true;
			}
			object staticMember = GetStaticMember(FindType("ZDOMan"), "instance");
			if (staticMember != null && TryInvokeCompatible(staticMember, "DestroyZDO", pet.Zdo))
			{
				return true;
			}
			return false;
		}

		private static bool IsPetZdo(object zdo)
		{
			if (zdo != null)
			{
				return GetZdoString(zdo, "ua.bohdan.decorativepet.marker") == "1";
			}
			return false;
		}

		private static bool IsPetComponent(object component)
		{
			if (component == null)
			{
				return false;
			}
			return IsPetZdo(GetZdo(GetMember(component, "m_nview") ?? GetNetView(GetMember(component, "gameObject"))));
		}

		private static object GetPetZdo(object component)
		{
			return GetZdo(GetMember(component, "m_nview") ?? GetNetView(GetMember(component, "gameObject")));
		}

		private static string FoodText(object tameable)
		{
			object obj = InvokeCompatible(tameable, "GetFoodLevel") ?? GetMember(tameable, "m_foodLevel");
			if (obj == null)
			{
				string text = Convert.ToString(InvokeCompatible(tameable, "GetStatusString"), CultureInfo.InvariantCulture) ?? "";
				bool flag = text.IndexOf("hungry", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("голод", StringComparison.OrdinalIgnoreCase) >= 0;
				return LocalText("Їжа: " + (flag ? "░░░░░░░░░░ 0%" : "██████████ 100%"), "Food: " + (flag ? "░░░░░░░░░░ 0%" : "██████████ 100%"));
			}
			float num;
			try
			{
				num = Convert.ToSingle(obj, CultureInfo.InvariantCulture);
			}
			catch
			{
				return "";
			}
			if (num > 1f)
			{
				object obj3 = GetMember(tameable, "m_foodLevelMax") ?? GetMember(tameable, "m_maxFood");
				float num2 = 0f;
				try
				{
					num2 = ((obj3 == null) ? 0f : Convert.ToSingle(obj3, CultureInfo.InvariantCulture));
				}
				catch
				{
				}
				num = ((num2 > 0f) ? (num / num2) : (num / 100f));
			}
			num = Math.Max(0f, Math.Min(1f, num));
			int num3 = (int)Math.Round(num * 10f);
			return "Food: " + new string('█', num3) + new string('░', 10 - num3) + " " + (int)Math.Round(num * 100f) + "%";
		}

		private static string LocalText(string ukrainian, string english)
		{
			object staticMember = GetStaticMember(FindType("Localization"), "instance");
			string text = Convert.ToString(GetMember(staticMember, "m_language"), CultureInfo.InvariantCulture);
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Convert.ToString(InvokeCompatible(staticMember, "GetSelectedLanguage"), CultureInfo.InvariantCulture);
			}
			if ((text ?? "").IndexOf("Ukrain", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return english;
			}
			return ukrainian;
		}

		private static bool HasFoodInHand(object player)
		{
			if (player == null)
			{
				return false;
			}
			string[] array = new string[2] { "m_rightItem", "m_leftItem" };
			foreach (string name in array)
			{
				object member = GetMember(GetMember(GetMember(player, name), "m_shared"), "m_food");
				try
				{
					if (member != null && Convert.ToSingle(member, CultureInfo.InvariantCulture) > 0f)
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		private static bool IsIgnoredPetAI(object ai)
		{
			if (_instance == null || ai == null)
			{
				return false;
			}
			foreach (PetRecord cachedPet in _instance._cachedPets)
			{
				if (cachedPet.MonsterAI == ai)
				{
					return cachedPet.IgnoreEnemies;
				}
			}
			object petZdo = GetPetZdo(ai);
			if (IsPetZdo(petZdo))
			{
				return GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1";
			}
			return false;
		}

		private static Type FindType(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			if (TypeCache.TryGetValue(name, out var value))
			{
				return value;
			}
			Type type = AccessTools.TypeByName(name);
			if (type != null)
			{
				TypeCache[name] = type;
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetType(name, throwOnError: false);
					if (type != null)
					{
						TypeCache[name] = type;
						return type;
					}
				}
				catch
				{
				}
			}
			TypeCache[name] = null;
			return null;
		}

		private static object GetStaticMember(Type type, string name)
		{
			if (type == null)
			{
				return null;
			}
			FieldInfo fieldInfo = FindFieldNoLog(type, name, isStatic: true);
			if (fieldInfo != null)
			{
				return fieldInfo.GetValue(null);
			}
			return FindPropertyNoLog(type, name, isStatic: true)?.GetValue(null, null);
		}

		private static object GetMember(object instance, string name)
		{
			if (instance == null)
			{
				return null;
			}
			try
			{
				FieldInfo fieldInfo = FindFieldNoLog(instance.GetType(), name, isStatic: false);
				if (fieldInfo != null)
				{
					return fieldInfo.GetValue(instance);
				}
				return FindPropertyNoLog(instance.GetType(), name, isStatic: false)?.GetValue(instance, null);
			}
			catch
			{
				return null;
			}
		}

		private static bool SetMember(object instance, string name, object value)
		{
			if (instance == null)
			{
				return false;
			}
			try
			{
				FieldInfo fieldInfo = FindFieldNoLog(instance.GetType(), name, isStatic: false);
				if (fieldInfo != null)
				{
					fieldInfo.SetValue(instance, value);
					return true;
				}
				PropertyInfo propertyInfo = FindPropertyNoLog(instance.GetType(), name, isStatic: false);
				if (propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(instance, value, null);
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static FieldInfo FindFieldNoLog(Type type, string name, bool isStatic)
		{
			BindingFlags bindingAttr = (BindingFlags)(0x32 | (isStatic ? 8 : 4));
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo field = type2.GetField(name, bindingAttr);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private static PropertyInfo FindPropertyNoLog(Type type, string name, bool isStatic)
		{
			BindingFlags bindingAttr = (BindingFlags)(0x32 | (isStatic ? 8 : 4));
			Type type2 = type;
			while (type2 != null)
			{
				PropertyInfo property = type2.GetProperty(name, bindingAttr);
				if (property != null)
				{
					return property;
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private static object GetComponent(object gameObject, Type componentType)
		{
			if (gameObject == null || componentType == null)
			{
				return null;
			}
			MethodInfo[] methods = gameObject.GetType().GetMethods(AccessTools.all);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name != "GetComponent")
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Type))
				{
					try
					{
						return methodInfo.Invoke(gameObject, new object[1] { componentType });
					}
					catch
					{
						return null;
					}
				}
			}
			return null;
		}

		private static object GetNetView(object gameObject)
		{
			if (gameObject == null)
			{
				return null;
			}
			Type componentType = FindType("ZNetView");
			return GetComponent(gameObject, componentType);
		}

		private static object GetZdo(object instance)
		{
			if (instance == null)
			{
				return null;
			}
			if (string.Equals(instance.GetType().Name, "ZDO", StringComparison.Ordinal))
			{
				return instance;
			}
			return InvokeCompatible(instance, "GetZDO") ?? GetMember(instance, "m_zdo");
		}

		private static object InvokeCompatible(object instance, string name, params object[] arguments)
		{
			if (instance == null)
			{
				return null;
			}
			foreach (MethodInfo item in from m in instance.GetType().GetMethods(AccessTools.all)
				where m.Name == name
				where m.GetParameters().Length == arguments.Length
				select m)
			{
				ParameterInfo[] parameters = item.GetParameters();
				bool flag = true;
				for (int num = 0; num < parameters.Length; num++)
				{
					object obj = arguments[num];
					if (obj != null && !parameters[num].ParameterType.IsInstanceOfType(obj) && (!parameters[num].ParameterType.IsValueType || !(parameters[num].ParameterType == obj.GetType())))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					try
					{
						return item.Invoke(instance, arguments);
					}
					catch
					{
					}
				}
			}
			return null;
		}

		private static bool TryInvokeCompatible(object instance, string name, params object[] arguments)
		{
			if (instance == null)
			{
				return false;
			}
			foreach (MethodInfo item in from m in instance.GetType().GetMethods(AccessTools.all)
				where m.Name == name
				where m.GetParameters().Length == arguments.Length
				select m)
			{
				ParameterInfo[] parameters = item.GetParameters();
				bool flag = true;
				for (int num = 0; num < parameters.Length; num++)
				{
					object obj = arguments[num];
					if (obj != null && !parameters[num].ParameterType.IsInstanceOfType(obj) && (!parameters[num].ParameterType.IsValueType || !(parameters[num].ParameterType == obj.GetType())))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					try
					{
						item.Invoke(instance, arguments);
						return true;
					}
					catch
					{
					}
				}
			}
			return false;
		}

		private static object GetDefault(Type type)
		{
			if (type == null || !type.IsValueType)
			{
				return null;
			}
			try
			{
				return Activator.CreateInstance(type);
			}
			catch
			{
				return null;
			}
		}

		private static string GetZdoString(object zdo, string key)
		{
			if (zdo == null)
			{
				return "";
			}
			object obj = InvokeCompatible(zdo, "GetString", key, "");
			if (obj == null)
			{
				obj = InvokeCompatible(zdo, "GetString", key);
			}
			if (obj != null)
			{
				return Convert.ToString(obj, CultureInfo.InvariantCulture);
			}
			return "";
		}

		private static void SetZdoString(object zdo, string key, string value)
		{
			if (zdo != null)
			{
				InvokeCompatible(zdo, "Set", key, value ?? "");
			}
		}

		private static object GetLocalPlayer()
		{
			return GetStaticMember(FindType("Player"), "m_localPlayer");
		}

		private static object FindPlayerByName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			object localPlayer = GetLocalPlayer();
			if (string.Equals(GetPlayerName(localPlayer), name, StringComparison.Ordinal))
			{
				return localPlayer;
			}
			Type type = FindType("Player");
			if (type == null)
			{
				return null;
			}
			object obj = null;
			try
			{
				obj = type.GetMethods(AccessTools.all).FirstOrDefault((MethodInfo m) => m.Name == "GetAllPlayers" && m.IsStatic && m.GetParameters().Length == 0)?.Invoke(null, null) ?? GetStaticMember(type, "s_players") ?? GetStaticMember(type, "m_players");
			}
			catch
			{
				return null;
			}
			if (!(obj is IEnumerable enumerable))
			{
				return null;
			}
			foreach (object item in enumerable)
			{
				if (string.Equals(GetPlayerName(item), name, StringComparison.Ordinal))
				{
					return item;
				}
			}
			return null;
		}

		private static string GetPlayerName(object player)
		{
			if (player == null)
			{
				return "";
			}
			object obj = InvokeCompatible(player, "GetPlayerName") ?? GetMember(player, "m_name") ?? GetMember(player, "m_playerName");
			if (obj != null)
			{
				return Convert.ToString(obj, CultureInfo.InvariantCulture);
			}
			return "";
		}

		private static string GetLocalPlayerName()
		{
			return GetPlayerName(GetLocalPlayer());
		}

		private static bool IsServer()
		{
			object staticMember = GetStaticMember(FindType("ZNet"), "instance");
			if (staticMember == null)
			{
				return false;
			}
			object obj = InvokeCompatible(staticMember, "IsServer");
			if (obj != null)
			{
				return Convert.ToBoolean(obj, CultureInfo.InvariantCulture);
			}
			return false;
		}

		private static object GetPosition(object transform)
		{
			return GetMember(transform, "position");
		}

		private static object OffsetPosition(object position, float x, float y, float z)
		{
			Type type = FindType("UnityEngine.Vector3");
			if (type == null || position == null)
			{
				return position;
			}
			try
			{
				float num = Convert.ToSingle(GetMember(position, "x"), CultureInfo.InvariantCulture);
				float num2 = Convert.ToSingle(GetMember(position, "y"), CultureInfo.InvariantCulture);
				float num3 = Convert.ToSingle(GetMember(position, "z"), CultureInfo.InvariantCulture);
				return type.GetConstructor(new Type[3]
				{
					typeof(float),
					typeof(float),
					typeof(float)
				})?.Invoke(new object[3]
				{
					num + x,
					num2 + y,
					num3 + z
				}) ?? position;
			}
			catch
			{
				return position;
			}
		}

		private static float VectorDistance(object first, object second)
		{
			Type type = FindType("UnityEngine.Vector3");
			if (type == null)
			{
				return float.MaxValue;
			}
			MethodInfo methodInfo = (from m in type.GetMethods(AccessTools.all)
				where m.Name == "Distance"
				where m.GetParameters().Length == 2
				select m).FirstOrDefault();
			try
			{
				object obj = methodInfo?.Invoke(null, new object[2] { first, second });
				return (obj == null) ? float.MaxValue : Convert.ToSingle(obj, CultureInfo.InvariantCulture);
			}
			catch
			{
				return float.MaxValue;
			}
		}

		private static object MoveTowards(object current, object target, float distance)
		{
			MethodInfo methodInfo = (from m in FindType("UnityEngine.Vector3")?.GetMethods(AccessTools.all)
				where m.Name == "MoveTowards"
				where m.GetParameters().Length == 3
				select m).FirstOrDefault();
			try
			{
				return methodInfo?.Invoke(null, new object[3] { current, target, distance });
			}
			catch
			{
				return null;
			}
		}

		private static int StableHash(string value)
		{
			int num = 5381;
			int num2 = num;
			if (value == null)
			{
				return num;
			}
			for (int i = 0; i < value.Length && value[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ value[i];
				if (i == value.Length - 1 || value[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ value[i + 1];
			}
			return num + num2 * 1566083941;
		}
	}
}