From b8858e40378e34c474c2ae8b50a3b0471e471bcc Mon Sep 17 00:00:00 2001 From: GRAnimated Date: Sun, 22 Mar 2026 21:11:37 -0400 Subject: [PATCH] Boss: Implement `BossRaidWheel` (#925) Co-authored-by: Claude Sonnet 4.6 --- data/file_list.yml | 22 ++--- src/Boss/BossRaid/BossRaidWheel.cpp | 122 ++++++++++++++++++++++++++++ src/Boss/BossRaid/BossRaidWheel.h | 31 +++++++ 3 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 src/Boss/BossRaid/BossRaidWheel.cpp create mode 100644 src/Boss/BossRaid/BossRaidWheel.h diff --git a/data/file_list.yml b/data/file_list.yml index 656ba621..65d72b94 100644 --- a/data/file_list.yml +++ b/data/file_list.yml @@ -8678,48 +8678,48 @@ Boss/BossRaid/BossRaidWheel.o: - offset: 0x045e74 size: 164 label: _ZN13BossRaidWheelC2EPKc - status: NotDecompiled + status: Matching - offset: 0x045f18 size: 176 label: _ZN13BossRaidWheelC1EPKc - status: NotDecompiled + status: Matching - offset: 0x045fc8 size: 220 label: _ZN13BossRaidWheel4initERKN2al13ActorInitInfoE - status: NotDecompiled + status: Matching - offset: 0x0460a4 size: 100 label: _ZN13BossRaidWheel12attackSensorEPN2al9HitSensorES2_ - status: NotDecompiled + status: Matching - offset: 0x046108 size: 8 label: _ZN13BossRaidWheel10receiveMsgEPKN2al9SensorMsgEPNS0_9HitSensorES5_ - status: NotDecompiled + status: Matching - offset: 0x046110 size: 68 label: _ZN13BossRaidWheel7controlEv - status: NotDecompiled + status: Matching - offset: 0x046154 size: 96 label: _ZN13BossRaidWheel7exeLandEv - status: NotDecompiled + status: Matching - offset: 0x0461b4 size: 476 label: _ZN13BossRaidWheel6exeRunEv - status: NotDecompiled + status: Matching - offset: 0x046390 size: 164 label: _ZN13BossRaidWheel10shotGroundERKN4sead7Vector3IfEES4_f - status: NotDecompiled + status: Matching - offset: 0x046434 size: 100 label: _ZNK12_GLOBAL__N_120BossRaidWheelNrvLand7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true - offset: 0x046498 size: 8 label: _ZNK12_GLOBAL__N_119BossRaidWheelNrvRun7executeEPN2al11NerveKeeperE - status: NotDecompiled + status: Matching guess: true Boss/BossUtil/BossStateChasePlayer.o: '.text': diff --git a/src/Boss/BossRaid/BossRaidWheel.cpp b/src/Boss/BossRaid/BossRaidWheel.cpp new file mode 100644 index 00000000..ef0c548e --- /dev/null +++ b/src/Boss/BossRaid/BossRaidWheel.cpp @@ -0,0 +1,122 @@ +#include "Boss/BossRaid/BossRaidWheel.h" + +#include "Library/Effect/EffectSystemInfo.h" +#include "Library/Joint/JointControllerKeeper.h" +#include "Library/LiveActor/ActorActionFunction.h" +#include "Library/LiveActor/ActorCollisionFunction.h" +#include "Library/LiveActor/ActorInitUtil.h" +#include "Library/LiveActor/ActorModelFunction.h" +#include "Library/LiveActor/ActorMovementFunction.h" +#include "Library/LiveActor/ActorPoseUtil.h" +#include "Library/LiveActor/ActorSensorUtil.h" +#include "Library/Math/MathUtil.h" +#include "Library/Math/ParabolicPath.h" +#include "Library/Nerve/NerveSetupUtil.h" +#include "Library/Nerve/NerveUtil.h" +#include "Library/Player/PlayerUtil.h" + +#include "Util/SensorMsgFunction.h" + +namespace { +NERVE_IMPL(BossRaidWheel, Land); +NERVE_IMPL(BossRaidWheel, Run); +NERVES_MAKE_NOSTRUCT(BossRaidWheel, Land, Run); +} // namespace + +BossRaidWheel::BossRaidWheel(const char* name) : al::LiveActor(name) {} + +void BossRaidWheel::init(const al::ActorInitInfo& info) { + al::initActorWithArchiveName(this, info, "BossRaidWheel", nullptr); + al::initNerve(this, &Land, 0); + al::initJointControllerKeeper(this, 2); + al::initJointLocalXRotator(this, &mRotate, "Rotate"); + al::initJointLocalZRotator(this, &mTilt, "JointRoot"); + al::setEffectFollowPosPtr(this, "FallSign", &mFallSignPos); + al::createAndSetColliderSpecialAndIgnoreOptionalPurpose(this, "BossRaidWheel", + "NoBossRaidWheel"); + mPath = new al::ParabolicPath(); + makeActorDead(); +} + +void BossRaidWheel::attackSensor(al::HitSensor* self, al::HitSensor* other) { + sead::Vector3f sideDir; + al::calcJointSideDir(&sideDir, this, "Rotate"); + if (al::isHitPlaneSensor(other, self, sideDir, 5.0f)) + al::sendMsgEnemyAttack(other, self); +} + +bool BossRaidWheel::receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, + al::HitSensor* self) { + if (rs::isMsgPlayerDisregardHomingAttack(msg)) + return true; + return false; +} + +void BossRaidWheel::control() { + mRotate = al::modf(mRotate + mRotateSpeed + 360.0f, 360.0f) + 0.0f; +} + +void BossRaidWheel::exeLand() { + if (al::isFirstStep(this)) { + al::startAction(this, "Appear"); + mRotateSpeed = 6.0f; + } + if (al::isActionEnd(this)) + al::setNerve(this, &Run); +} + +void BossRaidWheel::exeRun() { + if (al::isFirstStep(this)) + al::startAction(this, "Run"); + + f32 turnDeg; + if (al::isLessStep(this, 120 - mRunTurnOffset)) + turnDeg = al::calcNerveValue(this, 30 - mRunTurnOffset, 90 - mRunTurnOffset, 0.0f, 1.1f); + else + turnDeg = al::calcNerveValue(this, 120 - mRunTurnOffset, 180 - mRunTurnOffset, 1.1f, 0.0f); + + sead::Vector3f frontDir; + al::calcFrontDir(&frontDir, this); + + sead::Vector3f targetPos = al::getPlayerPos(this, 0); + al::turnQuatFrontToPosDegreeH(this, targetPos, turnDeg); + + sead::Vector3f newFrontDir; + al::calcFrontDir(&newFrontDir, this); + + f32 angle = al::calcAngleOnPlaneDegree(frontDir, newFrontDir, sead::Vector3f::ey); + mTilt = al::lerpValue(mTilt, angle * -16.0f, 0.05f); + + al::addVelocityToFront(this, 3.5f); + al::addVelocityToGravity(this, 1.0f); + al::scaleVelocityHV(this, 0.91f, 0.995f); + al::reboundVelocityFromCollision(this, 0.0f, 0.0f, 1.0f); + + if (al::isOnGroundNoVelocity(this, 10)) + al::tryStartActionIfNotPlaying(this, "Run"); + else + al::tryStartActionIfNotPlaying(this, "Fall"); + + if (al::isCollidedWall(this)) { + al::startHitReaction(this, "破壊"); + makeActorDead(); + } + + if (al::isGreaterEqualStep(this, 300)) + makeActorDead(); +} + +void BossRaidWheel::shotGround(const sead::Vector3f& pos, const sead::Vector3f& dir, + f32 earlyTurn) { + mRotate = 0.0f; + mTilt = 0.0f; + mRotateSpeed = 5.0f; + mRunTurnOffset = (s32)al::lerpValue(30.0f, 0.0f, earlyTurn); + + sead::Quatf quat; + al::makeQuatUpFront(&quat, sead::Vector3f::ey, dir); + al::resetQuatPosition(this, quat, pos); + al::setVelocityZero(this); + al::setNerve(this, &Land); + makeActorAlive(); +} diff --git a/src/Boss/BossRaid/BossRaidWheel.h b/src/Boss/BossRaid/BossRaidWheel.h new file mode 100644 index 00000000..c01111fe --- /dev/null +++ b/src/Boss/BossRaid/BossRaidWheel.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Library/LiveActor/LiveActor.h" + +namespace al { +class ParabolicPath; +} + +class BossRaidWheel : public al::LiveActor { +public: + BossRaidWheel(const char* name); + void init(const al::ActorInitInfo& info) override; + void attackSensor(al::HitSensor* self, al::HitSensor* other) override; + bool receiveMsg(const al::SensorMsg* msg, al::HitSensor* other, al::HitSensor* self) override; + void control() override; + + void exeLand(); + void exeRun(); + + void shotGround(const sead::Vector3f& pos, const sead::Vector3f& dir, f32 earlyTurn); + +private: + al::ParabolicPath* mPath = nullptr; + sead::Vector3f mFallSignPos = sead::Vector3f::zero; + s32 mRunTurnOffset = 0; + f32 mRotateSpeed = 5.0f; + f32 mRotate = 0.0f; + f32 mTilt = 0.0f; +}; + +static_assert(sizeof(BossRaidWheel) == 0x130);