ModelViewer/ModelViewerCharacterController/ModelViewerPlayerController.cs

using Sandbox;
using Sandbox.Citizen;
using static Sandbox.ModelRenderer;
using static Sandbox.PhysicsContact;

public class ModelViewerPlayerController : Component
{
	public enum CameraMode
	{
		Mayamode,
		Character,
		Free
	}

	[Property] public Vector3 Gravity { get; set; } = new Vector3( 0, 0, 800 );
	public Vector3 WishVelocity { get; private set; }
	[Property] public GameObject Body { get; set; }
	[Property] public GameObject Eye { get; set; }
	[Property] public CitizenAnimationHelper AnimationHelper { get; set; }
	[Property] public bool FirstPerson { get; set; }
	[Property] public CameraMode CurrentCameraMode { get; set; }
	[Property] public float Distance { get; set; } = 200;
	public Angles CameraAngles { get; set; }
	bool IsDucking { get; set; }
	float ducklevel = 0;
	bool IsAlt { get; set; }

	public Angles EyeAngles;

	[Sync]
	public bool IsRunning { get; set; }

	public Angles TargetAngles { get; set; }
	public Vector3 MayaPosOffet { get; set; } = Vector3.Up * 64;
	private Vector2 cameraAngles;

	public float SkidLevel { get; set; } = 0;

	Angles MayaAngles { get; set; }
	float LerpMode = 0;

	float startingFOV;

	bool IsFlying;
	public bool IsSwiming = false;

	bool usingGravity = true;

	public bool IsHoldingObject;

	protected override void OnAwake()
	{
		base.OnAwake();

	}

	protected override void OnEnabled()
	{
		base.OnEnabled();

		if ( IsProxy )
			return;

		var cam = Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
		if ( cam is not null )
		{
			EyeAngles = cam.WorldRotation.Angles();
			EyeAngles.roll = 0;

			startingFOV = cam.FieldOfView;
		}
	}

	public float CurrentGroundAngle
	{
		get
		{
			var trace = Scene.Trace.Ray( WorldPosition + Vector3.Up * 2, WorldPosition + Vector3.Down * 6 )
			.WithoutTags( "player", "collider" )
			.Radius( 8 )
			.Run();

			//	Gizmo.Draw.Color = Color.Red;
			//	Gizmo.Draw.Line( Target.WorldPosition + Vector3.Up * 2, Target.WorldPosition + Vector3.Down * 6 );

			if ( !trace.Hit )
				return 0;

			return trace.Normal.Angle( Vector3.Up );
		}
	}

