protected void HandleMovement() { var playerModel = GameState.Instance.PlayerRpgState; if (!GameState.Instance.PlayerFlags.Contains(PlayerFlags.Frozen) && !GameState.Instance.PlayerFlags.Contains(PlayerFlags.TotallyFrozen)) { //handle running float energyToRun = RpgValues.GetRunEnergyRate(playerModel); IsRunning = MappedInput.GetButton(DefaultControls.Sprint); if (RunWasBlocked && IsRunning) { IsRunning = false; } else if (RunWasBlocked && !IsRunning) { RunWasBlocked = false; } if (IsRunning && playerModel.Energy < energyToRun) { IsRunning = false; RunWasBlocked = true; QdmsMessageBus.Instance.PushBroadcast(new QdmsFlagMessage("RpgInsufficientEnergy")); } //TODO check against energy requirements //request an exit from ADS if (IsRunning && PlayerController.WeaponComponent != null) { PlayerController.WeaponComponent.RequestADSExit(); } //handle crouching if (MappedInput.GetButtonDown(DefaultControls.Crouch) && !IsRunning) { IsCrouching = !IsCrouching; DidChangeCrouch = true; SetCrouchState(); } //uncrouch if we try to sprint if (IsRunning && IsCrouching) { IsCrouching = false; DidChangeCrouch = true; SetCrouchState(); } if (IsGrounded) { //normal x/y movement var flatVelocity = new Vector3(Velocity.x, 0, Velocity.z); Vector3 moveVector = Vector3.zero; float maxAcceleration = IsCrouching ? MaxCrouchAcceleration : (IsRunning ? MaxSprintAcceleration : MaxWalkAcceleration); if (Mathf.Abs(MappedInput.GetAxis(DefaultControls.MoveY)) > InputDeadzone) { moveVector += (transform.forward * MappedInput.GetAxis(DefaultControls.MoveY) * maxAcceleration * Time.deltaTime); IsMoving = true; } if (Mathf.Abs(MappedInput.GetAxis(DefaultControls.MoveX)) > InputDeadzone) { moveVector += (transform.right * MappedInput.GetAxis(DefaultControls.MoveX) * maxAcceleration * Time.deltaTime); IsMoving = true; } if (Mathf.Approximately(moveVector.magnitude, 0) && !IsOnSlope) { moveVector = -flatVelocity.normalized * Mathf.Min(MaxBrakeAcceleration * Time.deltaTime, flatVelocity.magnitude); } //clamp velocity to maxwalk/maxrun/etc float maxSpeed = IsCrouching ? MaxCrouchSpeed * RpgValues.GetMoveSpeedMultiplier(playerModel) : (IsRunning ? MaxSprintSpeed * RpgValues.GetRunSpeedMultiplier(playerModel) : MaxWalkSpeed * RpgValues.GetMoveSpeedMultiplier(playerModel)); maxSpeed *= ConfigState.Instance.GetGameplayConfig().Difficulty.PlayerAgility; var newFlatVelocity = new Vector3(Velocity.x, 0, Velocity.z) + new Vector3(moveVector.x, 0, moveVector.z); if (newFlatVelocity.magnitude > maxSpeed) { newFlatVelocity = newFlatVelocity.normalized * maxSpeed; //this actually doesn't make a ton of physical sense but it does seem to work } Velocity = new Vector3(newFlatVelocity.x, Velocity.y, newFlatVelocity.z); if (IsRunning) { playerModel.Energy -= energyToRun; } } else { //air move: component wise, clamped //awkward bullshit to go from world to player space Vector3 refVelocity = Quaternion.AngleAxis(-transform.eulerAngles.y, Vector3.up) * Velocity; Vector3 newAddVelocity = Vector3.zero; float multiplier = ConfigState.Instance.GetGameplayConfig().Difficulty.PlayerAgility; multiplier *= RpgValues.GetAirMoveMultiplier(playerModel); float maxSpeedScaled = MaxAirSpeed * multiplier; float moveZ = MappedInput.GetAxis(DefaultControls.MoveY) * MaxAirAcceleration * multiplier * Time.deltaTime; if (Mathf.Abs(refVelocity.z) < maxSpeedScaled || Mathf.Sign(moveZ) != Mathf.Sign(refVelocity.z)) { newAddVelocity += new Vector3(0, 0, moveZ); } float moveX = MappedInput.GetAxis(DefaultControls.MoveX) * MaxAirAcceleration * multiplier * Time.deltaTime; if (Mathf.Abs(refVelocity.x) < maxSpeedScaled || Mathf.Sign(moveX) != Mathf.Sign(refVelocity.x)) { newAddVelocity += new Vector3(moveX, 0, 0); } Velocity += Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * newAddVelocity; } if (IsGrounded && (AllowSlopeJumping || !IsOnSlope)) { //jumping if (MappedInput.GetButtonDown(DefaultControls.Jump)) { var jumpVelocity = JumpInstantaneousVelocity * RpgValues.GetJumpVelocityMultiplier(playerModel) * ConfigState.Instance.GetGameplayConfig().Difficulty.PlayerAgility; float jumpEnergyUse = RpgValues.GetJumpEnergyUse(playerModel); if (playerModel.Energy >= jumpEnergyUse) { playerModel.Energy -= jumpEnergyUse; bool wasCrouched = IsCrouching; //uncrouch if we were crouched if (wasCrouched) { IsCrouching = false; DidChangeCrouch = true; SetCrouchState(); jumpVelocity += JumpCrouchBoostVelocity; } Velocity += Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * jumpVelocity; CharController.Move(Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * JumpInstantaneousDisplacement); JumpSound.Ref()?.Play(); DidJump = true; } else { //failed to jump QdmsMessageBus.Instance.PushBroadcast(new QdmsFlagMessage("RpgInsufficientEnergy")); } } } } //energy recovery if (!IsRunning && IsGrounded && !DidJump) { float energyGain = (IsMoving ? RpgValues.GetMovingEnergyRecoveryRate(playerModel) : RpgValues.GetIdleEnergyRecoveryRate(playerModel)) * Time.deltaTime; playerModel.Energy = Mathf.Min(playerModel.DerivedStats.MaxEnergy, playerModel.Energy + energyGain); } }