Untitled

mail@pastecode.io avatar
unknown
plain_text
23 days ago
42 kB
2
Indexable
Never
using System;
using UnityEngine;
using System.Collections;
using MFPS.PlayerController;
using UnityEngine.Serialization;
using MFPS.Runtime.Level;

[RequireComponent(typeof(CharacterController))]
public class bl_FirstPersonController : bl_FirstPersonControllerBase
{
    #region Public members

    [Header("Settings")] public float WalkSpeed = 4.5f;

    [FormerlySerializedAs("m_CrouchSpeed")]
    public float runSpeed = 8;

    public float stealthSpeed = 1;

    [FormerlySerializedAs("m_CrouchSpeed")]
    public float crouchSpeed = 2;

    public float slideSpeed = 10;
    [FormerlySerializedAs("m_ClimbSpeed")] public float climbSpeed = 1f;
    [FormerlySerializedAs("m_JumpSpeed")] public float jumpSpeed;
    public float acceleration = 9;
    public float slopeFriction = 3f;

    public float crouchTransitionSpeed = 0.25f;
    public float crouchHeight = 1.4f;

    public bool canSlide = true;
    [Range(0.2f, 1.5f)] public float slideTime = 0.75f;
    [Range(1, 12)] public float slideFriction = 10;
    [Range(0.1f, 2.5f)] public float slideCoolDown = 1.2f;
    public float slideCameraTiltAngle = -22;

    [Range(0, 2)] public float JumpMinRate = 0.82f;
    public float jumpMomentumBooster = 2;
    public float momentunDecaySpeed = 5;
    [Range(0, 2)] public float AirControlMultiplier = 0.8f;
    public float m_StickToGroundForce;
    public float m_GravityMultiplier;

    [LovattoToogle] public bool RunFovEffect = true;
    public float runFOVAmount = 8;
    [LovattoToogle] public bool KeepToCrouch = true;
    public bool canStealthMode = true;
    [Header("Falling")] [LovattoToogle] public bool FallDamage = true;
    [Range(0.1f, 5f)] public float SafeFallDistance = 3;
    [Range(3, 25)] public float DeathFallDistance = 15;
    public PlayerRunToAimBehave runToAimBehave = PlayerRunToAimBehave.StopRunning;

    [Header("Dropping")] public float dropControlSpeed = 25;
    public Vector2 dropTiltSpeedRange = new Vector2(20, 60);

    [Header("Mouse Look"), FormerlySerializedAs("m_MouseLook")]
    public MouseLook mouseLook;

    [FormerlySerializedAs("HeatRoot")] public Transform headRoot;
    public Transform CameraRoot;
    [Header("HeadBob")] [Range(0, 1.2f)] public float headBobMagnitude = 0.9f;
    public float headVerticalBobMagnitude = 0.4f;
    public LerpControlledBob m_JumpBob = new LerpControlledBob();
    [Header("FootSteps")] public bl_Footstep footstep;
    public AudioClip jumpSound; // the sound played when character leaves the ground.
    public AudioClip landSound; // the sound played when character touches back on ground.
    public AudioClip slideSound;

    [Header("UI")] public Sprite StandIcon;
    public Sprite CrouchIcon;

    #endregion

    #region Public properties

    public float RunFov{ get; set; } = 0;
    public CollisionFlags m_CollisionFlags{ get; set; }
    public override Vector3 Velocity{ get; set; }
    public override float VelocityMagnitude{ get; set; }
    public override bool isControlable{ get; set; } = true;

    public bool isSprintLock{ get; set; } = false;

    #endregion

    #region Private members

    private bool hasPlatformJump = false;
    private float PlatformJumpForce = 0;
    public bool m_Jump;
    private Vector2 m_Input = new Vector2();
    private Vector3 targetDirection, moveDirection = Vector3.zero;
    private bool m_PreviouslyGrounded;
    private bool m_Jumping = false;
    private bool Crounching = false;
    private AudioSource m_AudioSource;
    public bool Finish = false;
    private Vector3 defaultCameraRPosition;
    private bool isClimbing, isAiming = false;
    private bl_Ladder m_Ladder;
    private bool MoveToStarted = false;
#if MFPSM
    private VariableJoystick Joystick;
#endif
    private float PostGroundVerticalPos = 0;
    private bool isFalling = false;
    private int JumpDirection = 0;
    private float HigherPointOnJump;
    private CharacterController m_CharacterController;
    private float lastJumpTime = 0;
    private float WeaponWeight = 1;
    private bool hasTouchGround = false;
    private bool JumpInmune = false;
    private Transform m_Transform;
    private RaycastHit[] SurfaceRay = new RaycastHit[1];
    private Vector3 desiredMove, momentum = Vector3.zero;
    private float VerticalInput, HorizontalInput;
    private bool lastCrouchState = false;
    private float fallingTime = 0;
    private bool haslanding = false;
    private float capsuleRadious;
    private readonly Vector3 feetPositionOffset = new Vector3(0, 0.8f, 0);
    private float slideForce = 0;
    private float lastSlideTime = 0;
    private bl_PlayerReferences playerReferences;
    private Vector3 forwardVector = Vector3.forward;
    private PlayerState lastState = PlayerState.Idle;
    private bool forcedCrouch = false;
    private Vector3 surfaceNormal, surfacePoint = Vector3.zero;
    private float defaultStepOffset = 0.4f;
    private float desiredSpeed = 4;
    private float defaultHeight = 2;

