Decompiled source of TameRidingOverhaul v0.1.20

plugins/TameRidingOverhaul/TameRidingOverhaul.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Ainzworth.TameRidingOverhaul.Config;
using Ainzworth.TameRidingOverhaul.Runtime;
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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TameRidingOverhaul")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.20.0")]
[assembly: AssemblyInformationalVersion("0.1.20+bc2709d96a44acf14b771f82e0cfe6c91ea1be80")]
[assembly: AssemblyProduct("TameRidingOverhaul")]
[assembly: AssemblyTitle("TameRidingOverhaul")]
[assembly: AssemblyVersion("0.1.20.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 Ainzworth.TameRidingOverhaul
{
	[BepInPlugin("uk.ainzworth.valheim.tameridingoverhaul", "TameRidingOverhaul", "0.1.20")]
	public sealed class TameRidingOverhaulPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "uk.ainzworth.valheim.tameridingoverhaul";

		public const string PluginName = "TameRidingOverhaul";

		public const string PluginVersion = "0.1.20";

		private Harmony? _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			ModConfig.RegisterRuntimeRefreshHandlers();
			_harmony = new Harmony("uk.ainzworth.valheim.tameridingoverhaul");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"TameRidingOverhaul 0.1.20 loaded.");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			MountRuntime.Clear();
			MountMovement.RestoreAll();
			MountDirectInput.Reset();
			MountSteering.Clear();
			TrampleSystem.Clear();
			MountLevelMonitor.Clear();
			MountCombat.ClearAll();
			MountedAutoPickup.Reset();
			MountIdleState.Clear();
			TameSitSystem.ResetRuntime();
		}
	}
}
namespace Ainzworth.TameRidingOverhaul.Runtime
{
	internal static class MountAirborneState
	{
		private sealed class AnimatorInfo
		{
			internal Animator Animator;

			internal bool HasForwardSpeed;

			internal bool HasSidewaySpeed;
		}

		private static readonly int ForwardSpeedHash = Animator.StringToHash("forward_speed");

		private static readonly int SidewaySpeedHash = Animator.StringToHash("sideway_speed");

		private static readonly Dictionary<int, AnimatorInfo> Animators = new Dictionary<int, AnimatorInfo>();

		internal static void Update(Character character)
		{
			if (Object.op_Implicit((Object)(object)character) && character.HaveRider() && !character.IsOnGround())
			{
				SuppressGroundLocomotion(character);
			}
		}

		internal static void OnJump(Character character)
		{
			if (Object.op_Implicit((Object)(object)character))
			{
				SuppressGroundLocomotion(character);
			}
		}

		internal static void ClearFor(Character character)
		{
			if (Object.op_Implicit((Object)(object)character))
			{
				Animators.Remove(((Object)character).GetInstanceID());
			}
		}

		internal static void Clear()
		{
			Animators.Clear();
		}

		private static void SuppressGroundLocomotion(Character character)
		{
			character.SetRun(false);
			AnimatorInfo animatorInfo = GetAnimatorInfo(character);
			if (animatorInfo != null && Object.op_Implicit((Object)(object)animatorInfo.Animator))
			{
				if (animatorInfo.HasForwardSpeed)
				{
					animatorInfo.Animator.SetFloat(ForwardSpeedHash, 0f);
				}
				if (animatorInfo.HasSidewaySpeed)
				{
					animatorInfo.Animator.SetFloat(SidewaySpeedHash, 0f);
				}
			}
		}

