| 1 | #include "Player/PlayerActionSlopeSlideControl.h" |
| 2 | |
| 3 | #include "Library/LiveActor/ActorMovementFunction.h" |
| 4 | #include "Library/LiveActor/ActorPoseKeeper.h" |
| 5 | #include "Library/LiveActor/ActorPoseUtil.h" |
| 6 | #include "Library/Math/MathUtil.h" |
| 7 | |
| 8 | #include "Player/PlayerConst.h" |
| 9 | #include "Player/PlayerInput.h" |
| 10 | #include "Util/ObjUtil.h" |
| 11 | #include "Util/PlayerCollisionUtil.h" |
| 12 | |
| 13 | PlayerActionSlopeSlideControl::PlayerActionSlopeSlideControl(al::LiveActor* player, |
| 14 | const PlayerConst* pConst, |
| 15 | const PlayerInput* input, |
| 16 | const IUsePlayerCollision* collider) |
| 17 | : mPlayer(player), mConst(pConst), mInput(input), mCollision(collider) {} |
| 18 | |
| 19 | void PlayerActionSlopeSlideControl::setup() { |
| 20 | mGroundNormal = -al::getGravity(actor: mPlayer); |
| 21 | mDirSlide = {0.0f, 0.0f, 0.0f}; |
| 22 | |
| 23 | if (rs::isCollidedGround(mCollision)) { |
| 24 | al::calcDirSlide(&mDirSlide, al::getGravity(actor: mPlayer), |
| 25 | rs::getCollidedGroundNormal(mCollision)); |
| 26 | if (!rs::isJustLand(mCollision)) |
| 27 | mGroundNormal = rs::getCollidedGroundNormal(mCollision); |
| 28 | } |
| 29 | |
| 30 | mHorizontalVelocity = {0.0f, 0.0f, 0.0f}; |
| 31 | } |
| 32 | |
| 33 | void PlayerActionSlopeSlideControl::setupCutSlideOppositeDir() { |
| 34 | setup(); |
| 35 | |
| 36 | sead::Vector3f groundNormal = {0.0f, 0.0f, 0.0f}; |
| 37 | rs::calcGroundNormalOrGravityDir(&groundNormal, mPlayer, mCollision); |
| 38 | |
| 39 | sead::Vector3f slideDir = {0.0f, 0.0f, 0.0f}; |
| 40 | sead::Vector3f* velocityPtr = al::getVelocityPtr(actor: mPlayer); |
| 41 | al::alongVectorNormalH(velocityPtr, *velocityPtr, mGroundNormal, groundNormal); |
| 42 | if (rs::calcSlideDir(&slideDir, al::getGravity(actor: mPlayer), groundNormal)) |
| 43 | al::limitVectorOppositeDir(outVec: velocityPtr, inVec: slideDir, dir: *velocityPtr, scale: velocityPtr->length()); |
| 44 | |
| 45 | *velocityPtr -= mConst->getGravityMove() * groundNormal; |
| 46 | mGroundNormal = groundNormal; |
| 47 | } |
| 48 | |
| 49 | f32 PlayerActionSlopeSlideControl::update(f32 accel, f32 brake, f32 against, f32 anglePowerMax, |
| 50 | f32 maxSpeed, f32 sideAccel, f32 sideBrake, |
| 51 | f32 sideMaxSpeed, f32 turnDegree, f32 gravity, |
| 52 | bool isRolling) { |
| 53 | sead::Vector3f prevGroundNormal = mGroundNormal; |
| 54 | sead::Vector3f up = -al::getGravity(actor: mPlayer); |
| 55 | sead::Vector3f velocity = al::getVelocity(actor: mPlayer); |
| 56 | bool isOnGroundSlopeSlideEnd = rs::isOnGroundSlopeSlideEnd(mPlayer, mCollision, mConst); |
| 57 | if (rs::isOnGround(mPlayer, mCollision)) { |
| 58 | mGroundNormal = rs::getCollidedGroundNormal(mCollision); |
| 59 | up = mGroundNormal; |
| 60 | al::calcDirSlide(&mDirSlide, al::getGravity(actor: mPlayer), up); |
| 61 | al::alongVectorNormalH(&velocity, velocity, prevGroundNormal, up); |
| 62 | |
| 63 | sead::Vector3f velDirSlide = {0.0f, 0.0f, 0.0f}; |
| 64 | sead::Vector3f velSide = {0.0f, 0.0f, 0.0f}; |
| 65 | if (isOnGroundSlopeSlideEnd) { |
| 66 | al::separateVectorParallelVertical(&velSide, &velDirSlide, up, velocity); |
| 67 | velDirSlide *= brake; |
| 68 | velSide *= sideBrake; |
| 69 | if (isRolling) { |
| 70 | sead::Vector3f moveInput = {0.0f, 0.0f, 0.0f}; |
| 71 | mInput->calcMoveInput(&moveInput, up); |
| 72 | |
| 73 | sead::Vector3f normDirSlide = {0.0f, 0.0f, 0.0f}; |
| 74 | al::tryNormalizeOrZero(out: &normDirSlide, vec: velDirSlide); |
| 75 | |
| 76 | sead::Vector3f side; |
| 77 | side.setCross(a: up, b: normDirSlide); |
| 78 | al::tryNormalizeOrZero(out: &side); |
| 79 | |
| 80 | f32 sideInput = side.dot(t: moveInput); |
| 81 | sead::Vector3f moveSideVec = side * sideInput * sideAccel; |
| 82 | al::addVectorLimit(&velDirSlide, moveSideVec, maxSpeed); |
| 83 | } |
| 84 | } else { |
| 85 | al::separateVectorParallelVertical(&velDirSlide, &velSide, mDirSlide, velocity); |
| 86 | sead::Vector3f moveInput = {0.0f, 0.0f, 0.0f}; |
| 87 | mInput->calcMoveInput(&moveInput, up); |
| 88 | f32 speedDirSlide = velDirSlide.dot(t: mDirSlide); |
| 89 | if (speedDirSlide < 0.0) |
| 90 | velDirSlide *= brake; |
| 91 | |
| 92 | f32 moveInputAgainst = sead::Mathf::clamp(value: -mDirSlide.dot(t: moveInput), low: 0.0f, high: 1.0f); |
| 93 | f32 minSpeedNorm = sead::Mathf::clamp(value: 1.0f - (moveInputAgainst * against), low: 0.0, high: 1.0); |
| 94 | f32 speedNorm = sead::Mathf::clamp(value: speedDirSlide + 1.0f, low: minSpeedNorm, high: 1.0f); |
| 95 | |
| 96 | f32 anglePower = sead::Mathf::clamp( |
| 97 | value: 90.0f - al::calcAngleDegree(a: mDirSlide, b: al::getGravity(actor: mPlayer)), low: 0.0f, high: 90.0f); |
| 98 | |
| 99 | f32 anglePowerNorm = 1.0f; |
| 100 | if (anglePowerMax > 0.0) |
| 101 | anglePowerNorm = sead::Mathf::clampMax(val: anglePower / anglePowerMax, max_: 1.0f); |
| 102 | |
| 103 | al::addVectorLimit(&velDirSlide, mDirSlide * accel * anglePowerNorm * speedNorm, |
| 104 | maxSpeed); |
| 105 | velSide *= sideBrake; |
| 106 | sead::Vector3f inputDir = {0.0f, 0.0f, 0.0f}; |
| 107 | if (al::tryNormalizeOrZero(out: &inputDir, vec: moveInput)) { |
| 108 | sead::Vector3f sideDir; |
| 109 | sideDir.setCross(a: up, b: mDirSlide); |
| 110 | al::addVectorLimit(&velSide, |
| 111 | sideDir * sideDir.dot(t: inputDir) * moveInput.length() * sideAccel, |
| 112 | sideMaxSpeed); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | sead::Vector3f vel; |
| 117 | vel.x = velDirSlide.x + velSide.x; |
| 118 | vel.y = velDirSlide.y + velSide.y; |
| 119 | vel.z = velDirSlide.z + velSide.z; |
| 120 | al::limitLength(out: &vel, vec: vel, limit: maxSpeed); |
| 121 | al::setVelocity(actor: mPlayer, vel: vel - (mConst->getGravityMove() * up)); |
| 122 | } else { |
| 123 | mGroundNormal = up; |
| 124 | f32 prevUpVel = prevGroundNormal.dot(t: al::getVelocity(actor: mPlayer)); |
| 125 | al::alongVectorNormalH(al::getVelocityPtr(actor: mPlayer), al::getVelocity(actor: mPlayer), |
| 126 | prevGroundNormal, mGroundNormal); |
| 127 | sead::Vector3f* velPtr = al::getVelocityPtr(actor: mPlayer); |
| 128 | velPtr->x = (prevUpVel * mGroundNormal.x) + velPtr->x; |
| 129 | velPtr->y = (prevUpVel * mGroundNormal.y) + velPtr->y; |
| 130 | velPtr->z = (prevUpVel * mGroundNormal.z) + velPtr->z; |
| 131 | al::addVectorLimit(al::getVelocityPtr(actor: mPlayer), up * -gravity, mConst->getFallSpeedMax()); |
| 132 | } |
| 133 | |
| 134 | sead::Vector3f hVel = {0.0f, 0.0f, 0.0f}; |
| 135 | al::verticalizeVec(out: &hVel, vertical: up, vec: al::getVelocity(actor: mPlayer)); |
| 136 | sead::Vector3f hVelDir = {0.0f, 0.0f, 0.0f}; |
| 137 | |
| 138 | f32 rumbleStrength; |
| 139 | if (al::tryNormalizeOrZero(out: &hVelDir, vec: hVel)) { |
| 140 | sead::Vector3f front = {0.0f, 0.0f, 0.0f}; |
| 141 | al::calcFrontDir(front: &front, actor: mPlayer); |
| 142 | al::verticalizeVec(out: &front, vertical: up, vec: front); |
| 143 | al::tryNormalizeOrZero(out: &front); |
| 144 | if (turnDegree > 0.0) { |
| 145 | f32 angleFrontVel = 2 * al::calcAngleDegree(a: front, b: hVelDir); |
| 146 | f32 turn = sead::Mathf::clamp(value: angleFrontVel / turnDegree, low: 0.0f, high: 1.0f); |
| 147 | sead::Vector3f upFrontVel; |
| 148 | upFrontVel.setCross(a: front, b: hVelDir); |
| 149 | rumbleStrength = -al::sign(x: up.dot(t: upFrontVel)) * turn; |
| 150 | } else { |
| 151 | rumbleStrength = 0.0; |
| 152 | } |
| 153 | |
| 154 | al::turnVecToVecCosOnPlane(&front, hVelDir, up, |
| 155 | sead::Mathf::cos(t: sead::Mathf::deg2rad(deg: turnDegree))); |
| 156 | rs::slerpUpFront(mPlayer, up, front, mConst->getSlerpQuatRate(), |
| 157 | mConst->getHillPoseDegreeMax()); |
| 158 | } else { |
| 159 | rumbleStrength = 0.0; |
| 160 | } |
| 161 | |
| 162 | mHorizontalVelocity = hVel; |
| 163 | return rumbleStrength; |
| 164 | } |
| 165 | |