	protected override void OnUpdate()
	{
		// Eye input
		if ( !IsProxy )
		{
			if (CurrentCameraMode == CameraMode.Character && !IsHoldingObject )
			{
		
					EyeAngles.pitch += Input.MouseDelta.y * 0.1f;
					EyeAngles.yaw -= Input.MouseDelta.x * 0.1f;
					EyeAngles.roll = 0;

					EyeAngles.pitch = Math.Clamp( EyeAngles.pitch, -89.9f, 89.9f );
				
			}
			var cam = Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
			var lookDir = EyeAngles.ToRotation();
			

			if ( CurrentCameraMode == CameraMode.Character )
			{
				if ( FirstPerson )
				{
					cam.WorldPosition = Eye.WorldPosition;
					cam.WorldRotation = lookDir;					
				}
				else
				{
					cam.WorldPosition = WorldPosition + lookDir.Backward * Distance + Vector3.Up * (IsDucking ? 48 : 64);
					cam.WorldRotation = lookDir;
				}
			}
			else if ( CurrentCameraMode == CameraMode.Mayamode )
			{
				var newCameraPosition = WorldPosition + MayaPosOffet + cam.WorldRotation.Backward * Distance;

				cam.WorldPosition = newCameraPosition;
				cam.WorldRotation = Rotation.From( cameraAngles.x, -cameraAngles.y, 0 );

				EyeAngles.pitch += Input.MouseDelta.y * 0.1f;
				EyeAngles.yaw -= Input.MouseDelta.x * 0.1f;
				EyeAngles.roll = 0;

				EyeAngles.pitch = Math.Clamp( EyeAngles.pitch, -89.9f, 89.9f );

			}
			else if(CurrentCameraMode == CameraMode.Free)
			{
				HandleFlyCameraMovement( cam.GameObject );
			}

			if(IsDucking)
			{
				Eye.WorldPosition = Eye.WorldPosition.LerpTo( WorldPosition + Vector3.Up * 32, Time.Delta * 10 );
			}
			else
			{
				Eye.WorldPosition = Eye.WorldPosition.LerpTo( WorldPosition + Vector3.Up * 64, Time.Delta * 10 );
			}

			IsRunning = Input.Down( "Run" );

			IsAlt = Input.Down( "Walk" );

			float x = Input.MouseDelta.x;
			float y = Input.MouseDelta.y;

			if ( CurrentCameraMode == CameraMode.Mayamode )
			{
				if ( IsAlt )
				{
					if ( Input.Down( "attack1" ) )
					{
						Distance += Input.MouseDelta.y;
					}
					if ( Input.Down( "attack2" ) )
					{

						var translateX = cam.WorldRotation.Right * (-x * 10 * RealTime.Delta);
						var translateY = cam.WorldRotation.Up * (y * 10 * RealTime.Delta);

						// Move the camera in the local coordinates
						MayaPosOffet += translateX;
						MayaPosOffet += translateY;

						//MayaPosOffet += Input.MouseDelta.x * 0.1f * MayaRotation.Right;
						//MayaPosOffet += Input.MouseDelta.y * 0.1f * MayaRotation.Up;
					}
					else if ( !Input.Down( "attack1" ) )
					{
						cameraAngles += new Vector2( y * 0.10f, x * 0.10f );
						cameraAngles.x = Math.Clamp( cameraAngles.x, -89.9f, 89.9f );
						//MayaRotation = cameraAngles;
					}
				}

				//cam.WorldPosition = WorldPosition + MayaRotation.Backward * Distance + Vector3.Up * 75.0f;
			}
		}

		CurrentCameraMode = Input.Pressed( "menu" ) ? CurrentCameraMode == CameraMode.Character ? CameraMode.Mayamode : CameraMode.Character : CurrentCameraMode;

		IsFlying = Input.Pressed( "Fly" ) ? !IsFlying : IsFlying;

		if (Input.Pressed("flashlight"))
		{
			CurrentCameraMode = CameraMode.Free;
			lastPos = Eye.WorldPosition;
		}
		
		if(CurrentCameraMode == CameraMode.Free)
		{
			if ( Input.Down( "slot1" ) ) LerpMode = 0.0f;
			if ( Input.Down( "slot2" ) ) LerpMode = 0.5f;
			if ( Input.Down( "slot3" ) ) LerpMode = 0.9f;
		}
		
		IsDucking = Input.Down( "duck" ) && CurrentCameraMode != CameraMode.Free;

		var cc = GameObject.Components.Get<ModelViewerCharacterController>();
		if ( cc is null ) return;

		FirstPerson = Input.Pressed( "view" ) ? !FirstPerson : FirstPerson;

		if ( FirstPerson && CurrentCameraMode != CameraMode.Free )
		{
			var citi = Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants );

			foreach ( var c in citi )
			{
				if ( c.GameObject.Tags.Has( "arms" ) )
					continue;

				if ( string.Equals( c.GameObject.Name, "arms", System.StringComparison.InvariantCultureIgnoreCase ) )
					continue;

				c.RenderType = ShadowRenderType.ShadowsOnly;

			}
		}
		else
		{
			var citi = Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants );

