BITFALL/Assets/Artists/Scripts/Player/CharacterControllerPro/PlayerCharacterStates.cs

655 lines
19 KiB
C#

using System;
using BITFALL.Entities.Player.Movement.States;
using BITFALL.Player.Equip;
using BITFALL.Player.Movement;
using BITKit;
using BITKit.Entities;
using BITKit.PlayerCamera;
using BITKit.StateMachine;
using Cysharp.Threading.Tasks;
using Lightbug.CharacterControllerPro.Core;
using UnityEngine;
using UnityEngine.InputSystem;
// ReSharper disable InvertIf
// ReSharper disable RedundantJumpStatement
// ReSharper disable ConvertToConstant.Global
namespace BITFALL.Entities.Player.Movement.States
{
public abstract class BasicMovement : PlayerCharacterState, IPlayerMovementState
{
[SerializeField] protected Vector3 initialCameraPosition;
[SerializeField] protected float initialSpeed = 3f;
[SerializeField] protected float initialJumpForce = 5f;
[Inject] protected IKnockdown knockdown;
[Inject] protected IPlayerCameraService _cameraService;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
characterController.ReferenceSpeed = initialSpeed;
}
public override void BeforeUpdateMovement(float deltaTime)
{
characterController.CurrentCameraPosition.shouldBe = initialCameraPosition;
}
public override void AfterUpdateMovement(float deltaTime)
{
if (knockdown.IsKnockdown)
{
characterController.TransitionState<Knockdown>();
}
if (knockdown.IsKnockdown is false && characterController.ExpectParachute.shouldBe)
{
characterController.TransitionState<Parachute>();
characterController.ExpectParachute.Reset();
}
if (characterController.RequestClimb is false) return;
if(characterController.topBlocked)return;
// if (characterController.topBlocked is false && characterController.RequestClimb &&
// characterController.climbClosePoint.TryGetClosePoint(out var closePoint))
// {
// characterController.ExpectClimb.shouldBe = closePoint;
// characterController.TransitionState<Climb>();
// return;
// }
if (characterController.vaultPoint.TryGetClosePoint(out var closePoint))
{
characterController.ExpectClimb.shouldBe = closePoint;
characterController.TransitionState<Vault>();
characterController.ExpectJump.Reset();
characterController.RequestClimb = false;
return;
}
if (characterController.climbClosePoint.TryGetClosePoint(out closePoint))
{
characterController.ExpectClimb.shouldBe = closePoint;
characterController.TransitionState<Climb>();
characterController.ExpectJump.Reset();
characterController.RequestClimb = false;
return;
}
if (characterController.edgeClimbPoint.TryGetClosePoint(out closePoint))
{
characterController.ExpectClimb.shouldBe = closePoint;
characterController.TransitionState<EdgeClimb>();characterController.ExpectJump.Reset();
characterController.RequestClimb = false;
return;
}
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
var rotation = Quaternion.Euler(0, characterController.LookInput.y, 0);
var movementInput = characterController.MovementInput;
if (_cameraService?.IsCameraActivated is false)
{
rotation =Quaternion.LookRotation( _cameraService.CameraRotation * Vector3.forward,Vector3.up);
}
var moveVelocity = rotation * new Vector3(
movementInput.x * Mathf.Min(characterController.initialSpeed, initialSpeed),
0,
movementInput.z * initialSpeed
);
if (_cameraService?.IsCameraActivated is false)
{
moveVelocity = rotation * movementInput * initialSpeed;
}
if (characterController.limitSpeed.Allow)
{
switch (characterController.CurrentState)
{
case IPlayerWalkState:
case IPlayerCrouchState:
moveVelocity = Vector3.ClampMagnitude(moveVelocity, characterController.limitSpeed.Value);
break;
}
}
if (characterController.IsGrounded)
{
var effectiveGroundNormal = actor.GroundStableNormal;
var inputRight = Vector3.Cross(moveVelocity, Vector3.up);
var reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized *
moveVelocity.magnitude;
var targetMovementVelocity = reorientedInput; //* initialSpeed;
// Smooth movement Velocity
var newVelocity =
Vector3.Lerp(currentVelocity, targetMovementVelocity, 1f - Mathf.Exp(-16 * deltaTime));
currentVelocity = newVelocity;
if (characterController.ExpectJump.shouldBe)
{
actor.ForceNotGrounded();
if (characterController.landFreeze.AllowUpdateWithoutReset is false)
{
currentVelocity = Vector3.Lerp(currentVelocity, default, 0.8f);
}
currentVelocity.y+= initialJumpForce;
characterController.ExpectJump.Reset();
characterController.ExecuteCommand<OnPlayerJumpCommand>();
}
}
else
{
if (moveVelocity.sqrMagnitude > 0f && characterController.landFreeze.AllowUpdateWithoutReset)
{
var addedVelocity = moveVelocity * (3 * deltaTime);
var currentVelocityOnInputsPlane = Vector3.ProjectOnPlane(currentVelocity, Vector3.up);
// Limit air velocity from inputs
if (currentVelocityOnInputsPlane.magnitude < 5)
{
// clamp addedVel to make total vel not exceed max vel on inputs plane
Vector3 newTotal = Vector3.ClampMagnitude(currentVelocityOnInputsPlane + addedVelocity, 5);
addedVelocity = newTotal - currentVelocityOnInputsPlane;
}
else
{
// Make sure added vel doesn't go in the direction of the already-exceeding velocity
if (Vector3.Dot(currentVelocityOnInputsPlane, addedVelocity) > 0f)
{
addedVelocity =
Vector3.ProjectOnPlane(addedVelocity, currentVelocityOnInputsPlane.normalized);
}
}
// Prevent air-climbing sloped walls
if (actor.WallCollision)
{
if (Vector3.Dot(currentVelocity + addedVelocity, addedVelocity) > 0f)
{
var perpenticularObstructionNormal = Vector3
.Cross(Vector3.Cross(Vector3.up, actor.GroundContactNormal), Vector3.up).normalized;
addedVelocity = Vector3.ProjectOnPlane(addedVelocity, perpenticularObstructionNormal);
}
}
// Apply added velocity
currentVelocity += addedVelocity;
}
// Gravity
currentVelocity += -Vector3.up * (30 * deltaTime);
// Drag
currentVelocity *= (1f / (1f + (0.1f * deltaTime)));
}
}
public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
var newRotation = Quaternion.Euler(0, characterController.LookInput.y, 0);
if (_cameraService.IsCameraActivated is false)
{
if (Physics.Raycast(_cameraService.CameraPosition, _cameraService.CameraRotation * Vector3.forward,
out var hit, 256, LayerMask.GetMask("Default"), QueryTriggerInteraction.Ignore))
{
characterController.ViewForward = (hit.point - (characterController.Position +
characterController.Rotation *
characterController.ViewCenter)).normalized;
newRotation = Quaternion.LookRotation(characterController.ViewForward);
characterController.ViewRotation = newRotation;
characterController.FocusPoint = hit.point;
Debug.DrawLine(_cameraService.CameraPosition, hit.point, Color.green, 0.1f);
Debug.DrawLine(characterController.Position + characterController.ViewCenter,
characterController.Position +
characterController.ViewCenter + characterController.ViewRotation * Vector3.forward
, Color.blue, 0.1f);
}
else
{
characterController.ViewRotation = Quaternion.Euler(characterController.LookInput);;
characterController.FocusPoint = characterController.Position +
characterController.ViewRotation * Vector3.forward * 256;
Debug.DrawLine(_cameraService.CameraPosition, _cameraService.CameraRotation * Vector3.forward,
Color.red, 0.1f);
}
var rotationDirection = _cameraService.CameraRotation * characterController.MovementInput;
rotationDirection = Vector3.ProjectOnPlane(rotationDirection, Vector3.up);
if (rotationDirection.sqrMagnitude >= 0.16f)
{
var newPlayerRotation =Quaternion.LookRotation(rotationDirection) ;
currentRotation = Quaternion.Lerp(currentRotation, newPlayerRotation, 1f - Mathf.Exp(-16 * deltaTime));
}
if (characterController.allowFocus)
{
currentRotation =
Quaternion.LookRotation(_cameraService.CameraRotation * Vector3.forward, Vector3.up);
}
}
else
{
currentRotation = newRotation;
}
}
}
[Serializable]
public sealed class Walk:BasicMovement,IPlayerWalkState
{
[Inject] private InputActionGroup _inputActionGroup;
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
if (characterController.CurrentState != this) return;
switch (characterController.ExpectRun.shouldBe,characterController.ExpectCrouch.shouldBe)
{
case (_,true):
characterController.TransitionState<Crouch>();
return;
case (true,false) when characterController.pauseRun.Allow is false && characterController.MovementInput.z>0:
characterController.TransitionState<Run>();
return;
}
}
}
[Serializable]
public sealed class Parachute : PlayerCharacterState, IPlayerParachuteState
{
[SerializeField] private float moveDamping = 3f;
[SerializeField] private float damping = 1f;
[Inject] private IEquipService _equipService;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
characterController.InvokeOpenParachute();
_equipService.AllowEquip.AddDisableElements(this);
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
characterController.InvokeCloseParachute();
_equipService.AllowEquip.RemoveDisableElements(this);
}
public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
currentRotation = Quaternion.Euler(0,characterController.LookInput.y,0);
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
var rotation = Quaternion.Euler(0,characterController.LookInput.y,0);
var moveVelocity = rotation * characterController.MovementInput;
currentVelocity.y =
Mathf.MoveTowards(
currentVelocity.y,
-8,
damping * deltaTime
);
currentVelocity+= moveVelocity.normalized * (moveDamping * deltaTime);
currentVelocity.x = Mathf.Clamp(currentVelocity.x, -moveDamping, moveDamping);
currentVelocity.z = Mathf.Clamp(currentVelocity.z, -moveDamping, moveDamping);
}
public override void AfterUpdateMovement(float deltaTime)
{
if (actor.IsGrounded)
{
characterController.TransitionState<Walk>();
}
characterController.ExpectParachute.Reset();
characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition;
}
}
[Serializable]
public sealed class Run:BasicMovement,IPlayerRunState
{
public override void OnStateEntry(IState old)
{
characterController.ExpectRun.being = true;
}
public override void OnStateExit(IState old, IState newState)
{
characterController.ExpectRun.being = false;
}
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
switch (characterController.CurrentState)
{
case Walk:
case Run:
case Sprint:
case Crouch:
switch (characterController.ExpectRun.shouldBe, characterController.ExpectCrouch.shouldBe)
{
case (_, true):
characterController.TransitionState<Crouch>();
return;
case (false, false):
characterController.TransitionState<Walk>();
return;
}
if (characterController.MovementInput.z <= 0)
{
characterController.TransitionState<Walk>();
return;
}
if (characterController.pauseRun.Allow)
{
characterController.TransitionState<Walk>();
return;
}
if (characterController.ExpectSprint.shouldBe && characterController.MovementInput.z > 0)
{
characterController.TransitionState<Sprint>();
return;
}
break;
}
}
public override void ExecuteCommand<T>(T command)
{
if (Enabled is false) return;
if (command is PlayerCancelRunCommand)
{
characterController.TransitionState<Walk>();
}
}
}
[Serializable]
public sealed class Slide : BasicMovement, IPlayerSlideState
{
[Header(nameof(Slide))]
[SerializeField] private float additiveSpeed = 1f;
[SerializeField] private float damping = 1;
[SerializeField] private float stopSpeed = 0.64f;
[SerializeField] private bool alwaysSlide;
private bool _addedVelocity;
public override void OnStateEntry(IState old)
{
characterController.ExpectCrouch.being = true;
characterController.ExpectCrouch.shouldBe = true;
characterController.ExecuteCommand<PlayerCancelRunCommand>();
_addedVelocity = false;
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
if (alwaysSlide && actor.IsGrounded is false)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
}
else
{
if (_addedVelocity is false)
{
currentVelocity += currentVelocity.normalized * additiveSpeed;
_addedVelocity = true;
}
else
{
currentVelocity = Vector3.Lerp(currentVelocity, Vector3.zero, deltaTime * damping);
}
}
}
public override void AfterUpdateMovement(float deltaTime)
{
if (characterController.ExpectRun.shouldBe)
{
characterController.TransitionState<Run>();
return;
}
if (characterController.ExpectCrouch.shouldBe is false || (alwaysSlide is false && actor.IsGrounded is false))
{
characterController.TransitionState<Walk>();
return;
}
if (actor.Velocity.sqrMagnitude <= stopSpeed)
{
characterController.TransitionState<Crouch>();
}
characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition;
}
}
[Serializable]
public sealed class Crouch:BasicMovement,IPlayerCrouchState
{
public override void OnStateEntry(IState old)
{
characterController.ExpectCrouch.being = true;
}
public override void OnStateExit(IState old, IState newState)
{
characterController.ExpectCrouch.being = false;
}
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
if (characterController.ExpectRun.shouldBe)
{
characterController.TransitionState<Run>();
return;
}
if (characterController.ExpectCrouch.shouldBe is false)
{
characterController.TransitionState<Walk>();
return;
}
}
}
[Serializable]
public sealed class Sprint:BasicMovement,IPlayerSprintState
{
[SerializeField] private int slideCost = 16;
[SerializeField] private int staminaCost = 1;
public override void OnStateEntry(IState old)
{
characterController.ExpectSprint.being = true;
}
public override void OnStateExit(IState old, IState newState)
{
characterController.ExpectSprint.being = false;
}
public override void OnStateUpdate(float deltaTime)
{
if (actor.IsGrounded && actor.Velocity.GetLength() > characterController.initialSpeed/2)
characterController.Stamina -= staminaCost * deltaTime;
}
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
if (characterController.pauseRun.Allow)
{
characterController.TransitionState<Walk>();
return;
}
switch (characterController.ExpectRun.shouldBe,characterController.ExpectCrouch.shouldBe)
{
case (_,_) when characterController.Stamina is 0:
characterController.ExpectSprint.Reset();
characterController.TransitionState<Run>();
return;
case (_,true) when characterController.IsGrounded && characterController.Stamina > 0:
characterController.Stamina -= slideCost;
characterController.TransitionState<Slide>();
return;
case (_,true):
characterController.TransitionState<Crouch>();
return;
case (true,false) when characterController.MovementInput.z <=0:
case (false,false):
characterController.TransitionState<Walk>();
return;
}
}
public override void ExecuteCommand<T>(T command)
{
if (Enabled is false) return;
if(command is PlayerCancelRunCommand)
characterController.TransitionState<Walk>();
}
}
[Serializable]
public sealed class Knockdown : BasicMovement, IPlayerKnockdownState
{
public override void Initialize()
{
base.Initialize();
knockdown.OnKnockdown += OnKnockdown;
knockdown.OnRevive += OnRevive;
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
if (knockdown.IsPressured)
{
currentVelocity.x = 0;
currentVelocity.z = 0;
}
}
private void OnRevive()
{
if(Enabled)characterController.TransitionState<Walk>();
}
private void OnKnockdown()
{
characterController.TransitionState<Knockdown>();
}
}
[Serializable]
public sealed class Clip:PlayerCharacterState
{
[BITCommand]
public static void NoClip()
{
_clipAction?.Invoke();
}
private static Action _clipAction;
[SerializeField] private InputActionReference clipAction;
[SerializeReference, SubclassSelector] private IReference clipEnv;
[Inject] private IHealth _health;
[Inject] private InputActionGroup _inputActionGroup;
public override void Initialize()
{
base.Initialize();
_health.OnSetAlive += OnSetAlive;
Data.AddListener<bool>(clipEnv.Value, OnClip);
_clipAction = _ClipAction;
characterController.destroyCancellationToken.Register(() =>
{
Data.RemoveListender<bool>(clipEnv.Value, OnClip);
});
}
private void _ClipAction()
{
var clip = Data.Get<bool>(clipEnv.Value);
Data.Set(clipEnv.Value, !clip);
}
private async void OnClip(bool obj)
{
await UniTask.SwitchToMainThread();
if(_health.IsAlive is false) return;
if (obj && Enabled is false)
{
characterController.TransitionState<Clip>();
//BIT4Log.Log<Clip>("NoClip Enabled");
}else if (Enabled && obj is false)
{
characterController.TransitionState<Walk>();
//BIT4Log.Log<Clip>("NoClip Disabled");
}
}
private void OnSetAlive(bool obj)
{
if (Enabled && obj is false)
{
characterController.TransitionState<Walk>();
}
}
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
actor.Velocity = default;
actor.ColliderComponent.enabled = false;
actor.alwaysNotGrounded = true;
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
actor.ColliderComponent.enabled = true;
actor.alwaysNotGrounded = false;
}
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
actor.Position += characterController.ViewRotation * characterController.MovementInput * (
deltaTime * (_inputActionGroup.GetAction(characterController.RunAction).IsPressed()
? 8
: characterController.initialSpeed
));
actor.Rotation = Quaternion.Euler(0,characterController.LookInput.y,0);
}
}
}