		private static AnimatorInfo? GetAnimatorInfo(Character character)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Invalid comparison between Unknown and I4
			int instanceID = ((Object)character).GetInstanceID();
			if (Animators.TryGetValue(instanceID, out AnimatorInfo value) && Object.op_Implicit((Object)(object)value.Animator))
			{
				return value;
			}
			Animator componentInChildren = ((Component)character).GetComponentInChildren<Animator>();
			if (!Object.op_Implicit((Object)(object)componentInChildren))
			{
				return null;
			}
			bool hasForwardSpeed = false;
			bool hasSidewaySpeed = false;
			AnimatorControllerParameter[] parameters = componentInChildren.parameters;
			foreach (AnimatorControllerParameter val in parameters)
			{
				if ((int)val.type == 1)
				{
					if (val.nameHash == ForwardSpeedHash)
					{
						hasForwardSpeed = true;
					}
					else if (val.nameHash == SidewaySpeedHash)
					{
						hasSidewaySpeed = true;
					}
				}
			}
			AnimatorInfo animatorInfo = new AnimatorInfo
			{
				Animator = componentInChildren,
				HasForwardSpeed = hasForwardSpeed,
				HasSidewaySpeed = hasSidewaySpeed
			};
			Animators[instanceID] = animatorInfo;
			return animatorInfo;
		}
	}
	internal static class MountCombat
	{
		private readonly struct AttackCandidate
		{
			internal ItemData Item { get; }

			internal bool Secondary { get; }

			internal string Name { get; }

			internal AttackCandidate(ItemData item, bool secondary, string name)
			{
				Item = item;
				Secondary = secondary;
				Name = name;
			}
		}

		private sealed class ActiveAttackState
		{
			internal ItemData? PreviousWeapon;

			internal ItemData? AttackWeapon;
		}

		private static readonly Dictionary<int, ActiveAttackState> ActiveAttacks = new Dictionary<int, ActiveAttackState>();

		internal static void Update(Humanoid mount)
		{
			if (Object.op_Implicit((Object)(object)mount))
			{
				int instanceID = ((Object)mount).GetInstanceID();
				if (ActiveAttacks.TryGetValue(instanceID, out ActiveAttackState value) && !((Character)mount).InAttack())
				{
					RestoreWeapon(mount, value);
					ActiveAttacks.Remove(instanceID);
				}
			}
		}

		internal static bool TryAttack(Humanoid mount, Sadle sadle, int slot)
		{
			if (!ModConfig.CombatEnabled.Value || !Object.op_Implicit((Object)(object)mount) || slot < 0 || slot > 2)
			{
				return false;
			}
			Update(mount);
			if (((Character)mount).InAttack() || ActiveAttacks.ContainsKey(((Object)mount).GetInstanceID()))
			{
				return false;
			}
			int num = slot switch
			{
				0 => ModConfig.PrimaryAttackRequiredLevel.Value, 
				1 => ModConfig.SecondaryAttackRequiredLevel.Value, 
				_ => ModConfig.SpecialAttackRequiredLevel.Value, 
			};
			int level = ((Character)mount).GetLevel();
			if (level < num)
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogDebug((object)$"Mount attack slot {slot + 1} locked: level {level}, requires {num}.");
				}
				return false;
			}
			List<AttackCandidate> list = CollectAttacks(mount);
			if (slot >= list.Count)
			{
				if (ModConfig.DebugLogging.Value)
				{
					string prefabName = Utils.GetPrefabName(((Component)mount).gameObject);
					TameRidingOverhaulPlugin.Log.LogDebug((object)$"{prefabName} has only {list.Count} usable runtime attack(s); slot {slot + 1} unavailable.");
				}
				return false;
			}
			if (!MountStamina.CanAffordAttack(sadle, slot, out var cost))
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogDebug((object)$"Mount attack slot {slot + 1} blocked: insufficient stamina for cost {cost:F1}.");
				}
				return false;
			}
			AttackCandidate attackCandidate = list[slot];
			ItemData currentWeapon = mount.GetCurrentWeapon();
			if (!EquipAttackWeapon(mount, attackCandidate.Item))
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogWarning((object)("Could not equip mount attack item " + attackCandidate.Name + "; attack was not started."));
				}
				return false;
			}
			if (!((Character)mount).StartAttack((Character)null, attackCandidate.Secondary))
			{
				RestoreWeapon(mount, new ActiveAttackState
				{
					PreviousWeapon = currentWeapon,
					AttackWeapon = attackCandidate.Item
				});
				return false;
			}
			ActiveAttacks[((Object)mount).GetInstanceID()] = new ActiveAttackState
			{
				PreviousWeapon = currentWeapon,
				AttackWeapon = attackCandidate.Item
			};
			MountStamina.SpendAttack(sadle, cost);
			if (ModConfig.DebugLogging.Value)
			{
				TameRidingOverhaulPlugin.Log.LogDebug((object)$"Mount attack {slot + 1}: {attackCandidate.Name}, level={level}, secondary={attackCandidate.Secondary}, stamina={cost:F1}.");
			}
			return true;
		}

		internal static void ClearFor(Humanoid mount)
		{
			if (Object.op_Implicit((Object)(object)mount))
			{
				int instanceID = ((Object)mount).GetInstanceID();
				if (ActiveAttacks.TryGetValue(instanceID, out ActiveAttackState value))
				{
					RestoreWeapon(mount, value);
					ActiveAttacks.Remove(instanceID);
				}
			}
		}

		internal static void ClearAll()
		{
			ActiveAttacks.Clear();
		}

		private static List<AttackCandidate> CollectAttacks(Humanoid mount)
		{
			List<AttackCandidate> list = new List<AttackCandidate>(3);
			HashSet<ItemData> seen = new HashSet<ItemData>();
			Inventory inventory = mount.GetInventory();
			if (inventory != null)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					AddItemAttacks(allItem, list, seen);
					if (list.Count >= 3)
					{
						return list;
					}
				}
			}
			ItemData currentWeapon = mount.GetCurrentWeapon();
			if (currentWeapon != null)
			{
				AddItemAttacks(currentWeapon, list, seen);
			}
			if (list.Count < 3 && mount.m_defaultItems != null)
			{
				GameObject[] defaultItems = mount.m_defaultItems;
				foreach (GameObject val in defaultItems)
				{
					if (!Object.op_Implicit((Object)(object)val))
					{
						continue;
					}
					ItemDrop component = val.GetComponent<ItemDrop>();
					if (Object.op_Implicit((Object)(object)component) && component.m_itemData != null)
					{
						AddItemAttacks(component.m_itemData, list, seen);
						if (list.Count >= 3)
						{
							break;
						}
					}
				}
			}
			return list;
		}

		private static void AddItemAttacks(ItemData item, List<AttackCandidate> result, HashSet<ItemData> seen)
		{
			if (item != null && result.Count < 3 && seen.Add(item))
			{
				string text = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? Utils.GetPrefabName(item.m_dropPrefab) : item.m_shared.m_name);
				if (item.HavePrimaryAttack() && result.Count < 3)
				{
					result.Add(new AttackCandidate(item, secondary: false, text));
				}
				if (item.HaveSecondaryAttack() && result.Count < 3)
				{
					result.Add(new AttackCandidate(item, secondary: true, text + " (secondary)"));
				}
			}
		}

		private static bool EquipAttackWeapon(Humanoid mount, ItemData item)
		{
			if (mount.GetCurrentWeapon() == item)
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)mount.m_unarmedWeapon) && item == mount.m_unarmedWeapon.m_itemData)
			{
				ItemData currentWeapon = mount.GetCurrentWeapon();
				if (currentWeapon != null)
				{
					mount.UnequipItem(currentWeapon, false);
				}
				if (mount.GetCurrentWeapon() != item)
				{
					return mount.GetCurrentWeapon() == null;
				}
				return true;
			}
			return mount.EquipItem(item, false);
		}

		private static void RestoreWeapon(Humanoid mount, ActiveAttackState state)
		{
			if (!Object.op_Implicit((Object)(object)mount))
			{
				return;
			}
			ItemData currentWeapon = mount.GetCurrentWeapon();
			if (state.AttackWeapon != null && currentWeapon != null && currentWeapon != state.AttackWeapon)
			{
				return;
			}
			if (state.PreviousWeapon != null)
			{
				if (currentWeapon != state.PreviousWeapon)
				{
					mount.EquipItem(state.PreviousWeapon, false);
				}
			}
			else if (currentWeapon != null)
			{
				mount.UnequipItem(currentWeapon, false);
			}
		}
	}
	internal static class MountDirectInput
	{
		private static readonly FieldInfo? SaddleSpeedField = AccessTools.Field(typeof(Sadle), "m_speed");

		private static readonly MethodInfo? SaddleSetSpeedMethod = ResolveSetSpeedMethod();

		private static bool _previousBackwardHeld;

		private static bool _warnedStopResolution;

		internal static bool ForwardHeld { get; private set; }

		internal static bool BackwardHeld { get; private set; }

		internal static bool RunHeld { get; private set; }

		internal static bool SprintingActive { get; private set; }

		internal static void ApplyBeforeVanilla(Sadle sadle, ref Vector3 moveDir, ref bool run, ref bool autoRun, ref bool block)
		{
			if (!ModConfig.Enabled.Value || !MountRuntime.IsLocalSadle(sadle))
			{
				ResetInputState();
				return;
			}
			ForwardHeld = ZInput.GetButton("Forward");
			BackwardHeld = ZInput.GetButton("Backward");
			RunHeld = ZInput.GetButton("Run");
			autoRun = false;
			if (BackwardHeld && !_previousBackwardHeld)
			{
				MountSteering.TurnAround(sadle);
			}
			_previousBackwardHeld = BackwardHeld;
			if (ForwardHeld && !BackwardHeld)
			{
				SprintingActive = RunHeld && MountStamina.CanSprint(sadle);
				moveDir.z = 1f;
				run = SprintingActive;
			}
			else
			{
				SprintingActive = false;
				moveDir.z = 0f;
				run = false;
				ForceStop(sadle);
			}
			if (!ForwardHeld)
			{
				block = false;
			}
		}

		internal static void ApplyAfterVanilla(Sadle sadle)
		{
			if (ModConfig.Enabled.Value && MountRuntime.IsLocalSadle(sadle) && (!ForwardHeld || BackwardHeld))
			{
				ForceStop(sadle);
				MountIdleState.ApplyStoppedState(sadle);
			}
		}

		internal static void Reset()
		{
			ResetInputState();
		}

		private static void ForceStop(Sadle sadle)
		{
			if (!Object.op_Implicit((Object)(object)sadle))
			{
				return;
			}
			try
			{
				MethodInfo? saddleSetSpeedMethod = SaddleSetSpeedMethod;
				Type type = (((object)saddleSetSpeedMethod != null) ? saddleSetSpeedMethod.GetParameters()[0].ParameterType : null) ?? SaddleSpeedField?.FieldType;
				if (!(type == null))
				{
					object obj = ResolveStopValue(type);
					if (SaddleSetSpeedMethod != null)
					{
						SaddleSetSpeedMethod.Invoke(sadle, new object[1] { obj });
					}
					else if (SaddleSpeedField != null)
					{
						SaddleSpeedField.SetValue(sadle, obj);
					}
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogWarning((object)("Could not force saddle speed to stop: " + ex.GetBaseException().Message));
				}
			}
		}

		private static MethodInfo? ResolveSetSpeedMethod()
		{
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Sadle)))
			{
				if (string.Equals(declaredMethod.Name, "SetSpeed", StringComparison.Ordinal) && declaredMethod.GetParameters().Length == 1)
				{
					return declaredMethod;
				}
			}
			return null;
		}

		private static object ResolveStopValue(Type speedType)
		{
			if (!speedType.IsEnum)
			{
				return Convert.ChangeType(0, speedType);
			}
			string[] array = new string[4] { "Stop", "Stopped", "None", "Idle" };
			foreach (string value in array)
			{
				if (Enum.IsDefined(speedType, value))
				{
					return Enum.Parse(speedType, value);
				}
			}
			Array values = Enum.GetValues(speedType);
			if (values.Length > 0)
			{
				if (!_warnedStopResolution && ModConfig.DebugLogging.Value)
				{
					_warnedStopResolution = true;
					TameRidingOverhaulPlugin.Log.LogWarning((object)("Could not resolve a named Sadle stop speed for " + speedType.FullName + "; " + $"falling back to the first enum value ({values.GetValue(0)}). Values: " + string.Join(", ", Enum.GetNames(speedType))));
				}
				return values.GetValue(0);
			}
			return Enum.ToObject(speedType, 0);
		}

		private static void ResetInputState()
		{
			ForwardHeld = false;
			BackwardHeld = false;
			RunHeld = false;
			SprintingActive = false;
			_previousBackwardHeld = false;
			_warnedStopResolution = false;
		}
	}
	internal static class MountedAutoPickup
	{
		private const float DefaultPickupRange = 2f;

		private const int ColliderBufferSize = 128;

		private static readonly Collider[] ColliderBuffer = (Collider[])(object)new Collider[128];

		private static readonly HashSet<int> SeenItems = new HashSet<int>();

		private static readonly FieldInfo? EnableAutoPickupField = AccessTools.Field(typeof(Player), "m_enableAutoPickup");

		private static readonly FieldInfo? AutoPickupRangeField = AccessTools.Field(typeof(Player), "m_autoPickupRange");

		private static readonly FieldInfo? AutoPickupMaskField = AccessTools.Field(typeof(Player), "m_autoPickupMask");

		private static float _nextScanAt;

		private static bool _warnedMissingFields;

		internal static void Tick(Player player, Humanoid mount)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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)
			if (!ModConfig.MountedAutoPickupEnabled.Value || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)mount) || ((Character)player).IsTeleporting() || !IsVanillaAutoPickupEnabled() || Time.time < _nextScanAt)
			{
				return;
			}
			_nextScanAt = Time.time + Mathf.Max(0.02f, ModConfig.MountedAutoPickupScanInterval.Value);
			float num = Mathf.Max(0.1f, GetPickupRange(player) * Mathf.Max(0.1f, ModConfig.MountedAutoPickupRangeMultiplier.Value));
			int pickupMask = GetPickupMask(player);
			if (pickupMask == 0)
			{
				WarnMissingFieldsOnce();
				return;
			}
			Vector3 val = ((Component)mount).transform.position + Vector3.up * 0.75f;
			Inventory inventory = ((Humanoid)player).GetInventory();
			SeenItems.Clear();
			int num2 = Physics.OverlapSphereNonAlloc(val, num, ColliderBuffer, pickupMask);
			Collider[] array = ColliderBuffer;
			if (num2 >= ColliderBuffer.Length)
			{
				array = Physics.OverlapSphere(val, num, pickupMask);
				num2 = array.Length;
			}
			try
			{
				for (int i = 0; i < num2; i++)
				{
					Collider val2 = array[i];
					if (!Object.op_Implicit((Object)(object)val2) || !TryGetItemDrop(val2, out ItemDrop itemDrop))
					{
						continue;
					}
					int instanceID = ((Object)itemDrop).GetInstanceID();
					if (!SeenItems.Add(instanceID) || !CanAutoPickup(player, itemDrop))
					{
						continue;
					}
					if (!itemDrop.CanPickup(true))
					{
						itemDrop.RequestOwn();
					}
					else
					{
						if (itemDrop.InTar())
						{
							continue;
						}
						itemDrop.Load();
						if (inventory.CanAddItem(itemDrop.m_itemData, -1))
						{
							float weight = itemDrop.m_itemData.GetWeight(-1);
							if (!(inventory.GetTotalWeight() + weight > player.GetMaxCarryWeight()))
							{
								((Humanoid)player).Pickup(((Component)itemDrop).gameObject, true, true);
							}
						}
					}
				}
			}
			finally
			{
				if (array == ColliderBuffer)
				{
					Array.Clear(ColliderBuffer, 0, Math.Min(num2, ColliderBuffer.Length));
				}
				SeenItems.Clear();
			}
		}

		internal static void Reset()
		{
			_nextScanAt = 0f;
			SeenItems.Clear();
			Array.Clear(ColliderBuffer, 0, ColliderBuffer.Length);
		}

		private static bool CanAutoPickup(Player player, ItemDrop itemDrop)
		{
			if (!Object.op_Implicit((Object)(object)itemDrop) || !itemDrop.m_autoPickup || itemDrop.IsPiece())
			{
				return false;
			}
			string text = itemDrop.m_itemData?.m_shared?.m_name ?? string.Empty;
			if (string.IsNullOrEmpty(text) || ((Humanoid)player).HaveUniqueKey(text))
			{
				return false;
			}
			ZNetView component = ((Component)itemDrop).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return component.IsValid();
			}
			return false;
		}

		private static bool TryGetItemDrop(Collider collider, out ItemDrop itemDrop)
		{
			itemDrop = null;
			Rigidbody attachedRigidbody = collider.attachedRigidbody;
			if (!Object.op_Implicit((Object)(object)attachedRigidbody))
			{
				return false;
			}
			ItemDrop component = ((Component)attachedRigidbody).GetComponent<ItemDrop>();
			if (Object.op_Implicit((Object)(object)component))
			{
				itemDrop = component;
				return true;
			}
			FloatingTerrainDummy component2 = ((Component)attachedRigidbody).GetComponent<FloatingTerrainDummy>();
			if (Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component2.m_parent))
			{
				ItemDrop component3 = ((Component)component2.m_parent).gameObject.GetComponent<ItemDrop>();
				if (Object.op_Implicit((Object)(object)component3))
				{
					itemDrop = component3;
					return true;
				}
			}
			return false;
		}

		private static bool IsVanillaAutoPickupEnabled()
		{
			try
			{
				object obj = EnableAutoPickupField?.GetValue(null);
				if (obj is bool)
				{
					return (bool)obj;
				}
			}
			catch
			{
			}
			WarnMissingFieldsOnce();
			return false;
		}

		private static float GetPickupRange(Player player)
		{
			try
			{
				if (AutoPickupRangeField?.GetValue(player) is float num && num > 0f)
				{
					return num;
				}
			}
			catch
			{
			}
			return 2f;
		}

		private static int GetPickupMask(Player player)
		{
			try
			{
				if (AutoPickupMaskField?.GetValue(player) is int num && num != 0)
				{
					return num;
				}
			}
			catch
			{
			}
			return LayerMask.GetMask(new string[1] { "item" });
		}

		private static void WarnMissingFieldsOnce()
		{
			if (!_warnedMissingFields && ModConfig.DebugLogging.Value)
			{
				_warnedMissingFields = true;
				TameRidingOverhaulPlugin.Log.LogWarning((object)"Mounted auto-pickup could not resolve one or more vanilla pickup fields; fallback values are being used where possible.");
			}
		}
	}
	internal static class MountIdleState
	{
		private sealed class AnimatorInfo
		{
			internal Animator Animator;

			internal bool HasForwardSpeed;

			internal bool HasSidewaySpeed;
		}

		private const float IdleVelocityThreshold = 0.12f;

		private static readonly int ForwardSpeedHash = Animator.StringToHash("forward_speed");

		private static readonly int SidewaySpeedHash = Animator.StringToHash("sideway_speed");

		private static readonly Dictionary<int, AnimatorInfo> Animators = new Dictionary<int, AnimatorInfo>();

		internal static void ApplyStoppedState(Sadle sadle)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)sadle) || !MountRuntime.IsLocalSadle(sadle))
			{
				return;
			}
			Character character = sadle.GetCharacter();
			if (!Object.op_Implicit((Object)(object)character))
			{
				return;
			}
			character.SetMoveDir(Vector3.zero);
			character.SetRun(false);
			Vector3 velocity = character.GetVelocity();
			velocity.y = 0f;
			if (((Vector3)(ref velocity)).sqrMagnitude > 0.0144f)
			{
				return;
			}
			AnimatorInfo animatorInfo = GetAnimatorInfo(character);
			if (animatorInfo != null && Object.op_Implicit((Object)(object)animatorInfo.Animator))
			{
				if (animatorInfo.HasForwardSpeed)
				{
					animatorInfo.Animator.SetFloat(ForwardSpeedHash, 0f);
				}
				if (animatorInfo.HasSidewaySpeed)
				{
					animatorInfo.Animator.SetFloat(SidewaySpeedHash, 0f);
				}
			}
		}

		internal static void ClearFor(Character character)
		{
			if (Object.op_Implicit((Object)(object)character))
			{
				Animators.Remove(((Object)character).GetInstanceID());
			}
		}

		internal static void Clear()
		{
			Animators.Clear();
		}

		private static AnimatorInfo? GetAnimatorInfo(Character character)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Invalid comparison between Unknown and I4
			int instanceID = ((Object)character).GetInstanceID();
			if (Animators.TryGetValue(instanceID, out AnimatorInfo value) && Object.op_Implicit((Object)(object)value.Animator))
			{
				return value;
			}
			Animator componentInChildren = ((Component)character).GetComponentInChildren<Animator>();
			if (!Object.op_Implicit((Object)(object)componentInChildren))
			{
				return null;
			}
			bool hasForwardSpeed = false;
			bool hasSidewaySpeed = false;
			AnimatorControllerParameter[] parameters = componentInChildren.parameters;
			foreach (AnimatorControllerParameter val in parameters)
			{
				if ((int)val.type == 1)
				{
					if (val.nameHash == ForwardSpeedHash)
					{
						hasForwardSpeed = true;
					}
					else if (val.nameHash == SidewaySpeedHash)
					{
						hasSidewaySpeed = true;
					}
				}
			}
			AnimatorInfo animatorInfo = new AnimatorInfo
			{
				Animator = componentInChildren,
				HasForwardSpeed = hasForwardSpeed,
				HasSidewaySpeed = hasSidewaySpeed
			};
			Animators[instanceID] = animatorInfo;
			return animatorInfo;
		}
	}
	internal static class MountJump
	{
		private static readonly Dictionary<int, float> NextJumpTime = new Dictionary<int, float>();

		internal static bool TryJump(Character mount)
		{
			if (!ModConfig.JumpEnabled.Value || !Object.op_Implicit((Object)(object)mount) || !mount.IsOnGround())
			{
				return false;
			}
			int instanceID = ((Object)mount).GetInstanceID();
			if (NextJumpTime.TryGetValue(instanceID, out var value) && Time.time < value)
			{
				return false;
			}
			float jumpForce = mount.m_jumpForce;
			float jumpForceForward = mount.m_jumpForceForward;
			mount.m_jumpForce = jumpForce * Mathf.Max(0f, ModConfig.JumpForceMultiplier.Value);
			mount.m_jumpForceForward = jumpForceForward * Mathf.Max(0f, ModConfig.JumpForwardMultiplier.Value);
			try
			{
				mount.Jump(false);
				NextJumpTime[instanceID] = Time.time + Mathf.Max(0f, ModConfig.JumpCooldownSeconds.Value);
				return true;
			}
			finally
			{
				mount.m_jumpForce = jumpForce;
				mount.m_jumpForceForward = jumpForceForward;
			}
		}

		internal static void Clear(Character mount)
		{
			if (Object.op_Implicit((Object)(object)mount))
			{
				NextJumpTime.Remove(((Object)mount).GetInstanceID());
			}
		}
	}
	internal static class MountLevelMonitor
	{
		private static readonly Dictionary<int, int> LastLevel = new Dictionary<int, int>();

		internal static void Update(Character mount)
		{
			if (!Object.op_Implicit((Object)(object)mount) || !mount.HaveRider())
			{
				return;
			}
			int instanceID = ((Object)mount).GetInstanceID();
			int level = mount.GetLevel();
			if (!LastLevel.TryGetValue(instanceID, out var value))
			{
				LastLevel[instanceID] = level;
			}
			else if (value != level)
			{
				if (ModConfig.DebugLogging.Value)
				{
					string prefabName = Utils.GetPrefabName(((Component)mount).gameObject);
					TameRidingOverhaulPlugin.Log.LogDebug((object)($"Observed external mount level change for {prefabName}: {value} -> {level}. " + "TameRidingOverhaul only reads GetLevel() and never calls SetLevel()."));
				}
				LastLevel[instanceID] = level;
			}
		}

		internal static void ClearFor(Character mount)
		{
			if (Object.op_Implicit((Object)(object)mount))
			{
				LastLevel.Remove(((Object)mount).GetInstanceID());
			}
		}

		internal static void Clear()
		{
			LastLevel.Clear();
		}
	}
	internal static class MountMovement
	{
		private sealed class Snapshot
		{
			internal Character Character;

			internal float Speed;

			internal float Acceleration;

			internal float WalkSpeed;

			internal float RunSpeed;

			internal float TurnSpeed;

			internal float RunTurnSpeed;

			internal float SwimSpeed;

			internal float SwimTurnSpeed;

			internal float SwimAcceleration;

			internal float FlySlowSpeed;

			internal float FlyFastSpeed;

			internal float FlyTurnSpeed;
		}

		private static readonly Dictionary<int, Snapshot> Applied = new Dictionary<int, Snapshot>();

		internal static void Update(Character character)
		{
			if (!Object.op_Implicit((Object)(object)character))
			{
				return;
			}
			if (!ModConfig.Enabled.Value || !character.HaveRider())
			{
				Restore(character);
				return;
			}
			int instanceID = ((Object)character).GetInstanceID();
			if (!Applied.ContainsKey(instanceID))
			{
				Snapshot snapshot = new Snapshot
				{
					Character = character,
					Speed = character.m_speed,
					Acceleration = character.m_acceleration,
					WalkSpeed = character.m_walkSpeed,
					RunSpeed = character.m_runSpeed,
					TurnSpeed = character.m_turnSpeed,
					RunTurnSpeed = character.m_runTurnSpeed,
					SwimSpeed = character.m_swimSpeed,
					SwimTurnSpeed = character.m_swimTurnSpeed,
					SwimAcceleration = character.m_swimAcceleration,
					FlySlowSpeed = character.m_flySlowSpeed,
					FlyFastSpeed = character.m_flyFastSpeed,
					FlyTurnSpeed = character.m_flyTurnSpeed
				};
				Applied[instanceID] = snapshot;
				float num = Mathf.Max(0.05f, ModConfig.MoveSpeedMultiplier.Value);
				float num2 = Mathf.Max(0.05f, ModConfig.SprintSpeedMultiplier.Value);
				float num3 = Mathf.Max(0.05f, ModConfig.AccelerationMultiplier.Value);
				float num4 = Mathf.Max(0.05f, ModConfig.TurnSpeedMultiplier.Value);
				character.m_speed = snapshot.Speed * num;
				character.m_walkSpeed = snapshot.WalkSpeed * num;
				character.m_runSpeed = snapshot.RunSpeed * num2;
				character.m_acceleration = snapshot.Acceleration * num3;
				character.m_turnSpeed = snapshot.TurnSpeed * num4;
				character.m_runTurnSpeed = snapshot.RunTurnSpeed * num4;
				character.m_swimSpeed = snapshot.SwimSpeed * num;
				character.m_swimTurnSpeed = snapshot.SwimTurnSpeed * num4;
				character.m_swimAcceleration = snapshot.SwimAcceleration * num3;
				character.m_flySlowSpeed = snapshot.FlySlowSpeed * num;
				character.m_flyFastSpeed = snapshot.FlyFastSpeed * num2;
				character.m_flyTurnSpeed = snapshot.FlyTurnSpeed * num4;
				if (ModConfig.DebugLogging.Value)
				{
					string prefabName = Utils.GetPrefabName(((Component)character).gameObject);
					TameRidingOverhaulPlugin.Log.LogDebug((object)("Applied movement multipliers to " + prefabName + ": " + $"move={num:F2}, sprint={num2:F2}, accel={num3:F2}, turn={num4:F2}"));
				}
			}
		}

		internal static void Restore(Character character)
		{
			if (Object.op_Implicit((Object)(object)character))
			{
				int instanceID = ((Object)character).GetInstanceID();
				if (Applied.TryGetValue(instanceID, out Snapshot value))
				{
					RestoreSnapshot(value);
					Applied.Remove(instanceID);
				}
			}
		}

		internal static void RestoreAll()
		{
			foreach (Snapshot value in Applied.Values)
			{
				RestoreSnapshot(value);
			}
			Applied.Clear();
		}

		private static void RestoreSnapshot(Snapshot snapshot)
		{
			Character character = snapshot.Character;
			if (Object.op_Implicit((Object)(object)character))
			{
				character.m_speed = snapshot.Speed;
				character.m_acceleration = snapshot.Acceleration;
				character.m_walkSpeed = snapshot.WalkSpeed;
				character.m_runSpeed = snapshot.RunSpeed;
				character.m_turnSpeed = snapshot.TurnSpeed;
				character.m_runTurnSpeed = snapshot.RunTurnSpeed;
				character.m_swimSpeed = snapshot.SwimSpeed;
				character.m_swimTurnSpeed = snapshot.SwimTurnSpeed;
				character.m_swimAcceleration = snapshot.SwimAcceleration;
				character.m_flySlowSpeed = snapshot.FlySlowSpeed;
				character.m_flyFastSpeed = snapshot.FlyFastSpeed;
				character.m_flyTurnSpeed = snapshot.FlyTurnSpeed;
			}
		}
	}
	internal static class MountRuntime
	{
		private static Player? _player;

		private static Sadle? _sadle;

		internal static void Begin(Player player, IDoodadController controller)
		{
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || controller == null)
			{
				return;
			}
			Sadle val = (Sadle)(object)((controller is Sadle) ? controller : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				Component controlledComponent = controller.GetControlledComponent();
				if (Object.op_Implicit((Object)(object)controlledComponent))
				{
					val = controlledComponent.GetComponentInParent<Sadle>();
				}
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				_player = player;
				_sadle = val;
				Character character = val.GetCharacter();
				if (Object.op_Implicit((Object)(object)character))
				{
					TameSitSystem.OnMounted(character, player);
				}
				MountSteering.Reset(val);
				if (ModConfig.DebugLogging.Value)
				{
					Character character2 = val.GetCharacter();
					string text = (Object.op_Implicit((Object)(object)character2) ? Utils.GetPrefabName(((Component)character2).gameObject) : "<missing>");
					TameRidingOverhaulPlugin.Log.LogDebug((object)("Started mount control: " + text));
				}
			}
		}

		internal static void End(Player player)
		{
			if ((Object)(object)_player != (Object)(object)player)
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)_sadle))
			{
				Character character = _sadle.GetCharacter();
				if (Object.op_Implicit((Object)(object)character))
				{
					Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
					if (val != null)
					{
						MountCombat.ClearFor(val);
					}
					MountMovement.Restore(character);
					TrampleSystem.ClearFor(character);
					MountLevelMonitor.ClearFor(character);
					MountIdleState.ClearFor(character);
					TameSitSystem.OnDismounted(character, player);
				}
				MountStamina.ClearFor(_sadle);
			}
			MountedAutoPickup.Reset();
			MountDirectInput.Reset();
			MountSteering.Clear();
			_player = null;
			_sadle = null;
		}

		internal static bool TryGetLocal(out Player player, out Sadle sadle, out Humanoid mount)
		{
			player = _player;
			sadle = _sadle;
			mount = null;
			if (!ModConfig.Enabled.Value || !Object.op_Implicit((Object)(object)_player) || (Object)(object)_player != (Object)(object)Player.m_localPlayer || !((Character)_player).IsRiding() || !Object.op_Implicit((Object)(object)_sadle))
			{
				return false;
			}
			Character character = _sadle.GetCharacter();
			Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			mount = val;
			return true;
		}

		internal static bool IsLocalSadle(Sadle sadle)
		{
			if (ModConfig.Enabled.Value && Object.op_Implicit((Object)(object)_player) && (Object)(object)_player == (Object)(object)Player.m_localPlayer && ((Character)_player).IsRiding() && Object.op_Implicit((Object)(object)_sadle))
			{
				return (Object)(object)_sadle == (Object)(object)sadle;
			}
			return false;
		}

		internal static void Clear()
		{
			if (Object.op_Implicit((Object)(object)_player))
			{
				End(_player);
				return;
			}
			_sadle = null;
			MountDirectInput.Reset();
			MountSteering.Clear();
			MountStamina.ClearAll();
			MountCombat.ClearAll();
			MountIdleState.Clear();
		}
	}
	internal static class MountStamina
	{
		private sealed class State
		{
			internal float BeforeVanilla;

			internal bool HasBeforeVanilla;

			internal float LastSpendTime = float.NegativeInfinity;
		}

		private static readonly Dictionary<int, State> States = new Dictionary<int, State>();

		private static readonly MethodInfo? GetStaminaMethod = AccessTools.Method(typeof(Sadle), "GetStamina", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo? SetStaminaMethod = AccessTools.Method(typeof(Sadle), "SetStamina", new Type[1] { typeof(float) }, (Type[])null);

		private static readonly MethodInfo? GetMaxStaminaMethod = AccessTools.Method(typeof(Sadle), "GetMaxStamina", Type.EmptyTypes, (Type[])null);

		private static bool _reportedMissingAccess;

		internal static void CaptureBeforeVanilla(Sadle sadle)
		{
			if (IsActive(sadle) && TryGetStamina(sadle, out var stamina, out var _))
			{
				State state = GetState(sadle);
				state.BeforeVanilla = stamina;
				state.HasBeforeVanilla = true;
			}
		}

		internal static void ApplyAfterVanilla(Sadle sadle)
		{
			if (!IsActive(sadle))
			{
				ClearFor(sadle);
				return;
			}
			State state = GetState(sadle);
			if (!state.HasBeforeVanilla || !TryGetStamina(sadle, out var _, out var maxStamina))
			{
				return;
			}
			float num = state.BeforeVanilla;
			state.HasBeforeVanilla = false;
			float num2 = Mathf.Max(0f, Time.fixedDeltaTime);
			float num3 = ((MountDirectInput.ForwardHeld && !MountDirectInput.BackwardHeld && MountDirectInput.SprintingActive) ? Mathf.Max(0f, ModConfig.SprintStaminaDrainPerSecond.Value) : 0f);
			if (num3 > 0f)
			{
				num -= num3 * num2;
				state.LastSpendTime = Time.time;
			}
			else
			{
				float num4 = Mathf.Max(0f, ModConfig.StaminaRegenDelaySeconds.Value);
				if (Time.time - state.LastSpendTime >= num4)
				{
					num += Mathf.Max(0f, ModConfig.StaminaRegenPerSecond.Value) * num2;
				}
			}
			SetStamina(sadle, Mathf.Clamp(num, 0f, maxStamina));
		}

		internal static bool CanSprint(Sadle sadle)
		{
			if (!ModConfig.StaminaEnabled.Value)
			{
				return true;
			}
			if (!TryGetStamina(sadle, out var stamina, out var _))
			{
				return true;
			}
			return stamina > Mathf.Max(0f, ModConfig.MinimumSprintStamina.Value);
		}

		internal static bool CanAffordAttack(Sadle sadle, int slot, out float cost)
		{
			cost = GetAttackCost(slot);
			if (!ModConfig.StaminaEnabled.Value || cost <= 0f)
			{
				return true;
			}
			if (!TryGetStamina(sadle, out var stamina, out var _))
			{
				return true;
			}
			return stamina >= cost;
		}

		internal static void SpendAttack(Sadle sadle, float cost)
		{
			if (ModConfig.StaminaEnabled.Value && !(cost <= 0f) && TryGetStamina(sadle, out var stamina, out var maxStamina))
			{
				SetStamina(sadle, Mathf.Clamp(stamina - cost, 0f, maxStamina));
				GetState(sadle).LastSpendTime = Time.time;
			}
		}

		internal static float GetAttackCost(int slot)
		{
			return slot switch
			{
				0 => Mathf.Max(0f, ModConfig.PrimaryAttackStaminaCost.Value), 
				1 => Mathf.Max(0f, ModConfig.SecondaryAttackStaminaCost.Value), 
				2 => Mathf.Max(0f, ModConfig.SpecialAttackStaminaCost.Value), 
				_ => 0f, 
			};
		}

		internal static void ClearFor(Sadle sadle)
		{
			if (Object.op_Implicit((Object)(object)sadle))
			{
				States.Remove(((Object)sadle).GetInstanceID());
			}
		}

		internal static void ClearAll()
		{
			States.Clear();
		}

		private static bool IsActive(Sadle sadle)
		{
			if (ModConfig.Enabled.Value && ModConfig.StaminaEnabled.Value && Object.op_Implicit((Object)(object)sadle))
			{
				return MountRuntime.IsLocalSadle(sadle);
			}
			return false;
		}

		private static State GetState(Sadle sadle)
		{
			int instanceID = ((Object)sadle).GetInstanceID();
			if (!States.TryGetValue(instanceID, out State value))
			{
				value = new State();
				States[instanceID] = value;
			}
			return value;
		}

		private static bool TryGetStamina(Sadle sadle, out float stamina, out float maxStamina)
		{
			stamina = 0f;
			maxStamina = 100f;
			try
			{
				object obj = GetStaminaMethod?.Invoke(sadle, null);
				object obj2 = GetMaxStaminaMethod?.Invoke(sadle, null);
				if (obj == null)
				{
					ReportMissingAccessOnce();
					return false;
				}
				stamina = Convert.ToSingle(obj);
				if (obj2 != null)
				{
					maxStamina = Mathf.Max(1f, Convert.ToSingle(obj2));
				}
				else
				{
					maxStamina = Mathf.Max(100f, stamina);
				}
				return true;
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogWarning((object)("Could not read mount stamina: " + ex.Message));
				}
				return false;
			}
		}

		private static void SetStamina(Sadle sadle, float stamina)
		{
			try
			{
				if (SetStaminaMethod != null)
				{
					SetStaminaMethod.Invoke(sadle, new object[1] { stamina });
				}
				else
				{
					ReportMissingAccessOnce();
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogWarning((object)("Could not set mount stamina: " + ex.Message));
				}
			}
		}

		private static void ReportMissingAccessOnce()
		{
			if (!_reportedMissingAccess)
			{
				_reportedMissingAccess = true;
				TameRidingOverhaulPlugin.Log.LogWarning((object)"Could not access Sadle stamina methods. Custom stamina handling is disabled for this game build; riding remains functional.");
			}
		}
	}
	internal static class MountSteering
	{
		private static readonly Dictionary<int, float> DesiredYaw = new Dictionary<int, float>();

		private static readonly MethodInfo? FaceLookDirectionMethod = AccessTools.Method(typeof(Character), "FaceLookDirection", Type.EmptyTypes, (Type[])null);

		private static bool _warnedMissingFaceLookDirection;

		internal static void Reset(Sadle sadle)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)sadle))
			{
				Character character = sadle.GetCharacter();
				if (Object.op_Implicit((Object)(object)character))
				{
					DesiredYaw[((Object)sadle).GetInstanceID()] = ((Component)character).transform.eulerAngles.y;
				}
			}
		}

		internal static void Apply(Sadle sadle, ref Vector3 moveDir, ref Vector3 lookDir, ref bool block)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.DirectSteering.Value || !MountRuntime.IsLocalSadle(sadle))
			{
				return;
			}
			Character character = sadle.GetCharacter();
			if (Object.op_Implicit((Object)(object)character))
			{
				int instanceID = ((Object)sadle).GetInstanceID();
				if (!DesiredYaw.TryGetValue(instanceID, out var value))
				{
					value = ((Component)character).transform.eulerAngles.y;
				}
				float x = moveDir.x;
				bool num = Mathf.Abs(x) > 0.01f;
				bool flag = MountDirectInput.ForwardHeld || MountDirectInput.BackwardHeld;
				if (num)
				{
					value = Mathf.Repeat(value + x * Mathf.Max(0f, ModConfig.SteeringDegreesPerSecond.Value) * Time.deltaTime, 360f);
				}
				DesiredYaw[instanceID] = value;
				lookDir = Quaternion.Euler(0f, value, 0f) * Vector3.forward;
				moveDir.x = 0f;
				if (num && !flag)
				{
					character.SetLookDir(lookDir, 0f);
					ApplyFaceLookDirection(character, value);
					block = false;
				}
			}
		}

		internal static void ApplyStationaryFacingLock(Sadle sadle)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.DirectSteering.Value || !MountRuntime.IsLocalSadle(sadle))
			{
				return;
			}
			Character character = sadle.GetCharacter();
			if (Object.op_Implicit((Object)(object)character) && character.IsOnGround() && (!MountDirectInput.ForwardHeld || MountDirectInput.BackwardHeld))
			{
				int instanceID = ((Object)sadle).GetInstanceID();
				if (!DesiredYaw.TryGetValue(instanceID, out var value))
				{
					value = ((Component)character).transform.eulerAngles.y;
					DesiredYaw[instanceID] = value;
				}
				Vector3 val = Quaternion.Euler(0f, value, 0f) * Vector3.forward;
				character.SetLookDir(val, 0f);
				((Component)character).transform.rotation = Quaternion.Euler(0f, value, 0f);
			}
		}

		internal static void TurnAround(Sadle sadle)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.DirectSteering.Value || !MountRuntime.IsLocalSadle(sadle))
			{
				return;
			}
			Character character = sadle.GetCharacter();
			if (Object.op_Implicit((Object)(object)character))
			{
				int instanceID = ((Object)sadle).GetInstanceID();
				float value;
				float num = Mathf.Repeat((DesiredYaw.TryGetValue(instanceID, out value) ? value : ((Component)character).transform.eulerAngles.y) + 180f, 360f);
				Vector3 val = Quaternion.Euler(0f, num, 0f) * Vector3.forward;
				DesiredYaw[instanceID] = num;
				character.SetLookDir(val, 0f);
				((Component)character).transform.rotation = Quaternion.Euler(0f, num, 0f);
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogDebug((object)$"Mount turn-around: yaw={num:F1}");
				}
			}
		}

		private static void ApplyFaceLookDirection(Character character, float yaw)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (FaceLookDirectionMethod != null)
			{
				try
				{
					FaceLookDirectionMethod.Invoke(character, null);
					return;
				}
				catch (Exception ex)
				{
					if (ModConfig.DebugLogging.Value)
					{
						TameRidingOverhaulPlugin.Log.LogWarning((object)("FaceLookDirection invocation failed: " + ex.GetBaseException().Message));
					}
				}
			}
			((Component)character).transform.rotation = Quaternion.Euler(0f, yaw, 0f);
			if (!_warnedMissingFaceLookDirection && FaceLookDirectionMethod == null)
			{
				_warnedMissingFaceLookDirection = true;
				TameRidingOverhaulPlugin.Log.LogWarning((object)"Character.FaceLookDirection could not be resolved. Using direct stationary rotation fallback.");
			}
		}

		internal static void Clear()
		{
			DesiredYaw.Clear();
			_warnedMissingFaceLookDirection = false;
		}
	}
	internal static class TameSitSystem
	{
		private const string SitZdoKey = "uk.ainzworth.tameridingoverhaul.sit";

		private static int _lastManualToggleFrame = -1;

		private static Tameable? _lastHoveredTameable;

		private static int _lastHoverFrame = -100;

		internal static void Update(Character character)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.Enabled.Value && ModConfig.SitEnabled.Value && Object.op_Implicit((Object)(object)character) && !character.HaveRider() && IsSitting(character))
			{
				MonsterAI component = ((Component)character).GetComponent<MonsterAI>();
				if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.GetFollowTarget()))
				{
					component.SetFollowTarget((GameObject)null);
					((BaseAI)component).SetPatrolPoint();
				}
				character.SetMoveDir(Vector3.zero);
				character.SetRun(false);
			}
		}

		internal static bool TryHandleInteract(Tameable tameable, Humanoid user, bool hold)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (hold || !CanUseManualCommand(user, out Player player))
			{
				return false;
			}
			KeyboardShortcut value = ModConfig.SitKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey != 101 || !IsShortcutPressed(value))
			{
				return false;
			}
			Character val = ResolveRideableTame(tameable);
			if (!Object.op_Implicit((Object)(object)val) || val.HaveRider())
			{
				return false;
			}
			Toggle(val, player, showMessage: true);
			_lastManualToggleFrame = Time.frameCount;
			return true;
		}

		internal static void UpdateManualInput(Player player)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!CanUseManualCommand((Humanoid)(object)player, out Player player2) || _lastManualToggleFrame == Time.frameCount)
			{
				return;
			}
			KeyboardShortcut value = ModConfig.SitKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 101 || !IsShortcutDown(value))
			{
				return;
			}
			Tameable val = ((Time.frameCount - _lastHoverFrame <= 2 && Object.op_Implicit((Object)(object)_lastHoveredTameable)) ? _lastHoveredTameable : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				GameObject hoverObject = ((Humanoid)player2).GetHoverObject();
				if (Object.op_Implicit((Object)(object)hoverObject))
				{
					val = hoverObject.GetComponentInParent<Tameable>();
				}
			}
			Character val2 = ResolveRideableTame(val);
			if (Object.op_Implicit((Object)(object)val2) && !val2.HaveRider())
			{
				Toggle(val2, player2, showMessage: true);
				_lastManualToggleFrame = Time.frameCount;
			}
		}

		internal static void AppendHoverText(Tameable tameable, ref string hoverText)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.Enabled.Value || !ModConfig.SitEnabled.Value || !Object.op_Implicit((Object)(object)tameable) || !tameable.IsTamed())
			{
				return;
			}
			Character val = ResolveRideableTame(tameable);
			if (Object.op_Implicit((Object)(object)val) && !val.HaveRider())
			{
				_lastHoveredTameable = tameable;
				_lastHoverFrame = Time.frameCount;
				KeyboardShortcut value = ModConfig.SitKey.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
				{
					string text = FormatShortcut(value);
					string text2 = (IsSitting(val) ? "Release Sit" : "Sit");
					hoverText = hoverText + "\n[<color=yellow><b>" + text + "</b></color>] " + text2;
				}
			}
		}

		internal static void OnMounted(Character character, Player player)
		{
			if (ModConfig.Enabled.Value && ModConfig.SitEnabled.Value && Object.op_Implicit((Object)(object)character))
			{
				SetSitting(character, sitting: false, player, showMessage: false);
			}
		}

		internal static void OnDismounted(Character character, Player player)
		{
			if (ModConfig.Enabled.Value && ModConfig.SitEnabled.Value && ModConfig.SitOnDismount.Value && Object.op_Implicit((Object)(object)character))
			{
				SetSitting(character, sitting: true, player, showMessage: false);
			}
		}

		internal static bool IsSitting(Character character)
		{
			if (!Object.op_Implicit((Object)(object)character))
			{
				return false;
			}
			ZNetView component = ((Component)character).GetComponent<ZNetView>();
			ZDO val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
			if (val != null)
			{
				return val.GetBool("uk.ainzworth.tameridingoverhaul.sit", false);
			}
			return false;
		}

		internal static void ResetRuntime()
		{
			_lastManualToggleFrame = -1;
			_lastHoveredTameable = null;
			_lastHoverFrame = -100;
		}

		private static bool CanUseManualCommand(Humanoid user, out Player player)
		{
			player = (Player)(object)(((user is Player) ? user : null) ?? null);
			if (ModConfig.Enabled.Value && ModConfig.SitEnabled.Value && Object.op_Implicit((Object)(object)player))
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		private static Character? ResolveRideableTame(Tameable? tameable)
		{
			if (!Object.op_Implicit((Object)(object)tameable) || !tameable.IsTamed())
			{
				return null;
			}
			Character val = ((Component)tameable).GetComponent<Character>();
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = ((Component)tameable).GetComponentInParent<Character>();
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			Sadle val2 = ((Component)val).GetComponent<Sadle>();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				val2 = ((Component)val).GetComponentInChildren<Sadle>();
			}
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return null;
			}
			return val;
		}

		private static bool IsShortcutDown(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !AreRequiredModifiersPressed(shortcut))
			{
				return false;
			}
			if (IsLetterKey(((KeyboardShortcut)(ref shortcut)).MainKey))
			{
				if (!Input.anyKeyDown)
				{
					return false;
				}
				char c = (char)(97 + (((KeyboardShortcut)(ref shortcut)).MainKey - 97));
				string inputString = Input.inputString;
				for (int i = 0; i < inputString.Length; i++)
				{
					if (char.ToLowerInvariant(inputString[i]) == c)
					{
						return true;
					}
				}
				return false;
			}
			return Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey);
		}

		private static bool IsShortcutPressed(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && AreRequiredModifiersPressed(shortcut))
			{
				return Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey);
			}
			return false;
		}

		private static bool AreRequiredModifiersPressed(KeyboardShortcut shortcut)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!Input.GetKey(modifier))
				{
					return false;
				}
			}
			return true;
		}

		private static bool IsLetterKey(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)key >= 97)
			{
				return (int)key <= 122;
			}
			return false;
		}

		private static string FormatShortcut(KeyboardShortcut shortcut)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			string[] value = ((KeyboardShortcut)(ref shortcut)).Modifiers.Select(FormatKey).Concat(new string[1] { FormatKey(((KeyboardShortcut)(ref shortcut)).MainKey) }).ToArray();
			return string.Join(" + ", value);
		}

		private unsafe static string FormatKey(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			return (key - 303) switch
			{
				1 => "Shift", 
				0 => "Shift", 
				3 => "Ctrl", 
				2 => "Ctrl", 
				5 => "Alt", 
				4 => "Alt", 
				_ => ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(), 
			};
		}

		private static void Toggle(Character character, Player player, bool showMessage)
		{
			SetSitting(character, !IsSitting(character), player, showMessage);
		}

		private static void SetSitting(Character character, bool sitting, Player player, bool showMessage)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			Tameable component = ((Component)character).GetComponent<Tameable>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsTamed())
			{
				return;
			}
			ZNetView component2 = ((Component)character).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component2) && component2.IsValid() && !component2.IsOwner())
			{
				component2.ClaimOwnership();
			}
			ZDO val = ((Object.op_Implicit((Object)(object)component2) && component2.IsValid()) ? component2.GetZDO() : null);
			if (val != null)
			{
				val.Set("uk.ainzworth.tameridingoverhaul.sit", sitting);
			}
			MonsterAI component3 = ((Component)character).GetComponent<MonsterAI>();
			if (sitting)
			{
				if (Object.op_Implicit((Object)(object)component3))
				{
					component3.SetFollowTarget((GameObject)null);
					((BaseAI)component3).SetPatrolPoint();
				}
				if (val != null)
				{
					val.Set(ZDOVars.s_follow, string.Empty);
				}
				character.SetMoveDir(Vector3.zero);
				character.SetRun(false);
			}
			else if (Object.op_Implicit((Object)(object)component3))
			{
				((BaseAI)component3).ResetPatrolPoint();
			}
			if (showMessage && Object.op_Implicit((Object)(object)player))
			{
				string text = (sitting ? "Sit" : "Sit released");
				((Character)player).Message((MessageType)2, character.GetHoverName() + ": " + text, 0, (Sprite)null, false);
			}
			if (ModConfig.DebugLogging.Value)
			{
				string prefabName = Utils.GetPrefabName(((Component)character).gameObject);
				TameRidingOverhaulPlugin.Log.LogDebug((object)$"Sit state for {prefabName}: {sitting}");
			}
		}
	}
	internal static class TrampleSystem
	{
		private sealed class IgnoredCollision
		{
			internal Collider Mount;

			internal Collider Obstacle;

			internal float RestoreAt;
		}

		private static readonly Dictionary<int, float> NextScan = new Dictionary<int, float>();

		private static readonly Dictionary<int, float> RecentlyHit = new Dictionary<int, float>();

		private static readonly Dictionary<long, IgnoredCollision> Ignored = new Dictionary<long, IgnoredCollision>();

		internal static void Update(Character mount)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: 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_01e2: Unknown result type (might be due to invalid IL or missing references)
			RestoreExpiredCollisions();
			if (!ModConfig.TramplingEnabled.Value || !Object.op_Implicit((Object)(object)mount) || !mount.HaveRider())
			{
				return;
			}
			Vector3 velocity = mount.GetVelocity();
			if (((Vector3)(ref velocity)).magnitude < Mathf.Max(0f, ModConfig.TramplingMinimumSpeed.Value))
			{
				return;
			}
			int instanceID = ((Object)mount).GetInstanceID();
			if (NextScan.TryGetValue(instanceID, out var value) && Time.time < value)
			{
				return;
			}
			NextScan[instanceID] = Time.time + 0.1f;
			Collider collider = (Collider)(object)mount.GetCollider();
			Collider[] array = Physics.OverlapSphere(mount.GetCenterPoint(), Mathf.Max(0.1f, ModConfig.TramplingRadius.Value));
			foreach (Collider val in array)
			{
				if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val == (Object)(object)collider || ((Component)val).transform.IsChildOf(((Component)mount).transform))
				{
					continue;
				}
				Destructible componentInParent = ((Component)val).GetComponentInParent<Destructible>();
				GameObject val2 = (Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).gameObject : ((Component)val).gameObject);
				if (!IsTrampleable(val2, val, out string prefabName))
				{
					continue;
				}
				int instanceID2 = ((Object)val2).GetInstanceID();
				if (RecentlyHit.TryGetValue(instanceID2, out var value2) && Time.time < value2)
				{
					IgnoreBriefly(collider, val);
					continue;
				}
				RecentlyHit[instanceID2] = Time.time + 0.75f;
				IgnoreBriefly(collider, val);
				if (Object.op_Implicit((Object)(object)componentInParent))
				{
					HitData val3 = new HitData();
					float num = Mathf.Max(1f, ModConfig.TramplingDamage.Value);
					val3.m_damage.m_blunt = num;
					val3.m_damage.m_chop = num;
					val3.m_point = val.ClosestPoint(mount.GetCenterPoint());
					val3.m_dir = ((((Vector3)(ref velocity)).sqrMagnitude > 0.001f) ? ((Vector3)(ref velocity)).normalized : ((Component)mount).transform.forward);
					val3.SetAttacker(mount);
					componentInParent.Damage(val3);
				}
				if (ModConfig.DebugLogging.Value)
				{
					TameRidingOverhaulPlugin.Log.LogDebug((object)$"Trampled {prefabName} at speed {((Vector3)(ref velocity)).magnitude:F2}.");
				}
			}
			PruneRecentlyHit();
		}

		internal static void ClearFor(Character mount)
		{
			if (Object.op_Implicit((Object)(object)mount))
			{
				NextScan.Remove(((Object)mount).GetInstanceID());
			}
		}

		internal static void Clear()
		{
			foreach (IgnoredCollision value in Ignored.Values)
			{
				if (Object.op_Implicit((Object)(object)value.Mount) && Object.op_Implicit((Object)(object)value.Obstacle))
				{
					Physics.IgnoreCollision(value.Mount, value.Obstacle, false);
				}
			}
			Ignored.Clear();
			NextScan.Clear();
			RecentlyHit.Clear();
		}

		private static bool IsTrampleable(GameObject target, Collider hitCollider, out string prefabName)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			prefabName = Utils.GetPrefabName(target);
			string text = prefabName.ToLowerInvariant();
			if (Object.op_Implicit((Object)(object)target.GetComponent<Piece>()) || Object.op_Implicit((Object)(object)target.GetComponent<WearNTear>()) || Object.op_Implicit((Object)(object)target.GetComponent<Plant>()) || Object.op_Implicit((Object)(object)target.GetComponent<TreeBase>()) || Object.op_Implicit((Object)(object)target.GetComponent<TreeLog>()) || Object.op_Implicit((Object)(object)target.GetComponent<Character>()) || Object.op_Implicit((Object)(object)target.GetComponent<ItemDrop>()))
			{
				return false;
			}
			if ((!ModConfig.TrampleBushes.Value || (!text.Contains("bush") && !text.Contains("shrub"))) && (!ModConfig.TrampleBranches.Value || !text.Contains("branch") || text.Contains("log")) && (!ModConfig.TrampleSprouts.Value || !text.Contains("sprout")) && (!ModConfig.TrampleSmallTrees.Value || !IsKnownSmallTree(text)) && (!ModConfig.TrampleSaplings.Value || !text.Contains("sapling")))
			{
				return false;
			}
			if (IsExplicitVanillaSmallVegetation(text))
			{
				return true;
			}
			float num = Mathf.Max(0.1f, ModConfig.TramplingMaxObstacleSize.Value);
			Bounds bounds = hitCollider.bounds;
			Collider[] componentsInChildren = target.GetComponentsInChildren<Collider>();
			foreach (Collider val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					((Bounds)(ref bounds)).Encapsulate(val.bounds);
				}
			}
			Vector3 size = ((Bounds)(ref bounds)).size;
			return Mathf.Max(size.x, Mathf.Max(size.y, size.z)) <= num;
		}

		private static bool IsKnownSmallTree(string lowerPrefabName)
		{
			if (!lowerPrefabName.StartsWith("firtree_small", StringComparison.Ordinal) && !lowerPrefabName.StartsWith("beech_small", StringComparison.Ordinal) && !lowerPrefabName.StartsWith("yggashoot_small", StringComparison.Ordinal))
			{
				return lowerPrefabName.Contains("smalltree");
			}
			return true;
		}

		private static bool IsExplicitVanillaSmallVegetation(string lowerPrefabName)
		{
			switch (lowerPrefabName)
			{
			default:
				return lowerPrefabName == "yggashoot_small1";
			case "beech_small1":
			case "beech_small2":
			case "firtree_small":
			case "firtree_small_dead":
			case "bush01":
			case "bush01_heath":
			case "bush01_deepnorth":
			case "bush02_en":
			case "shrub_2":
			case "shrub_2_heath":
				return true;
			}
		}

		private static void IgnoreBriefly(Collider mount, Collider obstacle)
		{
			if (Object.op_Implicit((Object)(object)mount) && Object.op_Implicit((Object)(object)obstacle))
			{
				long key = ((long)((Object)mount).GetInstanceID() << 32) ^ (uint)((Object)obstacle).GetInstanceID();
				Physics.IgnoreCollision(mount, obstacle, true);
				if (Ignored.TryGetValue(key, out IgnoredCollision value))
				{
					value.RestoreAt = Time.time + 1.25f;
					return;
				}
				Ignored[key] = new IgnoredCollision
				{
					Mount = mount,
					Obstacle = obstacle,
					RestoreAt = Time.time + 1.25f
				};
			}
		}

		private static void RestoreExpiredCollisions()
		{
			if (Ignored.Count == 0)
			{
				return;
			}
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, IgnoredCollision> item in Ignored)
			{
				IgnoredCollision value = item.Value;
				if (!(Time.time < value.RestoreAt))
				{
					if (Object.op_Implicit((Object)(object)value.Mount) && Object.op_Implicit((Object)(object)value.Obstacle))
					{
						Physics.IgnoreCollision(value.Mount, value.Obstacle, false);
					}
					list.Add(item.Key);
				}
			}
			foreach (long item2 in list)
			{
				Ignored.Remove(item2);
			}
		}

		private static void PruneRecentlyHit()
		{
			if (RecentlyHit.Count < 64)
			{
				return;
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, float> item in RecentlyHit)
			{
				if (Time.time >= item.Value)
				{
					list.Add(item.Key);
				}
			}
			foreach (int item2 in list)
			{
				RecentlyHit.Remove(item2);
			}
		}
	}
}
namespace Ainzworth.TameRidingOverhaul.Patches
{
	[HarmonyPatch(typeof(Player), "StartDoodadControl")]
	internal static class PlayerStartDoodadControlPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance, IDoodadController shipControl)
		{
			MountRuntime.Begin(__instance, shipControl);
		}
	}
	[HarmonyPatch(typeof(Player), "StopDoodadControl")]
	internal static class PlayerStopDoodadControlPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Player __instance)
		{
			MountRuntime.End(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class PlayerSetControlsPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump)
		{
			if (ModConfig.Enabled.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && ((Character)__instance).IsRiding() && MountRuntime.TryGetLocal(out Player _, out Sadle _, out Humanoid _))
			{
				if (ModConfig.CombatEnabled.Value)
				{
					attack = false;
					attackHold = false;
					secondaryAttack = false;
					secondaryAttackHold = false;
					block = false;
					blockHold = false;
				}
				if (ModConfig.JumpEnabled.Value)
				{
					jump = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdateMountedActionsPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			if (!ModConfig.Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			TameSitSystem.UpdateManualInput(__instance);
			if (!MountRuntime.TryGetLocal(out Player _, out Sadle sadle, out Humanoid mount))
			{
				return;
			}
			MountedAutoPickup.Tick(__instance, mount);
			MountCombat.Update(mount);
			if (ModConfig.JumpEnabled.Value && ZInput.GetButtonDown("Jump"))
			{
				MountJump.TryJump((Character)(object)mount);
			}
			if (ModConfig.CombatEnabled.Value)
			{
				if (ZInput.GetButtonDown("Attack"))
				{
					MountCombat.TryAttack(mount, sadle, 0);
				}
				else if (ZInput.GetButtonDown("Block"))
				{
					MountCombat.TryAttack(mount, sadle, 1);
				}
				else if (ZInput.GetButtonDown("SecondaryAttack"))
				{
					MountCombat.TryAttack(mount, sadle, 2);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "Interact")]
	internal static class TameableSitInteractPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Tameable __instance, Humanoid user, bool hold, ref bool __result)
		{
			if (!TameSitSystem.TryHandleInteract(__instance, user, hold))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Tameable), "GetHoverText")]
	internal static class TameableSitHoverTextPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Tameable __instance, ref string __result)
		{
			TameSitSystem.AppendHoverText(__instance, ref __result);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class MonsterAISitUpdatePatch
	{
		[HarmonyPostfix]
		private static void Postfix(MonsterAI __instance)
		{
			if (ModConfig.Enabled.Value && ModConfig.SitEnabled.Value && Object.op_Implicit((Object)(object)__instance))
			{
				Character component = ((Component)__instance).GetComponent<Character>();
				if (Object.op_Implicit((Object)(object)component))
				{
					TameSitSystem.Update(component);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Sadle), "ApplyControlls")]
	internal static class SadleApplyControllsPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Sadle __instance, ref Vector3 moveDir, ref Vector3 lookDir, ref bool run, ref bool autoRun, ref bool block)
		{
			if (ModConfig.Enabled.Value)
			{
				MountDirectInput.ApplyBeforeVanilla(__instance, ref moveDir, ref run, ref autoRun, ref block);
				MountSteering.Apply(__instance, ref moveDir, ref lookDir, ref block);
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Sadle __instance)
		{
			MountDirectInput.ApplyAfterVanilla(__instance);
			MountSteering.ApplyStationaryFacingLock(__instance);
		}
	}
	[HarmonyPatch(typeof(Sadle), "FixedUpdate")]
	internal static class SadleFixedUpdatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(Sadle __instance)
		{
			MountStamina.CaptureBeforeVanilla(__instance);
		}

		[HarmonyPostfix]
		private static void Postfix(Sadle __instance)
		{
			MountStamina.ApplyAfterVanilla(__instance);
			Character character = __instance.GetCharacter();
			if (Object.op_Implicit((Object)(object)character))
			{
				MountMovement.Update(character);
				TameSitSystem.Update(character);
				if (ModConfig.Enabled.Value && character.HaveRider())
				{
					TrampleSystem.Update(character);
					MountLevelMonitor.Update(character);
				}
				else
				{
					TrampleSystem.ClearFor(character);
					MountJump.Clear(character);
					MountLevelMonitor.ClearFor(character);
				}
				MountSteering.ApplyStationaryFacingLock(__instance);
			}
		}
	}
}
namespace Ainzworth.TameRidingOverhaul.Config
{
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> DebugLogging { get; private set; }

