using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using BITFALL.Player.Movement; using BITKit; using BITKit.Entities; using BITKit.Selection; using BITKit.StateMachine; using JetBrains.Annotations; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; 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); public override void OnStateEntry(IState old) { base.OnStateEntry(old); actor.alwaysNotGrounded = true; actor.ForceNotGrounded(); actor.ColliderComponent.enabled = false; cancelInterval.Reset(); } 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(); } } public override void OnStateExit(IState old, IState newState) { actor.alwaysNotGrounded = false; actor.ColliderComponent.enabled = true; characterController.ExpectJump.Reset(); actor.ForceGrounded(); } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { currentVelocity =(characterController.ExpectClimb.shouldBe - characterController.transform.position)*lerpDelta; } 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; 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; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.RigidbodyComponent.IsKinematic = false; actor.ColliderComponent.enabled = true; characterController.LimitViewAngle = 0; } 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, Quaternion.Inverse(_offMeshLink.transform.rotation), 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 * 3); 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); 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 (characterController.ExpectJump.shouldBe|| characterController.ExpectCrouch.shouldBe) { Exit(); 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(); } 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; 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; characterController.ExpectCrouch.Reset(); characterController.LimitViewAngle = 100; } 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.enabled = true; characterController.LimitViewAngle = 0; } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { actor.Position = FixedPosition; } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { currentRotation = FixedRotation; //actor.Rotation = 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 (_fixedPlace?.Exit(characterController.Entity) is false) { return; } if (fixedPlace.Entry(characterController.Entity)) { _fixedPlace = fixedPlace; characterController.TransitionState(); return; } _fixedPlace = null; } public override void OnStateUpdate(float deltaTime) { } protected override void Exit() { base.Exit(); if (_fixedPlace is not null) { actor.Position = _fixedPlace.ExitPosition; } _fixedPlace = null; } } }