Decompiled source of FistsFollowLookLethalHands v2.3.1

BepInEx/plugins/LethalHandsHandsFollowPatch.dll

Decompiled 19 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalHands;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LethalHandsHandsFollowPatch")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LethalHandsHandsFollowPatch")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("86729b87-dbc5-4c70-8934-7bfb73c9bdbe")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BloodDraco.LethalHandsHandsFollowPatch;

[BepInPlugin("BloodDraco.LethalHandsHandsFollowPatch", "LethalHandsHandsFollowPatch", "2.3.1")]
public class LethalHandsHandsFollowPatch : BaseUnityPlugin
{
	public const string ModGuid = "BloodDraco.LethalHandsHandsFollowPatch";

	public const string ModName = "LethalHandsHandsFollowPatch";

	public const string ModVersion = "2.3.1";

	internal static LethalHandsHandsFollowPatch Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> OnlyWhenSquaredUp;

	internal static ConfigEntry<float> MaxPitchDegrees;

	internal static ConfigEntry<float> PitchStrength;

	internal static ConfigEntry<float> UpperArmWeight;

	internal static ConfigEntry<float> ForearmWeight;

	internal static ConfigEntry<float> HandWeight;

	internal static ConfigEntry<bool> EnableCameraHeightFollow;

	internal static ConfigEntry<float> HeightFollowStrength;

	internal static ConfigEntry<float> HeightMaxOffset;

	internal static ConfigEntry<float> HeightSmoothingSpeed;

	internal static ConfigEntry<float> DefaultArmHeight;

	internal static ConfigEntry<float> DefaultArmForward;

	internal static ConfigEntry<bool> EnableAutoForwardLookUp;

	internal static ConfigEntry<float> AutoForwardMaxOffset;

	internal static ConfigEntry<float> AutoForwardStrength;

	internal static ConfigEntry<float> AutoForwardCurvePower;

	internal static ConfigEntry<float> AutoForwardSmoothingSpeed;

	internal static ConfigEntry<float> AutoForwardStartDegrees;

	internal static ConfigEntry<bool> AutoForwardUpIsNegative;

	internal static ConfigEntry<bool> AutoForwardUseCameraForward;

	internal static ConfigEntry<string> ResetConfigBinding;

	internal static ConfigEntry<bool> EnableThirdPersonReplication;

	internal static ConfigEntry<float> NetSendRateHz;

	internal static ConfigEntry<bool> NetOnlySendWhenSquaredUp;

	internal static ConfigEntry<bool> DebugLogNetworking;

	internal static ConfigEntry<bool> EnableThirdPersonSpineBlend;

	internal static ConfigEntry<float> ThirdPersonMaxUpDegrees;

	internal static ConfigEntry<float> ThirdPersonStrength;

	internal static ConfigEntry<float> ThirdPersonSmoothingSpeed;

	internal static ConfigEntry<float> SpineWeight;

	internal static ConfigEntry<float> ChestWeight;

	internal static ConfigEntry<float> UpperChestWeight;

	internal static ConfigEntry<bool> ThirdPersonUpIsNegative;

	private InputAction _resetAction;

	private bool _resetActionHooked;

	private float _nextSendTime;

	private bool _lastSquaredUpState;