		internal static ConfigEntry<bool> DirectSteering { get; private set; }

		internal static ConfigEntry<float> SteeringDegreesPerSecond { get; private set; }

		internal static ConfigEntry<float> StationaryTurnAssist { get; private set; }

		internal static ConfigEntry<float> MoveSpeedMultiplier { get; private set; }

		internal static ConfigEntry<float> SprintSpeedMultiplier { get; private set; }

		internal static ConfigEntry<float> AccelerationMultiplier { get; private set; }

		internal static ConfigEntry<float> TurnSpeedMultiplier { get; private set; }

		internal static ConfigEntry<bool> JumpEnabled { get; private set; }

		internal static ConfigEntry<float> JumpForceMultiplier { get; private set; }

		internal static ConfigEntry<float> JumpForwardMultiplier { get; private set; }

		internal static ConfigEntry<float> JumpCooldownSeconds { get; private set; }

		internal static ConfigEntry<bool> CombatEnabled { get; private set; }

		internal static ConfigEntry<int> PrimaryAttackRequiredLevel { get; private set; }

		internal static ConfigEntry<int> SecondaryAttackRequiredLevel { get; private set; }

		internal static ConfigEntry<int> SpecialAttackRequiredLevel { get; private set; }

		internal static ConfigEntry<bool> StaminaEnabled { get; private set; }

