using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BITFALL.Player.Equip; using BITFALL.Player.Movement; using BITKit; using BITKit.Entities; using BITKit.Selection; using BITKit.StateMachine; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Interactions; using UnityEngine.Splines; using UnityEngine.UI; namespace BITFALL.Entities.Player.Movement.States { [Serializable] public class Vault : PlayerCharacterState, IPlayerVaultState { public float NormalizedTime { get => _normalizedTime; set { _normalizedTime = value; _manualUpdate=true; } } public int Phase { get; set; } [SerializeField] private AnimationCurve entryCurve; private float _elapsedTime; private float _normalizedTime; private bool _manualUpdate; private Vector3 _entryPosition; private Vector3 entryVelocity; public override void OnStateEntry(IState old) { base.OnStateEntry(old); Phase = 0; _elapsedTime = 0; actor.alwaysNotGrounded = true; //actor.ColliderComponent.enabled = false; actor.IsKinematic = true; actor.UseRootMotion = true; _normalizedTime = 0; _manualUpdate = false; actor.ForceNotGrounded(); _entryPosition = actor.Position; entryVelocity = actor.Velocity; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.alwaysNotGrounded = false; //actor.ColliderComponent.enabled = true; actor.IsKinematic = false; actor.UseRootMotion = false; if (characterController.vaultPoint is GetVaultPointFromCollider vaultPoint) { actor.Position = vaultPoint.EndPosition-Vector3.up*0.64f; } //actor.Velocity = lastVelocity; //actor.ForceGrounded(); characterController.ExpectJump.Reset(); characterController.RequestClimb = false; _normalizedTime = 0; _manualUpdate = false; Phase = 0; actor.ForceNotGrounded(); actor.Velocity = actor.Forward * 3 + actor.Up*-2; } public override void OnStateUpdate(float deltaTime) { _elapsedTime += deltaTime; base.OnStateUpdate(deltaTime); switch (Phase) { case 0 when _elapsedTime>0.16f: // if(Vector3.Distance( // characterController.ExpectClimb.shouldBe-Vector3.up*0.64f, // actor.Position) // <=0.16f) // Phase = 1; Phase = 1; break; case 1: if(NormalizedTime>=1) Exit(); if (_manualUpdate is false) { NormalizedTime+=deltaTime; } break; } } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { deltaTime =deltaTime<0.001f?0.001f:deltaTime; base.UpdateVelocity(ref currentVelocity, deltaTime); switch (Phase) { case 0: // actor.Position = // Vector3.MoveTowards( // actor.Position, // characterController.ExpectClimb.shouldBe - Vector3.up * 0.64f, // 4f // ); var endPos = characterController.ExpectClimb.shouldBe - Vector3.up * 0.64f; var t = entryCurve.Evaluate(_elapsedTime); actor.Position = Vector3.Lerp(_entryPosition, endPos, t); break; case 1 when characterController.vaultPoint is GetVaultPointFromCollider vaultPoint: var newPos = Vector3.Lerp( vaultPoint.StartPosition, vaultPoint.EndPosition, NormalizedTime); newPos.y = actor.Position.y; actor.Position = newPos; _entryPosition = (newPos - actor.Position)/ deltaTime;; break; } } public bool ManualCancel() { if (Enabled is false) return false; Exit(); return true; } } [Serializable] public class EdgeClimb : PlayerCharacterState, IPlayerEdgeClimbState { public int Phase { get; set; } [Inject] private IEquipService _equipService; public bool ManualCancel() { if (Enabled is false) return false; Exit(); return true; } public override void OnStateEntry(IState old) { base.OnStateEntry(old); actor.alwaysNotGrounded = true; actor.ColliderComponent.enabled = false; actor.UseRootMotion = true; characterController.LimitViewAngle = 45; Phase = 1; _equipService.AllowEquip.AddDisableElements(this); } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.alwaysNotGrounded = false; actor.ColliderComponent.enabled = true; actor.UseRootMotion = false; characterController.LimitViewAngle = 0; _equipService.AllowEquip.RemoveDisableElements(this); } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); switch (Phase) { case 1: var targetPos = characterController.ExpectClimb.shouldBe - Vector3.up * 1.6f; if(Vector3.Distance(targetPos, actor.Position) <=0.16f) Phase = 2; if(actor.Position.y >= targetPos.y) Phase = 2; actor.Position = targetPos; return; case 2: if (Vector3.Distance( characterController.ExpectClimb.shouldBe, actor.Position) <= 0.1f) { Exit(); } return; } } private float upVelocity; public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { base.UpdateVelocity(ref currentVelocity, deltaTime); switch (Phase) { case 1: // actor.Position = // // Vector3.MoveTowards( // // actor.Position, // // characterController.ExpectClimb.shouldBe-Vector3.up*1.6f, // // 4*deltaTime); // Vector3.Slerp(actor.Position, // characterController.ExpectClimb.shouldBe - Vector3.up * 1.6f, // 8 * deltaTime // ); var currentPosition = actor.Position; currentPosition = Vector3.Lerp( actor.Position, characterController.ExpectClimb.shouldBe - Vector3.up * 1.6f,32*deltaTime); currentPosition.y = actor.Position.y; currentPosition.y+=(upVelocity+=deltaTime)*deltaTime; actor.Position = currentPosition; break; } } public override void AfterUpdateMovement(float deltaTime) { characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition; } } [Serializable] public class Climb : PlayerCharacterState,IPlayerClimbState { [SerializeField] protected float lerpDelta = 5; private IntervalUpdate cancelInterval = new(0.16f); private Transform ignore; private bool needInit; public override void OnStateEntry(IState old) { ignore = characterController.climbClosePoint.Collider.transform; actor.PhysicsComponent.IgnoreCollision(ignore,true); base.OnStateEntry(old); characterController.ExpectCrouch.Reset(); actor.alwaysNotGrounded = true; actor.ForceNotGrounded(); actor.ColliderComponent.enabled = false; cancelInterval.Reset(); needInit = true; characterController.LimitViewAngle = 80; } public override void OnStateUpdate(float deltaTime) { var distance = Vector3.Distance(characterController.ExpectClimb.shouldBe, characterController.transform.position); if ( distance<=0.16f || actor.IsStable || cancelInterval.AllowUpdate && actor.Velocity.sqrMagnitude<=0.16f ) { Exit(); return; } if (characterController.Position.y >= characterController.ExpectClimb.shouldBe.y) { Exit(); } } public override void OnStateExit(IState old, IState newState) { actor.PhysicsComponent.IgnoreCollision(ignore,false); actor.alwaysNotGrounded = false; actor.ColliderComponent.enabled = true; characterController.ExpectJump.Reset(); actor.ForceGrounded(); characterController.LimitViewAngle = 0; // } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { if (needInit) { currentVelocity.y = 0; needInit = false; } var velocity =(characterController.ExpectClimb.shouldBe - characterController.transform.position)*lerpDelta; velocity += characterController.Rotation * characterController.MovementInput.normalized * 1.0f; currentVelocity = Vector3.Lerp(currentVelocity,velocity,lerpDelta*deltaTime); } public override void AfterUpdateMovement(float deltaTime) { characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition; } } [Serializable] public sealed class Link:PlayerCharacterState,IPlayerLinkState { public int LinkArea { get; private set; } private OffMeshLink _offMeshLink; private ISplineContainer _splineContainer; private float progress; private float increment; [Inject] private IEquipService _equipService; [Inject] private InputActionGroup _inputActionGroup; private readonly IntervalUpdate exitInterval = new(0.32f); public override void OnStateEntry(IState old) { base.OnStateEntry(old); characterController.ExpectCrouch.Reset(); characterController.ExpectJump.Reset(); actor.ForceNotGrounded(); actor.RigidbodyComponent.IsKinematic = true; actor.ColliderComponent.enabled = false; Init(characterController.OffMeshLink); characterController.LimitViewAngle = 45; _equipService.AllowEquip.AddDisableElements(this); exitInterval.Reset(); _inputActionGroup.RegisterCallback(characterController.CrouchAction, OnCrouch); _inputActionGroup.RegisterCallback(characterController.JumpAction, OnJump); } private void OnJump(InputAction.CallbackContext obj) { switch (obj) { case {interaction:PressInteraction,performed:true}: Exit(); return; } } private void OnCrouch(InputAction.CallbackContext obj) { switch (obj) { case { interaction: PressInteraction, performed: true }: Exit(); return; } } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.RigidbodyComponent.IsKinematic = false; actor.ColliderComponent.enabled = true; characterController.LimitViewAngle = 0; _equipService.AllowEquip.RemoveDisableElements(this); _inputActionGroup.UnRegisterCallback(characterController.CrouchAction, OnCrouch); _inputActionGroup.UnRegisterCallback(characterController.JumpAction, OnJump); } private void Init(OffMeshLink offMeshLink) { var offsetTransform = offMeshLink.transform; _offMeshLink = offMeshLink; offMeshLink.TryGetComponent(out _splineContainer); LinkArea = offMeshLink.area; if (_splineContainer is not null) { var isStart = Vector3.Distance( actor.Position, offsetTransform.TransformPoint(_splineContainer.Splines[0].Knots.First().Position) ) < Vector3.Distance( actor.Position, offsetTransform.TransformPoint(_splineContainer.Splines[0].Knots.Last().Position) ); progress = isStart ? 0 : 1; increment = isStart ? 1 : -1; } else { var offset = Mathf.Lerp(offMeshLink.startTransform.position.y,offMeshLink.endTransform.position.y,0.5f); var offsetPos = actor.Position; offsetPos.y = offset; actor.ForceNotGrounded(); actor.Position = Vector3.MoveTowards(actor.Position, offsetPos, 0.64f); progress = 0.5f; } } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { if (_splineContainer is null) { actor.Rotation = Quaternion.Lerp( actor.Rotation, _offMeshLink.transform.rotation * Quaternion.Euler(0,-180,0), 5 * deltaTime ); } } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { actor.ForceNotGrounded(); if (_offMeshLink is null) return; switch (_splineContainer) { case null: var positionA = _offMeshLink.startTransform.position; var positionB = positionA; positionB.y = _offMeshLink.endTransform.position.y; var positionC = actor.Position + Vector3.up * (characterController.MovementInput.z * deltaTime * 1.6f); Vector3 vectorAC = positionC - positionA; Vector3 vectorAB = positionB - positionA; Vector3 closestPoint = positionA + Vector3.Project(vectorAC, vectorAB); actor.Position = closestPoint; //currentVelocity = Vector3.Lerp(currentVelocity , (closestPoint - actor.Position) , 5 * deltaTime); characterController.UnityEntity.SetDirect(BITHash.Player.ActionSpeed,characterController.MovementInput.z); break; case not null: var trans = _offMeshLink.transform; var pos =trans.position + trans.rotation * _splineContainer.Splines[0].EvaluatePosition(progress +=increment * deltaTime); actor.Position =Vector3.Lerp(actor.Position , pos , 8*deltaTime) ; break; } } public override void OnStateUpdate(float deltaTime) { if (exitInterval.AllowUpdateWithoutReset is false) return; switch (_offMeshLink,_splineContainer) { case (null,null): case (not null,_) when !_offMeshLink || _offMeshLink.gameObject.activeInHierarchy is false: Exit(); return; case (not null,not null): if (progress is > 1.1f or < -0.1f) { Exit(); } return; case (not null,null): if (actor.Position.y > _offMeshLink.endTransform.position.y) { Exit(); actor.Position = _offMeshLink.endTransform.position; return; } if (actor.Position.y < _offMeshLink.startTransform.position.y) { Exit(); actor.Position = _offMeshLink.startTransform.position; return; } break; } } public override void AfterUpdateMovement(float deltaTime) { characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition; } } [Serializable] public sealed class Dodge : PlayerCharacterState,IPlayerDodgeState { [SerializeField] private int costStamina = 10; [SerializeField] private AnimationCurve curve; [SerializeField] private float duration = 0.32f; private int direction; private float process; [Inject] private IHealth _health; public override void Initialize() { base.Initialize(); _health.OnDamageFactory += OnDamageFactory; } public override void OnStateEntry(IState old) { base.OnStateEntry(old); direction = characterController.MovementInput.x > 0 ? 1 : -1; process = 0; characterController.Stamina-= costStamina; } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { currentVelocity = actor.transform.right * (direction * curve.Evaluate(process+=deltaTime / duration)); } public override void AfterUpdateMovement(float deltaTime) { if(process>=1) Exit(); if(actor.IsGrounded is false)Exit(); } private int OnDamageFactory(DamageMessage msg, int currentDamage) { if (characterController.CurrentState is not IPlayerDodgeState || msg.Initiator is not Entity initiator) return currentDamage; var _direction = characterController.Position - initiator.transform.position; var verticalAngle = Vector3.Angle(initiator.transform.forward, _direction) - 90.0f; //Debug.Log(verticalAngle); return 0; } } [Serializable] public sealed class Fixed:PlayerCharacterState,IPlayerFixedState { public object FixedObject=>_fixedPlace; public float3 FixedPosition=>_fixedPlace.FixedPosition; public quaternion FixedRotation=>_fixedPlace.FixedRotation; public float3 FixedLocalPosition => throw new NotImplementedException(); public quaternion FixedLocalRotation => throw new NotImplementedException(); private IPlayerFixedPlace _fixedPlace; [Inject] private ISelector _selector; [Inject] private IEquipService _equipService; [Inject] private IKnockdown _knockdown; public override void Initialize() { base.Initialize(); _selector.OnActive+=OnSelectorActive; } public override void OnStateEntry(IState old) { base.OnStateEntry(old); //actor.enabled = false; //actor.RigidbodyComponent.IsKinematic = true; actor.ColliderComponent.enabled = false; actor.PhysicsComponent.enabled = false; actor.alwaysNotGrounded = true; actor.constraintRotation = false; characterController.ExpectCrouch.Reset(); characterController.LimitViewAngle = 100; _equipService.AllowEquip.AddDisableElements(this); lastRotation = _fixedPlace.FixedRotation; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); //actor.RigidbodyComponent.IsKinematic = false; actor.ColliderComponent.enabled = true; actor.alwaysNotGrounded = false; actor.PhysicsComponent.enabled = true; actor.constraintRotation = true; //actor.enabled = true; characterController.LimitViewAngle = 0; _equipService.AllowEquip.RemoveDisableElements(this); } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { currentVelocity = default; actor.Position = FixedPosition + _fixedPlace.Velocity * deltaTime; ; } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { currentRotation = FixedRotation; } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); characterController.CurrentCameraPosition.shouldBe = characterController.FpvLocalPosition; if (characterController.ExpectCrouch.shouldBe && _fixedPlace.Exit(characterController.Entity)) { characterController.ExpectCrouch.Reset(); Exit(); }else if (_fixedPlace is null) { Exit(); } } private void OnSelectorActive(ISelectable obj) { if (obj is not MonoBehaviour monoBehaviour || monoBehaviour.TryGetComponent(out var fixedPlace) is false) return; if (_knockdown.IsKnockdown) return; if (_fixedPlace?.Exit(characterController.Entity) is false) { return; } if (fixedPlace.Entry(characterController.Entity)) { _fixedPlace = fixedPlace; characterController.TransitionState(); return; } _fixedPlace = null; } private quaternion lastRotation; public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); var rotation = Quaternion.Inverse(_fixedPlace.FixedRotation) * lastRotation; var euler =MathV.TransientRotationAxis(rotation.eulerAngles); var add = new float2(euler.x, -euler.y); characterController.AddViewEuler(add); lastRotation = _fixedPlace.FixedRotation; } protected override void Exit() { base.Exit(); if (_fixedPlace is not null) { actor.Position = _fixedPlace.ExitPosition; } _fixedPlace = null; } } }