			foreach ( var c in citi )
			{
				c.RenderType = ShadowRenderType.On;
			}
		}

		float rotateDifference = 0;

		if ( !IsAlt  )
		{
			TargetAngles = Rotation.LookAt( EyeAngles.Forward ).Angles().WithPitch( 0 ).WithRoll( 0 );

		}
		// rotate body to look angles
		if ( Body is not null && CurrentCameraMode != CameraMode.Free )
		{
			//if ( WishVelocity.Length > 0 && cc.IsOnGround )
			//{
			//	TargetAngles = Rotation.LookAt( WishVelocity ).Angles();
			//}
			Body.WorldRotation = Rotation.Slerp( Body.WorldRotation, Rotation.From( TargetAngles ), 8f * Time.Delta );
		}

		SkidLevel = SkidLevel.LerpTo( CurrentGroundAngle >= cc.GroundAngle && !cc.IsOnGround ? 1 : 0 , Time.Delta * 5f );

		if ( AnimationHelper is not null )
		{
			AnimationHelper.WithVelocity( cc.Velocity );
			AnimationHelper.WithWishVelocity( WishVelocity );
			AnimationHelper.IsGrounded = cc.IsOnGround;
			AnimationHelper.IsNoclipping = IsFlying;
			AnimationHelper.IsSwimming = IsSwiming;
			AnimationHelper.Target.Set( "skid", SkidLevel );

			rotateDifference = Input.MouseDelta.x * 10;
			AnimationHelper.MoveRotationSpeed = rotateDifference;

			if ( AnimationHelper.Target.GetFloat("skid") > 0.1f )
			{
				AnimationHelper.IsGrounded = true;
			}

			if ( CurrentCameraMode != CameraMode.Free )
			{
				AnimationHelper.WithLook( EyeAngles.Forward, 1, 1, 1.0f );
			}
			AnimationHelper.MoveStyle = IsRunning ? CitizenAnimationHelper.MoveStyles.Run : CitizenAnimationHelper.MoveStyles.Walk;
			AnimationHelper.DuckLevel = MathX.Lerp( AnimationHelper.DuckLevel, IsDucking ? 1 : 0, Time.Delta * 10.0f );
		}
	}

	[Broadcast]
	public void OnJump( float floatValue, string dataString, object[] objects, Vector3 position )
	{
		AnimationHelper?.TriggerJump();
	}

	float fJumps;

	protected override void OnFixedUpdate()
	{
		if ( IsProxy )
			return;
		if ( CurrentCameraMode != CameraMode.Free )
		{
			BuildWishVelocity();
		}
		else
		{
			WishVelocity = 0;
		}
		
		var cc = GameObject.Components.Get<ModelViewerCharacterController>();

		if ( cc.IsOnGround && Input.Down( "Jump" ) && CurrentCameraMode != CameraMode.Free )
		{
			float flGroundFactor = 1.0f;
			float flMul = 268.3281572999747f * 1.2f;
			//if ( Duck.IsActive )
			//	flMul *= 0.8f;

			cc.Punch( Vector3.Up * flMul * flGroundFactor );
			//	cc.IsOnGround = false;

			OnJump( fJumps, "Hello", new object[] { Time.Now.ToString(), 43.0f }, Vector3.Random );

			fJumps += 1.0f;

		}

		if(IsFlying)
		{ 
			usingGravity = false; 
		}
		else if (IsSwiming)
		{ 
			usingGravity = false;
		}
		else
		{
			usingGravity = true;
		}

		if ( usingGravity )
		{
			if ( cc.IsOnGround )
			{
				cc.Velocity = cc.Velocity.WithZ( 0 );
				cc.Accelerate( WishVelocity );
				cc.ApplyFriction( 4.0f );
			}
			else
			{
				cc.Velocity -= Gravity * Time.Delta * 0.5f;
				cc.Accelerate( WishVelocity.ClampLength( 50 ) );
				cc.ApplyFriction( 0.1f );
			}

			cc.Move();

			if ( !cc.IsOnGround )
			{
				cc.Velocity -= Gravity * Time.Delta * 0.5f;
			}
			else
			{
				cc.Velocity = cc.Velocity.WithZ( 0 );
			}
		}
		else
		{
			FlyingMovement();
		}
	}

	public void BuildWishVelocity()
	{
		var rot = EyeAngles.ToRotation();

		WishVelocity = 0;

		if ( Input.Down( "Forward" ) ) WishVelocity += rot.Forward;
		if ( Input.Down( "Backward" ) ) WishVelocity += rot.Backward;
		if ( Input.Down( "Left" ) ) WishVelocity += rot.Left;
		if ( Input.Down( "Right" ) ) WishVelocity += rot.Right;

		WishVelocity = WishVelocity.WithZ( 0 );

		if ( !WishVelocity.IsNearZeroLength ) WishVelocity = WishVelocity.Normal;

		if ( Input.Down( "Run" ) ) WishVelocity *= 320.0f;
		else if ( Input.Down( "duck" ) ) WishVelocity *= 75f;
		else WishVelocity *= 110.0f;
	}

	Vector3 lastPos;
	float overrideFOV = 75;
	public float flySpeed = 50.0f;
	private void HandleFlyCameraMovement(GameObject CameraObject)
	{
		var direction = Vector3.Zero;

		if ( Input.Down( "Forward" ) )
			direction += CameraObject.WorldRotation.Forward;
		if ( Input.Down( "Backward" ) )
			direction -= CameraObject.WorldRotation.Forward;
		if ( Input.Down( "Left" ) )
			direction -= CameraObject.WorldRotation.Right;
		if ( Input.Down( "Right" ) )
			direction += CameraObject.WorldRotation.Right;
		if ( Input.Down( "Jump" ) ) // Assuming "Space" is for moving upwards
			direction += Vector3.Up;
		if ( Input.Down( "Duck" ) ) // Assuming "LeftShift" is for moving downwards
			direction -= Vector3.Up;

		flySpeed = Input.Down( "Run" ) ? 150.0f : 50.0f;

		// Normalize the direction to ensure consistent speed in diagonal movement
		// Normalize the direction to ensure consistent speed in diagonal movement
		float magnitude = direction.Length;
		if ( magnitude > 0 )
		{
			direction.x /= magnitude;
			direction.y /= magnitude;
			direction.z /= magnitude;
		}

		lastPos = Vector3.Lerp( lastPos, lastPos + direction * flySpeed, Time.Delta * 10.0f );

		// Update the position based on the direction
		CameraObject.WorldPosition = Vector3.Lerp( CameraObject.WorldPosition, lastPos, 10 * RealTime.Delta * (1 - LerpMode) );

		// Adjusting view direction based on mouse movement
		EyeAngles.pitch += Input.MouseDelta.y * 0.03f;
		EyeAngles.yaw -= Input.MouseDelta.x * 0.03f;

		// Clamp pitch to prevent over-rotation (optional but useful to prevent full flips)
		EyeAngles.pitch = Math.Clamp( EyeAngles.pitch, -89.9f, 89.9f );

		if ( Input.Down( "Attack2" ) )
		{
			var cam = CameraObject.Components.Get<CameraComponent>();

			overrideFOV += Input.MouseDelta.y * 0.1f * (overrideFOV / 30f);
			overrideFOV = overrideFOV.Clamp( 50, 150 );

			cam.FieldOfView = overrideFOV;
		}
		else
		{
			// Convert EyeAngles to a rotation and set to the camera
			CameraObject.WorldRotation = Rotation.Lerp( CameraObject.WorldRotation, Rotation.From( EyeAngles ), 10 * RealTime.Delta * (1 - LerpMode) );
		}

	}

	private void FlyingMovement()
	{
		var direction = Vector3.Zero;

		var cc = GameObject.Components.Get<ModelViewerCharacterController>();

		var rot = EyeAngles.ToRotation();

		if ( Input.Down( "Forward" ) )
			direction += rot.Forward;
		if ( Input.Down( "Backward" ) )
			direction -= rot.Forward;
		if ( Input.Down( "Left" ) )
			direction -= rot.Right;
		if ( Input.Down( "Right" ) )
			direction += rot.Right;
		if ( Input.Down( "Jump" ) ) // Assuming "Space" is for moving upwards
			direction += Vector3.Up;
		if ( Input.Down( "Duck" ) ) // Assuming "LeftShift" is for moving downwards
			direction -= Vector3.Up;

		flySpeed = Input.Down( "Run" ) ? 500.0f : 300.0f;

		if ( IsSwiming )
		{
			flySpeed = Input.Down( "Run" ) ? 200.0f : 100.0f;
		}

		// Normalize the direction to ensure consistent speed in diagonal movement
		float magnitude = direction.Length;
		if ( magnitude > 0 )
		{
			direction.x /= magnitude;
			direction.y /= magnitude;
			direction.z /= magnitude;
		}

		cc.Velocity = Vector3.Lerp( cc.Velocity, cc.Velocity + direction * flySpeed, Time.Delta * 10.0f );
		cc.Accelerate( direction );
		cc.ApplyFriction( 5f );

		cc.IsOnGround = false;

		cc.Move();
	}
}