    #endregion

    #region Unity Methods

    /// <summary>
    /// 
    /// </summary>
    protected override void Awake(){
        if (!photonView.IsMine)
            return;

        base.Awake();
        m_Transform = transform;
        playerReferences = GetComponent<bl_PlayerReferences>();
        m_CharacterController = playerReferences.characterController;
#if MFPSM
        Joystick = FindObjectOfType<VariableJoystick>();
#endif
        defaultCameraRPosition = CameraRoot.localPosition;
        m_AudioSource = gameObject.AddComponent<AudioSource>();
        mouseLook.Init(m_Transform, headRoot);
        lastJumpTime = Time.time;
        defaultStepOffset = m_CharacterController.stepOffset;
        capsuleRadious = m_CharacterController.radius * 0.1f;
        defaultHeight = m_CharacterController.height;
        isControlable = bl_MatchTimeManagerBase.HaveTimeStarted();
    }
    
    /// <summary>
    /// 
    /// </summary>
    protected override void OnEnable(){
        bl_EventHandler.onRoundEnd += OnRoundEnd;
        bl_EventHandler.onChangeWeapon += OnChangeWeapon;
        bl_EventHandler.onMatchStart += OnMatchStart;
        bl_EventHandler.onGameSettingsChange += OnGameSettingsChanged;
        bl_EventHandler.onLocalAimChanged += OnAimChange;
#if MFPSM
        bl_TouchHelper.OnCrouch += OnCrouchClicked;
        bl_TouchHelper.OnJump += OnJump;
        bl_TouchHelper.OnProne += OnProne;
        bl_TouchHelper.OnSprintLock += OnSprintLock;

#endif
    }

    /// <summary>
    /// 
    /// </summary>
    protected override void OnDisable(){
        bl_EventHandler.onRoundEnd -= OnRoundEnd;
        bl_EventHandler.onChangeWeapon -= OnChangeWeapon;
        bl_EventHandler.onMatchStart -= OnMatchStart;
        bl_EventHandler.onGameSettingsChange -= OnGameSettingsChanged;
        bl_EventHandler.onLocalAimChanged -= OnAimChange;
#if MFPSM
        bl_TouchHelper.OnCrouch -= OnCrouchClicked;
        bl_TouchHelper.OnJump -= OnJump;
        bl_TouchHelper.OnProne -= OnProne;
        bl_TouchHelper.OnSprintLock -= OnSprintLock;
#endif
    }

    /// <summary>
    /// 
    /// </summary>
    public override void OnUpdate(){
        Velocity = m_CharacterController.velocity;
        VelocityMagnitude = Velocity.magnitude;

        if (Finish)
            return;

        MovementInput();
        GroundDetection();
        CheckStates();
    }

    /// <summary>
    /// 
    /// </summary>
    public override void OnLateUpdate(){
        UpdateMouseLook();

        if (m_CharacterController == null || !m_CharacterController.enabled) return;

        moveDirection = Vector3.Lerp(moveDirection, targetDirection, acceleration * Time.deltaTime);

        // apply the movement direction in the character controller
        // apply the movement in LateUpdate to avoid the camera jerky/jitter effect when move and rotate the player camera.
        m_CollisionFlags = m_CharacterController.Move(moveDirection * Time.deltaTime);
    }

    /// <summary>
    /// 
    /// </summary>
    public void FixedUpdate(){
        if (Finish || m_CharacterController == null || !m_CharacterController.enabled || MoveToStarted)
            return;

        //if player focus is in game
        if (bl_RoomMenu.Instance.isCursorLocked && !bl_GameData.Instance.isChating){
            //determine the player speed
            GetInput(out float s);
            desiredSpeed = Mathf.Lerp(desiredSpeed, s, Time.fixedDeltaTime * 8);
            speed = desiredSpeed;
        }
        else if (State != PlayerState.Sliding) //if player is not focus in game
        {
            m_Input = Vector2.zero;
        }

        if (isClimbing && m_Ladder != null){
            //climbing control
            OnClimbing();
        }
        else{
            //player movement
            Move();
        }
    }

    #endregion