	private void Awake()
	{
		//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable this patch.");
		OnlyWhenSquaredUp = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OnlyWhenSquaredUp", true, "Only apply while LethalHands is squared up.");
		MaxPitchDegrees = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "MaxPitchDegrees", 160f, "Clamp camera pitch.");
		PitchStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Look", "PitchStrength", 1f, "Pitch multiplier (0..1).");
		UpperArmWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Weights", "UpperArmWeight", 0.85f, "Weight for upper arms.");
		ForearmWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Weights", "ForearmWeight", 0.35f, "Weight for forearms.");
		HandWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Weights", "HandWeight", 0.1f, "Weight for hands/wrists.");
		EnableCameraHeightFollow = ((BaseUnityPlugin)this).Config.Bind<bool>("Height", "EnableCameraHeightFollow", true, "Follow camera height changes while squared up.");
		HeightFollowStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Height", "HeightFollowStrength", 1f, "Multiplier for camera height delta.");
		HeightMaxOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Height", "HeightMaxOffset", 0.12f, "Max absolute height offset in meters.");
		HeightSmoothingSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Height", "HeightSmoothingSpeed", 18f, "Smoothing speed for height delta.");
		DefaultArmHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Offsets", "DefaultArmHeight", 0.1f, "Base arm height offset in meters.");
		DefaultArmForward = ((BaseUnityPlugin)this).Config.Bind<float>("Offsets", "DefaultArmForward", 0.05f, "Base arm forward offset in meters.");
		EnableAutoForwardLookUp = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoForward", "EnableAutoForwardLookUp", true, "Push arms forward the more you look UP (starts at level).");
		AutoForwardMaxOffset = ((BaseUnityPlugin)this).Config.Bind<float>("AutoForward", "AutoForwardMaxOffset", 0.2f, "Max extra forward offset (meters) when looking fully up.");
		AutoForwardStrength = ((BaseUnityPlugin)this).Config.Bind<float>("AutoForward", "AutoForwardStrength", 2f, "0..2 strength multiplier for auto-forward.");
		AutoForwardCurvePower = ((BaseUnityPlugin)this).Config.Bind<float>("AutoForward", "AutoForwardCurvePower", 1.5f, "Curve shaping.");
		AutoForwardSmoothingSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("AutoForward", "AutoForwardSmoothingSpeed", 20f, "Smoothing speed (higher = snappier).");
		AutoForwardStartDegrees = ((BaseUnityPlugin)this).Config.Bind<float>("AutoForward", "AutoForwardStartDegrees", 0f, "Deadzone around level before auto-forward starts (degrees).");
		AutoForwardUpIsNegative = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoForward", "AutoForwardUpIsNegative", true, "If true, looking UP is negative pitch (typical Unity FPS).");
		AutoForwardUseCameraForward = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoForward", "AutoForwardUseCameraForward", true, "Use camera.forward projected into arm parent.");
		ResetConfigBinding = ((BaseUnityPlugin)this).Config.Bind<string>("Controls", "ResetConfigBinding", "<Keyboard>/f8", "InputSystem binding path to reset this mod config.");
		EnableThirdPersonReplication = ((BaseUnityPlugin)this).Config.Bind<bool>("Replication", "EnableThirdPersonReplication", true, "Send your look pitch to other clients so they can see third-person aim/spine pitch while squared up.");
		NetSendRateHz = ((BaseUnityPlugin)this).Config.Bind<float>("Replication", "NetSendRateHz", 13f, "How often to send pitch (Hz). 10-20 recommended.");
		NetOnlySendWhenSquaredUp = ((BaseUnityPlugin)this).Config.Bind<bool>("Replication", "NetOnlySendWhenSquaredUp", true, "Only send pitch while squared up (recommended).");
		DebugLogNetworking = ((BaseUnityPlugin)this).Config.Bind<bool>("Replication", "DebugLogNetworking", false, "Log networking sends/receives (spammy).");
		EnableThirdPersonSpineBlend = ((BaseUnityPlugin)this).Config.Bind<bool>("ThirdPerson", "EnableThirdPersonSpineBlend", true, "Apply replicated look pitch to other players' spines so you can see them aim/look up while squared up.");
		ThirdPersonMaxUpDegrees = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "ThirdPersonMaxUpDegrees", 55f, "Max degrees of UP pitch applied in third person.");
		ThirdPersonStrength = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "ThirdPersonStrength", 1f, "Multiplier for third-person spine pitch.");
		ThirdPersonSmoothingSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "ThirdPersonSmoothingSpeed", 18f, "Smoothing speed for third-person spine pitch.");
		SpineWeight = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "SpineWeight", 0.2f, "Weight applied to Spine bone.");
		ChestWeight = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "ChestWeight", 0.35f, "Weight applied to Chest bone.");
		UpperChestWeight = ((BaseUnityPlugin)this).Config.Bind<float>("ThirdPerson", "UpperChestWeight", 0.45f, "Weight applied to UpperChest bone.");
		ThirdPersonUpIsNegative = ((BaseUnityPlugin)this).Config.Bind<bool>("ThirdPerson", "ThirdPersonUpIsNegative", true, "If true, looking UP is negative pitch (typical Unity FPS).");
		Harmony val = new Harmony("BloodDraco.LethalHandsHandsFollowPatch");
		val.PatchAll(typeof(NetInitPatch));
		val.PatchAll(typeof(NetLifecyclePatch));
		val.PatchAll(typeof(AddSpineDriverPatch));
		val.PatchAll(typeof(EmoteConstraintPitchPatch));
	}

	private void OnEnable()
	{
		TryHookResetAction();
	}

	private void Update()
	{
		if (!_resetActionHooked)
		{
			TryHookResetAction();
		}
		if (EnableThirdPersonReplication.Value)
		{
			HandsFollowLookNet.EnsureInitialized();
		}
	}

	private void OnDisable()
	{
		UnhookResetAction();
		HandsFollowLookNet.Shutdown();
	}

	private void OnDestroy()
	{
		UnhookResetAction();
		HandsFollowLookNet.Shutdown();
	}

	private void LateUpdate()
	{
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled.Value || !EnableThirdPersonReplication.Value || (Object)(object)StartOfRound.Instance == (Object)null)
		{
			return;
		}
		PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController;
		if ((Object)(object)localPlayerController == (Object)null || localPlayerController.isPlayerDead)
		{
			return;
		}
		bool flag = false;
		if ((Object)(object)LethalHands.Instance != (Object)null)
		{
			flag = LethalHands.Instance.isSquaredUp;
		}
		if (flag != _lastSquaredUpState)
		{
			_lastSquaredUpState = flag;
			if (flag)
			{
				Log.LogInfo((object)"[LethalHandsHandsFollowPatch] Active (squared up).");
			}
			else
			{
				Log.LogInfo((object)"[LethalHandsHandsFollowPatch] Inactive (not squared up).");
			}
		}
		if (NetOnlySendWhenSquaredUp.Value && !flag)
		{
			HandsFollowLookNet.SendLocalPitch(localPlayerController, 0f, squaredUp: false);
			return;
		}
		float num = Mathf.Max(1f, NetSendRateHz.Value);
		float num2 = 1f / num;
		if (Time.unscaledTime < _nextSendTime)
		{
			return;
		}
		_nextSendTime = Time.unscaledTime + num2;
		Transform val = (((Object)(object)localPlayerController.gameplayCamera != (Object)null) ? ((Component)localPlayerController.gameplayCamera).transform : null);
		if (!((Object)(object)val == (Object)null))
		{
			float num3 = val.localEulerAngles.x;
			if (num3 > 180f)
			{
				num3 -= 360f;
			}
			HandsFollowLookNet.SendLocalPitch(localPlayerController, num3, flag);
		}
	}

	private void TryHookResetAction()
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		if (!_resetActionHooked && !((Object)(object)IngamePlayerSettings.Instance == (Object)null) && !((Object)(object)IngamePlayerSettings.Instance.playerInput == (Object)null) && !((Object)(object)IngamePlayerSettings.Instance.playerInput.actions == (Object)null))
		{
			_resetAction = new InputAction("BloodDraco_ResetLethalHandsHandsFollowPatch", (InputActionType)1, (string)null, (string)null, (string)null, (string)null);
			string text = ResetConfigBinding.Value;
			if (string.IsNullOrEmpty(text))
			{
				text = "<Keyboard>/f8";
			}
			InputActionSetupExtensions.AddBinding(_resetAction, text, (string)null, (string)null, (string)null);
			_resetAction.performed += OnResetPerformed;
			_resetAction.Enable();
			_resetActionHooked = true;
			Log.LogInfo((object)("[LethalHandsHandsFollowPatch] Reset action hooked: " + text));
		}
	}

	private void UnhookResetAction()
	{
		if (_resetAction == null)
		{
			_resetActionHooked = false;
			return;
		}
		try
		{
			_resetAction.performed -= OnResetPerformed;
			_resetAction.Disable();
			_resetAction.Dispose();
		}
		catch
		{
		}
		_resetAction = null;
		_resetActionHooked = false;
	}

	private void OnResetPerformed(CallbackContext ctx)
	{
		if (((CallbackContext)(ref ctx)).performed)
		{
			Log.LogInfo((object)"[LethalHandsHandsFollowPatch] Resetting config to defaults...");
			((BaseUnityPlugin)this).Config.Reload();
		}
	}
}
[HarmonyPatch]
internal static class NetInitPatch
{
	[HarmonyPatch(typeof(GameNetworkManager), "Start")]
	[HarmonyPostfix]
	private static void Postfix()
	{
		HandsFollowLookNet.EnsureInitialized();
	}
}
[HarmonyPatch]
internal static class NetLifecyclePatch
{
	[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
	[HarmonyPrefix]
	private static void Prefix()
	{
		HandsFollowLookNet.Shutdown();
	}
}
internal static class HandsFollowLookNet
{
	private const string MsgName = "LH_HFP_PITCH";

