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 Climb : PlayerCharacterState,IPlayerClimbState { [SerializeField] protected float lerpDelta = 5; private IntervalUpdate cancelInterval = new(0.16f); private bool needInit; public override void OnStateEntry(IState old) { 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.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): Exit(); break; case (not null,not null): if (progress is > 1.1f or < -0.1f) { Exit(); } break; case (not null,null): if (actor.Position.y > _offMeshLink.endTransform.position.y) { Exit(); actor.Position = _offMeshLink.endTransform.position; }else if (actor.Position.y < _offMeshLink.startTransform.position.y) { Exit(); actor.Position = _offMeshLink.startTransform.position; } 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; } } }