| 1 | #include "Npc/VolleyballBall.h" |
| 2 | |
| 3 | #include "Library/Item/ItemUtil.h" |
| 4 | #include "Library/Joint/JointControllerKeeper.h" |
| 5 | #include "Library/LiveActor/ActorActionFunction.h" |
| 6 | #include "Library/LiveActor/ActorClippingFunction.h" |
| 7 | #include "Library/LiveActor/ActorCollisionFunction.h" |
| 8 | #include "Library/LiveActor/ActorFlagFunction.h" |
| 9 | #include "Library/LiveActor/ActorInitUtil.h" |
| 10 | #include "Library/LiveActor/ActorMovementFunction.h" |
| 11 | #include "Library/LiveActor/ActorPoseUtil.h" |
| 12 | #include "Library/LiveActor/ActorSensorUtil.h" |
| 13 | #include "Library/Math/MathUtil.h" |
| 14 | #include "Library/Math/ParabolicPath.h" |
| 15 | #include "Library/Nerve/NerveSetupUtil.h" |
| 16 | #include "Library/Nerve/NerveUtil.h" |
| 17 | |
| 18 | #include "Npc/VolleyballNpc.h" |
| 19 | #include "Util/SensorMsgFunction.h" |
| 20 | |
| 21 | namespace { |
| 22 | NERVE_IMPL(VolleyballBall, Wait) |
| 23 | NERVE_IMPL(VolleyballBall, Attack) |
| 24 | NERVE_END_IMPL(VolleyballBall, OnGround) |
| 25 | NERVE_IMPL(VolleyballBall, Return) |
| 26 | NERVE_IMPL(VolleyballBall, ReturnSmash) |
| 27 | NERVE_IMPL(VolleyballBall, ReturnEnd) |
| 28 | NERVE_IMPL(VolleyballBall, Miss) |
| 29 | NERVE_IMPL(VolleyballBall, MissReaction) |
| 30 | NERVE_IMPL(VolleyballBall, Toss) |
| 31 | NERVE_IMPL(VolleyballBall, TossEnd) |
| 32 | NERVE_IMPL(VolleyballBall, Retry) |
| 33 | NERVE_IMPL(VolleyballBall, RetryEnd) |
| 34 | |
| 35 | NERVES_MAKE_NOSTRUCT(VolleyballBall, ReturnEnd, Toss, TossEnd) |
| 36 | NERVES_MAKE_STRUCT(VolleyballBall, Wait, Return, ReturnSmash, Attack, Miss, MissReaction, Retry, |
| 37 | OnGround, RetryEnd) |
| 38 | } // namespace |
| 39 | |
| 40 | const sead::Vector3f sCoinDropOffset = {0.0f, 200.0f, 200.0f}; |
| 41 | |
| 42 | VolleyballBall::VolleyballBall(const char* name) : al::LiveActor(name) {} |
| 43 | |
| 44 | void VolleyballBall::init(const al::ActorInitInfo& initInfo) { |
| 45 | al::initActorWithArchiveName(actor: this, initInfo, archiveName: "VolleyballBall" , suffix: nullptr); |
| 46 | al::initNerve(actor: this, nerve: &NrvVolleyballBall.Wait, maxStates: 0); |
| 47 | mAttackPath = new al::ParabolicPath(); |
| 48 | mReturnPath = new al::ParabolicPath(); |
| 49 | al::initJointControllerKeeper(this, 1); |
| 50 | al::initJointGlobalQuatController(this, &mOrientation, "AllRoot" ); |
| 51 | makeActorDead(); |
| 52 | } |
| 53 | |
| 54 | void VolleyballBall::control() {} |
| 55 | |
| 56 | bool VolleyballBall::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, |
| 57 | al::HitSensor* self) { |
| 58 | if (rs::isMsgSeedReflect(message)) { |
| 59 | rs::requestHitReactionToAttacker(message, self, other); |
| 60 | return true; |
| 61 | } |
| 62 | |
| 63 | if (isMiss()) { |
| 64 | if ((rs::isMsgCapAttack(message) || al::isMsgPlayerSpinAttack(msg: message)) && mMissDelay < 1) { |
| 65 | rs::requestHitReactionToAttacker(message, self, other); |
| 66 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Retry); |
| 67 | return true; |
| 68 | } |
| 69 | if (rs::isMsgCapAttack(message) || al::isMsgPlayerSpinAttack(msg: message) || |
| 70 | rs::checkMsgNpcTrampleReactionAll(message, other, self, false) || |
| 71 | rs::isMsgHosuiAttack(message) || rs::isMsgHosuiTrample(message)) { |
| 72 | rs::requestHitReactionToAttacker(message, self, other); |
| 73 | if (mMissReactionDelay < 1) |
| 74 | al::setNerve(user: this, nerve: &NrvVolleyballBall.MissReaction); |
| 75 | mMissReactionDelay = 3; |
| 76 | return true; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | if (_138) |
| 81 | return false; |
| 82 | |
| 83 | if (al::isSensorSimple(self)) |
| 84 | return false; |
| 85 | |
| 86 | if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack) && |
| 87 | !al::isNerve(user: this, nerve: &NrvVolleyballBall.OnGround) && |
| 88 | !al::isNerve(user: this, nerve: &NrvVolleyballBall.Return)) |
| 89 | return false; |
| 90 | |
| 91 | if (al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack) && al::isLessStep(user: this, step: 20)) |
| 92 | return false; |
| 93 | |
| 94 | if (al::isMsgPlayerKick(msg: message) || rs::isMsgUpperPunchAll(message) || |
| 95 | rs::isMsgCapAttack(message) || rs::isMsgPlayerBallToss(message) || |
| 96 | al::isMsgPlayerSpinAttack(msg: message)) { |
| 97 | if (al::isNerve(user: this, nerve: &NrvVolleyballBall.OnGround) && al::isGreaterEqualStep(user: this, step: 15)) |
| 98 | return true; |
| 99 | |
| 100 | if (al::isSensorName(self, "Smash" ) && !al::isMsgPlayerSpinAttack(msg: message)) |
| 101 | return false; |
| 102 | |
| 103 | if (al::isMsgPlayerSpinAttack(msg: message) && |
| 104 | al::getTrans(actor: mNpc).y <= al::getTrans(actor: this).y + -100.0f && |
| 105 | al::getTrans(actor: mNpc).y <= al::getSensorPos(other).y + -30.0f) { |
| 106 | if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Return)) |
| 107 | mNpc->addSuccessCount(); |
| 108 | al::startHitReactionHitEffect(actor: this, name: "スマッシュ" , other, self); |
| 109 | al::setNerve(user: this, nerve: &NrvVolleyballBall.ReturnSmash); |
| 110 | return true; |
| 111 | } |
| 112 | |
| 113 | if (!al::isNerve(user: this, nerve: &NrvVolleyballBall.Return)) { |
| 114 | mNpc->addSuccessCount(); |
| 115 | rs::requestHitReactionToAttacker(message, self, other); |
| 116 | al::startHitReactionHitEffect(actor: this, name: "リターン" , other, self); |
| 117 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Return); |
| 118 | return true; |
| 119 | } |
| 120 | } |
| 121 | return false; |
| 122 | } |
| 123 | |
| 124 | void VolleyballBall::attackSensor(al::HitSensor* self, al::HitSensor* other) { |
| 125 | if (al::isNerve(user: this, nerve: &NrvVolleyballBall.Return) || |
| 126 | al::isNerve(user: this, nerve: &NrvVolleyballBall.ReturnSmash) || |
| 127 | al::isNerve(user: this, nerve: &NrvVolleyballBall.Attack)) |
| 128 | return; |
| 129 | |
| 130 | if (al::isSensorName(self, "Push" ) && !rs::sendMsgPushToPlayer(source: other, target: self)) |
| 131 | al::sendMsgPush(receiver: other, sender: self); |
| 132 | } |
| 133 | |
| 134 | void VolleyballBall::attack(const sead::Vector3f& startPosition, const sead::Vector3f& endPosition, |
| 135 | f32 attackSpeed) { |
| 136 | al::resetPosition(actor: this, trans: startPosition); |
| 137 | mEndPosition.set(endPosition); |
| 138 | mAttackPath->initFromUpVectorAddHeight(start: startPosition, end: mEndPosition, up: sead::Vector3f::ey, height: 500.0f); |
| 139 | mPathTime = mAttackPath->calcPathTimeFromAverageSpeed(frames: attackSpeed); |
| 140 | mAttackSpeed = attackSpeed; |
| 141 | al::invalidateClipping(actor: this); |
| 142 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Attack); |
| 143 | appear(); |
| 144 | } |
| 145 | |
| 146 | void VolleyballBall::toss(const sead::Vector3f& startPosition, const sead::Vector3f& endPosition) { |
| 147 | al::resetPosition(actor: this, trans: startPosition); |
| 148 | mEndPosition.set(endPosition); |
| 149 | al::invalidateClipping(actor: this); |
| 150 | al::offCollide(actor: this); |
| 151 | al::setNerve(user: this, nerve: &Toss); |
| 152 | appear(); |
| 153 | } |
| 154 | |
| 155 | void VolleyballBall::reset() { |
| 156 | if (al::isAlive(actor: this) && !al::isNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd)) { |
| 157 | al::startHitReaction(actor: this, name: "消滅" ); |
| 158 | kill(); |
| 159 | } |
| 160 | al::validateClipping(actor: this); |
| 161 | _138 = false; |
| 162 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Wait); |
| 163 | } |
| 164 | |
| 165 | bool VolleyballBall::isMiss() const { |
| 166 | return al::isNerve(user: this, nerve: &NrvVolleyballBall.Miss) || |
| 167 | al::isNerve(user: this, nerve: &NrvVolleyballBall.MissReaction); |
| 168 | } |
| 169 | |
| 170 | bool VolleyballBall::isReturnEnd() const { |
| 171 | return al::isNerve(user: this, nerve: &ReturnEnd); |
| 172 | } |
| 173 | |
| 174 | bool VolleyballBall::isTossEnd() const { |
| 175 | return al::isNerve(user: this, nerve: &TossEnd); |
| 176 | } |
| 177 | |
| 178 | bool VolleyballBall::isActive() const { |
| 179 | return !al::isNerve(user: this, nerve: &NrvVolleyballBall.Wait); |
| 180 | } |
| 181 | |
| 182 | bool VolleyballBall::isRetry() const { |
| 183 | return al::isNerve(user: this, nerve: &NrvVolleyballBall.Retry); |
| 184 | } |
| 185 | |
| 186 | bool VolleyballBall::isRetryEnd() const { |
| 187 | return al::isNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd); |
| 188 | } |
| 189 | |
| 190 | void VolleyballBall::exeWait() {} |
| 191 | |
| 192 | void VolleyballBall::exeAttack() { |
| 193 | if (al::isFirstStep(user: this)) { |
| 194 | al::startAction(actor: this, actionName: "Reaction" ); |
| 195 | al::startHitReactionHitEffect(actor: this, name: "リターン" , |
| 196 | pos: (al::getTrans(actor: this) + al::getTrans(actor: mNpc)) * 0.5f); |
| 197 | } |
| 198 | |
| 199 | if (al::isActionPlaying(actor: this, actionName: "Reaction" ) && al::isActionEnd(actor: this)) |
| 200 | al::startAction(actor: this, actionName: "FlyWait" ); |
| 201 | |
| 202 | sead::Vector3f prevPos = al::getTrans(actor: this); |
| 203 | sead::Vector3f newPos = sead::Vector3f::zero; |
| 204 | mAttackPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime); |
| 205 | al::resetPosition(actor: this, trans: newPos); |
| 206 | |
| 207 | sead::Quatf mOrientationCopy = mOrientation; |
| 208 | al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this), |
| 209 | 90.0f); |
| 210 | if (al::isGreaterEqualStep(user: this, step: mPathTime)) { |
| 211 | al::resetPosition(actor: this, trans: mEndPosition); |
| 212 | al::setNerve(user: this, nerve: &NrvVolleyballBall.OnGround); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | void VolleyballBall::exeOnGround() { |
| 217 | if (al::isFirstStep(user: this)) { |
| 218 | al::startAction(actor: this, actionName: "Wait" ); |
| 219 | al::setVelocityJump(actor: this, speed: 10.0f); |
| 220 | al::startHitReaction(actor: this, name: "着地" ); |
| 221 | al::onCollide(actor: this); |
| 222 | } |
| 223 | |
| 224 | al::addVelocityToGravity(actor: this, force: 0.98f); |
| 225 | al::scaleVelocity(actor: this, factor: 0.98f); |
| 226 | |
| 227 | if (al::isOnGround(this, 0)) { |
| 228 | mMissDelay = 60; |
| 229 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Miss); |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | void VolleyballBall::endOnGround() { |
| 234 | al::setVelocityZero(this); |
| 235 | al::offCollide(actor: this); |
| 236 | } |
| 237 | |
| 238 | void VolleyballBall::exeReturn() { |
| 239 | if (al::isFirstStep(user: this)) { |
| 240 | sead::Vector3f startPosition = mAttackPath->getStart(); |
| 241 | mReturnPath->initFromUpVectorAddHeight(start: al::getTrans(actor: this), end: startPosition, |
| 242 | up: sead::Vector3f::ey, height: 500.0f); |
| 243 | al::startAction(actor: this, actionName: "Reaction" ); |
| 244 | } |
| 245 | |
| 246 | if (al::isActionPlaying(actor: this, actionName: "Reaction" ) && al::isActionEnd(actor: this)) |
| 247 | al::startAction(actor: this, actionName: "FlyWait" ); |
| 248 | |
| 249 | sead::Vector3f prevPos = al::getTrans(actor: this); |
| 250 | sead::Vector3f newPos = sead::Vector3f::zero; |
| 251 | mReturnPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime); |
| 252 | al::resetPosition(actor: this, trans: newPos); |
| 253 | |
| 254 | sead::Quatf mOrientationCopy = mOrientation; |
| 255 | al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this), |
| 256 | 90.0f); |
| 257 | if (al::isGreaterEqualStep(user: this, step: mPathTime)) |
| 258 | al::setNerve(user: this, nerve: &ReturnEnd); |
| 259 | } |
| 260 | |
| 261 | void VolleyballBall::exeReturnSmash() { |
| 262 | sead::Vector3f targetVec = al::getTrans(actor: mNpc) - al::getTrans(actor: this); |
| 263 | if (al::isFirstStep(user: this)) { |
| 264 | sead::Vector3f direction = targetVec; |
| 265 | al::normalize(vec: &direction); |
| 266 | al::setVelocityToDirection(actor: this, dir: direction, speed: 50.0f); |
| 267 | al::startAction(actor: this, actionName: "Smash" ); |
| 268 | al::makeQuatFrontNoSupport(outQuat: &mOrientation, front: al::getVelocity(actor: this)); |
| 269 | } |
| 270 | |
| 271 | f32 targetLen = targetVec.length(); |
| 272 | if (al::getVelocity(actor: this).length() > targetLen) { |
| 273 | s32 smashCoins = al::getRandom(min: 1, max: 4); |
| 274 | mLastSmashCoins = (mLastSmashCoins == 3 && smashCoins == 3) ? 1 : smashCoins; |
| 275 | if (mLastSmashCoins > 0) { |
| 276 | for (s32 i = mLastSmashCoins; i != 0; i--) { |
| 277 | sead::Vector3f frontDir = mNpc->getFrontDir(); |
| 278 | al::rotateVectorDegreeY(&frontDir, al::getRandom(min: -80.0f, max: 80.0f)); |
| 279 | sead::Vector3f pose = sCoinDropOffset; |
| 280 | al::multVecPose(posOut: &pose, actor: mNpc, posIn: pose); |
| 281 | al::appearItemTiming(actor: mNpc, "スマッシュ" , pose, frontDir, sensor: nullptr); |
| 282 | } |
| 283 | } |
| 284 | al::resetPosition(actor: this, trans: mAttackPath->getStart()); |
| 285 | mNpc->startSmashReaction(); |
| 286 | al::setNerve(user: this, nerve: &ReturnEnd); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | void VolleyballBall::exeReturnEnd() { |
| 291 | al::validateClipping(actor: this); |
| 292 | kill(); |
| 293 | } |
| 294 | |
| 295 | void VolleyballBall::exeMiss() { |
| 296 | if (al::isFirstStep(user: this)) { |
| 297 | al::startAction(actor: this, actionName: "Wait" ); |
| 298 | al::setVelocityZero(this); |
| 299 | } |
| 300 | mMissDelay--; |
| 301 | } |
| 302 | |
| 303 | void VolleyballBall::exeMissReaction() { |
| 304 | if (al::isFirstStep(user: this)) |
| 305 | al::startAction(actor: this, actionName: "Reaction" ); |
| 306 | mMissReactionDelay--; |
| 307 | mMissDelay--; |
| 308 | if (al::isActionEnd(actor: this)) |
| 309 | al::setNerve(user: this, nerve: &NrvVolleyballBall.Miss); |
| 310 | } |
| 311 | |
| 312 | void VolleyballBall::exeToss() { |
| 313 | if (al::isFirstStep(user: this)) |
| 314 | al::setVelocityJump(actor: this, speed: 12.0f); |
| 315 | al::addVelocityToGravity(actor: this, force: 0.4f); |
| 316 | if (al::getVelocity(actor: this).y <= 0.0f && mEndPosition.y >= al::getTrans(actor: this).y) { |
| 317 | al::setVelocityZero(this); |
| 318 | al::resetPosition(actor: this, trans: mEndPosition); |
| 319 | al::setNerve(user: this, nerve: &TossEnd); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | void VolleyballBall::exeTossEnd() {} |
| 324 | |
| 325 | void VolleyballBall::exeRetry() { |
| 326 | if (al::isFirstStep(user: this)) { |
| 327 | sead::Vector3f startPosition = mAttackPath->getStart(); |
| 328 | mReturnPath->initFromUpVectorAddHeight(start: al::getTrans(actor: this), end: startPosition, |
| 329 | up: sead::Vector3f::ey, height: 500.0f); |
| 330 | al::startAction(actor: this, actionName: "Reaction" ); |
| 331 | mPathTime = mReturnPath->calcPathTimeFromAverageSpeed(frames: 12.0f); |
| 332 | } |
| 333 | |
| 334 | if (al::isActionPlaying(actor: this, actionName: "Reaction" ) && al::isActionEnd(actor: this)) |
| 335 | al::startAction(actor: this, actionName: "FlyWait" ); |
| 336 | sead::Vector3f prevPos = al::getTrans(actor: this); |
| 337 | sead::Vector3f newPos = sead::Vector3f::zero; |
| 338 | mReturnPath->calcPosition(pos: &newPos, prog: al::getNerveStep(user: this) / (f32)mPathTime); |
| 339 | al::resetPosition(actor: this, trans: newPos); |
| 340 | |
| 341 | sead::Quatf mOrientationCopy = mOrientation; |
| 342 | al::rotateQuatRollBall(&mOrientation, mOrientationCopy, prevPos - newPos, al::getGravity(actor: this), |
| 343 | 90.0f); |
| 344 | if (al::isGreaterEqualStep(user: this, step: mPathTime)) |
| 345 | al::setNerve(user: this, nerve: &NrvVolleyballBall.RetryEnd); |
| 346 | } |
| 347 | |
| 348 | void VolleyballBall::exeRetryEnd() {} |
| 349 | |