	internal static readonly Dictionary<ulong, float> PitchByClient = new Dictionary<ulong, float>();

	internal static readonly Dictionary<ulong, bool> SquaredUpByClient = new Dictionary<ulong, bool>();

	private static bool _initialized = false;

	private static bool _handlerRegistered = false;

	internal static void EnsureInitialized()
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		if (_initialized)
		{
			return;
		}
		try
		{
			if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null)
			{
				return;
			}
			if (!_handlerRegistered)
			{
				NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("LH_HFP_PITCH", new HandleNamedMessageDelegate(OnMessageReceived));
				_handlerRegistered = true;
				if (LethalHandsHandsFollowPatch.Log != null)
				{
					LethalHandsHandsFollowPatch.Log.LogInfo((object)"[LethalHandsHandsFollowPatch] Net handler registered.");
				}
			}
			_initialized = true;
		}
		catch (Exception ex)
		{
			if (LethalHandsHandsFollowPatch.Log != null)
			{
				LethalHandsHandsFollowPatch.Log.LogWarning((object)("[LethalHandsHandsFollowPatch] Net init failed (will retry): " + ex.Message));
			}
		}
	}

	internal static void Shutdown()
	{
		_initialized = false;
		try
		{
			if (_handlerRegistered && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.CustomMessagingManager != null)
			{
				NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("LH_HFP_PITCH");
				_handlerRegistered = false;
				if (LethalHandsHandsFollowPatch.Log != null)
				{
					LethalHandsHandsFollowPatch.Log.LogInfo((object)"[LethalHandsHandsFollowPatch] Net handler unregistered.");
				}
			}
		}
		catch
		{
		}
	}

	internal static void SendLocalPitch(PlayerControllerB localPlayer, float pitchDeg, bool squaredUp)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: 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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: 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)
		EnsureInitialized();
		if ((Object)(object)NetworkManager.Singleton == (Object)null)
		{
			return;
		}
		CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager;
		if (customMessagingManager == null)
		{
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(13, (Allocator)2, -1);
		try
		{
			ulong playerClientId = localPlayer.playerClientId;
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref playerClientId, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref pitchDeg, default(ForPrimitives));
			byte b = (squaredUp ? ((byte)1) : ((byte)0));
			((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			if (NetworkManager.Singleton.IsServer)
			{
				ApplyPitch(playerClientId, pitchDeg, squaredUp);
				customMessagingManager.SendNamedMessageToAll("LH_HFP_PITCH", val, (NetworkDelivery)3);
			}
			else
			{
				customMessagingManager.SendNamedMessage("LH_HFP_PITCH", 0uL, val, (NetworkDelivery)3);
			}
			if (LethalHandsHandsFollowPatch.DebugLogNetworking.Value && LethalHandsHandsFollowPatch.Log != null)
			{
				LethalHandsHandsFollowPatch.Log.LogInfo((object)("[Net] Send pitch=" + pitchDeg.ToString("F1") + " squared=" + squaredUp));
			}
		}
		catch
		{
		}
		finally
		{
			((FastBufferWriter)(ref val)).Dispose();
		}
	}

	private static void OnMessageReceived(ulong senderClientId, FastBufferReader reader)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: 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_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: 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_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ulong clientId = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref clientId, default(ForPrimitives));
			float pitchDeg = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref pitchDeg, default(ForPrimitives));
			byte b = default(byte);
			((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
			bool squaredUp = b != 0;
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer)
			{
				ApplyPitch(clientId, pitchDeg, squaredUp);
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(13, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clientId, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref pitchDeg, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
					NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll("LH_HFP_PITCH", val, (NetworkDelivery)3);
				}
				finally
				{
					((FastBufferWriter)(ref val)).Dispose();
				}
			}
			else
			{
				ApplyPitch(clientId, pitchDeg, squaredUp);
			}
			if (LethalHandsHandsFollowPatch.DebugLogNetworking.Value && LethalHandsHandsFollowPatch.Log != null)
			{
				LethalHandsHandsFollowPatch.Log.LogInfo((object)("[Net] Recv id=" + clientId + " pitch=" + pitchDeg.ToString("F1") + " squared=" + squaredUp));
			}
		}
		catch
		{
		}
	}

	private static void ApplyPitch(ulong clientId, float pitchDeg, bool squaredUp)
	{
		PitchByClient[clientId] = pitchDeg;
		SquaredUpByClient[clientId] = squaredUp;
	}
}
internal class ThirdPersonSpinePitchDriver : MonoBehaviour
{
	private PlayerControllerB _player;