    /// <summary>
    /// Triggered when the state of this player controller has changed.
    /// </summary>
    private void OnStateChanged(PlayerState from, PlayerState to){
        if (from == PlayerState.Crouching || to == PlayerState.Crouching){
            DoCrouchTransition();
        }
        else if (from == PlayerState.Sliding || to == PlayerState.Sliding){
            DoCrouchTransition();
        }
        else if (from == PlayerState.Proning || to == PlayerState.Proning){
            DoCrouchTransition();
        }


        bl_EventHandler.DispatchLocalPlayerStateChange(from, to);
    }

    /// <summary>
    /// Handle the player input key/buttons for the player controller
    /// </summary>
    void MovementInput(){
        if (State == PlayerState.Sliding){
            slideForce -= Time.deltaTime * slideFriction;
            speed = slideForce;
            if (bl_GameInput.Jump()){
                State = PlayerState.Jumping;
                m_Jump = true;
            }
            else return;
        }

        ProneInput();

        if (bl_UtilityHelper.isMobile) return;

        if (!m_Jump && State != PlayerState.Crouching && State != PlayerState.Proning && (Time.time - lastJumpTime) >
            JumpMinRate){
            m_Jump = bl_GameInput.Jump();
        }

        if (State != PlayerState.Jumping && State != PlayerState.Climbing){
            if (forcedCrouch) return;
            if (KeepToCrouch){
                Crounching = bl_GameInput.Crouch();
                if (Crounching != lastCrouchState){
                    OnCrouchChanged();
                    lastCrouchState = Crounching;
                }
            }
            else{
                if (bl_GameInput.Crouch(GameInputType.Down)){
                    Crounching = !Crounching;
                    OnCrouchChanged();
                }
            }
        }
    }

    /// <summary>
    /// Check when the player is in a surface (not jumping or falling)
    /// </summary>
    void GroundDetection(){
        //if the player has touch the ground after falling
        if (!m_PreviouslyGrounded && m_CharacterController.isGrounded){
            OnLand();
        }
        else if (m_PreviouslyGrounded && !m_CharacterController.isGrounded) //when the player start jumping
        {
            if (!isFalling){
                PostGroundVerticalPos = m_Transform.position.y;
                isFalling = true;
                fallingTime = Time.time;
            }
        }

        if (isFalling){
            VerticalDirectionCheck();
        }

        if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded){
            targetDirection.y = 0f;
        }

        if (forcedCrouch){
            if ((Time.frameCount % 10) == 0){
                if (!IsHeadHampered()){
                    forcedCrouch = false;
                    State = PlayerState.Idle;
                }
            }
        }

