Decompiled source of FehuMounts v420.100.3

plugins/FehuMounts/FehuMounts.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FehuMounts")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("420.100.3.0")]
[assembly: AssemblyInformationalVersion("420.100.3-rc.1")]
[assembly: AssemblyProduct("FehuMounts")]
[assembly: AssemblyTitle("FehuMounts")]
[assembly: AssemblyVersion("420.100.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 FehuMounts
{
	internal static class DeerRules
	{
		public static float SpeedMultiplier(int stars)
		{
			if (stars < 5)
			{
				return stars switch
				{
					3 => 1.15f, 
					4 => 1.25f, 
					_ => 1f, 
				};
			}
			return 1.35f;
		}

		public static int OffspringStars(int first, int second, double roll, int maximum)
		{
			int num = Math.Min(first, second);
			int num2 = Math.Max(first, second);
			return Math.Max(0, Math.Min(maximum, (roll < 0.7) ? num : ((roll < 0.95) ? num2 : (num2 + 1))));
		}

		public static bool MayUse(long owner, long player)
		{
			if (player != 0L)
			{
				if (owner != 0L)
				{
					return owner == player;
				}
				return true;
			}
			return false;
		}

		public static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		public static float PregnancyRemaining(float remaining, float seconds)
		{
			if (!(remaining <= 0f))
			{
				return Math.Max(0.001f, remaining - Math.Max(0f, seconds));
			}
			return 0f;
		}

		public static bool CanGiveBirth(int population, int maximum)
		{
			return population < Math.Max(2, maximum);
		}

		public static string CleanName(string value)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = value ?? "";
			foreach (char c in text)
			{
				if (!char.IsControl(c) && c != '<' && c != '>')
				{
					stringBuilder.Append(c);
				}
				if (stringBuilder.Length == 20)
				{
					break;
				}
			}
			return stringBuilder.ToString().Trim();
		}

		public static float Stamina(float current, float maximum, float seconds, bool sprinting, int stars)
		{
			float num = ((stars >= 5) ? 10.5f : ((stars == 4) ? 12.75f : 15f));
			return Math.Max(0f, Math.Min(maximum, current + (sprinting ? (0f - num) : 10f) * Math.Max(0f, seconds)));
		}
	}
	internal static class DeerSettings
	{
		private static ConfigFile _config = null;

		private static readonly Dictionary<ConfigEntryBase, object> LocalValues = new Dictionary<ConfigEntryBase, object>();

		internal static bool ClientReady;

		internal static ConfigEntry<string> Foods = null;

		internal static ConfigEntry<float> GrowthSeconds = null;

		internal static ConfigEntry<bool> ProtectFawns = null;

		internal static ConfigEntry<float> WalkSpeed = null;

		internal static ConfigEntry<float> RunSpeed = null;

		internal static bool InputAllowed
		{
			get
			{
				if (!Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible())
				{
					if (!((Object)(object)Chat.instance == (Object)null))
					{
						return !Chat.instance.HasFocus();
					}
					return true;
				}
				return false;
			}
		}

		internal static string[] FoodNames => (from x in Foods.Value.Split(',')
			select x.Trim() into x
			where x.Length > 0
			select x).Distinct().ToArray();

		private static ConfigEntryBase[] Shared => (ConfigEntryBase[])(object)new ConfigEntryBase[26]
		{
			(ConfigEntryBase)Plugin.EnableDeerTaming,
			(ConfigEntryBase)Plugin.EnableDeerBreeding,
			(ConfigEntryBase)Plugin.EnableDeerRiding,
			(ConfigEntryBase)Plugin.MinStarsToRide,
			(ConfigEntryBase)Plugin.MaxDeerStars,
			(ConfigEntryBase)Plugin.MateRange,
			(ConfigEntryBase)Plugin.PregnancyDuration,
			(ConfigEntryBase)Plugin.MaxNearbyDeer,
			(ConfigEntryBase)Plugin.NearbyDeerRange,
			(ConfigEntryBase)Plugin.BreedingCooldown,
			(ConfigEntryBase)Plugin.DeerFoodScanRange,
			(ConfigEntryBase)Plugin.TamingFoodProgressPerEat,
			(ConfigEntryBase)Plugin.UseBoarLikeTaming,
			(ConfigEntryBase)Plugin.DeerTamingDuration,
			(ConfigEntryBase)Plugin.DeerTamingTickSeconds,
			(ConfigEntryBase)Plugin.InstantTamingPerFood,
			(ConfigEntryBase)Plugin.FollowStartDistance,
			(ConfigEntryBase)Plugin.FollowStopDistance,
			(ConfigEntryBase)Plugin.FollowTeleportDistance,
			(ConfigEntryBase)Plugin.OnlyOwnerCommands,
			(ConfigEntryBase)Plugin.OnlyOwnerCanRide,
			(ConfigEntryBase)Foods,
			(ConfigEntryBase)GrowthSeconds,
			(ConfigEntryBase)ProtectFawns,
			(ConfigEntryBase)WalkSpeed,
			(ConfigEntryBase)RunSpeed
		};

		internal static void Bind(ConfigFile config)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			_config = config;
			Foods = config.Bind<string>("Taming", "DeerFoods", "Raspberry,Blueberries,Carrot,Turnip,Mushroom,Cloudberry,Onion", "Exact food prefab IDs, comma separated. Server controls gameplay.");
			GrowthSeconds = config.Bind<float>("Breeding", "FawnGrowthSeconds", 2000f, new ConfigDescription("Loaded, server-simulated seconds needed for a fawn to grow up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 86400f), Array.Empty<object>()));
			ProtectFawns = config.Bind<bool>("Breeding", "ProtectFawns", true, "Protect fawns from the butcher knife. Does not protect against enemies.");
			WalkSpeed = config.Bind<float>("Riding", "WalkSpeed", 5f, new ConfigDescription("Mounted walk/jog speed before star and loyalty bonuses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 12f), Array.Empty<object>()));
			RunSpeed = config.Bind<float>("Riding", "RunSpeed", 10f, new ConfigDescription("Mounted gallop speed before star and loyalty bonuses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			ConfigEntry<bool> val = config.Bind<bool>("Migration", "LegacySettingsMigrated", false, "Internal: legacy settings have been considered once.");
			if (!val.Value)
			{
				ConfigEntry<int> val2 = config.Bind<int>("General", "MinStarsToRide", 2, "Legacy key. Use Riding/MinStarsToRide.");
				if (Plugin.MinStarsToRide.Value == 2 && val2.Value != 2)
				{
					Plugin.MinStarsToRide.Value = val2.Value;
				}
				val.Value = true;
			}
		}

		internal static void Publish()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write("420.100.3");
			ConfigEntryBase[] shared = Shared;
			foreach (ConfigEntryBase val2 in shared)
			{
				val.Write(Convert.ToString(val2.BoxedValue, CultureInfo.InvariantCulture));
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "FehuMounts.SettingsV110", new object[1] { val });
		}

		internal static void Receive(long sender, ZPackage packet)
		{
			if (DeerNetwork.IsServer || (Object)(object)ZNet.instance == (Object)null || sender != ZNet.instance.GetServerPeer()?.m_uid || packet.Size() > 8192)
			{
				return;
			}
			try
			{
				if (packet.ReadString() != "420.100.3")
				{
					ClientReady = false;
					Plugin.Log.LogWarning((object)"[FehuMounts] Server/client version mismatch. Install the same FehuMounts build.");
					return;
				}
				ConfigEntryBase[] shared = Shared;
				object[] array = new object[shared.Length];
				for (int i = 0; i < shared.Length; i++)
				{
					array[i] = Convert.ChangeType(packet.ReadString(), shared[i].SettingType, CultureInfo.InvariantCulture);
					if (array[i] is float value && !DeerRules.IsFinite(value))
					{
						return;
					}
				}
				bool saveOnConfigSet = _config.SaveOnConfigSet;
				_config.SaveOnConfigSet = false;
				try
				{
					for (int j = 0; j < shared.Length; j++)
					{
						if (!LocalValues.ContainsKey(shared[j]))
						{
							LocalValues.Add(shared[j], shared[j].BoxedValue);
						}
						shared[j].BoxedValue = array[j];
					}
					ClientReady = true;
				}
				finally
				{
					_config.SaveOnConfigSet = saveOnConfigSet;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FehuMounts] Settings rejected: " + ex.Message));
			}
		}

		internal static void RestoreLocal()
		{
			bool saveOnConfigSet = _config.SaveOnConfigSet;
			_config.SaveOnConfigSet = false;
			try
			{
				foreach (KeyValuePair<ConfigEntryBase, object> localValue in LocalValues)
				{
					localValue.Key.BoxedValue = localValue.Value;
				}
			}
			finally
			{
				_config.SaveOnConfigSet = saveOnConfigSet;
				LocalValues.Clear();
				ClientReady = false;
			}
		}
	}
	internal sealed class DeerNetwork : MonoBehaviour
	{
		private const string Rpc = "FehuMounts.RequestV110";

		private ZRoutedRpc? _registered;

		private float _nextSettings;

		private readonly Dictionary<long, float> _lastRequest = new Dictionary<long, float>();

		internal static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private void Update()
		{
			if (ZRoutedRpc.instance != null)
			{
				if (_registered != ZRoutedRpc.instance)
				{
					DeerSettings.RestoreLocal();
					ZRoutedRpc.instance.Register<ZPackage>("FehuMounts.RequestV110", (Action<long, ZPackage>)Receive);
					ZRoutedRpc.instance.Register<ZPackage>("FehuMounts.SettingsV110", (Action<long, ZPackage>)DeerSettings.Receive);
					_registered = ZRoutedRpc.instance;
					_lastRequest.Clear();
					_nextSettings = 0f;
				}
				if (IsServer && Time.time >= _nextSettings)
				{
					_nextSettings = Time.time + 3f;
					DeerSettings.Publish();
				}
			}
		}

		internal static void Request(ZNetView? view, string action, string text = "", Vector3 direction = default(Vector3), bool run = false)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)view == (Object)null) && view.IsValid() && ZRoutedRpc.instance != null && !((Object)(object)Player.m_localPlayer == (Object)null) && (IsServer || DeerSettings.ClientReady))
			{
				ZPackage val = new ZPackage();
				val.Write(view.GetZDO().m_uid);
				val.Write(action);
				val.Write(text);
				val.Write(direction);
				val.Write(run);
				long num = (IsServer ? ZNet.GetUID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0));
				if (num != 0L)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "FehuMounts.RequestV110", new object[1] { val });
				}
			}
		}

		private void OnDestroy()
		{
			DeerSettings.RestoreLocal();
		}

		private void Receive(long sender, ZPackage packet)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer || packet.Size() > 512)
			{
				return;
			}
			Player val;
			if (sender == ZNet.GetUID())
			{
				val = Player.m_localPlayer;
			}
			else
			{
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					return;
				}
				GameObject val2 = ZNetScene.instance.FindInstance(peer.m_characterID);
				val = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<Player>() : null);
			}
			if ((Object)(object)val == (Object)null || ((Character)val).IsDead())
			{
				return;
			}
			float time = Time.time;
			if (_lastRequest.TryGetValue(sender, out var value) && time - value < 0.04f)
			{
				return;
			}
			_lastRequest[sender] = time;
			if (_lastRequest.Count > 512)
			{
				_lastRequest.Clear();
			}
			try
			{
				ZDOID val3 = packet.ReadZDOID();
				string action = packet.ReadString();
				string text = packet.ReadString();
				Vector3 direction = packet.ReadVector3();
				bool run = packet.ReadBool();
				GameObject val4 = ZNetScene.instance.FindInstance(val3);
				if (!((Object)(object)val4 == (Object)null))
				{
					Character component = val4.GetComponent<Character>();
					DeerLifecycle component2 = val4.GetComponent<DeerLifecycle>();
					ZNetView component3 = val4.GetComponent<ZNetView>();
					if (Plugin.IsDeer(component) && !((Object)(object)component2 == (Object)null) && !((Object)(object)component3 == (Object)null) && component3.IsValid() && component3.IsOwner() && !(Vector3.Distance(((Component)val).transform.position, val4.transform.position) > 5f))
					{
						component2.Handle(val, action, text, direction, run);
					}
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugMode.Value)
				{
					Plugin.Log.LogWarning((object)("[FehuMounts] Rejected malformed request: " + ex.Message));
				}
			}
		}
	}
	internal sealed class DeerLifecycle : MonoBehaviour
	{
		internal static readonly HashSet<ZDOID> ServerObjects = new HashSet<ZDOID>();

		private ZDOID _registeredId;

		private bool _registeredObject;

		private static readonly MethodInfo? MoveToMethod = typeof(BaseAI).GetMethod("MoveTo", BindingFlags.Instance | BindingFlags.NonPublic);

		internal const string RiderKey = "FehuMounts_RiderID";

		internal const string StaminaKey = "FehuMounts_MountStamina";

		internal const string NameKey = "FehuMounts.Name";

		private const string FawnKey = "FehuMounts.Fawn";

		private const string AgeKey = "FehuMounts.Age";

		private const string PregnancyKey = "FehuMounts.PregnancyRemaining";

		private const string ChildStarsKey = "FehuMounts.ChildStars";

		private ZNetView _view;

		private Character _deer;

		private Tameable _tameable;

		private AnimalAI _ai;

		private Vector3 _rideDirection;

		private bool _running;

		private float _lastInput;

		private float _lastPet = -10f;

		private float _elapsed;

		private float _nextPublish;

		private float _stamina = -1f;

		private long _previousRider;

		private bool _sessionInitialized;

		private float _baseSpeed;

		private float _baseWalkSpeed;

		private float _baseRunSpeed;

		internal static bool IsFawn(ZNetView? view)
		{
			if ((Object)(object)view != (Object)null && view.IsValid())
			{
				return view.GetZDO().GetBool("FehuMounts.Fawn", false);
			}
			return false;
		}

		internal static bool HasRider(Character deer)
		{
			ZNetView component = ((Component)deer).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				return component.GetZDO().GetLong("FehuMounts_RiderID", 0L) != 0;
			}
			return false;
		}

		internal static float LoyaltyBonus(Character deer)
		{
			ZNetView component = ((Component)deer).GetComponent<ZNetView>();
			int num = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO().GetInt("FehuMounts_Loyalty", 0) : 0);
			if (num < 90)
			{
				if (num < 60)
				{
					if (num < 30)
					{
						return 1f;
					}
					return 1.05f;
				}
				return 1.1f;
			}
			return 1.15f;
		}

		internal static void WalkTo(AnimalAI ai, Vector3 position, bool run)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (MoveToMethod == null)
			{
				((BaseAI)ai).StopMoving();
				return;
			}
			MoveToMethod.Invoke(ai, new object[4] { 0.25f, position, 1.5f, run });
		}

		private void Awake()
		{
			_view = ((Component)this).GetComponent<ZNetView>();
			_deer = ((Component)this).GetComponent<Character>();
			_tameable = ((Component)this).GetComponent<Tameable>();
			_ai = ((Component)this).GetComponent<AnimalAI>();
			if ((Object)(object)_deer != (Object)null)
			{
				_baseSpeed = _deer.m_speed;
				_baseWalkSpeed = _deer.m_walkSpeed;
				_baseRunSpeed = _deer.m_runSpeed;
			}
		}

		private void Update()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			if (!DeerNetwork.IsServer || (Object)(object)_view == (Object)null || !_view.IsValid() || (Object)(object)_deer == (Object)null || (Object)(object)_ai == (Object)null)
			{
				return;
			}
			if (_tameable == null)
			{
				_tameable = ((Component)this).GetComponent<Tameable>();
			}
			if ((Object)(object)_tameable == (Object)null)
			{
				return;
			}
			if (!_registeredObject)
			{
				_registeredId = _view.GetZDO().m_uid;
				ServerObjects.Add(_registeredId);
				_registeredObject = true;
			}
			if (!_view.IsOwner())
			{
				_view.ClaimOwnership();
				return;
			}
			ZDO zDO = _view.GetZDO();
			if (!_sessionInitialized)
			{
				_sessionInitialized = true;
				zDO.Set("FehuMounts_RiderID", 0L);
			}
			if (Plugin.IsFehuTamed(_view, _tameable) && !_deer.IsTamed())
			{
				_deer.SetTamed(true);
			}
			long num = zDO.GetLong("FehuMounts_RiderID", 0L);
			if (num != _previousRider)
			{
				_rideDirection = Vector3.zero;
				_lastInput = Time.time;
				_previousRider = num;
			}
			if (num != 0L)
			{
				Player player = Player.GetPlayer(num);
				if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || Time.time - _lastInput > 1.5f || !Plugin.EnableDeerRiding.Value || IsFawn(_view))
				{
					Dismount();
				}
				else
				{
					if (_stamina < 0f)
					{
						_stamina = zDO.GetFloat("FehuMounts_MountStamina", (float)MaxStamina(_deer));
					}
					bool flag = _running && _stamina > 0f && ((Vector3)(ref _rideDirection)).sqrMagnitude > 0.01f;
					_deer.m_speed = DeerSettings.WalkSpeed.Value;
					_deer.m_walkSpeed = DeerSettings.WalkSpeed.Value;
					_deer.m_runSpeed = DeerSettings.RunSpeed.Value;
					if (((Vector3)(ref _rideDirection)).sqrMagnitude > 0.01f)
					{
						((BaseAI)_ai).MoveTowards(_rideDirection, flag);
					}
					else
					{
						((BaseAI)_ai).StopMoving();
					}
					_deer.SetRun(flag);
					_stamina = DeerRules.Stamina(_stamina, MaxStamina(_deer), Time.deltaTime, flag, _deer.GetLevel() - 1);
				}
			}
			else
			{
				if (_stamina < 0f)
				{
					_stamina = zDO.GetFloat("FehuMounts_MountStamina", (float)MaxStamina(_deer));
				}
				_stamina = DeerRules.Stamina(_stamina, MaxStamina(_deer), Time.deltaTime, sprinting: false, _deer.GetLevel() - 1);
			}
			if (Time.time >= _nextPublish)
			{
				_nextPublish = Time.time + 0.25f;
				zDO.Set("FehuMounts_MountStamina", _stamina);
			}
			_elapsed += Time.deltaTime;
			if (_elapsed < 1f)
			{
				return;
			}
			float elapsed = _elapsed;
			_elapsed = 0f;
			if (IsFawn(_view))
			{
				float num2 = zDO.GetFloat("FehuMounts.Age", 0f) + elapsed;
				zDO.Set("FehuMounts.Age", num2);
				if (num2 >= DeerSettings.GrowthSeconds.Value)
				{
					zDO.Set("FehuMounts.Fawn", false);
					_tameable.m_tamedEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID));
				}
			}
			float num3 = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f);
			if (num3 > 0f && Plugin.EnableDeerBreeding.Value)
			{
				zDO.Set("FehuMounts.PregnancyRemaining", DeerRules.PregnancyRemaining(num3, elapsed));
			}
		}

		private void OnDisable()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (_registeredObject)
			{
				ServerObjects.Remove(_registeredId);
			}
			_registeredObject = false;
		}

		internal void Handle(Player player, string action, string text, Vector3 direction, bool run)
		{
			//IL_0052: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = _view.GetZDO();
			long playerID = player.GetPlayerID();
			long ownerId = Plugin.GetOwnerId(_view);
			if (!Plugin.IsFehuTamed(_view, _tameable))
			{
				return;
			}
			long num = zDO.GetLong("FehuMounts_RiderID", 0L);
			if (action == "input")
			{
				if (num == playerID && DeerRules.IsFinite(direction.x) && DeerRules.IsFinite(direction.y) && DeerRules.IsFinite(direction.z))
				{
					direction.y = 0f;
					_rideDirection = Vector3.ClampMagnitude(direction, 1f);
					_running = run;
					_lastInput = Time.time;
				}
			}
			else if (action == "dismount")
			{
				if (num == playerID)
				{
					Dismount();
				}
			}
			else
			{
				if ((action != "pet" && action != "rename" && action != "follow" && action != "ride") || ((!(action == "ride") || Plugin.OnlyOwnerCanRide.Value) && (!(action == "follow") || Plugin.OnlyOwnerCommands.Value) && !DeerRules.MayUse(ownerId, playerID)))
				{
					return;
				}
				if (ownerId == 0L)
				{
					zDO.Set(Plugin.OwnerHash, playerID);
					zDO.Set(Plugin.OwnerNameHash, player.GetPlayerName());
				}
				switch (action)
				{
				case "pet":
					if (!(Time.time - _lastPet < 5f))
					{
						_lastPet = Time.time;
						((Component)this).GetComponent<DeerLoyaltyController>()?.AddLoyalty(2);
						_tameable.m_petEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID));
					}
					return;
				case "rename":
				{
					string text2 = DeerRules.CleanName(text);
					zDO.Set("FehuMounts.Name", text2);
					zDO.Set(ZDOVars.s_tamedName, text2);
					return;
				}
				case "follow":
					if (num == 0L)
					{
						bool flag = !zDO.GetBool(Plugin.FollowHash, false);
						zDO.Set(Plugin.FollowHash, flag);
						zDO.Set(Plugin.StayPositionHash, ((Component)this).transform.position);
						return;
					}
					break;
				}
				if (action == "ride" && num == 0L && !IsFawn(_view) && Plugin.EnableDeerRiding.Value && _deer.GetLevel() - 1 >= Plugin.MinStarsToRide.Value)
				{
					zDO.Set("FehuMounts_RiderID", playerID);
					_rideDirection = Vector3.zero;
					_lastInput = Time.time;
				}
			}
		}

		private void Dismount()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = _view.GetZDO();
			zDO.Set("FehuMounts_RiderID", 0L);
			zDO.Set(Plugin.FollowHash, false);
			zDO.Set(Plugin.StayPositionHash, ((Component)this).transform.position);
			_rideDirection = Vector3.zero;
			((BaseAI)_ai).StopMoving();
			_deer.m_speed = _baseSpeed;
			_deer.m_walkSpeed = _baseWalkSpeed;
			_deer.m_runSpeed = _baseRunSpeed;
		}

		internal static int MaxStamina(Character deer)
		{
			int num = deer.GetLevel() - 1;
			if (num < 5)
			{
				return num switch
				{
					3 => 125, 
					4 => 140, 
					_ => 100, 
				};
			}
			return 160;
		}

		internal void UpdateBreeding()
		{
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.EnableDeerBreeding.Value || (Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner() || (Object)(object)_tameable == (Object)null || IsFawn(_view))
			{
				return;
			}
			ZDO zDO = _view.GetZDO();
			float num = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f);
			if (num > 0f)
			{
				if (num <= 0.001f && DeerRules.CanGiveBirth(CountPopulation(includePregnancy: false), Plugin.MaxNearbyDeer.Value))
				{
					GiveBirth();
				}
			}
			else
			{
				if (!Plugin.EnableDeerBreeding.Value || !Plugin.IsFehuTamed(_view, _tameable) || _tameable.IsHungry() || Plugin.GetOwnerId(_view) == 0L || CoolingDown(_view) || CountPopulation(includePregnancy: true) >= Mathf.Max(2, Plugin.MaxNearbyDeer.Value))
				{
					return;
				}
				DeerLifecycle[] array = Object.FindObjectsByType<DeerLifecycle>((FindObjectsSortMode)0);
				foreach (DeerLifecycle deerLifecycle in array)
				{
					if (!((Object)(object)deerLifecycle == (Object)(object)this) && !((Object)(object)deerLifecycle._view == (Object)null) && deerLifecycle._view.IsValid() && deerLifecycle._view.IsOwner() && !IsFawn(deerLifecycle._view) && !((Object)(object)deerLifecycle._tameable == (Object)null) && Plugin.IsFehuTamed(deerLifecycle._view, deerLifecycle._tameable) && !deerLifecycle._tameable.IsHungry() && Plugin.GetOwnerId(deerLifecycle._view) == Plugin.GetOwnerId(_view) && !CoolingDown(deerLifecycle._view) && !(deerLifecycle._view.GetZDO().GetFloat("FehuMounts.PregnancyRemaining", 0f) > 0f) && !(Vector3.Distance(((Component)this).transform.position, ((Component)deerLifecycle).transform.position) > Mathf.Max(1f, Plugin.MateRange.Value)))
					{
						long num2 = Now() + (long)(Mathf.Max(1f, Plugin.BreedingCooldown.Value) * 10000000f);
						zDO.Set(Plugin.BreedingCooldownHash, num2);
						deerLifecycle._view.GetZDO().Set(Plugin.BreedingCooldownHash, num2);
						zDO.Set("FehuMounts.PregnancyRemaining", Mathf.Max(1f, Plugin.PregnancyDuration.Value));
						int num3 = DeerRules.OffspringStars(_deer.GetLevel() - 1, deerLifecycle._deer.GetLevel() - 1, Random.value, Mathf.Clamp(Plugin.MaxDeerStars.Value, 0, 5));
						zDO.Set("FehuMounts.ChildStars", num3);
						_tameable.m_sootheEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID));
						break;
					}
				}
			}
		}

		private static long Now()
		{
			return ZNet.instance.GetTime().Ticks;
		}

		private static bool CoolingDown(ZNetView view)
		{
			return view.GetZDO().GetLong(Plugin.BreedingCooldownHash, 0L) > Now();
		}

		private int CountPopulation(bool includePregnancy)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			DeerLifecycle[] array = Object.FindObjectsByType<DeerLifecycle>((FindObjectsSortMode)0);
			foreach (DeerLifecycle deerLifecycle in array)
			{
				if (!((Object)(object)deerLifecycle._view == (Object)null) && deerLifecycle._view.IsValid() && !(Vector3.Distance(((Component)this).transform.position, ((Component)deerLifecycle).transform.position) > Mathf.Max(1f, Plugin.NearbyDeerRange.Value)))
				{
					num++;
					if (includePregnancy && deerLifecycle._view.GetZDO().GetFloat("FehuMounts.PregnancyRemaining", 0f) > 0f)
					{
						num++;
					}
				}
			}
			return num;
		}

		private unsafe void GiveBirth()
		{
			//IL_0021: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = ZNetScene.instance.GetPrefab("Deer");
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab, ((Component)this).transform.position, ((Component)this).transform.rotation);
			ZNetView component = val.GetComponent<ZNetView>();
			Character component2 = val.GetComponent<Character>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)component2 == (Object)null)
			{
				if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner())
				{
					ZNetScene.instance.Destroy(val);
				}
				return;
			}
			ZDO zDO = component.GetZDO();
			zDO.Set("FehuMounts.Fawn", true);
			zDO.Set("FehuMounts.Age", 0f);
			component2.SetLevel(_view.GetZDO().GetInt("FehuMounts.ChildStars", 0) + 1);
			Plugin.CompleteTaming(val, Plugin.GetOwnerId(_view), _view.GetZDO().GetString(Plugin.OwnerNameHash, ""));
			zDO.Set(ZDOVars.s_tameLastFeeding, Now());
			_view.GetZDO().Set("FehuMounts.PregnancyRemaining", 0f);
			ManualLogSource log = Plugin.Log;
			ZDOID uid = zDO.m_uid;
			log.LogInfo((object)("[FehuMounts] Fawn born: " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString()));
		}

		internal static string Describe(Character deer, ZNetView view)
		{
			ZDO zDO = view.GetZDO();
			string text = DeerRules.CleanName(zDO.GetString("FehuMounts.Name", ""));
			bool flag = IsFawn(view);
			if (text.Length == 0)
			{
				text = (flag ? "Fawn" : "Deer");
			}
			int num = Math.Max(0, deer.GetLevel() - 1);
			string text2 = text + " — " + num + "★";
			string text3 = zDO.GetString(Plugin.OwnerNameHash, "");
			if (text3.Length > 0)
			{
				text2 = text2 + "\nOwner: " + DeerRules.CleanName(text3);
			}
			if (flag)
			{
				text2 = text2 + "\nGrowing: " + Mathf.FloorToInt(100f * Mathf.Clamp01(zDO.GetFloat("FehuMounts.Age", 0f) / DeerSettings.GrowthSeconds.Value)) + "%";
			}
			text2 = text2 + "\nLoyalty: " + zDO.GetInt("FehuMounts_Loyalty", 0);
			Tameable component = ((Component)deer).GetComponent<Tameable>();
			text2 += (((Object)(object)component != (Object)null && component.IsHungry()) ? " · Hungry" : " · Fed");
			float num2 = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f);
			if (num2 > 0f)
			{
				text2 = text2 + "\nExpecting fawn: " + Mathf.CeilToInt(num2) + "s";
			}
			text2 = text2 + "\n[E] " + (zDO.GetBool(Plugin.FollowHash, false) ? "Stay" : "Follow") + " · [Q] Pet · [R] Rename";
			if (!flag && Plugin.EnableDeerRiding.Value && num >= Plugin.MinStarsToRide.Value)
			{
				text2 += "\n[Shift + E] Ride Deer";
			}
			if (Plugin.GetOwnerId(view) == 0L)
			{
				text2 += "\nFirst owner interaction claims this deer";
			}
			return text2;
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class FawnProtectionPatch
	{
		private static bool Prefix(Character __instance, HitData hit)
		{
			if (!Plugin.IsDeer(__instance) || !DeerSettings.ProtectFawns.Value || !DeerLifecycle.IsFawn(((Component)__instance).GetComponent<ZNetView>()))
			{
				return true;
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			ItemData val2 = (((Object)(object)val != (Object)null) ? ((Humanoid)val).GetCurrentWeapon() : null);
			if (!((Object)(object)val2?.m_dropPrefab == (Object)null))
			{
				return !((Object)val2.m_dropPrefab).name.Equals("KnifeButcher", StringComparison.Ordinal);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_SetTamed")]
	internal static class DeerNativeTamingAuthorityPatch
	{
		private static bool Prefix(Character __instance, long sender)
		{
			if (!((Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null) && DeerNetwork.IsServer)
			{
				return sender == ZNet.GetUID();
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ZDO), "SetOwner")]
	internal static class DeerSimulationOwnerPatch
	{
		private static void Prefix(ZDO __instance, ref long __0)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (DeerNetwork.IsServer && DeerLifecycle.ServerObjects.Contains(__instance.m_uid))
			{
				__0 = ZNet.GetUID();
			}
		}
	}
	[BepInPlugin("wmymysl.fehumounts", "FehuMounts", "420.100.3")]
	[BepInIncompatibility("nick008.tameabledeer")]
	[BepInIncompatibility("qua8ion.valheim.rideabletames")]
	[BepInIncompatibility("adrirf7.TameableAnimals")]
	[BepInIncompatibility("Tie.ServerTaming")]
	[BepInIncompatibility("meldurson.LetMeTameYou")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "wmymysl.fehumounts";

		public const string ModName = "FehuMounts";

		public const string ModVersion = "420.100.3";

		internal static ConfigEntry<bool> EnableDeerTaming = null;

		internal static ConfigEntry<bool> EnableDeerBreeding = null;

		internal static ConfigEntry<bool> EnableDeerRiding = null;

		internal static ConfigEntry<bool> EnableWhistle = null;

		internal static ConfigEntry<int> MinStarsToRide = null;

		internal static ConfigEntry<int> MaxDeerStars = null;

		internal static ConfigEntry<float> MateRange = null;

		internal static ConfigEntry<float> PregnancyDuration = null;

		internal static ConfigEntry<int> MaxNearbyDeer = null;

		internal static ConfigEntry<float> NearbyDeerRange = null;

		internal static ConfigEntry<float> BreedingCooldown = null;

		internal static ConfigEntry<float> WhistleRange = null;

		internal static ConfigEntry<bool> OnlyOwnerCanSummon = null;

		internal static ConfigEntry<bool> OnlyOwnerCommands = null;

		internal static ConfigEntry<float> DeerFoodScanRange = null;

		internal static ConfigEntry<bool> ManualTamingFallback = null;

		internal static ConfigEntry<float> TamingFoodProgressPerEat = null;

		internal static ConfigEntry<bool> UseBoarLikeTaming = null;

		internal static ConfigEntry<float> DeerTamingDuration = null;

		internal static ConfigEntry<float> DeerTamingTickSeconds = null;

		internal static ConfigEntry<bool> InstantTamingPerFood = null;

		internal static ConfigEntry<float> FollowStartDistance = null;

		internal static ConfigEntry<float> FollowStopDistance = null;

		internal static ConfigEntry<float> FollowTeleportDistance = null;

		internal static ConfigEntry<bool> OnlyOwnerCanRide = null;

		internal static ConfigEntry<bool> DebugRawFood = null;

		internal static ConfigEntry<bool> DebugMode = null;

		internal static ConfigEntry<bool> DebugFood = null;

		internal static ConfigEntry<bool> DebugBreeding = null;

		internal static ConfigEntry<bool> DebugRiding = null;

		internal static ManualLogSource Log = null;

		internal const string TameProgressKey = "FehuMounts_TameProgress";

		internal static readonly int TameProgressLogHash = StableHash("FehuMounts.TameProgressLog");

		internal static readonly int OwnerHash = StableHash("FehuMounts.Owner");

		internal static readonly int OwnerNameHash = StableHash("FehuMounts.OwnerName");

		internal static readonly int IsTamedHash = StableHash("FehuMounts.IsTamed");

		internal static readonly int CommandStateHash = StableHash("FehuMounts.CommandState");

		internal static readonly int StayPositionHash = StableHash("FehuMounts.StayPosition");

		internal static readonly int BreedingCooldownHash = StableHash("FehuMounts.BreedingCooldown");

		internal static readonly int FollowHash = StableHash("FehuMounts.Follow");

		private static readonly Harmony Harmony = new Harmony("wmymysl.fehumounts");

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			EnableDeerTaming = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerTaming", true, "Enable vanilla taming behavior for Deer.");
			EnableDeerBreeding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerBreeding", true, "Enable tamed Deer breeding.");
			EnableDeerRiding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerRiding", true, "Enable riding for tamed Deer with enough stars.");
			EnableWhistle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableWhistle", false, "Reserved for the future Fehu Whistle phase. No whistle is registered in this build.");
			MinStarsToRide = ((BaseUnityPlugin)this).Config.Bind<int>("Riding", "MinStarsToRide", 2, "Minimum Deer stars required to ride.");
			MaxDeerStars = ((BaseUnityPlugin)this).Config.Bind<int>("Breeding", "MaxDeerStars", 5, "Maximum Deer stars produced by breeding.");
			MateRange = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "MateRange", 8f, "Maximum distance between two deer that can breed.");
			PregnancyDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "PregnancyDuration", 120f, "Seconds before a fed breeding pair can create offspring.");
			MaxNearbyDeer = ((BaseUnityPlugin)this).Config.Bind<int>("Breeding", "MaxNearbyDeer", 6, "Maximum deer allowed within NearbyDeerRange before breeding stops.");
			NearbyDeerRange = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "NearbyDeerRange", 20f, "Range used for breeding crowd checks.");
			BreedingCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "BreedingCooldown", 300f, "Minimum seconds before the same deer can mate again.");
			WhistleRange = ((BaseUnityPlugin)this).Config.Bind<float>("Whistle", "WhistleRange", 80f, "Reserved for the future Fehu Whistle phase.");
			OnlyOwnerCanSummon = ((BaseUnityPlugin)this).Config.Bind<bool>("Whistle", "OnlyOwnerCanSummon", true, "Reserved for the future Fehu Whistle phase.");
			OnlyOwnerCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "OnlyOwnerCommands", true, "Restrict custom Deer follow/stay commands to the feeding owner.");
			DeerFoodScanRange = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerFoodScanRange", 8f, "Range in meters used by DeerFoodConsumer to scan dropped food.");
			ManualTamingFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "ManualTamingFallback", true, "Use ZDO-based taming progress when vanilla OnConsumedItem cannot run without MonsterAI.");
			TamingFoodProgressPerEat = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "TamingFoodProgressPerEat", 20f, "Percentage points of permanent taming progress granted by every eaten food item.");
			UseBoarLikeTaming = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "UseBoarLikeTaming", true, "Increase deer tameness slowly while it is fed and calm.");
			DeerTamingDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerTamingDuration", 1800f, "Seconds required to tame a continuously fed, calm deer.");
			DeerTamingTickSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerTamingTickSeconds", 2f, "Server-authoritative interval for deer taming progress.");
			InstantTamingPerFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "InstantTamingPerFood", false, "Legacy behavior. Keep false for boar-like taming.");
			FollowStartDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowStartDistance", 8f, "Distance at which a following deer starts moving to its owner.");
			FollowStopDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowStopDistance", 3f, "Distance at which a following deer stops near its owner.");
			FollowTeleportDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowTeleportDistance", 80f, "Legacy key: maximum follow distance. Deer stop beyond this distance; no teleportation.");
			OnlyOwnerCanRide = ((BaseUnityPlugin)this).Config.Bind<bool>("Riding", "OnlyOwnerCanRide", true, "Restrict riding to the deer owner.");
			DebugRawFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugRawFood", false, "Write raw ItemDrop name fields for every scanned food item.");
			DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugMode", false, "Write general FehuMounts diagnostics to the BepInEx log.");
			DebugFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugFood", false, "Write food scans and per-feed diagnostics.");
			DebugBreeding = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugBreeding", false, "Write breeding checks and diagnostics.");
			DebugRiding = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugRiding", false, "Write mount eligibility and riding diagnostics.");
			DeerSettings.Bind(((BaseUnityPlugin)this).Config);
			((Component)this).gameObject.AddComponent<DeerNetwork>();
			Harmony.PatchAll();
			Log.LogInfo((object)"FehuMounts 420.100.3 RC loaded. Fehu Whistle remains disabled.");
		}

		private void OnDestroy()
		{
			Harmony.UnpatchSelf();
		}

		internal static bool IsDeer(Character? character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			return Utils.GetPrefabName(((Component)character).gameObject).Equals("Deer", StringComparison.Ordinal);
		}

		internal static void PatchDeerPrefab(ZNetScene scene)
		{
			GameObject val = FindPrefab(scene, "Deer");
			if ((Object)(object)val == (Object)null)
			{
				WriteDebug("[FehuMounts] Deer prefab FAIL");
				return;
			}
			WriteDebug("[FehuMounts] Deer prefab found");
			EnsureDeerTameable(val, isRuntimeInstance: false);
		}

		internal static void EnsureDeerTameable(GameObject deer, bool isRuntimeInstance)
		{
			AnimalAI component = deer.GetComponent<AnimalAI>();
			MonsterAI component2 = deer.GetComponent<MonsterAI>();
			BaseAI component3 = deer.GetComponent<BaseAI>();
			Character component4 = deer.GetComponent<Character>();
			Tameable component5 = deer.GetComponent<Tameable>();
			Component component6 = deer.GetComponent("Creature");
			WriteComponentReport(component, component2, component3, component6, component4, component5);
			if ((Object)(object)component2 != (Object)null || (Object)(object)deer.GetComponent<Sadle>() != (Object)null)
			{
				Log.LogWarning((object)"[FehuMounts] Conflicting deer AI/saddle detected. Disable other deer taming/riding mods.");
			}
			else if (!((Object)(object)component == (Object)null) && !((Object)(object)component4 == (Object)null))
			{
				Tameable val;
				if ((Object)(object)component5 == (Object)null)
				{
					val = deer.AddComponent<Tameable>();
					WriteDebug("[FehuMounts] Tameable added");
				}
				else
				{
					val = component5;
					WriteDebug("[FehuMounts] Tameable existing");
				}
				AssignTameableToAnimalAi(component, val);
				ConfigureLikeBoar(val);
				List<ItemDrop> list = BuildFoodList();
				if ((Object)(object)deer.GetComponent<DeerLifecycle>() == (Object)null)
				{
					deer.AddComponent<DeerLifecycle>();
				}
				(deer.GetComponent<DeerFoodConsumer>() ?? deer.AddComponent<DeerFoodConsumer>()).Configure(val, list);
				if (deer.GetComponent<FehuMountRideController>() == null)
				{
					deer.AddComponent<FehuMountRideController>();
				}
				if (deer.GetComponent<DeerCommandController>() == null)
				{
					deer.AddComponent<DeerCommandController>();
				}
				if (deer.GetComponent<DeerLoyaltyController>() == null)
				{
					deer.AddComponent<DeerLoyaltyController>();
				}
				if ((Object)(object)deer.GetComponent<DeerBreedingController>() == (Object)null)
				{
					deer.AddComponent<DeerBreedingController>();
				}
				if ((Object)(object)deer.GetComponent<DeerVisualController>() == (Object)null)
				{
					deer.AddComponent<DeerVisualController>();
				}
				((Behaviour)val).enabled = true;
				WriteDebug("[FehuMounts] Deer patch SUCCESS");
				if (isRuntimeInstance)
				{
					WriteDebug($"[FehuMounts] Runtime Deer patched instance={((Object)deer).name} hasTameable={(Object)(object)val != (Object)null} hasMonsterAI=false consumeItems={list.Count}");
					WriteDebug("[FehuMounts] Deer final hover should be Tameable");
					WriteDebug($"[FehuMounts] Deer has Tameable enabled={((Behaviour)val).enabled}");
					WriteDebug($"[FehuMounts] Deer consumeItems count={list.Count}");
				}
			}
		}

		private static void ConfigureLikeBoar(Tameable tameable)
		{
			GameObject? obj = FindPrefab(ZNetScene.instance, "Boar");
			Tameable val = ((obj != null) ? obj.GetComponent<Tameable>() : null);
			if ((Object)(object)val != (Object)null)
			{
				tameable.m_fedDuration = val.m_fedDuration;
				tameable.m_tamingTime = val.m_tamingTime;
				tameable.m_tamingSpeedMultiplierRange = val.m_tamingSpeedMultiplierRange;
				tameable.m_tamingBoostMultiplier = val.m_tamingBoostMultiplier;
				tameable.m_tamedEffect = val.m_tamedEffect;
				tameable.m_sootheEffect = val.m_sootheEffect;
				tameable.m_petEffect = val.m_petEffect;
			}
			else
			{
				tameable.m_fedDuration = 600f;
				tameable.m_tamingTime = 1800f;
			}
			tameable.m_startsTamed = false;
			tameable.m_tamingTime = Mathf.Max(1f, DeerTamingDuration.Value);
			tameable.m_commandable = true;
			tameable.m_unsummonDistance = 0f;
			tameable.m_saddle = null;
			tameable.m_saddleItem = null;
			tameable.m_dropSaddleOnDeath = false;
		}

		private static void AssignTameableToAnimalAi(AnimalAI? animalAi, Tameable? tameable)
		{
			if (!((Object)(object)animalAi == (Object)null) && !((Object)(object)tameable == (Object)null))
			{
				typeof(BaseAI).GetField("m_tamable", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(animalAi, tameable);
			}
		}

		private static List<ItemDrop> BuildFoodList()
		{
			List<ItemDrop> list = new List<ItemDrop>();
			string[] foodNames = DeerSettings.FoodNames;
			foreach (string prefabName in foodNames)
			{
				AddFood(list, prefabName);
			}
			WriteDebug($"[FehuMounts] Deer Tameable consumeItems count={list.Count}");
			WriteDebug("[FehuMounts] Foods assigned: Raspberry, Blueberries, Carrot, Turnip");
			return list;
		}

		private static void WriteComponentReport(AnimalAI? animalAi, MonsterAI? monsterAi, BaseAI? baseAi, Component? creature, Character? character, Tameable? tameable)
		{
			WriteDebug("[FehuMounts] Deer components:");
			WriteDebug("- AnimalAI " + (((Object)(object)animalAi != (Object)null) ? "FOUND" : "MISSING"));
			WriteDebug("- MonsterAI " + (((Object)(object)monsterAi != (Object)null) ? "FOUND" : "MISSING"));
			WriteDebug("- BaseAI " + (((Object)(object)baseAi != (Object)null) ? "FOUND" : "MISSING"));
			WriteDebug("- Creature " + (((Object)(object)creature != (Object)null) ? "FOUND" : "MISSING"));
			WriteDebug("- Character " + (((Object)(object)character != (Object)null) ? "FOUND" : "MISSING"));
			WriteDebug("- Tameable " + (((Object)(object)tameable != (Object)null) ? "FOUND" : "MISSING"));
			if ((Object)(object)animalAi != (Object)null)
			{
				WriteDebug("[FehuMounts] AnimalAI found");
			}
		}

		private static void AddFood(List<ItemDrop> foods, string prefabName)
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(prefabName) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				WriteDebug("[FehuMounts] Added food " + prefabName + " FAIL");
				return;
			}
			foods.Add(val2);
			string[] obj = new string[6]
			{
				"[FehuMounts] Added food ",
				prefabName,
				" OK objectdb_prefab=",
				((Object)val).name,
				" drop_prefab=",
				null
			};
			GameObject dropPrefab = val2.m_itemData.m_dropPrefab;
			obj[5] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? "null";
			WriteDebug(string.Concat(obj));
		}

		private static GameObject? FindPrefab(ZNetScene? scene, string prefabName)
		{
			if ((Object)(object)scene == (Object)null)
			{
				return null;
			}
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab != (Object)null && ((Object)prefab).name.Equals(prefabName, StringComparison.Ordinal))
				{
					return prefab;
				}
			}
			return null;
		}

		private static void WriteDebug(string message)
		{
			if (DebugMode != null && DebugMode.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void WriteFoodDebug(string message)
		{
			if (DebugFood.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void WriteBreedingDebug(string message)
		{
			if (DebugBreeding.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void WriteRidingDebug(string message)
		{
			if (DebugRiding.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static int StableHash(string text)
		{
			int num = 5381;
			foreach (char c in text)
			{
				num = ((num << 5) + num) ^ c;
			}
			return num;
		}

		internal static long GetOwnerId(ZNetView view)
		{
			if (!view.IsValid())
			{
				return 0L;
			}
			return view.GetZDO().GetLong(OwnerHash, 0L);
		}

		internal static bool IsFehuTamed(ZNetView? view, Tameable? tameable)
		{
			if ((Object)(object)view != (Object)null && view.IsValid())
			{
				if (!view.GetZDO().GetBool(IsTamedHash, false) && !view.GetZDO().GetBool(ZDOVars.s_tamed, false))
				{
					if ((Object)(object)tameable != (Object)null)
					{
						return tameable.IsTamed();
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal static void CompleteTaming(GameObject deer, long ownerId = 0L, string ownerName = "")
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = deer.GetComponent<ZNetView>();
			Tameable component2 = deer.GetComponent<Tameable>();
			Character component3 = deer.GetComponent<Character>();
			if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && !((Object)(object)component3 == (Object)null) && component.IsValid() && component.IsOwner() && DeerNetwork.IsServer)
			{
				ZDO zDO = component.GetZDO();
				if (ownerId == 0L)
				{
					ownerId = GetOwnerId(component);
				}
				if (ownerId != 0L)
				{
					zDO.Set(OwnerHash, ownerId);
				}
				if (!string.IsNullOrEmpty(ownerName))
				{
					zDO.Set(OwnerNameHash, ownerName);
				}
				component3.SetTamed(true);
				zDO.Set("FehuMounts_TameProgress", 100f);
				zDO.Set(IsTamedHash, true);
				zDO.Set(ZDOVars.s_tamed, true);
				zDO.Set(ZDOVars.s_tameTimeLeft, 0f);
				zDO.Set(StayPositionHash, deer.transform.position);
				zDO.Set(CommandStateHash, "Stay");
				zDO.Set(FollowHash, false);
				((Behaviour)(deer.GetComponent<DeerCommandController>() ?? deer.AddComponent<DeerCommandController>())).enabled = true;
				int num = Mathf.Max(0, component3.GetLevel() - 1);
				if (EnableDeerRiding.Value && num >= MinStarsToRide.Value)
				{
					((Behaviour)(deer.GetComponent<FehuMountRideController>() ?? deer.AddComponent<FehuMountRideController>())).enabled = true;
				}
				component2.m_tamedEffect.Create(deer.transform.position, deer.transform.rotation, deer.transform, 1f, 0, default(ZDOID));
				Log.LogInfo((object)$"[FehuMounts] COMPLETE TAMING deer={zDO.m_uid} owner={ownerId} stars={num} vanillaTamed={component2.IsTamed()}");
			}
		}
	}
	internal sealed class DeerFoodConsumer : MonoBehaviour
	{
		private const float ScanInterval = 2f;

		private readonly List<ItemDrop> _allowedFoods = new List<ItemDrop>();

		private Tameable? _tameable;

		private ZNetView? _znetView;

		private float _nextScan;

		private float _nextTamingTick;

		public void Configure(Tameable tameable, List<ItemDrop> allowedFoods)
		{
			_tameable = tameable;
			_allowedFoods.Clear();
			_allowedFoods.AddRange(allowedFoods);
		}

		private void Awake()
		{
			_tameable = ((Component)this).GetComponent<Tameable>();
			_znetView = ((Component)this).GetComponent<ZNetView>();
		}

		private void Update()
		{
			UpdateTamingProgress();
			if (Time.time < _nextScan)
			{
				return;
			}
			_nextScan = Time.time + 2f;
			if (_tameable == null)
			{
				_tameable = ((Component)this).GetComponent<Tameable>();
			}
			if (_znetView == null)
			{
				_znetView = ((Component)this).GetComponent<ZNetView>();
			}
			bool flag = DeerNetwork.IsServer && (Object)(object)_tameable != (Object)null && (Object)(object)_znetView != (Object)null && _znetView.IsValid() && _znetView.IsOwner();
			bool flag2 = (Object)(object)_tameable != (Object)null && _tameable.IsTamed();
			bool flag3 = (Object)(object)_tameable != (Object)null && _tameable.IsHungry();
			Plugin.WriteFoodDebug($"[FehuMounts] DeerFood scan owner={flag} tamed={flag2} hungry={flag3}");
			if (!flag || (Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || (!flag2 && !Plugin.EnableDeerTaming.Value) || !flag3)
			{
				return;
			}
			ItemDrop val = FindNearbyAllowedFood();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)val).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			component.ClaimOwnership();
			if (!component.IsOwner())
			{
				return;
			}
			long num = component.GetZDO().GetLong(ZDOVars.s_creator, 0L);
			if (val.RemoveOne())
			{
				if (Plugin.GetOwnerId(_znetView) == 0L && num != 0L && (Object)(object)Player.GetPlayer(num) != (Object)null)
				{
					_znetView.GetZDO().Set(Plugin.OwnerHash, num);
					_znetView.GetZDO().Set(Plugin.OwnerNameHash, Player.GetPlayer(num).GetPlayerName());
				}
				if (ApplyManualFeedFallback())
				{
					PlayHeartEffect(_tameable.m_sootheEffect);
				}
			}
		}

		private ItemDrop? FindNearbyAllowedFood()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, Plugin.DeerFoodScanRange.Value);
			ItemDrop result = null;
			float num = Plugin.DeerFoodScanRange.Value;
			HashSet<ItemDrop> hashSet = new HashSet<ItemDrop>();
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				ItemDrop val2 = ((Component)val).GetComponent<ItemDrop>() ?? ((Component)val).GetComponentInParent<ItemDrop>();
				if (!((Object)(object)val2 == (Object)null) && hashSet.Add(val2))
				{
					float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)val2).transform.position);
					string match;
					bool flag = IsAllowed(val2, out match);
					string text = val2.m_itemData.m_shared.m_name ?? string.Empty;
					string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
					if (Plugin.DebugRawFood.Value)
					{
						Plugin.Log.LogInfo((object)("[FehuMounts] RAW item.name=" + ((Object)val2).name));
						Plugin.Log.LogInfo((object)("[FehuMounts] RAW gameObject.name=" + ((Object)((Component)val2).gameObject).name));
						Plugin.Log.LogInfo((object)("[FehuMounts] RAW shared_name=" + text));
						Plugin.Log.LogInfo((object)("[FehuMounts] RAW localized_name=" + text2));
					}
					Plugin.WriteFoodDebug($"[FehuMounts] Nearby item={Utils.GetPrefabName(((Component)val2).gameObject)} stack={val2.m_itemData.m_stack} allowed={flag} distance={num2:F1}");
					Plugin.WriteFoodDebug("[FehuMounts] Food match=" + match);
					if (flag && num2 <= num)
					{
						result = val2;
						num = num2;
					}
				}
			}
			return result;
		}

		private bool IsAllowed(ItemDrop itemDrop, out string match)
		{
			string prefabName = Utils.GetPrefabName(((Component)itemDrop).gameObject);
			string[] foodNames = DeerSettings.FoodNames;
			foreach (string text in foodNames)
			{
				if (prefabName.Equals(text, StringComparison.Ordinal))
				{
					match = "prefab-id:" + text;
					return true;
				}
			}
			match = "none";
			return false;
		}

		private bool ApplyManualFeedFallback()
		{
			if ((Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid())
			{
				return false;
			}
			try
			{
				ZDO zDO = _znetView.GetZDO();
				zDO.Set(ZDOVars.s_tameLastFeeding, ((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTime().Ticks : DateTime.UtcNow.Ticks);
				float num = zDO.GetFloat("FehuMounts_TameProgress", 0f);
				if (Plugin.InstantTamingPerFood.Value)
				{
					num = Mathf.Min(100f, num + Mathf.Max(0f, Plugin.TamingFoodProgressPerEat.Value));
					SetTamingProgress(zDO, num);
				}
				Plugin.WriteFoodDebug($"[FehuMounts] Deer ate food progress={num:F1}%");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[FehuMounts] Deer manual feed fallback FAIL: " + ex.Message));
				return false;
			}
		}

		private void UpdateTamingProgress()
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextTamingTick)
			{
				return;
			}
			_nextTamingTick = Time.time + Mathf.Max(0.25f, Plugin.DeerTamingTickSeconds.Value);
			if (_tameable == null)
			{
				_tameable = ((Component)this).GetComponent<Tameable>();
			}
			if (_znetView == null)
			{
				_znetView = ((Component)this).GetComponent<ZNetView>();
			}
			if ((Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid() || !_znetView.IsOwner() || !DeerNetwork.IsServer)
			{
				return;
			}
			ZDO zDO = _znetView.GetZDO();
			if (DeerLifecycle.IsFawn(_znetView))
			{
				return;
			}
			if (_tameable.IsTamed())
			{
				if (!zDO.GetBool(Plugin.IsTamedHash, false))
				{
					Plugin.CompleteTaming(((Component)this).gameObject, 0L);
					Plugin.Log.LogInfo((object)"[FehuMounts] Deer detected vanilla tame state / tameall");
				}
				return;
			}
			float num = zDO.GetFloat("FehuMounts_TameProgress", 0f);
			if (num >= 100f)
			{
				Plugin.CompleteTaming(((Component)this).gameObject, 0L);
			}
			else
			{
				if (!Plugin.EnableDeerTaming.Value || !Plugin.UseBoarLikeTaming.Value || _tameable.IsHungry())
				{
					return;
				}
				BaseAI component = ((Component)this).GetComponent<BaseAI>();
				Player closestPlayer = Player.GetClosestPlayer(((Component)this).transform.position, 10f);
				if ((!((Object)(object)component != (Object)null) || !component.IsAlerted()) && !((Object)(object)closestPlayer != (Object)null))
				{
					float num2 = 100f * Mathf.Max(0.25f, Plugin.DeerTamingTickSeconds.Value) / Mathf.Max(1f, Plugin.DeerTamingDuration.Value);
					num = Mathf.Min(100f, num + num2);
					SetTamingProgress(zDO, num);
					Plugin.WriteFoodDebug($"[FehuMounts] Deer taming progress {num:F1}%");
					if (num >= 100f)
					{
						Plugin.CompleteTaming(((Component)this).gameObject, 0L);
					}
				}
			}
		}

		private void SetTamingProgress(ZDO zdo, float progress)
		{
			float num = Mathf.Clamp(progress, 0f, 100f);
			zdo.Set("FehuMounts_TameProgress", num);
			zdo.Set(ZDOVars.s_tameTimeLeft, Mathf.Max(0f, _tameable.m_tamingTime * (1f - num / 100f) - 0.01f));
		}

		private void PlayHeartEffect(EffectList effect)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject[] array = effect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID));
				Plugin.WriteFoodDebug($"[FehuMounts] Heart effect played={array != null && array.Length != 0}");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[FehuMounts] Heart effect played=false error=" + ex.Message));
			}
		}
	}
	internal sealed class DeerBreedingController : MonoBehaviour
	{
		private float _nextCheck;

		private void Update()
		{
			if (DeerNetwork.IsServer && !(Time.time < _nextCheck))
			{
				_nextCheck = Time.time + 2f;
				((Component)this).GetComponent<DeerLifecycle>()?.UpdateBreeding();
			}
		}
	}
	internal sealed class DeerVisualController : MonoBehaviour
	{
		private Character? _character;

		private Vector3 _baseScale;

		private bool _initialized;

		private void Awake()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_character = ((Component)this).GetComponent<Character>();
			_baseScale = ((Component)this).transform.localScale;
			_initialized = true;
		}

		private void Start()
		{
			ApplyScale();
		}

		private void OnEnable()
		{
			if (_initialized)
			{
				ApplyScale();
			}
		}

		private void Update()
		{
			ApplyScale();
		}

		private void ApplyScale()
		{
			//IL_00e0: 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)
			if (_character == null)
			{
				_character = ((Component)this).GetComponent<Character>();
			}
			if (!((Object)(object)_character == (Object)null))
			{
				float num = Mathf.Clamp(_character.GetLevel() - 1, 0, Plugin.MaxDeerStars.Value) switch
				{
					5 => 1.4f, 
					4 => 1.3f, 
					3 => 1.2f, 
					2 => 1.1f, 
					1 => 1.05f, 
					_ => 1f, 
				};
				ZNetView component = ((Component)this).GetComponent<ZNetView>();
				if (DeerLifecycle.IsFawn(component))
				{
					float num2 = Mathf.Clamp01(component.GetZDO().GetFloat("FehuMounts.Age", 0f) / DeerSettings.GrowthSeconds.Value);
					num *= Mathf.Lerp(0.45f, 0.95f, num2);
				}
				((Component)this).transform.localScale = _baseScale * num;
			}
		}
	}
	internal sealed class FehuMountRideController : MonoBehaviour
	{
		private Player? _localRider;

		private Transform? _seat;

		private float _nextInput;

		private bool _wasUse;

		private float _dismountUntil;

		public bool TryStartRide(Player rider)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			DeerNetwork.Request(((Component)this).GetComponent<ZNetView>(), "ride");
			return true;
		}

		private void Update()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)component == (Object)null || !component.IsValid() || (Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (component.GetZDO().GetLong("FehuMounts_RiderID", 0L) != localPlayer.GetPlayerID())
			{
				_dismountUntil = 0f;
				Detach();
				return;
			}
			if (Time.time < _dismountUntil)
			{
				if (Time.time >= _nextInput)
				{
					_nextInput = Time.time + 0.2f;
					DeerNetwork.Request(component, "dismount");
				}
				return;
			}
			if ((Object)(object)_localRider == (Object)null)
			{
				if (_seat == null)
				{
					_seat = CreateSeat();
				}
				if (((Character)localPlayer).IsAttached())
				{
					DeerNetwork.Request(component, "dismount");
					return;
				}
				((Character)localPlayer).AttachStart(_seat, ((Component)this).gameObject, true, false, false, "attach_asksvin", new Vector3(1.5f, 0.1f, 0f), (Transform)null);
				if (!((Character)localPlayer).IsAttached())
				{
					return;
				}
				_localRider = localPlayer;
				_wasUse = ZInput.GetButton("Use");
				_nextInput = Time.time + 0.15f;
			}
			if (!((Character)localPlayer).IsAttached() || ((Character)localPlayer).IsDead())
			{
				RequestDismount(component);
			}
			else
			{
				if (Time.time < _nextInput)
				{
					return;
				}
				_nextInput = Time.time + 0.1f;
				if (!DeerSettings.InputAllowed)
				{
					DeerNetwork.Request(component, "input");
					return;
				}
				bool button = ZInput.GetButton("Use");
				if ((button && !_wasUse) || ZInput.GetButtonDown("Jump"))
				{
					RequestDismount(component);
					_wasUse = button;
					return;
				}
				_wasUse = button;
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(ZInput.GetButton("Right") ? 1f : (ZInput.GetButton("Left") ? (-1f) : 0f), 0f, ZInput.GetButton("Forward") ? 1f : (ZInput.GetButton("Backward") ? (-1f) : 0f));
				Vector3 direction = ((Component)localPlayer).transform.TransformDirection(((Vector3)(ref val)).normalized);
				DeerNetwork.Request(component, "input", "", direction, ZInput.GetButton("Run"));
			}
		}

		private Transform CreateSeat()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("FehuMountSeat");
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.localPosition = new Vector3(0f, 1.05f, -0.15f);
			return val.transform;
		}

		private void RequestDismount(ZNetView view)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			_dismountUntil = Time.time + 2f;
			DeerNetwork.Request(view, "dismount");
			Detach();
		}

		private void Detach()
		{
			if ((Object)(object)_localRider != (Object)null && ((Character)_localRider).IsAttached())
			{
				((Character)_localRider).AttachStop();
			}
			_localRider = null;
		}

		private void OnDisable()
		{
			Detach();
		}

		private void OnGUI()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_localRider == (Object)null))
			{
				ZNetView component = ((Component)this).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					float num = component.GetZDO().GetFloat("FehuMounts_MountStamina", 0f);
					GUI.Box(new Rect((float)Screen.width / 2f - 150f, (float)Screen.height - 100f, 300f, 52f), "Deer stamina: " + Mathf.CeilToInt(num) + " / " + DeerLifecycle.MaxStamina(((Component)this).GetComponent<Character>()) + "\nE / Jump: dismount");
				}
			}
		}

		internal static void ApplyBonuses(Sadle sadle, Character deer)
		{
		}

		internal static float SpeedMultiplier(Character deer)
		{
			return DeerRules.SpeedMultiplier(deer.GetLevel() - 1);
		}
	}
	internal sealed class DeerLoyaltyController : MonoBehaviour
	{
		private const string LoyaltyKey = "FehuMounts_Loyalty";

		private Tameable? _tameable;

		private ZNetView? _view;

		private Character? _character;

		private float _nextTick;

		private float _baseWalkSpeed;

		private float _baseRunSpeed;

		private void Awake()
		{
			_tameable = ((Component)this).GetComponent<Tameable>();
			_view = ((Component)this).GetComponent<ZNetView>();
			_character = ((Component)this).GetComponent<Character>();
			if ((Object)(object)_character != (Object)null)
			{
				_baseWalkSpeed = _character.m_walkSpeed;
				_baseRunSpeed = _character.m_runSpeed;
			}
		}

		private void Update()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextTick)
			{
				return;
			}
			_nextTick = Time.time + 60f;
			if (!((Object)(object)_tameable == (Object)null) && !((Object)(object)_view == (Object)null) && !((Object)(object)_character == (Object)null) && _view.IsValid() && _view.IsOwner() && DeerNetwork.IsServer && Plugin.IsFehuTamed(_view, _tameable))
			{
				long ownerId = Plugin.GetOwnerId(_view);
				Player val = ((ownerId == 0L) ? null : Player.GetPlayer(ownerId));
				if ((Object)(object)val != (Object)null && Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) <= 10f)
				{
					AddLoyalty(1);
				}
				int num = _view.GetZDO().GetInt("FehuMounts_Loyalty", 0);
				float num2 = ((num >= 90) ? 1.15f : ((num >= 60) ? 1.1f : ((num >= 30) ? 1.05f : 1f)));
				_character.m_walkSpeed = _baseWalkSpeed * num2;
				_character.m_runSpeed = _baseRunSpeed * num2;
			}
		}

		public void AddLoyalty(int amount)
		{
			if (!((Object)(object)_view == (Object)null) && _view.IsValid() && _view.IsOwner() && DeerNetwork.IsServer)
			{
				ZDO zDO = _view.GetZDO();
				zDO.Set("FehuMounts_Loyalty", Mathf.Clamp(zDO.GetInt("FehuMounts_Loyalty", 0) + amount, 0, 100));
			}
		}
	}
	internal sealed class DeerCommandController : MonoBehaviour
	{
		private Tameable? _tameable;

		private AnimalAI? _animalAi;

		private ZNetView? _znetView;

		private float _nextUpdate;

		private void Awake()
		{
			_tameable = ((Component)this).GetComponent<Tameable>();
			_animalAi = ((Component)this).GetComponent<AnimalAI>();
			_znetView = ((Component)this).GetComponent<ZNetView>();
		}

		private void Update()
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			HandlePetInput();
			HandleRenameInput();
			if (Time.time < _nextUpdate)
			{
				return;
			}
			_nextUpdate = Time.time + 0.25f;
			if (_tameable == null)
			{
				_tameable = ((Component)this).GetComponent<Tameable>();
			}
			if (_animalAi == null)
			{
				_animalAi = ((Component)this).GetComponent<AnimalAI>();
			}
			if (_znetView == null)
			{
				_znetView = ((Component)this).GetComponent<ZNetView>();
			}
			if (!DeerNetwork.IsServer || (Object)(object)_tameable == (Object)null || (Object)(object)_animalAi == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid() || !_znetView.IsOwner() || !Plugin.IsFehuTamed(_znetView, _tameable) || _znetView.GetZDO().GetLong("FehuMounts_RiderID", 0L) != 0L)
			{
				return;
			}
			ZDO zDO = _znetView.GetZDO();
			if (!zDO.GetBool(Plugin.FollowHash, false))
			{
				((BaseAI)_animalAi).StopMoving();
				Vector3 vec = zDO.GetVec3(Plugin.StayPositionHash, ((Component)this).transform.position);
				float num = Vector3.Distance(((Component)this).transform.position, vec);
				if (num > 6f)
				{
					_ = vec - ((Component)this).transform.position;
					DeerLifecycle.WalkTo(_animalAi, vec, run: false);
					Plugin.WriteRidingDebug($"[FehuMounts] STAY HOLD distanceFromStay={num:F1}");
				}
				return;
			}
			long ownerId = Plugin.GetOwnerId(_znetView);
			Player val = ((ownerId != 0L) ? Player.GetPlayer(ownerId) : null);
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 val2 = ((Component)val).transform.position - ((Component)this).transform.position;
				val2.y = 0f;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (magnitude > Plugin.FollowTeleportDistance.Value)
				{
					((BaseAI)_animalAi).StopMoving();
				}
				else if (magnitude <= Plugin.FollowStopDistance.Value)
				{
					((BaseAI)_animalAi).StopMoving();
				}
				else if (magnitude < Plugin.FollowStartDistance.Value)
				{
					((BaseAI)_animalAi).StopMoving();
				}
				else
				{
					DeerLifecycle.WalkTo(_animalAi, ((Component)val).transform.position, magnitude > Plugin.FollowStartDistance.Value * 1.5f);
				}
			}
		}

		private void HandlePetInput()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (DeerSettings.InputAllowed && Input.GetKeyDown((KeyCode)113) && !((Object)(object)_tameable == (Object)null) && !((Object)(object)_znetView == (Object)null) && Plugin.IsFehuTamed(_znetView, _tameable))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)localPlayer.GetHoverCreature() != (Object)(object)((Component)this).GetComponent<Character>()))
				{
					DeerNetwork.Request(_znetView, "pet");
				}
			}
		}

		private void HandleRenameInput()
		{
			if (!DeerSettings.InputAllowed || !Input.GetKeyDown((KeyCode)114) || (Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !Plugin.IsFehuTamed(_znetView, _tameable))
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.GetHoverCreature() != (Object)(object)((Component)this).GetComponent<Character>() || (Object)(object)TextInput.instance == (Object)null)
			{
				return;
			}
			long ownerId = Plugin.GetOwnerId(_znetView);
			if (Plugin.OnlyOwnerCommands.Value && ownerId != 0L && ownerId != localPlayer.GetPlayerID())
			{
				((Character)localPlayer).Message((MessageType)2, "This deer belongs to another owner.", 0, (Sprite)null, false);
				return;
			}
			try
			{
				Plugin.Log.LogInfo((object)"[FehuMounts] R rename key detected");
				TextInput.instance.RequestText((TextReceiver)(object)new DeerRenameReceiver(_znetView), "Rename Deer", 20);
				Plugin.Log.LogInfo((object)"[FehuMounts] Rename dialog opened");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[FehuMounts] Rename failed: " + ex.Message));
			}
		}

		public static bool ToggleFollow(Tameable tameable, Player player, bool showMessage)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)tameable).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !Plugin.IsFehuTamed(component, tameable))
			{
				return false;
			}
			DeerNetwork.Request(component, "follow");
			return true;
		}
	}
	internal sealed class DeerRenameReceiver : TextReceiver
	{
		private readonly ZNetView _view;

		public DeerRenameReceiver(ZNetView view)
		{
			_view = view;
		}

		public string GetText()
		{
			if (!_view.IsValid())
			{
				return "";
			}
			return _view.GetZDO().GetString("FehuMounts.Name", "");
		}

		public void SetText(string text)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			DeerNetwork.Request(_view, "rename", text);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			Plugin.PatchDeerPrefab(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class CharacterAwakePatch
	{
		private static void Postfix(Character __instance)
		{
			if (Plugin.IsDeer(__instance))
			{
				Plugin.EnsureDeerTameable(((Component)__instance).gameObject, isRuntimeInstance: true);
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "Update")]
	internal static class DeerTameableUpdatePatch
	{
		private static bool Prefix(Tameable __instance)
		{
			return (Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null;
		}
	}
	[HarmonyPatch(typeof(AnimalAI), "UpdateAI")]
	internal static class FehuTamedDeerCalmPatch
	{
		private static bool Prefix(AnimalAI __instance)
		{
			Tameable component = ((Component)__instance).GetComponent<Tameable>();
			ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>();
			if ((Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null || !Plugin.IsFehuTamed(component2, component))
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Tameable), "GetHoverText")]
	internal static class DeerTameableHoverPatch
	{
		private static bool Prefix(Tameable __instance, ref string __result)
		{
			Character component = ((Component)__instance).GetComponent<Character>();
			ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>();
			if (!Plugin.IsDeer(component) || (Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return true;
			}
			float num = component2.GetZDO().GetFloat("FehuMounts_TameProgress", 0f);
			if (!Plugin.IsFehuTamed(component2, __instance))
			{
				__result = $"Deer (Tameness {Mathf.FloorToInt(num)}%, Acclimatizing)";
				return false;
			}
			__result = DeerLifecycle.Describe(component, component2);
			return false;
		}
	}
	[HarmonyPatch(typeof(Tameable), "Command")]
	internal static class DeerTameableCommandPatch
	{
		private static bool Prefix(Tameable __instance, Humanoid user, bool message)
		{
			if (!Plugin.IsDeer(((Component)__instance).GetComponent<Character>()))
			{
				return true;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val != null)
			{
				DeerCommandController.ToggleFollow(__instance, val, message);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Tameable), "Interact")]
	internal static class DeerTameableInteractPatch
	{
		private static bool Prefix(Tameable __instance, object[] __args, ref bool __result)
		{
			Character component = ((Component)__instance).GetComponent<Character>();
			ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>();
			if (!Plugin.IsDeer(component) || (Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !Plugin.IsFehuTamed(component2, __instance))
			{
				return true;
			}
			Player val = null;
			foreach (object obj in __args)
			{
				Player val2 = (Player)((obj is Player) ? obj : null);
				if (val2 != null)
				{
					val = val2;
					break;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305))
			{
				__result = false;
				return false;
			}
			if ((Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) && component.GetLevel() - 1 >= Plugin.MinStarsToRide.Value)
			{
				FehuMountRideController fehuMountRideController = ((Component)__instance).GetComponent<FehuMountRideController>() ?? ((Component)__instance).gameObject.AddComponent<FehuMountRideController>();
				__result = fehuMountRideController.TryStartRide(val);
				return false;
			}
			__result = DeerCommandController.ToggleFollow(__instance, val, showMessage: true);
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "GetJogSpeedFactor")]
	internal static class DeerJogSpeedPatch
	{
		private static void Postfix(Character __instance, ref float __result)
		{
			if (Plugin.IsDeer(__instance) && DeerLifecycle.HasRider(__instance))
			{
				__result *= FehuMountRideController.SpeedMultiplier(__instance);
				__result *= DeerLifecycle.LoyaltyBonus(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "GetRunSpeedFactor")]
	internal static class DeerRunSpeedPatch
	{
		private static void Postfix(Character __instance, ref float __result)
		{
			if (Plugin.IsDeer(__instance) && DeerLifecycle.HasRider(__instance))
			{
				__result *= FehuMountRideController.SpeedMultiplier(__instance);
				__result *= DeerLifecycle.LoyaltyBonus(__instance);
			}
		}
	}
}