		internal static ConfigEntry<float> MoveStaminaDrainPerSecond { get; private set; }

		internal static ConfigEntry<float> SprintStaminaDrainPerSecond { get; private set; }

		internal static ConfigEntry<float> StaminaRegenPerSecond { get; private set; }

		internal static ConfigEntry<float> StaminaRegenDelaySeconds { get; private set; }

		internal static ConfigEntry<float> MinimumSprintStamina { get; private set; }

		internal static ConfigEntry<float> PrimaryAttackStaminaCost { get; private set; }

		internal static ConfigEntry<float> SecondaryAttackStaminaCost { get; private set; }

		internal static ConfigEntry<float> SpecialAttackStaminaCost { get; private set; }

		internal static ConfigEntry<bool> MountedAutoPickupEnabled { get; private set; }

		internal static ConfigEntry<float> MountedAutoPickupRangeMultiplier { get; private set; }

		internal static ConfigEntry<float> MountedAutoPickupScanInterval { get; private set; }

		internal static ConfigEntry<bool> TramplingEnabled { get; private set; }

		internal static ConfigEntry<float> TramplingMinimumSpeed { get; private set; }

		internal static ConfigEntry<float> TramplingRadius { get; private set; }

		internal static ConfigEntry<float> TramplingMaxObstacleSize { get; private set; }