	private Animator _anim;

	private Transform _spine;

	private Transform _chest;

	private Transform _upperChest;

	private float _smoothedUp;

	private void Awake()
	{
		_player = ((Component)this).GetComponentInParent<PlayerControllerB>();
		if ((Object)(object)_player == (Object)null)
		{
			((Behaviour)this).enabled = false;
			return;
		}
		_anim = _player.playerBodyAnimator;
		if ((Object)(object)_anim == (Object)null)
		{
			((Behaviour)this).enabled = false;
			return;
		}
		_spine = SafeGetBone(_anim, (HumanBodyBones)7);
		_chest = SafeGetBone(_anim, (HumanBodyBones)8);
		_upperChest = SafeGetBone(_anim, (HumanBodyBones)54);
		if ((Object)(object)_chest == (Object)null)
		{
			_chest = _spine;
		}
		if ((Object)(object)_upperChest == (Object)null)
		{
			_upperChest = _chest;
		}
		if ((Object)(object)_spine == (Object)null && (Object)(object)_chest == (Object)null && (Object)(object)_upperChest == (Object)null)
		{
			((Behaviour)this).enabled = false;
		}
	}

	private Transform SafeGetBone(Animator a, HumanBodyBones b)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return a.GetBoneTransform(b);
		}
		catch
		{
			return null;
		}
	}

	private void LateUpdate()
	{
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: 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_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		if (LethalHandsHandsFollowPatch.Enabled.Value && LethalHandsHandsFollowPatch.EnableThirdPersonSpineBlend.Value && !((Object)(object)_player == (Object)null) && !((Object)(object)_anim == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !_player.isPlayerDead && (!((Object)(object)StartOfRound.Instance.localPlayerController != (Object)null) || !((Object)(object)_player == (Object)(object)StartOfRound.Instance.localPlayerController)) && HandsFollowLookNet.SquaredUpByClient.TryGetValue(_player.playerClientId, out var value) && value && HandsFollowLookNet.PitchByClient.TryGetValue(_player.playerClientId, out var value2))
		{
			float num = (LethalHandsHandsFollowPatch.ThirdPersonUpIsNegative.Value ? (0f - value2) : value2);
			if (num < 0f)
			{
				num = 0f;
			}
			float num2 = Mathf.Max(0f, LethalHandsHandsFollowPatch.ThirdPersonMaxUpDegrees.Value);
			if (num > num2)
			{
				num = num2;
			}
			float num3 = Mathf.Clamp(LethalHandsHandsFollowPatch.ThirdPersonStrength.Value, 0f, 3f);
			float num4 = num * num3;
			float num5 = Mathf.Max(1f, LethalHandsHandsFollowPatch.ThirdPersonSmoothingSpeed.Value);
			float num6 = 1f - Mathf.Exp((0f - num5) * Time.deltaTime);
			_smoothedUp = Mathf.Lerp(_smoothedUp, num4, num6);
			float num7 = Mathf.Clamp01(LethalHandsHandsFollowPatch.SpineWeight.Value);
			float num8 = Mathf.Clamp01(LethalHandsHandsFollowPatch.ChestWeight.Value);
			float num9 = Mathf.Clamp01(LethalHandsHandsFollowPatch.UpperChestWeight.Value);
			if ((Object)(object)_spine != (Object)null)
			{
				_spine.localRotation = Quaternion.AngleAxis(_smoothedUp * num7, Vector3.right) * _spine.localRotation;
			}
			if ((Object)(object)_chest != (Object)null)
			{
				_chest.localRotation = Quaternion.AngleAxis(_smoothedUp * num8, Vector3.right) * _chest.localRotation;
			}
			if ((Object)(object)_upperChest != (Object)null)
			{
				_upperChest.localRotation = Quaternion.AngleAxis(_smoothedUp * num9, Vector3.right) * _upperChest.localRotation;
			}
		}
	}
}
[HarmonyPatch]
internal static class AddSpineDriverPatch
{
	[HarmonyPatch(typeof(PlayerControllerB), "Start")]
	[HarmonyPostfix]
	private static void Postfix(PlayerControllerB __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.playerBodyAnimator == (Object)null) && LethalHandsHandsFollowPatch.EnableThirdPersonSpineBlend.Value && !((Object)(object)((Component)__instance).GetComponent<ThirdPersonSpinePitchDriver>() != (Object)null))
			{
				((Component)__instance).gameObject.AddComponent<ThirdPersonSpinePitchDriver>();
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch]
internal static class EmoteConstraintPitchPatch
{
	private struct SavedState
	{
		public bool appliedRot;