        m_PreviouslyGrounded = m_CharacterController.isGrounded;
    }

    /// <summary>
    /// Apply the movement direction to the CharacterController
    /// </summary>
    void Move(){
        //if the player is touching the surface
        if (m_CharacterController.isGrounded){
            OnSurface();
            //vertical resistance
            moveDirection.y = -m_StickToGroundForce;
            hasTouchGround = true;
            //has a pending jump
            if (m_Jump || hasPlatformJump){
                DoJump();
            }
        }
        else //if the player is not touching the ground
        {
            //if the player is dropping
            if (State == PlayerState.Dropping){
                //handle the air movement in different process
                OnDropping();
                return;
            }
            else if (State == PlayerState.Gliding){
                //handle the gliding movement in different function
                OnGliding();
                return;
            }

            OnAir();
        }
    }

    /// <summary>
    /// Control the player when is in a surface
    /// </summary>
    void OnSurface(){
        // always move along the camera forward as it is the direction that it being aimed at
        // desiredMove = (m_Transform.forward * m_Input.y) + (m_Transform.right * m_Input.x);

        desiredMove.Set(m_Input.x, 0.0f, m_Input.y);
        desiredMove = m_Transform.TransformDirection(desiredMove);

        // get a normal for the surface that is being touched to move along it
        Physics.SphereCastNonAlloc(m_Transform.localPosition, capsuleRadious, Vector3.down, SurfaceRay,
            m_CharacterController.height * 0.5f, Physics.AllLayers, QueryTriggerInteraction.Ignore);

        //determine the movement angle based in the normal of the current player surface
        desiredMove = Vector3.ProjectOnPlane(desiredMove, SurfaceRay[0].normal);
        targetDirection.x = desiredMove.x * speed;
        targetDirection.z = desiredMove.z * speed;

        SlopeControl();
    }

    /// <summary>
    /// Control the player when is in air (not dropping nor gliding)
    /// </summary>
    void OnAir(){
        desiredMove = (m_Transform.forward * Mathf.Clamp01(m_Input.y)) + (m_Transform.right * m_Input.x);
        desiredMove += momentum;

        targetDirection += Physics.gravity * m_GravityMultiplier * Time.fixedDeltaTime;
        targetDirection.x = (desiredMove.x * speed) * AirControlMultiplier;
        targetDirection.z = (desiredMove.z * speed) * AirControlMultiplier;

        momentum = Vector3.Lerp(momentum, Vector3.zero, Time.fixedDeltaTime * momentunDecaySpeed);
    }

    /// <summary>
    /// Called when the player hit a surface after falling/jumping
    /// </summary>
    void OnLand(){
        //land camera effect
        StartCoroutine(m_JumpBob.DoBobCycle());

        isFalling = false;
        float fallDistance = CalculateFall();
        bl_EventHandler.DispatchPlayerLandEvent(fallDistance);

        haslanding = true;
        JumpDirection = 0;
        targetDirection.y = 0f;
        m_Jumping = false;
        m_CharacterController.stepOffset = defaultStepOffset;
        if (State != PlayerState.Crouching && State != PlayerState.Proning)
            State = PlayerState.Idle;
    }

    /// <summary>
    /// 
    /// </summary>
    void OnCrouchChanged(){
        Proning = false;
        if (Crounching){
            State = PlayerState.Crouching;
            bl_UIReferences.Instance.PlayerUI.PlayerStateIcon.sprite = CrouchIcon;

            //Slide implementation
            if (canSlide && VelocityMagnitude > WalkSpeed && GetLocalVelocity().z > 0.1f){
                DoSlide();
            }
        }
        else{
            if (!IsHeadHampered()){
                State = PlayerState.Idle;
                bl_UIReferences.Instance.PlayerUI.PlayerStateIcon.sprite = StandIcon;
            }
            else forcedCrouch = true;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public void DoCrouchTransition(){
        StopCoroutine(nameof(CrouchTransition));
        StartCoroutine(nameof(CrouchTransition));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    IEnumerator CrouchTransition(){
        float height = 2f;
        float radius = 0.4f;
        Vector3 center = Vector3.up;
        Vector3 verticalCameraPos = defaultCameraRPosition;
        float transitionTime = crouchTransitionSpeed;
        //
        if (Crounching || State == PlayerState.Sliding){
            height = 1.4f;
            verticalCameraPos.y = 1.2f;
        }
        else if (State == PlayerState.Proning){
            height = proneCapsuleHeight;
            radius = proneCapsuleHeight;
            verticalCameraPos.y = proneCameraHeight;
            transitionTime = proneTransitionTime;
        }

        //
        float originHeight = m_CharacterController.height;
        Vector3 originCenter = m_CharacterController.center;
        Vector3 originCameraPosition = CameraRoot.localPosition;
        center.y = height / 2f;

        m_CharacterController.radius = radius;
        float d = 0;
        while (d < 1){
            d += Time.deltaTime / transitionTime;
            m_CharacterController.height = Mathf.Lerp(originHeight, height, d);
            m_CharacterController.center = Vector3.Lerp(originCenter, center, d);
            CameraRoot.localPosition = Vector3.Lerp(originCameraPosition, verticalCameraPos, d);
            yield return null;
        }
    }

    /// <summary>
    /// Make the player jump
    /// </summary>
    public override void DoJump(){
        momentum = desiredMove * jumpMomentumBooster;
        moveDirection.y = (hasPlatformJump) ? PlatformJumpForce : jumpSpeed;
        targetDirection.y = moveDirection.y;
        PlayJumpSound();
        m_Jump = false;
        m_Jumping = true;
        hasPlatformJump = false;
        if (State == PlayerState.Sliding) mouseLook.SetTiltAngle(0);
        Crounching = false;
        Proning = false;
        State = PlayerState.Jumping;
        lastJumpTime = Time.time;
        m_CharacterController.stepOffset = 0;
    }

    /// <summary>
    /// Make the player slide
    /// </summary>
    public override void DoSlide(){
        if ((Time.time - lastSlideTime) < slideTime * slideCoolDown)
            return; //wait the equivalent of one extra slide before be able to slide again
        if (m_Jumping) return;

        Vector3 startPosition = (m_Transform.position - feetPositionOffset) +
                                (m_Transform.forward * m_CharacterController.radius);
        if (Physics.Linecast(startPosition, startPosition + m_Transform.forward))
            return; //there is something in front of the feet's

        State = PlayerState.Sliding;
        slideForce = slideSpeed; //slide force will be continually decreasing
        speed = slideSpeed;
        //playerReferences.gunManager.HeadAnimator.Play("slide-start", 0, 0); // if you want to use an animation instead
        mouseLook.SetTiltAngle(slideCameraTiltAngle);
        if (slideSound != null){
            m_AudioSource.clip = slideSound;
            m_AudioSource.volume = 0.7f;
            m_AudioSource.Play();
        }

        mouseLook.UseOnlyCameraRotation();
        this.InvokeAfter(slideTime, () => {
            if (Crounching && !bl_UtilityHelper.isMobile)
                State = PlayerState.Crouching;
            else if (State != PlayerState.Jumping)
                State = PlayerState.Idle;

            Crounching = false;
            DoCrouchTransition();
            lastSlideTime = Time.time;
            mouseLook.SetTiltAngle(0);
            mouseLook.PortBodyOrientationToCamera();
        });
    }

    /// <summary>
    /// Detect slope limit and apply slide physics.
    /// </summary>
    void SlopeControl(){
        float angle = Vector3.Angle(Vector3.up, surfaceNormal);

        if (angle <= m_CharacterController.slopeLimit || angle >= 75) return;

        Vector3 direction = Vector3.up - surfaceNormal * Vector3.Dot(Vector3.up, surfaceNormal);
        float speed = slideSpeed + 1 + Time.deltaTime;

        targetDirection += direction * -speed;
        targetDirection.y = targetDirection.y - surfacePoint.y;
    }

    /// <summary>
    /// Make the player dropping
    /// </summary>
    public override void DoDrop(){
        if (isGrounded){
            Debug.Log("Can't drop when player is in a surface");
            return;
        }

        State = PlayerState.Dropping;
    }

    /// <summary>
    /// Called each frame when the player is dropping (fall control is On)
    /// </summary>
    void OnDropping(){
        //get the camera upside down angle
        float tilt = Mathf.InverseLerp(0, 90, mouseLook.VerticalAngle);
        //normalize it
        tilt = Mathf.Clamp01(tilt);
        if (mouseLook.VerticalAngle <= 0 || mouseLook.VerticalAngle >= 180) tilt = 0;
        //get the forward direction of the player camera
        desiredMove = headRoot.forward * Mathf.Clamp01(m_Input.y);
        if (desiredMove.y > 0) desiredMove.y = 0;

        //calculate the drop speed based in the upside down camera angle
        float dropSpeed = Mathf.Lerp(m_GravityMultiplier * dropTiltSpeedRange.x,
            m_GravityMultiplier * dropTiltSpeedRange.y, tilt);
        targetDirection = Physics.gravity * dropSpeed * Time.fixedDeltaTime;
        //if the player press the vertical input -> add velocity in the direction where the camera is looking at
        targetDirection += desiredMove * dropControlSpeed;

        //apply the movement direction in the character controller
        m_CollisionFlags = m_CharacterController.Move(targetDirection * Time.fixedDeltaTime);
    }

    /// <summary>
    /// Make the player glide
    /// </summary>
    public override void DoGliding(){
        if (isGrounded){
            Debug.Log("Can't gliding when player is in a surface");
            return;
        }

        State = PlayerState.Gliding;
    }

    /// <summary>
    /// Called each frame when the player is gliding
    /// </summary>
    void OnGliding(){
        desiredMove = (m_Transform.forward * m_Input.y) + (m_Transform.right * m_Input.x);
        //how much can the player control the player when is in air.
        float airControlMult = AirControlMultiplier * 5;
        //fall gravity amount
        float gravity = m_GravityMultiplier * 15;

        targetDirection = Physics.gravity * gravity * Time.fixedDeltaTime;
        targetDirection.x = (desiredMove.x * speed) * airControlMult;
        targetDirection.z = (desiredMove.z * speed) * airControlMult;

        m_CollisionFlags = m_CharacterController.Move(targetDirection * Time.fixedDeltaTime);
    }

    /// <summary>
    /// 
    /// </summary>
    void OnClimbing(){
        if (m_Ladder.HasPending){
            if (!MoveToStarted){
                StartCoroutine(MoveTo(m_Ladder.GetCurrent, false));
            }
        }
        else{
            desiredMove = m_Ladder.transform.rotation * forwardVector * m_Input.y;
            targetDirection.y = desiredMove.y * climbSpeed;
            targetDirection.x = desiredMove.x * climbSpeed;
            targetDirection.z = desiredMove.z * climbSpeed;
            if (bl_GameInput.Jump()){
                ToggleClimbing();
                m_Ladder.JumpOut();
                targetDirection.y = jumpSpeed;
                targetDirection.z = 30;
                lastJumpTime = Time.time;
            }

            m_CollisionFlags = m_CharacterController.Move(targetDirection * Time.smoothDeltaTime);
        }
    }

    /// <summary>
    /// Math behind the fall damage calculation
    /// </summary>
    private float CalculateFall(){
        float fallDistance = HigherPointOnJump - m_Transform.position.y;
        if (JumpDirection == -1){
            float normalized = PostGroundVerticalPos - m_Transform.position.y;
            fallDistance = Mathf.Abs(normalized);
        }

        if (FallDamage && hasTouchGround && haslanding){
            if (JumpInmune){
                JumpInmune = false;
                return fallDistance;
            }

            if ((Time.time - fallingTime) <= 0.4f){
                bl_EventHandler.DispatchPlayerLandEvent(0.2f);
                return fallDistance;
            }

            float ave = fallDistance / DeathFallDistance;
            if (fallDistance > SafeFallDistance){
                int damage = Mathf.FloorToInt(ave * 100);
                playerReferences.playerHealthManager.DoFallDamage(damage);
            }

            PlayLandingSound(ave);
            fallingTime = Time.time;
        }
        else PlayLandingSound(1);

        return fallDistance;
    }

    /// <summary>
    /// Check in which vertical direction is the player translating to
    /// </summary>
    void VerticalDirectionCheck(){
        if (m_Transform.position.y == PostGroundVerticalPos) return;

        //if the direction has not been decided yet
        if (JumpDirection == 0){
            //is the player below or above from the surface he was?
            // 1 = above (jump), -1 = below (falling)
            JumpDirection = (m_Transform.position.y > PostGroundVerticalPos) ? 1 : -1;
        }
        else if (JumpDirection == 1) //if the player jump
        {
            //but not start falling
            if (m_Transform.position.y < PostGroundVerticalPos){
                //get the higher point he reached jumping
                HigherPointOnJump = PostGroundVerticalPos;
            }
            else //if still going up
            {
                PostGroundVerticalPos = m_Transform.position.y;
            }
        }
        else //if the player was falling without jumping
        {
        }
    }

    /// <summary>
    /// 
    /// </summary>
    private void GetInput(out float outputSpeed){
        if (!isControlable){
            m_Input = Vector2.zero;
            outputSpeed = 0;
            return;
        }

        // Read input
        HorizontalInput = bl_GameInput.Horizontal;
        VerticalInput = bl_GameInput.Vertical;

#if MFPSM
        if (bl_UtilityHelper.isMobile){
            HorizontalInput = Joystick.Horizontal;
            if (isSprintLock){
                VerticalInput = 1.25f;
            }
            else{
                VerticalInput = Joystick.Vertical;
                VerticalInput = VerticalInput * 1.25f;
            }
        }
#endif
        if (State == PlayerState.Sliding){
            VerticalInput = 1;
            HorizontalInput = 0;
        }

        // m_Input = Vector2.Lerp(m_Input, new Vector2(HorizontalInput, VerticalInput), Time.deltaTime * 25);
        m_Input.Set(HorizontalInput, VerticalInput);

        float inputMagnitude = m_Input.magnitude;
        //if the player is dropping, the speed is calculated in the dropping function
        if (State == PlayerState.Dropping || State == PlayerState.Gliding){
            outputSpeed = 0;
            return;
        }

        if (State != PlayerState.Climbing && State != PlayerState.Sliding){
            if (inputMagnitude > 0 && State != PlayerState.Crouching && State != PlayerState.Proning){
                if (VelocityMagnitude > 0){
                    float forwardVelocity = GetLocalVelocity().z;
                    if (!bl_UtilityHelper.isMobile){
                        // On standalone builds, walk/run speed is modified by a key press.
                        // keep track of whether or not the character is walking or running
                        if (bl_GameInput.Run() && forwardVelocity > 0.1f){
                            if (runToAimBehave == PlayerRunToAimBehave.AimWhileRunning)
                                State = PlayerState.Running;
                            else if (runToAimBehave == PlayerRunToAimBehave.StopRunning)
                                State = isAiming ? PlayerState.Walking : PlayerState.Running;
                        }
                        else if (canStealthMode && bl_GameInput.Stealth() && VelocityMagnitude > 0.1f){
                            State = PlayerState.Stealth;
                        }
                        else{
                            State = PlayerState.Walking;
                        }
                    }
                    else{
                        if (VerticalInput > 1 && forwardVelocity > 0.1f){
                            State = PlayerState.Running;
                        }
                        else if (canStealthMode && VerticalInput > 0.05f && VerticalInput <= 0.15f){
                            State = PlayerState.Stealth;
                        }
                        else{
                            State = PlayerState.Walking;
                        }
                    }
                }
                else{
                    if (State != PlayerState.Jumping){
                        State = PlayerState.Idle;
                    }
                }
            }
            else if (m_CharacterController.isGrounded){
                if (State != PlayerState.Jumping && State != PlayerState.Crouching && State != PlayerState.Proning){
                    State = PlayerState.Idle;
                }
            }
        }

        if (State == PlayerState.Proning){
            outputSpeed = proneSpeed;
        }
        else if (Crounching){
            outputSpeed = (State == PlayerState.Crouching) ? crouchSpeed : runSpeed;
            if (State == PlayerState.Sliding){
                outputSpeed += 1;
            }
        }
        else{
            // set the desired speed to be walking or running
            outputSpeed = (State == PlayerState.Running && m_CharacterController.isGrounded) ? runSpeed : WalkSpeed;
            if (State == PlayerState.Stealth){
                outputSpeed = stealthSpeed;
            }
        }

        // normalize input if it exceeds 1 in combined length:
        if (inputMagnitude > 1){
            m_Input.Normalize();
        }

        if (RunFovEffect){
            float rf = (State == PlayerState.Running && m_CharacterController.isGrounded) ? runFOVAmount : 0;
            RunFov = Mathf.Lerp(RunFov, rf, Time.deltaTime * 6);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public override void PlayFootStepSound(){
        if (State == PlayerState.Sliding) return;
        if (!m_CharacterController.isGrounded && !isClimbing)
            return;

        if (!isClimbing){
            if (State == PlayerState.Stealth || State == PlayerState.Crouching){
                footstep?.SetVolumeMuliplier(footstep.stealthModeVolumeMultiplier);
            }
            else footstep?.SetVolumeMuliplier(1f);

            footstep?.DetectAndPlaySurface();
        }
        else{
            footstep?.PlayStepForTag("Generic");
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public override void PlatformJump(float force){
        hasPlatformJump = true;
        PlatformJumpForce = force;
        JumpInmune = true;
    }

#if MFPSM
    /// <summary>
    /// 
    /// </summary>
    void OnCrouchClicked(){
        Crounching = !Crounching;
        OnCrouchChanged();
    }

    void OnJump(){
        if (!m_Jump && State != PlayerState.Crouching){
            m_Jump = true;
        }
    }

    void OnProne(){
        if (CanProne()){
            Proning = !Proning;
            Crounching = false;
            OnProneChanged();
        }
    }

    void OnSprintLock(){
        isSprintLock = !isSprintLock;
    }


#endif

    /// <summary>
    /// 
    /// </summary>
    public override void UpdateMouseLook(){
        mouseLook.Update();

        if (bl_GameInput.InputFocus != MFPSInputFocus.Player) return;

        if (!isClimbing){
            mouseLook.UpdateLook(m_Transform, headRoot);
        }
        else{
            mouseLook.UpdateLook(m_Transform, headRoot, m_Ladder.InsertionPoint);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    private void CheckStates(){
        if (lastState == State) return;
        OnStateChanged(lastState, State);
        lastState = State;
    }

    /// <summary>
    /// 
    /// </summary>
    private void PlayLandingSound(float vol = 1){
        vol = Mathf.Clamp(vol, 0.05f, 1);
        m_AudioSource.clip = landSound;
        m_AudioSource.volume = vol;
        m_AudioSource.Play();
    }

    /// <summary>
    /// 
    /// </summary>
    private void PlayJumpSound(){
        m_AudioSource.volume = 1;
        m_AudioSource.clip = jumpSound;
        m_AudioSource.Play();
    }

    /// <summary>
    /// Check if the player has a obstacle above the head
    /// </summary>
    /// <returns></returns>
    public bool IsHeadHampered(){
        Vector3 origin = m_Transform.localPosition + m_CharacterController.center +
                         Vector3.up * m_CharacterController.height * 0.5F;
        float dist = 2.05f - m_CharacterController.height;
        return Physics.Raycast(origin, Vector3.up, dist);
    }


    #region External Events

    void OnRoundEnd(){
        Finish = true;
    }

    void OnChangeWeapon(int id){
        isAiming = false;
        var currentWeapon = playerReferences.gunManager.GetCurrentWeapon();
        if (currentWeapon != null){
            WeaponWeight = currentWeapon.WeaponWeight;
        }
        else{
            WeaponWeight = 0;
        }
    }

    void OnMatchStart(){
        isControlable = true;
    }

    void OnGameSettingsChanged() => mouseLook.FetchSettings();

    void OnAimChange(bool aim){
        isAiming = aim;
        mouseLook.OnAimChange(aim);
    }

    #endregion

    /// <summary>
    /// 
    /// </summary>
    private void ToggleClimbing(){
        isClimbing = !isClimbing;
        State = (isClimbing) ? PlayerState.Climbing : PlayerState.Idle;
        if (isClimbing)
            bl_InputInteractionIndicator.ShowIndication(bl_Input.GetButtonName("Jump"), bl_GameTexts.JumpOffLadder);
        else bl_InputInteractionIndicator.SetActiveIfSame(false, bl_GameTexts.JumpOffLadder);
    }

    /// <summary>
    /// 
    /// </summary>
    IEnumerator MoveTo(Vector3 pos, bool down){
        if (m_Transform == null) m_Transform = transform;

        MoveToStarted = true;
        float t = 0;
        Vector3 from = m_Transform.position;
        while (t < 1){
            t += Time.deltaTime / 0.4f;
            m_Transform.position = Vector3.Lerp(from, pos, t);
            yield return null;
        }

        if (down){
            bl_EventHandler.onPlayerLand(0.5f);
        }

        if (m_Ladder != null){
            m_Ladder.HasPending = false;
        }

        MoveToStarted = false;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="other"></param>
    void OnTriggerEnter(Collider other){
        if (!photonView.IsMine) return;
        if (other.transform.parent == null)
            return;

        var ladder = other.GetComponentInParent<bl_Ladder>();
        if (ladder != null){
            if (!ladder.CanUse)
                return;

            m_Ladder = ladder;
            if (other.transform.name == bl_Ladder.BottomColName){
                m_Ladder.InsertionPoint = other.transform;
                if (!isClimbing){
                    JumpInmune = true;
                    m_Ladder.ToBottom();
                    ToggleClimbing();
                }
                else{
                    ToggleClimbing();
                    m_Ladder.HasPending = false;
                }
            }
            else if (other.transform.name == bl_Ladder.TopColName){
                m_Ladder.InsertionPoint = other.transform;
                if (isClimbing){
                    m_Ladder.SetToTop();
                    if (!MoveToStarted){
                        StartCoroutine(MoveTo(m_Ladder.GetCurrent, true));
                    }
                }
                else{
                    m_Ladder.ToMiddle();
                }

                ToggleClimbing();
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    private void OnControllerColliderHit(ControllerColliderHit hit){
        surfaceNormal = hit.normal;
        surfacePoint = hit.point;
        /// Enable this if you want player controller apply force on contact to rigidbodys
        /// is commented by default for performance matters.
        /* Rigidbody body = hit.collider.attachedRigidbody;
         //dont move the rigidbody if the character is on top of it
         if (m_CollisionFlags == CollisionFlags.Below)
         {
             return;
         }

         if (body == null || body.isKinematic)
         {
             return;
         }
         body.AddForceAtPosition(m_CharacterController.velocity * 0.1f, hit.point, ForceMode.Impulse);*/
    }

    internal float _speed = 0;

    public float speed{
        get{ return _speed; }
        set{
            _speed = value - WeaponWeight;
            float min = 1.75f;
            if (State == PlayerState.Stealth || State == PlayerState.Proning){
                min = 1;
            }

            _speed = Mathf.Max(_speed, min);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override float GetCurrentSpeed(){
        return speed;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override float GetSpeedOnState(PlayerState playerState, bool includeModifiers){
        switch (playerState){
            case PlayerState.Walking:
                return includeModifiers ? WalkSpeed - WeaponWeight : WalkSpeed;
            case PlayerState.Running:
                return includeModifiers ? runSpeed - WeaponWeight : runSpeed;
            case PlayerState.Crouching:
                return includeModifiers ? crouchSpeed - WeaponWeight : crouchSpeed;
            case PlayerState.Dropping:
                return dropTiltSpeedRange.y;
            case PlayerState.Proning:
                return proneSpeed;
        }

        return includeModifiers ? WalkSpeed - WeaponWeight : WalkSpeed;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override PlayerRunToAimBehave GetRunToAimBehave(){
        return runToAimBehave;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override MouseLookBase GetMouseLook(){
        return mouseLook;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override bl_Footstep GetFootStep(){
        return footstep;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override float GetSprintFov(){
        return RunFov;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="vertical"></param>
    /// <returns></returns>
    public override float GetHeadBobMagnitudes(bool vertical){
        if (vertical) return headVerticalBobMagnitude;
        return headBobMagnitude;
    }

    public Vector3 GetLocalVelocity() => m_Transform.InverseTransformDirection(Velocity);
    public Vector3 MovementDirection => targetDirection;

    public override bool isGrounded{
        get{ return m_CharacterController.isGrounded; }
    }

    [SerializeField]
    public class LerpControlledBob
    {
        public float BobDuration;
        public float BobAmount;

        private float m_Offset = 0f;


        // provides the offset that can be used
        public float Offset(){
            return m_Offset;
        }


        public IEnumerator DoBobCycle(){
            // make the camera move down slightly
            float t = 0f;
            while (t < BobDuration){
                m_Offset = Mathf.Lerp(0f, BobAmount, t / BobDuration);
                t += Time.deltaTime;
                yield return new WaitForFixedUpdate();
            }

            // make it move back to neutral
            t = 0f;
            while (t < BobDuration){
                m_Offset = Mathf.Lerp(BobAmount, 0f, t / BobDuration);
                t += Time.deltaTime;
                yield return new WaitForFixedUpdate();
            }

            m_Offset = 0f;
        }
    }

    #region Prone

    [Header("Prone")] public bool enableProne = true;
    public float proneSpeed = 1;
    public float proneCapsuleHeight = 0.25f;
    public float proneCameraHeight = 0.1f;
    public float proneTransitionTime = 0.5f;

    public Sprite proneIcon;

    //
    public bool Proning = false;

    /// <summary>
    /// 
    /// </summary>
    private void ProneInput(){
        if (CanProne()){
            if (bl_GameInput.Prone()){
                Proning = !Proning;
                Crounching = false;
                OnProneChanged();
            }
        }
    }

    void OnProneChanged(){
        if (Proning){
            State = PlayerState.Proning;
            bl_UIReferences.Instance.PlayerUI.PlayerStateIcon.sprite = proneIcon;
        }
        else{
            if (!IsHeadHampered()){
                State = PlayerState.Idle;
                bl_UIReferences.Instance.PlayerUI.PlayerStateIcon.sprite = StandIcon;
            }
            else forcedCrouch = true;
        }
    }

    private bool CanProne(){
        if (!enableProne || !isGrounded){
            return false;
        }

        return State != PlayerState.Jumping && State != PlayerState.Climbing;
    }

    #endregion
}
Leave a Comment