Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TameRidingOverhaul v0.1.20
plugins/TameRidingOverhaul/TameRidingOverhaul.dll
Decompiled 3 weeks agousing 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(); } } } }