		public Quaternion savedRot;

		public bool appliedPos;

		public Vector3 savedPos;
	}

	private static bool baselineValid = false;

	private static float baselineCamY = 0f;

	private static int baselinePlayerId = 0;

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

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

	[HarmonyPatch(typeof(EmoteConstraint), "ActUponConstraints")]
	[HarmonyPrefix]
	private static void Prefix(EmoteConstraint __instance, ref SavedState __state)
	{
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: 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_03b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0402: Unknown result type (might be due to invalid IL or missing references)
		//IL_0409: Unknown result type (might be due to invalid IL or missing references)
		//IL_040e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0415: Unknown result type (might be due to invalid IL or missing references)
		//IL_041a: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_042d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0432: Unknown result type (might be due to invalid IL or missing references)
		//IL_043b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0440: Unknown result type (might be due to invalid IL or missing references)
		//IL_0442: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
		__state.appliedRot = false;
		__state.appliedPos = false;
		if (!LethalHandsHandsFollowPatch.Enabled.Value || (Object)(object)__instance == (Object)null || !__instance.constraintActive || (Object)(object)__instance.emoteBone == (Object)null || (Object)(object)__instance.originalBone == (Object)null)
		{
			return;
		}
		PlayerControllerB componentInParent = ((Component)__instance).GetComponentInParent<PlayerControllerB>();
		if ((Object)(object)componentInParent == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null)
		{
			return;
		}
		PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController;
		if ((Object)(object)localPlayerController == (Object)null || (Object)(object)componentInParent != (Object)(object)localPlayerController || componentInParent.isPlayerDead)
		{
			return;
		}
		bool flag = false;
		if ((Object)(object)LethalHands.Instance != (Object)null)
		{
			flag = LethalHands.Instance.isSquaredUp;
		}
		if (LethalHandsHandsFollowPatch.OnlyWhenSquaredUp.Value && !flag)
		{
			baselineValid = false;
			return;
		}
		Transform val = (((Object)(object)componentInParent.gameplayCamera != (Object)null) ? ((Component)componentInParent.gameplayCamera).transform : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Transform emoteBone = __instance.emoteBone;
		Transform parent = emoteBone.parent;
		if ((Object)(object)parent == (Object)null)
		{
			return;
		}
		float boneWeight = GetBoneWeight(((Object)__instance.originalBone).name);
		if (boneWeight <= 0f)
		{
			return;
		}
		float num = val.localEulerAngles.x;
		if (num > 180f)
		{
			num -= 360f;
		}
		float value = LethalHandsHandsFollowPatch.MaxPitchDegrees.Value;
		num = Mathf.Clamp(num, 0f - value, value);
		float num2 = Mathf.Clamp01(LethalHandsHandsFollowPatch.PitchStrength.Value);
		float num3 = num * num2 * boneWeight;
		Vector3 val2 = parent.InverseTransformDirection(val.right);
		if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f)
		{
			return;
		}
		((Vector3)(ref val2)).Normalize();
		__state.savedRot = emoteBone.localRotation;
		emoteBone.localRotation = Quaternion.AngleAxis(num3, val2) * emoteBone.localRotation;
		__state.appliedRot = true;
		int instanceID = ((Object)componentInParent).GetInstanceID();
		float num4 = 0f;
		if (LethalHandsHandsFollowPatch.EnableCameraHeightFollow.Value)
		{
			if (!baselineValid || baselinePlayerId != instanceID)
			{
				baselinePlayerId = instanceID;
				baselineCamY = val.localPosition.y;
				baselineValid = true;
				smoothY[instanceID] = 0f;
			}
			float target = val.localPosition.y - baselineCamY;
			num4 = SmoothValue(smoothY, instanceID, target, LethalHandsHandsFollowPatch.HeightSmoothingSpeed.Value) * Mathf.Clamp(LethalHandsHandsFollowPatch.HeightFollowStrength.Value, 0f, 3f);
			float num5 = Mathf.Max(0f, LethalHandsHandsFollowPatch.HeightMaxOffset.Value);
			num4 = Mathf.Clamp(num4, 0f - num5, num5);
		}
		float value2 = LethalHandsHandsFollowPatch.DefaultArmHeight.Value;
		float value3 = LethalHandsHandsFollowPatch.DefaultArmForward.Value;
		float num6 = 0f;
		if (LethalHandsHandsFollowPatch.EnableAutoForwardLookUp.Value)
		{
			float num7 = (LethalHandsHandsFollowPatch.AutoForwardUpIsNegative.Value ? (0f - num) : num);
			if (num7 < 0f)
			{
				num7 = 0f;
			}
			float num8 = Mathf.Clamp(LethalHandsHandsFollowPatch.AutoForwardStartDegrees.Value, 0f, value - 1f);
			if (num7 <= num8)
			{
				num6 = SmoothValue(smoothForward, instanceID, 0f, LethalHandsHandsFollowPatch.AutoForwardSmoothingSpeed.Value);
			}
			else
			{
				float num9 = Mathf.Max(0.001f, value - num8);
				float num10 = Mathf.Clamp01((num7 - num8) / num9);
				float num11 = Mathf.Max(0.1f, LethalHandsHandsFollowPatch.AutoForwardCurvePower.Value);
				float num12 = Mathf.Pow(num10, num11);
				float num13 = Mathf.Max(0f, LethalHandsHandsFollowPatch.AutoForwardMaxOffset.Value);
				float num14 = Mathf.Clamp(LethalHandsHandsFollowPatch.AutoForwardStrength.Value, 0f, 2f);
				float target2 = num12 * num13 * num14;
				num6 = SmoothValue(smoothForward, instanceID, target2, LethalHandsHandsFollowPatch.AutoForwardSmoothingSpeed.Value);
			}
		}
		Vector3 val3 = Vector3.forward;
		if (LethalHandsHandsFollowPatch.AutoForwardUseCameraForward.Value)
		{
			Vector3 val4 = parent.InverseTransformDirection(val.forward);
			if (((Vector3)(ref val4)).sqrMagnitude > 0.0001f)
			{
				((Vector3)(ref val4)).Normalize();
				val3 = val4;
			}
		}
		Vector3 val5 = (Vector3.up * (value2 + num4) + val3 * (value3 + num6)) * boneWeight;
		if (((Vector3)(ref val5)).sqrMagnitude > 1E-07f)
		{
			__state.savedPos = emoteBone.localPosition;
			emoteBone.localPosition += val5;
			__state.appliedPos = true;
		}
	}