		internal static ConfigEntry<float> TramplingDamage { get; private set; }

		internal static ConfigEntry<bool> TrampleBushes { get; private set; }

		internal static ConfigEntry<bool> TrampleBranches { get; private set; }

		internal static ConfigEntry<bool> TrampleSprouts { get; private set; }

		internal static ConfigEntry<bool> TrampleSmallTrees { get; private set; }

		internal static ConfigEntry<bool> TrampleSaplings { get; private set; }

		internal static ConfigEntry<bool> SitEnabled { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> SitKey { get; private set; }

		internal static ConfigEntry<bool> SitOnDismount { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Invalid comparison between Unknown and I4
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Invalid comparison between Unknown and I4
			//IL_0544: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c3: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enables TameRidingOverhaul.");
			DebugLogging = config.Bind<bool>("General", "DebugLogging", false, "Writes additional mount diagnostics to the BepInEx log.");
			DirectSteering = config.Bind<bool>("Movement", "DirectSteering", true, "Replaces the sluggish vanilla left/right steering with direct mount steering.");
			SteeringDegreesPerSecond = config.Bind<float>("Movement", "SteeringDegreesPerSecond", 160f, "How quickly A/D changes the desired mount heading while DirectSteering is enabled.");
			StationaryTurnAssist = config.Bind<float>("Movement", "StationaryTurnAssist", 0f, "Deprecated compatibility setting. TameRidingOverhaul no longer injects forward movement while steering at standstill.");
			MoveSpeedMultiplier = config.Bind<float>("Movement", "MoveSpeedMultiplier", 1.35f, "Multiplier applied to normal mounted movement speed.");
			SprintSpeedMultiplier = config.Bind<float>("Movement", "SprintSpeedMultiplier", 1.7f, "Multiplier applied to mounted run/sprint speed. Shift continues to use Valheim's saddle stamina system.");
			AccelerationMultiplier = config.Bind<float>("Movement", "AccelerationMultiplier", 2f, "Multiplier applied to mounted acceleration and braking response.");
			TurnSpeedMultiplier = config.Bind<float>("Movement", "TurnSpeedMultiplier", 2.5f, "Multiplier applied to the creature's normal and running turn speeds while ridden.");
			JumpEnabled = config.Bind<bool>("Jump", "Enabled", true, "Allows the ridden creature to jump with the normal Valheim Jump input (Space by default).");
			JumpForceMultiplier = config.Bind<float>("Jump", "JumpForceMultiplier", 1f, "Vertical jump force multiplier for ridden creatures.");
			JumpForwardMultiplier = config.Bind<float>("Jump", "JumpForwardMultiplier", 1f, "Forward jump force multiplier for ridden creatures.");
			JumpCooldownSeconds = config.Bind<float>("Jump", "CooldownSeconds", 0.5f, "Minimum time between mount jumps.");
			CombatEnabled = config.Bind<bool>("Combat", "Enabled", true, "Redirects Valheim's attack controls to the ridden creature's own attacks while mounted.");
			PrimaryAttackRequiredLevel = config.Bind<int>("Combat", "PrimaryAttackRequiredLevel", 1, "Creature level required for LMB / Attack 1. Vanilla level 1 is a no-star creature.");
			SecondaryAttackRequiredLevel = config.Bind<int>("Combat", "SecondaryAttackRequiredLevel", 2, "Creature level required for RMB / Attack 2. Vanilla level 2 is a one-star creature.");
			SpecialAttackRequiredLevel = config.Bind<int>("Combat", "SpecialAttackRequiredLevel", 3, "Creature level required for MMB / Attack 3 (Special). Vanilla level 3 is a two-star creature.");
			StaminaEnabled = config.Bind<bool>("Stamina", "Enabled", true, "Uses TameRidingOverhaul's mounted stamina model for movement, sprinting, attacks, and regeneration.");
			MoveStaminaDrainPerSecond = config.Bind<float>("Stamina", "MoveDrainPerSecond", 0f, "Deprecated/ignored. Normal riding never consumes stamina; only sprinting and attacks do.");
			SprintStaminaDrainPerSecond = config.Bind<float>("Stamina", "SprintDrainPerSecond", 8f, "Stamina drained per second while sprinting. Normal riding has no continuous stamina cost.");
			StaminaRegenPerSecond = config.Bind<float>("Stamina", "RegenerationPerSecond", 18f, "Stamina restored per second whenever sprint/attack recovery delay has elapsed, including while riding normally.");
			StaminaRegenDelaySeconds = config.Bind<float>("Stamina", "RegenerationDelaySeconds", 0.75f, "Delay after movement stamina use or an attack before mounted stamina begins regenerating.");
			MinimumSprintStamina = config.Bind<float>("Stamina", "MinimumSprintStamina", 1f, "Sprint falls back to normal riding when stamina is at or below this value.");
			PrimaryAttackStaminaCost = config.Bind<float>("Stamina", "PrimaryAttackCost", 10f, "Stamina cost of LMB / Attack 1.");
			SecondaryAttackStaminaCost = config.Bind<float>("Stamina", "SecondaryAttackCost", 16f, "Stamina cost of RMB / Attack 2.");
			SpecialAttackStaminaCost = config.Bind<float>("Stamina", "SpecialAttackCost", 24f, "Stamina cost of MMB / Attack 3 (Special).");
			MountedAutoPickupEnabled = config.Bind<bool>("AutoPickup", "Enabled", true, "Keeps Valheim auto-pickup active while riding. The normal Valheim auto-pickup toggle is still respected.");
			MountedAutoPickupRangeMultiplier = config.Bind<float>("AutoPickup", "RangeMultiplier", 1f, "Multiplier applied to the player's current auto-pickup range while mounted. This also respects pickup-range changes made by other mods.");
			MountedAutoPickupScanInterval = config.Bind<float>("AutoPickup", "ScanIntervalSeconds", 0.08f, "How often the mounted pickup scan runs. Lower values react faster but perform more physics scans.");
			TramplingEnabled = config.Bind<bool>("Trampling", "Enabled", true, "Allows a moving mount to break or pass through selected small vegetation.");
			TramplingMinimumSpeed = config.Bind<float>("Trampling", "MinimumSpeed", 2f, "Minimum world-space speed required before trampling is active.");
			TramplingRadius = config.Bind<float>("Trampling", "Radius", 1.35f, "Radius around the mount checked for small trampleable obstacles.");
			TramplingMaxObstacleSize = config.Bind<float>("Trampling", "MaxObstacleSize", 3f, "Maximum world-space dimension of an obstacle that may be trampled. This prevents large logs/trees from being classified as small vegetation.");
			TramplingDamage = config.Bind<float>("Trampling", "Damage", 10000f, "Blunt/chop damage applied to trampleable destructible vegetation.");
			TrampleBushes = config.Bind<bool>("Trampling", "Bushes", true, "Trample bush prefabs.");
			TrampleBranches = config.Bind<bool>("Trampling", "Branches", true, "Trample small branch prefabs. Large logs are explicitly rejected.");
			TrampleSprouts = config.Bind<bool>("Trampling", "Sprouts", true, "Trample prefabs whose names identify them as sprouts.");
			TrampleSmallTrees = config.Bind<bool>("Trampling", "SmallTrees", true, "Trample small non-TreeBase vegetation such as FirTree_small. Full trees and logs are never trampled.");
			TrampleSaplings = config.Bind<bool>("Trampling", "Saplings", false, "Also trample natural sapling prefabs. Player-placed/Plant/Piece objects are still protected. Disabled by default.");
			SitEnabled = config.Bind<bool>("Sit", "Enabled", true, "Enables the Sit command for tamed rideable creatures. Sitting clears follow behavior and keeps the tame stationary.");
			SitKey = config.Bind<KeyboardShortcut>("Sit", "Keybind", new KeyboardShortcut((KeyCode)121, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Shortcut used while looking at a tamed rideable creature to toggle Sit. Default Shift+Y avoids Valheim's Shift+E tame rename interaction and the T emote-wheel binding.");
			SitOnDismount = config.Bind<bool>("Sit", "SitOnDismount", true, "Automatically puts the ridden tame into Sit when the local player dismounts. Mounting automatically releases Sit.");
			ConfigEntry<bool> val = config.Bind<bool>("Internal", "SitKeyDefaultMigration_0_1_15", false, "Internal one-time migration marker for the Sit keybind default.");
			KeyboardShortcut value = SitKey.Value;
			if (!val.Value && (int)((KeyboardShortcut)(ref value)).MainKey == 101 && ((KeyboardShortcut)(ref value)).Modifiers.SequenceEqual((IEnumerable<KeyCode>)(object)new KeyCode[1] { (KeyCode)304 }))
			{
				SitKey.Value = new KeyboardShortcut((KeyCode)103, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 });
			}
			if (!val.Value)
			{
				val.Value = true;
			}
			ConfigEntry<bool> val2 = config.Bind<bool>("Internal", "SitKeyDefaultMigration_0_1_17", false, "Internal one-time migration marker for the Sit keybind default to Shift+Y.");
			KeyboardShortcut value2 = SitKey.Value;
			if (!val2.Value && (int)((KeyboardShortcut)(ref value2)).MainKey == 103 && ((KeyboardShortcut)(ref value2)).Modifiers.SequenceEqual((IEnumerable<KeyCode>)(object)new KeyCode[1] { (KeyCode)304 }))
			{
				SitKey.Value = new KeyboardShortcut((KeyCode)121, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 });
			}
			if (!val2.Value)
			{
				val2.Value = true;
			}
		}

		internal static void RegisterRuntimeRefreshHandlers()
		{
			Enabled.SettingChanged += RefreshMovement;
			MoveSpeedMultiplier.SettingChanged += RefreshMovement;
			SprintSpeedMultiplier.SettingChanged += RefreshMovement;
			AccelerationMultiplier.SettingChanged += RefreshMovement;
			TurnSpeedMultiplier.SettingChanged += RefreshMovement;
			static void RefreshMovement(object? _, EventArgs __)
			{
				MountMovement.RestoreAll();
			}
		}
	}
}