Game/Weapon/WeaponModel/ViewModel.Bob.cs
public sealed partial class ViewModel
{
	private const float HL2BobCycleMax = 0.45f;
	private const float HL2BobUp = 0.5f;
	private static float MaxViewModelLag = 1.5f;

	[ConVar( "sv_viewmodel_lag_do_angles", ConVarFlags.Cheat | ConVarFlags.Replicated )]
	private static bool ViewModelLagDoAngles { get; set; } = true;

	private float bobtime;
	private float lastbobtime;
	private float lateralBob;
	private float verticalBob;
	private Vector3 lastFacing;

	private void CalcViewModelView( CameraComponent camera )
	{
		var origin = camera.WorldPosition;
		var angles = camera.WorldRotation.Angles();
		var originalAngles = angles;

		var playerController = GetComponentInParent<PlayerController>();
		if ( GamePreferences.ViewBobbing )
		{
			AddViewmodelBob( playerController, ref origin, ref angles );
		}

		CalcViewModelLag( ref origin, ref angles, originalAngles );

		WorldPosition = origin;
		WorldRotation = angles.ToRotation();
	}

	private float CalcViewmodelBob( PlayerController playerController )
	{
		if ( Time.Delta == 0.0f || !playerController.IsValid() )
			return 0.0f;

		var speed = playerController.Velocity.WithZ( 0 ).Length;
		speed = speed.Clamp( -320.0f, 320.0f );

		var bobOffset = speed.Remap( 0.0f, 320.0f, 0.0f, 1.0f );

		bobtime += (Time.Now - lastbobtime) * bobOffset;
		lastbobtime = Time.Now;

		var cycle = bobtime - (int)(bobtime / HL2BobCycleMax) * HL2BobCycleMax;
		cycle /= HL2BobCycleMax;

		if ( cycle < HL2BobUp )
		{
			cycle = MathF.PI * cycle / HL2BobUp;
		}
		else
		{
			cycle = MathF.PI + MathF.PI * (cycle - HL2BobUp) / (1.0f - HL2BobUp);
		}

		verticalBob = speed * 0.005f;
		verticalBob = verticalBob * 0.3f + verticalBob * 0.7f * MathF.Sin( cycle );
		verticalBob = verticalBob.Clamp( -7.0f, 4.0f );

		cycle = bobtime - (int)(bobtime / HL2BobCycleMax * 2.0f) * HL2BobCycleMax * 2.0f;
		cycle /= HL2BobCycleMax * 2.0f;

		if ( cycle < HL2BobUp )
		{
			cycle = MathF.PI * cycle / HL2BobUp;
		}
		else
		{
			cycle = MathF.PI + MathF.PI * (cycle - HL2BobUp) / (1.0f - HL2BobUp);
		}

		lateralBob = speed * 0.005f;
		lateralBob = lateralBob * 0.3f + lateralBob * 0.7f * MathF.Sin( cycle );
		lateralBob = lateralBob.Clamp( -7.0f, 4.0f );

		return 0.0f;
	}

	private void AddViewmodelBob( PlayerController playerController, ref Vector3 origin, ref Angles angles )
	{
		var rotation = angles.ToRotation();

		CalcViewmodelBob( playerController );

		origin += rotation.Forward * verticalBob * 0.1f;
		origin += Vector3.Up * verticalBob * 0.1f;

		angles.roll += verticalBob * 0.5f;
		angles.pitch -= verticalBob * 0.4f;
		angles.yaw -= lateralBob * 0.3f;

		origin += rotation.Right * lateralBob * 0.8f;
	}

	private void CalcViewModelLag( ref Vector3 origin, ref Angles angles, Angles originalAngles )
	{
		var originalOrigin = origin;
		var viewModelAngles = angles;
		var forward = angles.ToRotation().Forward;

		if ( Time.Delta != 0.0f )
		{
			var difference = forward - lastFacing;
			var speed = 5.0f;
			var differenceLength = difference.Length;

			if ( differenceLength > MaxViewModelLag && MaxViewModelLag > 0.0f )
			{
				var scale = differenceLength / MaxViewModelLag;
				speed *= scale;
			}

			lastFacing += difference * speed * Time.Delta;
			lastFacing = lastFacing.Normal;
			origin -= difference * 5.0f;
		}

		if ( !ViewModelLagDoAngles )
			return;

		var originalRotation = originalAngles.ToRotation();
		forward = originalRotation.Forward;
		var right = originalRotation.Right;
		var up = originalRotation.Up;

		var pitch = originalAngles.pitch;
		if ( pitch > 180.0f )
		{
			pitch -= 360.0f;
		}
		else if ( pitch < -180.0f )
		{
			pitch += 360.0f;
		}

		if ( MaxViewModelLag == 0.0f )
		{
			origin = originalOrigin;
			angles = viewModelAngles;
		}

		origin += forward * -pitch * 0.035f;
		origin += right * -pitch * 0.03f;
		origin += up * -pitch * 0.02f;
	}
}