	[HarmonyPatch(typeof(EmoteConstraint), "ActUponConstraints")]
	[HarmonyPostfix]
	private static void Postfix(EmoteConstraint __instance, ref SavedState __state)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.emoteBone == (Object)null))
		{
			if (__state.appliedRot)
			{
				__instance.emoteBone.localRotation = __state.savedRot;
			}
			if (__state.appliedPos)
			{
				__instance.emoteBone.localPosition = __state.savedPos;
			}
		}
	}

	private static float SmoothValue(Dictionary<int, float> dict, int key, float target, float speed)
	{
		if (!dict.TryGetValue(key, out var value))
		{
			value = 0f;
		}
		if (speed < 1f)
		{
			speed = 1f;
		}
		float num = 1f - Mathf.Exp((0f - speed) * Time.deltaTime);
		return dict[key] = Mathf.Lerp(value, target, num);
	}

	private static float GetBoneWeight(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return 0f;
		}
		if (ContainsAny(name, "UpperArm", "upperarm", "uparm", "Shoulder", "Clavicle"))
		{
			return Clamp01(LethalHandsHandsFollowPatch.UpperArmWeight.Value);
		}
		if (ContainsAny(name, "LowerArm", "lowerarm", "ForeArm", "forearm", "loarm", "Elbow"))
		{
			return Clamp01(LethalHandsHandsFollowPatch.ForearmWeight.Value);
		}
		if (ContainsAny(name, "Hand", "hand", "Wrist", "wrist", "Palm", "palm"))
		{
			return Clamp01(LethalHandsHandsFollowPatch.HandWeight.Value);
		}
		return 0f;
	}

	private static float Clamp01(float v)
	{
		if (v < 0f)
		{
			return 0f;
		}
		if (v > 1f)
		{
			return 1f;
		}
		return v;
	}

	private static bool ContainsAny(string s, params string[] needles)
	{
		for (int i = 0; i < needles.Length; i++)
		{
			if (s.IndexOf(needles[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}
}