| 1 | #include "Item/Coin.h" |
| 2 | |
| 3 | #include "Library/Collision/CollisionPartsKeeperUtil.h" |
| 4 | #include "Library/Collision/CollisionPartsTriangle.h" |
| 5 | #include "Library/Collision/PartsConnector.h" |
| 6 | #include "Library/Controller/PadRumbleFunction.h" |
| 7 | #include "Library/Demo/DemoFunction.h" |
| 8 | #include "Library/LiveActor/ActorActionFunction.h" |
| 9 | #include "Library/LiveActor/ActorAreaFunction.h" |
| 10 | #include "Library/LiveActor/ActorClippingFunction.h" |
| 11 | #include "Library/LiveActor/ActorCollisionFunction.h" |
| 12 | #include "Library/LiveActor/ActorFlagFunction.h" |
| 13 | #include "Library/LiveActor/ActorInitInfo.h" |
| 14 | #include "Library/LiveActor/ActorInitUtil.h" |
| 15 | #include "Library/LiveActor/ActorModelFunction.h" |
| 16 | #include "Library/LiveActor/ActorMovementFunction.h" |
| 17 | #include "Library/LiveActor/ActorPoseUtil.h" |
| 18 | #include "Library/LiveActor/ActorSensorUtil.h" |
| 19 | #include "Library/Math/MathUtil.h" |
| 20 | #include "Library/Nature/NatureUtil.h" |
| 21 | #include "Library/Nerve/NerveSetupUtil.h" |
| 22 | #include "Library/Nerve/NerveUtil.h" |
| 23 | #include "Library/Placement/PlacementFunction.h" |
| 24 | #include "Library/Se/SeFunction.h" |
| 25 | #include "Library/Shadow/ActorShadowUtil.h" |
| 26 | #include "Library/Stage/StageSwitchUtil.h" |
| 27 | |
| 28 | #include "Item/CoinRotateCalculator.h" |
| 29 | #include "Item/CoinStateAppearRotate.h" |
| 30 | #include "System/GameDataFunction.h" |
| 31 | #include "System/GameDataUtil.h" |
| 32 | #include "Util/AreaUtil.h" |
| 33 | #include "Util/DemoUtil.h" |
| 34 | #include "Util/ExternalForceKeeper.h" |
| 35 | #include "Util/SensorMsgFunction.h" |
| 36 | #include "Util/ShadowUtil.h" |
| 37 | #include "Util/WaterSurfaceShadow.h" |
| 38 | |
| 39 | namespace { |
| 40 | NERVE_IMPL_(Coin, WaitConnectMtx, Wait) |
| 41 | NERVE_IMPL(Coin, Wait) |
| 42 | NERVE_IMPL(Coin, Appear) |
| 43 | NERVE_IMPL(Coin, AppearCoinLead) |
| 44 | NERVE_IMPL_(Coin, WaitPlayerDead, Wait) |
| 45 | NERVE_IMPL(Coin, Got) |
| 46 | NERVE_IMPL_(Coin, WaitCircle, Wait) |
| 47 | NERVE_IMPL_(Coin, PopUpNormal, PopUp) |
| 48 | NERVE_IMPL(Coin, CountUp) |
| 49 | NERVE_IMPL_(Coin, CountUpFive, CountUp) |
| 50 | NERVE_IMPL(Coin, CountUpDelay) |
| 51 | NERVE_IMPL_(Coin, WaitCoinRail, Wait) |
| 52 | NERVE_IMPL_(Coin, PopUpPlayerDeadTimeBalloon, PopUp) |
| 53 | NERVE_IMPL_(Coin, PopUpPlayerDead, PopUp) |
| 54 | NERVE_IMPL_(Coin, AppearOnDemo, Appear) |
| 55 | NERVE_IMPL_(Coin, WaitOnDemo, Wait) |
| 56 | NERVE_IMPL_(Coin, GotNoCoin, Got) |
| 57 | NERVE_IMPL(Coin, WaitOnDemoEnd) |
| 58 | NERVE_IMPL(Coin, BlowUpDelay) |
| 59 | |
| 60 | NERVES_MAKE_NOSTRUCT(Coin, BlowUpDelay) |
| 61 | NERVES_MAKE_STRUCT(Coin, WaitConnectMtx, Wait, Appear, AppearCoinLead, WaitPlayerDead, Got, |
| 62 | WaitCircle, , CountUp, CountUpFive, CountUpDelay, WaitCoinRail, |
| 63 | , , AppearOnDemo, WaitOnDemo, GotNoCoin, |
| 64 | WaitOnDemoEnd) |
| 65 | } // namespace |
| 66 | |
| 67 | const sead::Vector3f sAppearAboveVelocity(0.0f, 25.0f, 0.0f); |
| 68 | |
| 69 | Coin::Coin(const char* name, bool isDemo) : al::LiveActor(name), mIsDemo(isDemo) {} |
| 70 | |
| 71 | void Coin::init(const al::ActorInitInfo& initInfo) { |
| 72 | const char* modelName = "Coin" ; |
| 73 | if (al::isPlaced(initInfo)) { |
| 74 | mIsPlaced = true; |
| 75 | alPlacementFunction::tryGetModelName(modelName: &modelName, initInfo); |
| 76 | } else { |
| 77 | mIsPlaced = false; |
| 78 | } |
| 79 | |
| 80 | const char* suffix = mIsDemo ? "MoveDemo" : nullptr; |
| 81 | al::initActorWithArchiveName(actor: this, initInfo, archiveName: modelName, suffix); |
| 82 | |
| 83 | bool isConnectToCollision = false; |
| 84 | al::tryGetArg(arg: &isConnectToCollision, initInfo, key: "IsConnectToCollision" ); |
| 85 | if (!isConnectToCollision) |
| 86 | al::initNerve(actor: this, nerve: &NrvCoin.Wait, maxStates: 1); |
| 87 | |
| 88 | else |
| 89 | al::initNerve(actor: this, nerve: &NrvCoin.WaitConnectMtx, maxStates: 1); |
| 90 | |
| 91 | al::offCollide(actor: this); |
| 92 | mExternalForceKeeper = new ExternalForceKeeper(); |
| 93 | mRotateCalculator = new CoinRotateCalculator(this); |
| 94 | |
| 95 | mStartingQuat.set(al::getQuat(actor: this)); |
| 96 | al::tryAddDisplayOffset(actor: this, initInfo); |
| 97 | al::tryGetDisplayOffset(offset: &mDisplayOffset, initInfo); |
| 98 | mMtxConnector = al::createMtxConnector(actor: this); |
| 99 | |
| 100 | mShadowSize = rs::setShadowDropLength(this, initInfo, "本体" ); |
| 101 | al::expandClippingRadiusByShadowLength(actor: this, nullptr, radius: mShadowSize); |
| 102 | mWaterSurfaceShadow = rs::tryCreateWaterSurfaceCoinShadow(initInfo); |
| 103 | |
| 104 | if (mMtxConnector != nullptr) |
| 105 | al::tryGetArg(arg: &mIsConnectToCollisionBack, initInfo, key: "IsConnectToCollisionBack" ); |
| 106 | |
| 107 | if (al::trySyncStageSwitchAppear(actor: this)) |
| 108 | al::setNerve(user: this, nerve: &NrvCoin.Appear); |
| 109 | |
| 110 | bool isCoinSave = false; |
| 111 | al::tryGetArg(arg: &isCoinSave, initInfo, key: "IsCoinSave" ); |
| 112 | if (isCoinSave) { |
| 113 | mSaveObjInfo = rs::createSaveObjInfoNoWriteSaveDataInSameWorldResetMiniGame(actorInitInfo: initInfo); |
| 114 | if (rs::isOnSaveObjInfo(saveObjInfo: mSaveObjInfo)) { |
| 115 | kill(); |
| 116 | return; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | mStateAppearRotate = new CoinStateAppearRotate(this, nullptr, mDisplayOffset, "誘導コイン出現" ); |
| 121 | al::initNerveState(user: this, state: mStateAppearRotate, nerve: &NrvCoin.AppearCoinLead, hostName: "誘導コイン出現" ); |
| 122 | mPoseTrans = al::getTrans(actor: this); |
| 123 | mPoseQuat = al::getQuat(actor: this); |
| 124 | al::registActorToDemoInfo(actor: this, initInfo); |
| 125 | } |
| 126 | |
| 127 | void Coin::initAfterPlacement() { |
| 128 | if (mMtxConnector != nullptr) { |
| 129 | if (!mIsConnectToCollisionBack) { |
| 130 | al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, 50.0f, 400.0f); |
| 131 | } else { |
| 132 | sead::Vector3f frontDir = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 133 | al::calcFrontDir(front: &frontDir, actor: this); |
| 134 | al::attachMtxConnectorToCollision( |
| 135 | mtxConnector: mMtxConnector, actor: this, al::getTrans(actor: this) + frontDir * 50.0f, frontDir * -400.0f); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitPlayerDead)) { |
| 140 | sead::Vector3f hitPos = sead::Vector3f::zero; |
| 141 | al::Triangle triangle; |
| 142 | |
| 143 | if (alCollisionUtil::getFirstPolyOnArrow( |
| 144 | this, &hitPos, &triangle, al::getTrans(actor: this) + sead::Vector3f(0.0f, 20.0f, 0.0f), |
| 145 | sead::Vector3f::ey * -200.0f, nullptr, nullptr)) { |
| 146 | al::resetPosition(actor: this, trans: hitPos + 80.0f * sead::Vector3f::ey); |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | kill(); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | void Coin::appear() { |
| 155 | mExternalForceKeeper->reset(); |
| 156 | mRotateCalculator->reset(); |
| 157 | if (al::isNerve(user: this, nerve: &NrvCoin.Got) && mIsPlaced) |
| 158 | return; |
| 159 | al::LiveActor::appear(); |
| 160 | al::showModelIfHide(actor: this); |
| 161 | } |
| 162 | |
| 163 | void Coin::makeActorAlive() { |
| 164 | al::LiveActor::makeActorAlive(); |
| 165 | al::showModelIfHide(actor: this); |
| 166 | } |
| 167 | |
| 168 | // https://decomp.me/scratch/5pT1P |
| 169 | // NON_MATCHING: Different stack pointer order |
| 170 | void Coin::control() { |
| 171 | sead::Vector3f force = sead::Vector3f::zero; |
| 172 | mExternalForceKeeper->calcForce(force: &force); |
| 173 | mExternalForceKeeper->reset(); |
| 174 | bool checkWater; |
| 175 | if (!al::isNerve(user: this, nerve: &NrvCoin.Wait) && !al::isNerve(user: this, nerve: &NrvCoin.WaitPlayerDead)) |
| 176 | checkWater = true; |
| 177 | else |
| 178 | checkWater = false; |
| 179 | |
| 180 | mRotateCalculator->update(force, checkWater); |
| 181 | |
| 182 | if (!rs::isActiveDemo(this) && mTimeLimit > 0) { |
| 183 | mTimeLimit--; |
| 184 | if (mTimeLimit <= 0) { |
| 185 | kill(); |
| 186 | return; |
| 187 | } |
| 188 | if (!(mTimeLimit >= 201) && al::blinkModel(actor: this, mTimeLimit, 6, 0)) |
| 189 | al::startSe(this, "PgBlink" ); |
| 190 | } |
| 191 | |
| 192 | if (!al::isNerve(user: this, nerve: &NrvCoin.Got)) |
| 193 | rs::tryUpdateWaterSurfaceCoinShadow(mWaterSurfaceShadow, this, mShadowSize); |
| 194 | } |
| 195 | |
| 196 | void Coin::endClipped() { |
| 197 | mRotateCalculator->reset(); |
| 198 | al::LiveActor::endClipped(); |
| 199 | } |
| 200 | |
| 201 | bool Coin::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) { |
| 202 | if (rs::isMsgPlayerDisregardTargetMarker(message) || |
| 203 | mExternalForceKeeper->receiveMsg(message, other, self)) |
| 204 | return true; |
| 205 | |
| 206 | if (al::isMsgRestart(msg: message)) { |
| 207 | if (al::isNerve(user: this, nerve: &NrvCoin.Got)) |
| 208 | return false; |
| 209 | al::setNerve(user: this, nerve: &NrvCoin.AppearOnDemo); |
| 210 | appear(); |
| 211 | return true; |
| 212 | } |
| 213 | |
| 214 | if (rs::isMsgTimerAthleticDemoStart(message)) { |
| 215 | rs::addDemoActor(this, false); |
| 216 | return true; |
| 217 | } |
| 218 | |
| 219 | if (rs::isMsgConductLightning(message) && isAnyWaitOrAppear() && isWait()) |
| 220 | return true; |
| 221 | |
| 222 | if (rs::isMsgItemGetAll(message) && isAnyWaitOrAppear()) { |
| 223 | al::invalidateClipping(actor: this); |
| 224 | al::startHitReaction(actor: this, name: "取得" ); |
| 225 | al::setNerve(user: this, nerve: &NrvCoin.Got); |
| 226 | return true; |
| 227 | } |
| 228 | |
| 229 | if (rs::isMsgFishingItemGet(message) && isAnyWaitOrAppear()) { |
| 230 | appearCountUp(); |
| 231 | return true; |
| 232 | } |
| 233 | |
| 234 | if (rs::isMsgItemAmiiboKoopa(message) && isAnyWaitOrAppear()) { |
| 235 | sead::Vector3f direction = sead::Vector3f::zero; |
| 236 | al::calcDirBetweenSensorsH(&direction, other, self); |
| 237 | al::setVelocity(actor: this, vel: direction * 20.0f + 40.0f * sead::Vector3f::ey); |
| 238 | al::setNerve(user: this, nerve: &NrvCoin.PopUpNormal); |
| 239 | return true; |
| 240 | } |
| 241 | |
| 242 | return false; |
| 243 | } |
| 244 | |
| 245 | void Coin::tryCreateMtxConnector() { |
| 246 | if (mMtxConnector == nullptr) |
| 247 | mMtxConnector = al::createMtxConnector(actor: this); |
| 248 | } |
| 249 | |
| 250 | void Coin::appearCirclePlacement() { |
| 251 | appear(); |
| 252 | al::setNerve(user: this, nerve: &NrvCoin.WaitCircle); |
| 253 | } |
| 254 | |
| 255 | void Coin::(bool startHitReaction) { |
| 256 | al::validateHitSensors(this); |
| 257 | al::setNerve(user: this, nerve: &NrvCoin.PopUpNormal); |
| 258 | appear(); |
| 259 | al::invalidateClipping(actor: this); |
| 260 | if (startHitReaction) |
| 261 | al::startHitReaction(actor: this, name: "飛出し出現" ); |
| 262 | |
| 263 | sead::Vector3f frontDir = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 264 | sead::Vector3f upDir = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 265 | al::calcFrontDir(front: &frontDir, actor: this); |
| 266 | al::calcUpDir(up: &upDir, actor: this); |
| 267 | al::setVelocity(actor: this, vel: frontDir * 7.5f + upDir * 20.0f); |
| 268 | } |
| 269 | |
| 270 | void Coin::() { |
| 271 | if (!al::isNerve(user: this, nerve: &NrvCoin.Got) || !mIsPlaced) { |
| 272 | appearPopUpCommon(startHitReaction: true); |
| 273 | mTimeLimit = 600; |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | void Coin::() { |
| 278 | if (!al::isNerve(user: this, nerve: &NrvCoin.Got) || !mIsPlaced) { |
| 279 | appearPopUpCommon(startHitReaction: false); |
| 280 | mTimeLimit = 600; |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | void Coin::() { |
| 285 | appearPopUp(); |
| 286 | |
| 287 | sead::Vector3f frontDir = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 288 | sead::Vector3f upDir = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 289 | al::calcFrontDir(front: &frontDir, actor: this); |
| 290 | al::calcUpDir(up: &upDir, actor: this); |
| 291 | f32 frontOffset = al::getRandom(min: 10.0f, max: 15.0f); |
| 292 | f32 upOffset = al::getRandom(min: 20.0f, max: 30.0f); |
| 293 | al::setVelocity(actor: this, vel: frontOffset * frontDir + upOffset * upDir); |
| 294 | } |
| 295 | |
| 296 | void Coin::appearAbove() { |
| 297 | appear(); |
| 298 | al::invalidateClipping(actor: this); |
| 299 | al::setVelocity(actor: this, vel: sAppearAboveVelocity); |
| 300 | al::setNerve(user: this, nerve: &NrvCoin.PopUpNormal); |
| 301 | al::startHitReaction(actor: this, name: "真上に出現" ); |
| 302 | mTimeLimit = -1; |
| 303 | } |
| 304 | |
| 305 | inline void appearCountUpReset(al::LiveActor* actor, ExternalForceKeeper* forceKeeper) { |
| 306 | al::offCollide(actor); |
| 307 | al::invalidateClipping(actor); |
| 308 | al::invalidateHitSensors(actor); |
| 309 | sead::Vector3f direction = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 310 | al::calcQuatUp(up: &direction, actor); |
| 311 | al::setVelocityToDirection(actor, dir: direction, speed: 16.0f); |
| 312 | forceKeeper->reset(); |
| 313 | actor->appear(); |
| 314 | } |
| 315 | |
| 316 | void Coin::appearCountUpCommon(sead::Vector3f* velocity, s32 delay) { |
| 317 | al::setVelocityZero(this); |
| 318 | sead::Vector3f quatUp = sead::Vector3f::zero; |
| 319 | al::calcQuatUp(up: &quatUp, actor: this); |
| 320 | velocity->set(quatUp * 16.0f); |
| 321 | |
| 322 | if (delay != 0) { |
| 323 | al::setNerve(user: this, nerve: &NrvCoin.CountUpDelay); |
| 324 | al::hideModelIfShow(actor: this); |
| 325 | return; |
| 326 | } |
| 327 | |
| 328 | al::setNerve(user: this, nerve: &NrvCoin.CountUp); |
| 329 | al::setVelocity(actor: this, vel: *velocity); |
| 330 | } |
| 331 | |
| 332 | void Coin::appearCountUp() { |
| 333 | al::setNerve(user: this, nerve: &NrvCoin.CountUp); |
| 334 | al::setQuat(actor: this, quat: sead::Quatf::unit); |
| 335 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 336 | mTimeLimit = -1; |
| 337 | } |
| 338 | |
| 339 | void Coin::appearCountUpFiveCount() { |
| 340 | al::setNerve(user: this, nerve: &NrvCoin.CountUpFive); |
| 341 | al::setQuat(actor: this, quat: sead::Quatf::unit); |
| 342 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 343 | mTimeLimit = -1; |
| 344 | } |
| 345 | |
| 346 | void Coin::appearCountUp3(s32 coinIndex) { |
| 347 | sead::Vector3f direction = sead::Vector3f::zero; |
| 348 | |
| 349 | switch (coinIndex) { |
| 350 | case 0: |
| 351 | mCountUpDelay = 0; |
| 352 | direction = sead::Vector3f(0.0f, 0.0f, 50.0f); |
| 353 | break; |
| 354 | case 1: |
| 355 | mCountUpDelay = 5; |
| 356 | direction = sead::Vector3f(40.0f, 0.0f, -30.0f); |
| 357 | break; |
| 358 | case 2: |
| 359 | mCountUpDelay = 10; |
| 360 | direction = sead::Vector3f(-40.0f, 0.0f, -30.0f); |
| 361 | break; |
| 362 | default: |
| 363 | break; |
| 364 | } |
| 365 | |
| 366 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 367 | al::rotateVectorQuat(&direction, al::getQuat(actor: this)); |
| 368 | al::getTransPtr(actor: this)->add(a: direction); |
| 369 | mTimeLimit = -1; |
| 370 | appearCountUpCommon(velocity: &mBlowVelocity, delay: mCountUpDelay); |
| 371 | } |
| 372 | |
| 373 | void Coin::appearCountUp5(s32 coinIndex) { |
| 374 | sead::Vector3f direction = sead::Vector3f::zero; |
| 375 | |
| 376 | switch (coinIndex) { |
| 377 | case 0: |
| 378 | mCountUpDelay = 0; |
| 379 | direction = sead::Vector3f(0.0f, 0.0f, 220.0f); |
| 380 | break; |
| 381 | case 1: |
| 382 | mCountUpDelay = 5; |
| 383 | direction = sead::Vector3f(-120.0f, 0.0f, -200.0f); |
| 384 | break; |
| 385 | case 2: |
| 386 | mCountUpDelay = 10; |
| 387 | direction = sead::Vector3f(240.0f, 0.0f, 50.0f); |
| 388 | break; |
| 389 | case 3: |
| 390 | mCountUpDelay = 15; |
| 391 | direction = sead::Vector3f(-240.0f, 0.0f, 50.0f); |
| 392 | break; |
| 393 | case 4: |
| 394 | mCountUpDelay = 20; |
| 395 | direction = sead::Vector3f(120.0f, 0.0f, -200.0f); |
| 396 | break; |
| 397 | default: |
| 398 | break; |
| 399 | } |
| 400 | |
| 401 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 402 | al::rotateVectorQuat(&direction, al::getQuat(actor: this)); |
| 403 | al::setTrans(actor: this, trans: al::getTrans(actor: this) + direction); |
| 404 | mTimeLimit = -1; |
| 405 | appearCountUpCommon(velocity: &mBlowVelocity, delay: mCountUpDelay); |
| 406 | } |
| 407 | |
| 408 | void Coin::appearCountUp10(s32 coinIndex) { |
| 409 | sead::Vector3f direction = sead::Vector3f(200.0f, 200.0f, 200.0f); |
| 410 | al::rotateVectorDegreeY(&direction, coinIndex * 36.0f); |
| 411 | |
| 412 | // Weird optimization here. It's supposed to be a switch case. |
| 413 | if ((coinIndex == 5) || (coinIndex == 0)) |
| 414 | mCountUpDelay = coinIndex == 0 ? 0 : 20; |
| 415 | |
| 416 | else if ((coinIndex == 6) || (coinIndex == 1)) |
| 417 | mCountUpDelay = coinIndex == 1 ? 5 : 25; |
| 418 | |
| 419 | else if ((coinIndex == 7) || (coinIndex == 2)) |
| 420 | mCountUpDelay = coinIndex == 2 ? 10 : 30; |
| 421 | |
| 422 | else if ((coinIndex == 8) || (coinIndex == 3)) |
| 423 | mCountUpDelay = coinIndex == 3 ? 15 : 35; |
| 424 | |
| 425 | else if ((coinIndex == 9) || (coinIndex == 4)) |
| 426 | mCountUpDelay = coinIndex == 4 ? 20 : 40; |
| 427 | |
| 428 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 429 | al::rotateVectorQuat(&direction, al::getQuat(actor: this)); |
| 430 | al::getTransPtr(actor: this)->add(a: direction); |
| 431 | mTimeLimit = -1; |
| 432 | appearCountUpCommon(velocity: &mBlowVelocity, delay: mCountUpDelay); |
| 433 | } |
| 434 | |
| 435 | void Coin::appearCountUpFixPos10(s32 coinIndex) { |
| 436 | al::setNerve(user: this, nerve: &NrvCoin.CountUp); |
| 437 | appearCountUpReset(actor: this, forceKeeper: mExternalForceKeeper); |
| 438 | mTimeLimit = -1; |
| 439 | mCountUpDelay = coinIndex * 8; |
| 440 | appearCountUpCommon(velocity: &mBlowVelocity, delay: mCountUpDelay); |
| 441 | } |
| 442 | |
| 443 | void Coin::appearCoinRail() { |
| 444 | al::setNerve(user: this, nerve: &NrvCoin.WaitCoinRail); |
| 445 | appear(); |
| 446 | } |
| 447 | |
| 448 | void Coin::appearCoinChameleon(const sead::Vector3f& trans, const sead::Quatf& quat, |
| 449 | const sead::Vector3f& offset) { |
| 450 | al::setTrans(actor: this, trans); |
| 451 | al::setQuat(actor: this, quat); |
| 452 | mChameleonOffset = offset; |
| 453 | appear(); |
| 454 | al::setNerve(user: this, nerve: &NrvCoin.Wait); |
| 455 | } |
| 456 | |
| 457 | void Coin::appearLimitTime(s32 timeLimit) { |
| 458 | al::setNerve(user: this, nerve: &NrvCoin.AppearCoinLead); |
| 459 | al::invalidateClipping(actor: this); |
| 460 | appear(); |
| 461 | mTimeLimit = timeLimit; |
| 462 | } |
| 463 | |
| 464 | void Coin::appearBlow(const sead::Vector3f& velocity, s32 timeLimit) { |
| 465 | appear(); |
| 466 | al::invalidateClipping(actor: this); |
| 467 | al::startHitReaction(actor: this, name: "飛出し出現" ); |
| 468 | al::setVelocity(actor: this, vel: velocity); |
| 469 | mTimeLimit = timeLimit; |
| 470 | al::validateHitSensors(this); |
| 471 | al::showModelIfHide(actor: this); |
| 472 | |
| 473 | if (mMtxConnector != nullptr) |
| 474 | al::disconnectMtxConnector(mtxConnector: mMtxConnector); |
| 475 | |
| 476 | rotate(); |
| 477 | al::setNerve(user: this, nerve: &NrvCoin.PopUpNormal); |
| 478 | } |
| 479 | |
| 480 | void Coin::rotate() { |
| 481 | if (al::isNerve(user: this, nerve: &NrvCoin.CountUp) || al::isNerve(user: this, nerve: &NrvCoin.CountUpFive)) { |
| 482 | al::rotateQuatYDirDegree(actor: this, deg: 9.5f); |
| 483 | return; |
| 484 | } |
| 485 | |
| 486 | if (mMtxConnector != nullptr && al::isMtxConnectorConnecting(mtxConnector: mMtxConnector) && |
| 487 | (al::isNerve(user: this, nerve: &NrvCoin.Appear) || al::isNerve(user: this, nerve: &NrvCoin.WaitConnectMtx))) { |
| 488 | al::connectPoseQT(actor: this, mtxConnector: mMtxConnector, quat: mPoseQuat, trans: mPoseTrans); |
| 489 | al::getTransPtr(actor: this)->add(a: mChameleonOffset); |
| 490 | } else { |
| 491 | al::setQuat(actor: this, quat: mStartingQuat); |
| 492 | } |
| 493 | |
| 494 | al::rotateQuatYDirDegree(actor: this, deg: mRotateCalculator->getRotate()); |
| 495 | } |
| 496 | |
| 497 | void Coin::appearBlowUpCommon(s32 delayTime, f32 horizontalForce, f32 verticalForce, s32 coinCount, |
| 498 | s32 coinIndex) { |
| 499 | appear(); |
| 500 | al::invalidateClipping(actor: this); |
| 501 | mCountUpDelay = delayTime; |
| 502 | sead::Vector3f horizontalDirection = horizontalForce * sead::Vector3f::ez; |
| 503 | al::rotateVectorDegreeY(&horizontalDirection, (360.0 / coinCount) * coinIndex); |
| 504 | mBlowVelocity = verticalForce * sead::Vector3f::ey + horizontalDirection; |
| 505 | al::setVelocityZero(this); |
| 506 | if (mCountUpDelay == 0) { |
| 507 | appearBlow(velocity: mBlowVelocity, timeLimit: 480); |
| 508 | } else { |
| 509 | al::hideModelIfShow(actor: this); |
| 510 | al::setNerve(user: this, nerve: &BlowUpDelay); |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | void Coin::appearBlowUp(s32 coinIndex, s32 coinCount) { |
| 515 | appearBlowUpCommon(delayTime: coinIndex * 5, horizontalForce: 6.0f, verticalForce: 40.0f, coinCount, coinIndex); |
| 516 | } |
| 517 | |
| 518 | void Coin::appearBlowUpLittle(s32 coinIndex, s32 coinCount) { |
| 519 | appearBlowUpCommon(delayTime: coinIndex * 5, horizontalForce: 3.0f, verticalForce: 35.0f, coinCount, coinIndex); |
| 520 | } |
| 521 | |
| 522 | void Coin::appearFall(const sead::Vector3f& velocity, s32 timeLimit) { |
| 523 | appear(); |
| 524 | al::invalidateClipping(actor: this); |
| 525 | al::setVelocity(actor: this, vel: velocity); |
| 526 | mTimeLimit = timeLimit; |
| 527 | al::setNerve(user: this, nerve: &NrvCoin.PopUpNormal); |
| 528 | } |
| 529 | |
| 530 | void Coin::appearPlayerDead(const sead::Vector3f& position, const sead::Vector3f& direction, |
| 531 | bool isInWater, bool isTimeBalloon) { |
| 532 | al::resetPosition(actor: this, trans: position); |
| 533 | |
| 534 | if (isInWater) |
| 535 | al::setVelocity(actor: this, vel: 30.0f * sead::Vector3f::ey + direction * 15.0f); |
| 536 | else if (rs::isInLowGravityArea(actor: this)) |
| 537 | al::setVelocity(actor: this, vel: 30.0f * sead::Vector3f::ey + direction * 5.0f); |
| 538 | else |
| 539 | al::setVelocity(actor: this, vel: 40.0f * sead::Vector3f::ey + direction * 5.0f); |
| 540 | |
| 541 | al::onCollide(actor: this); |
| 542 | appear(); |
| 543 | al::invalidateClipping(actor: this); |
| 544 | al::startHitReaction(actor: this, name: "飛出し出現[死亡]" ); |
| 545 | mTimeLimit = -1; |
| 546 | |
| 547 | if (isTimeBalloon) |
| 548 | al::setNerve(user: this, nerve: &NrvCoin.PopUpPlayerDeadTimeBalloon); |
| 549 | else |
| 550 | al::setNerve(user: this, nerve: &NrvCoin.PopUpPlayerDead); |
| 551 | } |
| 552 | |
| 553 | void Coin::appearPlayerDeadReplace(const sead::Vector3f& position) { |
| 554 | al::setNerve(user: this, nerve: &NrvCoin.WaitPlayerDead); |
| 555 | al::resetPosition(actor: this, trans: position); |
| 556 | al::LiveActor::appear(); |
| 557 | } |
| 558 | |
| 559 | void Coin::setShadowDropLength(f32 shadowLength) { |
| 560 | al::setShadowMaskDropLength(actor: this, dropLength: shadowLength, maskName: "本体" ); |
| 561 | al::expandClippingRadiusByShadowLength(actor: this, nullptr, radius: shadowLength); |
| 562 | } |
| 563 | |
| 564 | void Coin::get() { |
| 565 | al::startHitReaction(actor: this, name: "取得" ); |
| 566 | al::setNerve(user: this, nerve: &NrvCoin.Got); |
| 567 | } |
| 568 | |
| 569 | bool Coin::isGot() const { |
| 570 | return al::isNerve(user: this, nerve: &NrvCoin.Got); |
| 571 | } |
| 572 | |
| 573 | bool Coin::isGotOrRotate() const { |
| 574 | if (al::isNerve(user: this, nerve: &NrvCoin.Got)) |
| 575 | return true; |
| 576 | if (al::isNerve(user: this, nerve: &NrvCoin.AppearCoinLead)) |
| 577 | return true; |
| 578 | return false; |
| 579 | } |
| 580 | |
| 581 | bool Coin::isWait() const { |
| 582 | if (al::isNerve(user: this, nerve: &NrvCoin.Wait)) |
| 583 | return true; |
| 584 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitCircle)) |
| 585 | return true; |
| 586 | return false; |
| 587 | } |
| 588 | |
| 589 | bool Coin::isAnyWaitOrAppear() const { |
| 590 | if (al::isNerve(user: this, nerve: &NrvCoin.Wait)) |
| 591 | return true; |
| 592 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitCircle)) |
| 593 | return true; |
| 594 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitCoinRail)) |
| 595 | return true; |
| 596 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitConnectMtx)) |
| 597 | return true; |
| 598 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitOnDemo)) |
| 599 | return true; |
| 600 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitOnDemoEnd)) |
| 601 | return true; |
| 602 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitPlayerDead)) |
| 603 | return true; |
| 604 | if (al::isNerve(user: this, nerve: &NrvCoin.PopUpNormal) && al::isGreaterEqualStep(user: this, step: 60)) |
| 605 | return true; |
| 606 | if (al::isNerve(user: this, nerve: &NrvCoin.AppearCoinLead)) |
| 607 | return true; |
| 608 | if (al::isNerve(user: this, nerve: &NrvCoin.Appear)) |
| 609 | return true; |
| 610 | return false; |
| 611 | } |
| 612 | |
| 613 | void Coin::exeAppear() { |
| 614 | f32 rotateOffset = al::calcNerveEaseOutValue(user: this, max: 54, start: -720.0f, end: 0.0f); |
| 615 | mRotateCalculator->setRotateOffset(rotateOffset); |
| 616 | rotate(); |
| 617 | |
| 618 | if (al::isNerve(user: this, nerve: &NrvCoin.Appear)) { |
| 619 | if (al::isFirstStep(user: this)) |
| 620 | al::startHitReaction(actor: this, name: "通常出現" ); |
| 621 | al::setNerveAtGreaterEqualStep(user: this, nerve: &NrvCoin.Wait, step: 54); |
| 622 | return; |
| 623 | } |
| 624 | |
| 625 | if (al::isNerve(user: this, nerve: &NrvCoin.AppearOnDemo)) |
| 626 | al::setNerveAtGreaterEqualStep(user: this, nerve: &NrvCoin.WaitOnDemo, step: 54); |
| 627 | } |
| 628 | |
| 629 | void Coin::exeAppearCoinLead() { |
| 630 | if (al::updateNerveState(user: this)) |
| 631 | al::setNerve(user: this, nerve: &NrvCoin.Wait); |
| 632 | } |
| 633 | |
| 634 | void Coin::() { |
| 635 | if (al::isFirstStep(user: this)) { |
| 636 | al::invalidateClipping(actor: this); |
| 637 | al::onCollide(actor: this); |
| 638 | al::validateHitSensors(this); |
| 639 | } |
| 640 | |
| 641 | rotate(); |
| 642 | |
| 643 | if (al::isInWater(this) || al::isInWaterArea(actor: this)) { |
| 644 | al::addVelocityToGravity(actor: this, force: 0.6f); |
| 645 | al::scaleVelocity(actor: this, factor: 0.95f); |
| 646 | } else { |
| 647 | if (rs::isInLowGravityArea(actor: this)) |
| 648 | al::addVelocityToGravity(actor: this, force: 1.0f); |
| 649 | else |
| 650 | al::addVelocityToGravity(actor: this, force: 1.5f); |
| 651 | al::scaleVelocity(actor: this, factor: 0.99f); |
| 652 | } |
| 653 | |
| 654 | if ((al::isCollidedFloorCode(this, "Poison" ) || al::isCollidedFloorCode(this, "DamageFire" ) || |
| 655 | al::isInDeathArea(actor: this)) && |
| 656 | al::getVelocity(actor: this).y < 0.0f) { |
| 657 | al::startHitReaction(actor: this, name: "消滅" ); |
| 658 | kill(); |
| 659 | return; |
| 660 | } |
| 661 | |
| 662 | if ((al::isNerve(user: this, nerve: &NrvCoin.PopUpPlayerDead) || |
| 663 | al::isNerve(user: this, nerve: &NrvCoin.PopUpPlayerDeadTimeBalloon)) && |
| 664 | al::isGreaterEqualStep(user: this, step: 5)) { |
| 665 | sead::Vector3f surfacePos = sead::Vector3f::zero; |
| 666 | sead::Vector3f surfaceNormal = sead::Vector3f::zero; |
| 667 | if (al::calcFindFireSurface(&surfacePos, &surfaceNormal, this, |
| 668 | al::getTrans(actor: this) + 50.0f * sead::Vector3f::ey, |
| 669 | sead::Vector3f::ey, 50.0f)) { |
| 670 | al::startHitReaction(actor: this, name: "消滅" ); |
| 671 | kill(); |
| 672 | return; |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | if (al::isNerve(user: this, nerve: &NrvCoin.PopUpPlayerDeadTimeBalloon) && |
| 677 | al::isGreaterEqualStep(user: this, step: 60)) { |
| 678 | al::startHitReaction(actor: this, name: "消滅" ); |
| 679 | kill(); |
| 680 | return; |
| 681 | } |
| 682 | |
| 683 | if (al::getVelocity(actor: this).y < 0.0f && al::isCollidedGround(this)) { |
| 684 | if (al::calcSpeed(actor: this) < 15.0f) { |
| 685 | if (mMtxConnector != nullptr) { |
| 686 | al::disconnectMtxConnector(mtxConnector: mMtxConnector); |
| 687 | mPoseTrans = al::getTrans(actor: this); |
| 688 | al::attachMtxConnectorToCollision(mtxConnector: mMtxConnector, actor: this, false); |
| 689 | } |
| 690 | al::offCollide(actor: this); |
| 691 | al::setVelocityZero(this); |
| 692 | al::setNerve(user: this, nerve: &NrvCoin.WaitConnectMtx); |
| 693 | return; |
| 694 | } |
| 695 | al::getVelocityPtr(actor: this)->y *= -0.75f; |
| 696 | al::startSeWithParam(this, "PgBound" , al::getVelocityPtr(actor: this)->y, "" ); |
| 697 | } |
| 698 | } |
| 699 | |
| 700 | void Coin::exeCountUpDelay() { |
| 701 | if (al::isGreaterEqualStep(user: this, step: mCountUpDelay)) { |
| 702 | al::setVelocity(actor: this, vel: mBlowVelocity); |
| 703 | al::setNerve(user: this, nerve: &NrvCoin.CountUp); |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | void Coin::exeCountUp() { |
| 708 | if (al::isFirstStep(user: this)) { |
| 709 | al::startAction(actor: this, actionName: "Got" ); |
| 710 | if (al::isNerve(user: this, nerve: &NrvCoin.CountUpFive)) |
| 711 | al::startHitReaction(actor: this, name: "自動取得5枚[開始]" ); |
| 712 | else |
| 713 | al::startHitReaction(actor: this, name: "自動取得[開始]" ); |
| 714 | |
| 715 | if (al::isNerve(user: this, nerve: &NrvCoin.CountUpFive)) |
| 716 | for (u32 i = 0; i < 5; i++) |
| 717 | GameDataFunction::addCoin(writer: this, count: 1); |
| 718 | else |
| 719 | GameDataFunction::addCoin(writer: this, count: 1); |
| 720 | al::showModelIfHide(actor: this); |
| 721 | } |
| 722 | |
| 723 | rotate(); |
| 724 | |
| 725 | sead::Vector3f direction = sead::Vector3f(0.0f, 0.0f, 0.0f); |
| 726 | al::calcQuatGravity(out: &direction, quat: al::getQuat(actor: this)); |
| 727 | al::addVelocityToDirection(actor: this, dir: direction, force: 0.6f); |
| 728 | al::scaleVelocity(actor: this, factor: 0.99f); |
| 729 | al::getVelocity(actor: this); |
| 730 | al::getQuat(actor: this); |
| 731 | |
| 732 | if (al::isGreaterEqualStep(user: this, step: 32)) { |
| 733 | al::startHitReaction(actor: this, name: "自動取得[終了]" ); |
| 734 | al::setNerve(user: this, nerve: &NrvCoin.GotNoCoin); |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | void Coin::exeWait() { |
| 739 | if (al::isFirstStep(user: this)) { |
| 740 | if (mTimeLimit == -1 && !al::isNerve(user: this, nerve: &NrvCoin.WaitCoinRail) && |
| 741 | !al::isNerve(user: this, nerve: &NrvCoin.WaitCircle)) |
| 742 | al::validateClipping(actor: this); |
| 743 | al::startAction(actor: this, actionName: "Wait" ); |
| 744 | } |
| 745 | |
| 746 | if (al::isNerve(user: this, nerve: &NrvCoin.WaitOnDemo)) { |
| 747 | mRotateCalculator->increaseObjCountOffset(); |
| 748 | rotate(); |
| 749 | if (!rs::isActiveDemo(this)) |
| 750 | al::setNerve(user: this, nerve: &NrvCoin.WaitOnDemoEnd); |
| 751 | return; |
| 752 | } |
| 753 | |
| 754 | rotate(); |
| 755 | } |
| 756 | |
| 757 | void Coin::exeWaitOnDemoEnd() { |
| 758 | rotate(); |
| 759 | } |
| 760 | |
| 761 | void Coin::exeGot() { |
| 762 | if (al::isFirstStep(user: this)) { |
| 763 | al::startAction(actor: this, actionName: "Got" ); |
| 764 | if (!al::isNerve(user: this, nerve: &NrvCoin.GotNoCoin)) |
| 765 | GameDataFunction::addCoin(writer: this, count: 1); |
| 766 | |
| 767 | al::onStageSwitch(user: this, linkName: "SwitchGetOn" ); |
| 768 | alPadRumbleFunction::startPadRumble(actor: this, name: "コッ(最小)" , near: 1000.0f, far: 5000.0f); |
| 769 | if (mWaterSurfaceShadow != nullptr) |
| 770 | mWaterSurfaceShadow->disappearShadow(); |
| 771 | } |
| 772 | |
| 773 | if (al::isActionEnd(actor: this)) { |
| 774 | if (mSaveObjInfo != nullptr) |
| 775 | rs::onSaveObjInfo(saveObjInfo: mSaveObjInfo); |
| 776 | kill(); |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | void Coin::exeBlowUpDelay() { |
| 781 | if (al::isGreaterEqualStep(user: this, step: mCountUpDelay)) { |
| 782 | al::showModelIfHide(actor: this); |
| 783 | appearBlow(velocity: mBlowVelocity, timeLimit: 480); |
| 784 | } |
| 785 | } |
| 786 | |
| 787 | const sead::Vector3f& CoinFunction::getAppearAboveVelocity() { |
| 788 | return sAppearAboveVelocity; |
| 789 | } |
| 790 | |
| 791 | void CoinFunction::appearCoinBlowVeryLittle(Coin* coin, const sead::Vector3f& direction) { |
| 792 | bool isInWater = al::isInWater(coin) || al::isInWaterArea(actor: coin); |
| 793 | |
| 794 | sead::Vector3f velocity = (isInWater ? 12.0f : 3.5f) * direction; |
| 795 | sead::Vector3f verticalVelocity = (isInWater ? 35.0f : 30.0f) * sead::Vector3f::ey; |
| 796 | velocity += verticalVelocity; |
| 797 | |
| 798 | coin->appearBlow(velocity, timeLimit: 600); |
| 799 | } |
| 800 | |
| 801 | void CoinFunction::appearCoinBlowLittle(Coin* coin, const sead::Vector3f& direction) { |
| 802 | bool isInWater = al::isInWater(coin) || al::isInWaterArea(actor: coin); |
| 803 | |
| 804 | sead::Vector3f velocity = (isInWater ? 15.0f : 4.5f) * direction; |
| 805 | sead::Vector3f verticalVelocity = (isInWater ? 40.0f : 30.0f) * sead::Vector3f::ey; |
| 806 | velocity += verticalVelocity; |
| 807 | |
| 808 | coin->appearBlow(velocity, timeLimit: 600); |
| 809 | } |
| 810 | |
| 811 | void CoinFunction::appearCoinBlowMiddle(Coin* coin, const sead::Vector3f& direction) { |
| 812 | bool isInWater = al::isInWater(coin) || al::isInWaterArea(actor: coin); |
| 813 | |
| 814 | sead::Vector3f velocity = (isInWater ? 18.0f : 6.0f) * direction; |
| 815 | sead::Vector3f verticalVelocity = (isInWater ? 45.0f : 35.0f) * sead::Vector3f::ey; |
| 816 | velocity += verticalVelocity; |
| 817 | |
| 818 | coin->appearBlow(velocity, timeLimit: 600); |
| 819 | } |
| 820 | |
| 821 | void CoinFunction::appearCoinBlowLarge(Coin* coin, const sead::Vector3f& direction) { |
| 822 | bool isInWater = al::isInWater(coin) || al::isInWaterArea(actor: coin); |
| 823 | |
| 824 | sead::Vector3f velocity = (isInWater ? 23.0f : 7.5f) * direction; |
| 825 | sead::Vector3f verticalVelocity = (isInWater ? 50.0f : 40.0f) * sead::Vector3f::ey; |
| 826 | velocity += verticalVelocity; |
| 827 | |
| 828 | coin->appearBlow(velocity, timeLimit: 600); |
| 829 | } |
| 830 | |
| 831 | void CoinFunction::appearCoinBlowLargeEnemy(Coin* coin, const sead::Vector3f& direction) { |
| 832 | sead::Vector3f velocity = 3.5f * direction; |
| 833 | sead::Vector3f verticalVelocity = 30.0f * sead::Vector3f::ey; |
| 834 | velocity += verticalVelocity; |
| 835 | |
| 836 | coin->appearBlow(velocity, timeLimit: 1200); |
| 837 | } |
| 838 | |
| 839 | void CoinFunction::appearCoinBlowBoss(Coin* coin, const sead::Vector3f& direction) { |
| 840 | sead::Vector3f velocity = 8.0f * direction; |
| 841 | sead::Vector3f verticalVelocity = 40.0f * sead::Vector3f::ey; |
| 842 | velocity += verticalVelocity; |
| 843 | |
| 844 | coin->appearBlow(velocity, timeLimit: 1200); |
| 845 | } |
| 846 | |
| 847 | void CoinFunction::appearCoinBlowGk(Coin* coin, const sead::Vector3f& direction) { |
| 848 | sead::Vector3f velocity = 7.0f * direction; |
| 849 | sead::Vector3f verticalVelocity = 40.0f * sead::Vector3f::ey; |
| 850 | velocity += verticalVelocity; |
| 851 | |
| 852 | coin->appearBlow(velocity, timeLimit: 1200); |
| 853 | } |
| 854 | |