From e374cba7f91d4c2938cf77a925205d00b663fb97 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Mon, 31 Aug 2026 01:11:43 -0400 Subject: [PATCH 01/23] Decompile Boss Pakkun class structure --- include/Enemy/BossPakkun.hpp | 267 +++++++++++++++++++++++++++---- src/Enemy/bosspakkun.cpp | 294 +++++++++++++++++++++++++++++++++++ 2 files changed, 535 insertions(+), 26 deletions(-) diff --git a/include/Enemy/BossPakkun.hpp b/include/Enemy/BossPakkun.hpp index c1f8125bd..ef13aa122 100644 --- a/include/Enemy/BossPakkun.hpp +++ b/include/Enemy/BossPakkun.hpp @@ -1,36 +1,251 @@ #ifndef ENEMY_BOSS_PAKKUN_HPP #define ENEMY_BOSS_PAKKUN_HPP +#include +#include +#include +#include #include -class TLiveActor; +class MActor; +class TAreaCylinderManager; +class TWaterEmitInfo; + +class TBossPakkun; +class TBossPakkunMtxCalc; +class TBPHeadHit; +class TBPNavel; +class TBPPolDrop; +class TBPTornado; +class TBPVomit; + +class TBossPakkunParams : public TSpineEnemyParams { +public: + TBossPakkunParams(const char*); + +public: + /* 0x0A8 */ TParamRT mSLWaitFrameStg0; + /* 0x0BC */ TParamRT mSLWaterMarkLimit; + /* 0x0D0 */ TParamRT mSLSwingLength; + /* 0x0E4 */ TParamRT mSLPollBallStampScale; + /* 0x0F8 */ TParamRT mSLTumbleTime; + /* 0x10C */ TParamRT mSLAnmBlendTime0; + /* 0x120 */ TParamRT mSLFlySpeed; + /* 0x134 */ TParamRT mSLPivotSpeed; + /* 0x148 */ TParamRT mSLPivotSpeedAware; + /* 0x15C */ TParamRT mSLVomitAnmRate; + /* 0x170 */ TParamRT mSLHeadHomingLimit; + /* 0x184 */ TParamRT mSLDamageAngle; + /* 0x198 */ TParamRT mSLTornadoProp; + /* 0x1AC */ TParamRT mSLTornadoSpeed; + /* 0x1C0 */ TParamRT mSLTornadoRollSpeed; + /* 0x1D4 */ TParamRT mSLTornadoMoveInit; + /* 0x1E8 */ TParamRT mSLTornadoMoveInc; + /* 0x1FC */ TParamRT mSLTornadoMoveLimit; + /* 0x210 */ TParamRT mSLWaterHitTimer; + /* 0x224 */ TParamRT mSLHoverTimer; + /* 0x238 */ TParamRT mSLPollBallRange; + /* 0x24C */ TParamRT mSLPollBallSpeed; + /* 0x260 */ TParamRT mSLPollBallFront; +}; + +class TBossPakkunMtxCalc : public M3UMtxCalcSIAnmBlendQuat { +public: + TBossPakkunMtxCalc(TBossPakkun*); + + virtual void calc(u16); + + void setAnm(int); + void joinAnm(int); + void calcHeadDir(u16); + void calcBellyScale(u16); + +public: + /* 0x64 */ TBossPakkun* mOwner; +}; + +class TBossPakkunManager : public TEnemyManager { +public: + TBossPakkunManager(const char*, int); + + virtual void load(JSUMemoryInputStream&); + virtual void createModelData(); + + void initJParticle(); + +public: + /* 0x54 */ int unk54; +}; + +class TBossPakkun : public TSpineEnemy { +public: + TBossPakkun(const char* name = "ボスパックン"); + + virtual void perform(u32, JDrama::TGraphics*); + virtual BOOL receiveMessage(THitActor*, u32); + virtual void init(TLiveManager*); + virtual void setGroundCollision(); + virtual void kill(); + virtual const char** getBasNameTable() const; + + void flyToCurPathNode(f32, f32); + void changeBck(int); + void killSmallEnemies(); + void launchTornado(); + void launchPolDrop(); + void gotTrampleDamage(); + void gotHipDropDamage(); + void gotWaterDamage(); + void gotFlyingDamage(); + BOOL inArea(const JGeometry::TVec3&); + void resetWaterMark(); + void startTornadoBlur(); + void ignoreWaterCheck(); + BOOL is2ndFightNow() const; + void showMessage(u32); + void rumblePad(int, const JGeometry::TVec3&); + void startBGM(); + void checkMarioRiding(); + + TBossPakkunParams* getBossPakkunParams() const + { + return static_cast(getSaveParam()); + } + +public: + /* 0x150 */ TBossPakkunMtxCalc* mMtxCalc; + /* 0x154 */ f32 unk154; + /* 0x158 */ TBPPolDrop* mPolDrop; + /* 0x15C */ TBPVomit* mVomit; + /* 0x160 */ TBPTornado* mTornado; + /* 0x164 */ TBPHeadHit* mHeadHit; + /* 0x168 */ TBPNavel* mNavel; + /* 0x16C */ s8 unk16C; + /* 0x170 */ s32 unk170; + /* 0x174 */ s32 unk174; + /* 0x178 */ s32 unk178; + /* 0x17C */ u8 unk17C; + /* 0x180 */ MActor* unk180; + /* 0x184 */ f32 unk184; + /* 0x188 */ TAreaCylinderManager* unk188; + /* 0x18C */ TWaterEmitInfo* unk18C; + /* 0x190 */ u8 unk190; + /* 0x194 */ JGeometry::TVec3 unk194; + /* 0x1A0 */ JGeometry::TVec3 unk1A0; + /* 0x1AC */ JGeometry::TVec3 unk1AC; + /* 0x1B8 */ s32 unk1B8; + /* 0x1BC */ u8 unk1BC; + /* 0x1C0 */ s32 unk1C0; + /* 0x1C4 */ u8 unk1C4; + /* 0x1C8 */ f32 unk1C8; + /* 0x1CC */ u8 unk1CC; +}; + +class TBPNavel : public THitActor { +public: + TBPNavel(TBossPakkun*, const char*); + + virtual void perform(u32, JDrama::TGraphics*); + virtual BOOL receiveMessage(THitActor*, u32); + +public: + /* 0x68 */ TBossPakkun* mOwner; +}; + +class TBPHeadHit : public THitActor { +public: + TBPHeadHit(TBossPakkun*, const char*); + + virtual void perform(u32, JDrama::TGraphics*); + virtual BOOL receiveMessage(THitActor*, u32); + + void throwActor(THitActor*); + +public: + /* 0x68 */ TBossPakkun* mOwner; +}; + +class TBPTornado : public THitActor { +public: + TBPTornado(TBossPakkun*, const char* name = ""); + + virtual void perform(u32, JDrama::TGraphics*); + + void launch(const JGeometry::TVec3&); + void vanish(); + +public: + /* 0x68 */ TBossPakkun* mOwner; + /* 0x6C */ MActor* mActor; + /* 0x70 */ JGeometry::TVec3 unk70; + /* 0x7C */ JGeometry::TVec3 unk7C; + /* 0x88 */ JGeometry::TVec3 unk88; + /* 0x94 */ f32 unk94; + /* 0x98 */ s32 unk98; +}; + +class TBPVomit : public JDrama::TViewObj { +public: + TBPVomit(TBossPakkun*, const char* name = ""); + + virtual void perform(u32, JDrama::TGraphics*); + + void vomitFinished(); + void vomit(); + +public: + /* 0x10 */ TBossPakkun* mOwner; + /* 0x14 */ MActor* unk14; + /* 0x18 */ MActor* unk18; +}; + +class TBPPolDrop : public THitActor { +public: + TBPPolDrop(TBossPakkun*, const char* name = ""); + + virtual void perform(u32, JDrama::TGraphics*); + + void launch(const JGeometry::TVec3&, const JGeometry::TVec3&); + void move(); + void drop(); + +public: + /* 0x68 */ TBossPakkun* mOwner; + /* 0x6C */ JGeometry::TVec3 unk6C; + /* 0x78 */ MActor* unk78; + /* 0x7C */ MActor* unk7C; + /* 0x80 */ s32 unk80; + /* 0x84 */ s32 unk84; + /* 0x88 */ f32 unk88; +}; -DECLARE_NERVE(TNerveBPWait, TLiveActor); -DECLARE_NERVE(TNerveBPCannon, TLiveActor); -DECLARE_NERVE(TNerveBPVomit, TLiveActor); -DECLARE_NERVE(TNerveBPTornado, TLiveActor); -DECLARE_NERVE(TNerveBPPivot, TLiveActor); -DECLARE_NERVE(TNerveBPSwallow, TLiveActor); -DECLARE_NERVE(TNerveBPTumbleIn, TLiveActor); -DECLARE_NERVE(TNerveBPTumble, TLiveActor); -DECLARE_NERVE(TNerveBPTumbleOut, TLiveActor); -DECLARE_NERVE(TNerveBPGetUp, TLiveActor); -DECLARE_NERVE(TNerveBPSwing, TLiveActor); -DECLARE_NERVE(TNerveBPStompReact, TLiveActor); -DECLARE_NERVE(TNerveBPJumpReact, TLiveActor); -DECLARE_NERVE(TNerveBPPreDie, TLiveActor); -DECLARE_NERVE(TNerveBPDie, TLiveActor); -DECLARE_NERVE(TNerveBPTakeOff, TLiveActor); -DECLARE_NERVE(TNerveBPFly, TLiveActor); -DECLARE_NERVE(TNerveBPTouchDown, TLiveActor); -DECLARE_NERVE(TNerveBPFlyCannon, TLiveActor); -DECLARE_NERVE(TNerveBPFlyPivot, TLiveActor); -DECLARE_NERVE(TNerveBPHover, TLiveActor); -DECLARE_NERVE(TNerveBPFall, TLiveActor); -DECLARE_NERVE(TNerveBPSleep, TLiveActor); -DECLARE_NERVE(TNerveBPBreakSleep, TLiveActor); -DECLARE_NERVE(TNerveBPWaitL, TLiveActor); DECLARE_NERVE(TNerveBPCannonL, TLiveActor); +DECLARE_NERVE(TNerveBPWaitL, TLiveActor); +DECLARE_NERVE(TNerveBPBreakSleep, TLiveActor); +DECLARE_NERVE(TNerveBPSleep, TLiveActor); +DECLARE_NERVE(TNerveBPFall, TLiveActor); +DECLARE_NERVE(TNerveBPHover, TLiveActor); +DECLARE_NERVE(TNerveBPFlyPivot, TLiveActor); +DECLARE_NERVE(TNerveBPFlyCannon, TLiveActor); +DECLARE_NERVE(TNerveBPTouchDown, TLiveActor); +DECLARE_NERVE(TNerveBPFly, TLiveActor); +DECLARE_NERVE(TNerveBPTakeOff, TLiveActor); +DECLARE_NERVE(TNerveBPDie, TLiveActor); +DECLARE_NERVE(TNerveBPPreDie, TLiveActor); +DECLARE_NERVE(TNerveBPJumpReact, TLiveActor); +DECLARE_NERVE(TNerveBPStompReact, TLiveActor); +DECLARE_NERVE(TNerveBPSwing, TLiveActor); +DECLARE_NERVE(TNerveBPGetUp, TLiveActor); +DECLARE_NERVE(TNerveBPTumbleOut, TLiveActor); +DECLARE_NERVE(TNerveBPTumble, TLiveActor); +DECLARE_NERVE(TNerveBPTumbleIn, TLiveActor); +DECLARE_NERVE(TNerveBPSwallow, TLiveActor); +DECLARE_NERVE(TNerveBPPivot, TLiveActor); +DECLARE_NERVE(TNerveBPTornado, TLiveActor); +DECLARE_NERVE(TNerveBPVomit, TLiveActor); +DECLARE_NERVE(TNerveBPCannon, TLiveActor); +DECLARE_NERVE(TNerveBPWait, TLiveActor); + DECLARE_NERVE(TNervePakkunGenerate, TLiveActor); DECLARE_NERVE(TNervePakkunStay, TLiveActor); DECLARE_NERVE(TNervePakkunAppear, TLiveActor); diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 8b1378917..78636b722 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -1 +1,295 @@ +#include +static const char* bosspakkun_bastable[] = { + nullptr, + nullptr, + "/scene/bosspakkun/bas/bosspaku_ball_end.bas", + "/scene/bosspakkun/bas/bosspaku_down.bas", + "/scene/bosspakkun/bas/bosspaku_down_end.bas", + nullptr, + "/scene/bosspakkun/bas/bosspaku_down_loop.bas", + nullptr, + "/scene/bosspakkun/bas/bosspaku_fall_end.bas", + nullptr, + "/scene/bosspakkun/bas/bosspaku_fall_start.bas", + "/scene/bosspakkun/bas/bosspaku_fly.bas", + "/scene/bosspakkun/bas/bosspaku_fly_pollut.bas", + "/scene/bosspakkun/bas/bosspaku_fly_start.bas", + "/scene/bosspakkun/bas/bosspaku_getup.bas", + "/scene/bosspakkun/bas/bosspaku_head.bas", + "/scene/bosspakkun/bas/bosspaku_hovering.bas", + "/scene/bosspakkun/bas/bosspaku_jump_reaction.bas", + "/scene/bosspakkun/bas/bosspaku_land.bas", + "/scene/bosspakkun/bas/bosspaku_panpan.bas", + "/scene/bosspakkun/bas/bosspaku_pollut_end.bas", + "/scene/bosspakkun/bas/bosspaku_pollut_start.bas", + "/scene/bosspakkun/bas/bosspaku_return.bas", + "/scene/bosspakkun/bas/bosspaku_sleep.bas", + "/scene/bosspakkun/bas/bosspaku_tornado.bas", + nullptr, + "/scene/bosspakkun/bas/bosspaku_water_hit.bas", + nullptr, + nullptr, + nullptr, +}; + +TBossPakkunParams::TBossPakkunParams(const char* path) + : TSpineEnemyParams(path) + , PARAM_INIT(mSLWaitFrameStg0, 400) + , PARAM_INIT(mSLWaterMarkLimit, 600) + , PARAM_INIT(mSLSwingLength, 600.0f) + , PARAM_INIT(mSLPollBallStampScale, 1.0f) + , PARAM_INIT(mSLTumbleTime, 2400) + , PARAM_INIT(mSLAnmBlendTime0, 60) + , PARAM_INIT(mSLFlySpeed, 5.0f) + , PARAM_INIT(mSLPivotSpeed, 0.7f) + , PARAM_INIT(mSLPivotSpeedAware, 1.5f) + , PARAM_INIT(mSLVomitAnmRate, 0.6f) + , PARAM_INIT(mSLHeadHomingLimit, 30.0f) + , PARAM_INIT(mSLDamageAngle, 180.0f) + , PARAM_INIT(mSLTornadoProp, 0.4f) + , PARAM_INIT(mSLTornadoSpeed, 2.0f) + , PARAM_INIT(mSLTornadoRollSpeed, 0.06f) + , PARAM_INIT(mSLTornadoMoveInit, 10000.0f) + , PARAM_INIT(mSLTornadoMoveInc, 1.0f) + , PARAM_INIT(mSLTornadoMoveLimit, 10720.0f) + , PARAM_INIT(mSLWaterHitTimer, 20) + , PARAM_INIT(mSLHoverTimer, 1200) + , PARAM_INIT(mSLPollBallRange, 10000.0f) + , PARAM_INIT(mSLPollBallSpeed, 20.0f) + , PARAM_INIT(mSLPollBallFront, 1000.0f) +{ + TParams::load(mPrmPath); +} + +TBPPolDrop::TBPPolDrop(TBossPakkun* owner, const char* name) + : THitActor(name) + , mOwner(owner) + , unk78(nullptr) + , unk7C(nullptr) + , unk80(0) + , unk84(0) + , unk88(0.0f) +{ +} + +void TBPPolDrop::drop() { } + +void TBPPolDrop::move() { } + +void TBPPolDrop::launch(const JGeometry::TVec3&, + const JGeometry::TVec3&) +{ +} + +void TBPPolDrop::perform(u32, JDrama::TGraphics*) { } + +TBPVomit::TBPVomit(TBossPakkun* owner, const char* name) + : JDrama::TViewObj(name) + , mOwner(owner) + , unk14(nullptr) + , unk18(nullptr) +{ +} + +void TBPVomit::vomit() { } + +void TBPVomit::vomitFinished() { } + +void TBPVomit::perform(u32, JDrama::TGraphics*) { } + +TBPTornado::TBPTornado(TBossPakkun* owner, const char* name) + : THitActor(name) + , mOwner(owner) + , mActor(nullptr) + , unk94(0.0f) + , unk98(0) +{ +} + +void TBPTornado::vanish() { } + +void TBPTornado::perform(u32, JDrama::TGraphics*) { } + +void TBPTornado::launch(const JGeometry::TVec3&) { } + +TBPHeadHit::TBPHeadHit(TBossPakkun* owner, const char* name) + : THitActor(name) + , mOwner(owner) +{ +} + +BOOL TBPHeadHit::receiveMessage(THitActor*, u32) { return false; } + +void TBPHeadHit::throwActor(THitActor*) { } + +void TBPHeadHit::perform(u32, JDrama::TGraphics*) { } + +TBPNavel::TBPNavel(TBossPakkun* owner, const char* name) + : THitActor(name) + , mOwner(owner) +{ +} + +BOOL TBPNavel::receiveMessage(THitActor*, u32) { return false; } + +void TBPNavel::perform(u32, JDrama::TGraphics*) { } + +TBossPakkunMtxCalc::TBossPakkunMtxCalc(TBossPakkun* owner) + : M3UMtxCalcSIAnmBlendQuat(false) + , mOwner(owner) +{ +} + +void TBossPakkunMtxCalc::calcBellyScale(u16) { } + +void TBossPakkunMtxCalc::calcHeadDir(u16) { } + +void TBossPakkunMtxCalc::joinAnm(int) { } + +void TBossPakkunMtxCalc::setAnm(int) { } + +void TBossPakkunMtxCalc::calc(u16) { } + +TBossPakkun::TBossPakkun(const char* name) + : TSpineEnemy(name) + , mMtxCalc(nullptr) + , unk154(0.0f) + , mPolDrop(nullptr) + , mVomit(nullptr) + , mTornado(nullptr) + , mHeadHit(nullptr) + , mNavel(nullptr) + , unk16C(0) + , unk170(0) + , unk174(0) + , unk178(0) + , unk17C(0) + , unk180(nullptr) + , unk184(0.0f) + , unk188(nullptr) + , unk18C(nullptr) + , unk190(0) + , unk1B8(0) + , unk1BC(0) + , unk1C0(0) + , unk1C4(0) + , unk1C8(0.0f) + , unk1CC(0) +{ +} + +void TBossPakkun::init(TLiveManager*) { } + +void TBossPakkun::checkMarioRiding() { } + +void TBossPakkun::startBGM() { } + +void TBossPakkun::rumblePad(int, const JGeometry::TVec3&) { } + +void TBossPakkun::showMessage(u32) { } + +BOOL TBossPakkun::is2ndFightNow() const { return false; } + +void TBossPakkun::ignoreWaterCheck() { } + +void TBossPakkun::startTornadoBlur() { } + +void TBossPakkun::resetWaterMark() { } + +BOOL TBossPakkun::inArea(const JGeometry::TVec3&) { return false; } + +void TBossPakkun::gotFlyingDamage() { } + +void TBossPakkun::gotWaterDamage() { } + +void TBossPakkun::gotHipDropDamage() { } + +void TBossPakkun::gotTrampleDamage() { } + +void TBossPakkun::launchPolDrop() { } + +void TBossPakkun::launchTornado() { } + +void TBossPakkun::killSmallEnemies() { } + +void TBossPakkun::changeBck(int) { } + +void TBossPakkun::flyToCurPathNode(f32, f32) { } + +const char** TBossPakkun::getBasNameTable() const +{ + return bosspakkun_bastable; +} + +void TBossPakkun::setGroundCollision() { } + +void TBossPakkun::kill() { } + +BOOL TBossPakkun::receiveMessage(THitActor*, u32) { return false; } + +void TBossPakkun::perform(u32, JDrama::TGraphics*) { } + +TBossPakkunManager::TBossPakkunManager(const char* name, int value) + : TEnemyManager(name) + , unk54(value) +{ +} + +void TBossPakkunManager::initJParticle() { } + +void TBossPakkunManager::createModelData() { } + +void TBossPakkunManager::load(JSUMemoryInputStream&) { } + +DEFINE_NERVE(TNerveBPWait, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPCannon, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPVomit, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTornado, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPPivot, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPSwallow, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTumbleIn, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTumble, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTumbleOut, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPGetUp, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPSwing, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPStompReact, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPJumpReact, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPPreDie, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPDie, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTakeOff, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPFly, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPTouchDown, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPFlyCannon, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPFlyPivot, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPHover, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPFall, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPSleep, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPBreakSleep, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPWaitL, TLiveActor) { return false; } + +DEFINE_NERVE(TNerveBPCannonL, TLiveActor) { return false; } From 95899b157b6489023be3bf77b3f6dfb4b9258911 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:06:43 -0400 Subject: [PATCH 02/23] Decompile Boss Pakkun nerve behaviors --- src/Enemy/bosspakkun.cpp | 523 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 501 insertions(+), 22 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 78636b722..e6e6e9832 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -1,4 +1,27 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include static const char* bosspakkun_bastable[] = { nullptr, @@ -33,6 +56,19 @@ static const char* bosspakkun_bastable[] = { nullptr, }; +// fabricated +static inline JGeometry::TVec3 fromPolar(s16 angle, f32 radius) +{ + return JGeometry::TVec3(radius * JMASSin(angle), 0.0f, + radius * JMASCos(angle)); +} + +// fabricated +static inline JGeometry::TVec3 fromPolar(f32 angle, f32 radius) +{ + return fromPolar(static_cast(DEG2SHORTANGLE(angle)), radius); +} + TBossPakkunParams::TBossPakkunParams(const char* path) : TSpineEnemyParams(path) , PARAM_INIT(mSLWaitFrameStg0, 400) @@ -77,9 +113,19 @@ void TBPPolDrop::drop() { } void TBPPolDrop::move() { } -void TBPPolDrop::launch(const JGeometry::TVec3&, - const JGeometry::TVec3&) +void TBPPolDrop::launch(const JGeometry::TVec3& position, + const JGeometry::TVec3& velocity) { + unk6C = velocity; + mPosition = position; + mScaling.set(1.0f, 1.0f, 1.0f); + mRotation.zero(); + unk80 = 1; + unk84 = 0; + unk78->setBck("pollut_ball"); + unk78->setBtk("pollut_ball_01"); + unk78->setBtk("pollut_ball_02"); + unk88 = position.y; } void TBPPolDrop::perform(u32, JDrama::TGraphics*) { } @@ -92,7 +138,17 @@ TBPVomit::TBPVomit(TBossPakkun* owner, const char* name) { } -void TBPVomit::vomit() { } +void TBPVomit::vomit() +{ + unk14->setBckFromIndex(0); + unk18->setBckFromIndex(1); + + MtxPtr mtx = mOwner->getModel()->getBaseTRMtx(); + unk14->getModel()->setBaseTRMtx(mtx); + unk14->getModel()->setBaseScale(mOwner->mScaling); + unk18->getModel()->setBaseTRMtx(mtx); + unk18->getModel()->setBaseScale(mOwner->mScaling); +} void TBPVomit::vomitFinished() { } @@ -143,11 +199,71 @@ TBossPakkunMtxCalc::TBossPakkunMtxCalc(TBossPakkun* owner) void TBossPakkunMtxCalc::calcBellyScale(u16) { } -void TBossPakkunMtxCalc::calcHeadDir(u16) { } +void TBossPakkunMtxCalc::calcHeadDir(u16 jointIndex) +{ + if (jointIndex != 18) + return; + + MtxPtr headMtx = mOwner->getModel()->getAnmMtx(jointIndex); + JGeometry::TVec3 toMario = SMS_GetMarioPos(); + toMario.x -= headMtx[0][3]; + toMario.y -= headMtx[1][3]; + toMario.z -= headMtx[2][3]; + + JGeometry::TVec3 headAxis(headMtx[0][1], 0.0f, headMtx[2][1]); + f32 headAngle = MsGetRotFromZaxisY(headAxis); + f32 desiredAngle; + if (mOwner->mMActor->checkCurBckFromIndex(25)) { + desiredAngle = mOwner->unk184 + MsGetRotFromZaxisY(toMario); + desiredAngle = MsWrap(desiredAngle, 0.0f, 360.0f); + } else { + desiredAngle = headAngle; + } + + f32 delta = MsAngleDiff(desiredAngle, headAngle); + f32 limit = mOwner->getBossPakkunParams()->mSLHeadHomingLimit.get(); + if (delta > limit) + delta = limit; + else if (delta < -limit) + delta = -limit; + + f32 turn = MsAngleDiff(delta, mOwner->unk184); + if (turn > 1.0f) + turn = 1.0f; + else if (turn < -1.0f) + turn = -1.0f; + mOwner->unk184 += turn; + + Mtx rotation; + MsMtxSetRotRPH(rotation, 0.0f, mOwner->unk184, 0.0f); + MTXConcat(headMtx, rotation, headMtx); + MTXConcat(J3DSys::mCurrentMtx, rotation, J3DSys::mCurrentMtx); +} + +void TBossPakkunMtxCalc::joinAnm(int index) +{ + J3DAnmTransformKey* anm + = mOwner->getActorKeeper()->getMActorAnmData()->getUnk2C()->getAnmPtr( + index); -void TBossPakkunMtxCalc::joinAnm(int) { } + if (mNewAnm == anm) + return; -void TBossPakkunMtxCalc::setAnm(int) { } + mOldAnm = mNewAnm; + mNewAnm = anm; + mMotionBlendRatio = 1.0f; +} + +void TBossPakkunMtxCalc::setAnm(int index) +{ + J3DAnmTransformKey* anm + = mOwner->getActorKeeper()->getMActorAnmData()->getUnk2C()->getAnmPtr( + index); + + mNewAnm = anm; + mOldAnm = nullptr; + mMotionBlendRatio = 0.0f; +} void TBossPakkunMtxCalc::calc(u16) { } @@ -185,11 +301,42 @@ void TBossPakkun::checkMarioRiding() { } void TBossPakkun::startBGM() { } -void TBossPakkun::rumblePad(int, const JGeometry::TVec3&) { } +void TBossPakkun::rumblePad(int type, const JGeometry::TVec3& position) +{ + if (!SMS_IsMarioTouchGround4cm()) + return; + + JGeometry::TVec3 delta = SMS_GetMarioPos(); + delta -= position; + f32 distance = delta.length(); + f32 power = (3000.0f - distance) / 1000.0f; + + if (power < 0.0f) + return; + if (power > 1.0f) + power = 1.0f; + + switch (type) { + case 0: + power *= 0.4f; + break; + case 1: + power *= 0.7f; + break; + case 2: + break; + } + + unk1C8 = power; + SMSRumbleMgr->start(8, &unk1C8); +} void TBossPakkun::showMessage(u32) { } -BOOL TBossPakkun::is2ndFightNow() const { return false; } +BOOL TBossPakkun::is2ndFightNow() const +{ + return gpMarDirector->mMap == 2 && gpMarDirector->unk7D == 4; +} void TBossPakkun::ignoreWaterCheck() { } @@ -197,7 +344,15 @@ void TBossPakkun::startTornadoBlur() { } void TBossPakkun::resetWaterMark() { } -BOOL TBossPakkun::inArea(const JGeometry::TVec3&) { return false; } +BOOL TBossPakkun::inArea(const JGeometry::TVec3& position) +{ + if (unk188 == nullptr) { + unk188 = static_cast( + gpConductor->search("ゲロエリアマネージャー")); + } + + return unk188 == nullptr ? false : unk188->contain(position); +} void TBossPakkun::gotFlyingDamage() { } @@ -207,13 +362,80 @@ void TBossPakkun::gotHipDropDamage() { } void TBossPakkun::gotTrampleDamage() { } -void TBossPakkun::launchPolDrop() { } +void TBossPakkun::launchPolDrop() +{ + if (mPolDrop->unk80 != 0) + return; + + JGeometry::TVec3 launchPosition; + if (checkLiveFlag(LIVE_FLAG_CLIPPED_OUT)) { + launchPosition = mPosition; + launchPosition.x += 1.0f; + } else { + getJointTransByIndex(18, &launchPosition); + } + + f32 marioYaw = gpMarioOriginal->mRotation.y; + f32 front = getBossPakkunParams()->mSLPollBallFront.get(); + JGeometry::TVec3 targetOffset = fromPolar(marioYaw, front); + JGeometry::TVec3 target = targetOffset; + target += SMS_GetMarioPos(); + + JGeometry::TVec3 velocity; + SMSCalcJumpVelocityXZ(target, launchPosition, + getBossPakkunParams()->mSLPollBallSpeed.get(), 0.1f, + &velocity); + mPolDrop->launch(launchPosition, velocity); +} void TBossPakkun::launchTornado() { } void TBossPakkun::killSmallEnemies() { } -void TBossPakkun::changeBck(int) { } +void TBossPakkun::changeBck(int index) +{ + if (mMActor->checkCurBckFromIndex(index) + && !mMActor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) + return; + + int previous = mMActor->getCurAnmIdx(ANM_TYPE_BCK); + mMtxCalc->joinAnm(index); + mMActor->setFrameCtrlForBck(index); + + if (index == 21) { + mMActor->getFrameCtrl(ANM_TYPE_BCK) + ->setRate(getBossPakkunParams()->mSLVomitAnmRate.get()); + } + + f32 blendTime = -1.0f; + switch (previous) { + case 25: + if (index == 21 || index == 26) + blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); + break; + case 18: + case 2: + case 20: + if (index == 25) + blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); + break; + case 26: + if (index == 22) + blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); + break; + } + + if (blendTime < 0.0f) { + J3DFrameCtrl* ctrl = mMActor->getFrameCtrl(ANM_TYPE_BCK); + if (ctrl != nullptr) + blendTime = 0.1f * ctrl->getEnd(); + } + + unk154 = blendTime == 0.0f ? 1.0f : 1.0f / blendTime; + + const char** table = getBasNameTable(); + setAnmSound(table == nullptr ? nullptr : table[index]); +} void TBossPakkun::flyToCurPathNode(f32, f32) { } @@ -242,11 +464,137 @@ void TBossPakkunManager::createModelData() { } void TBossPakkunManager::load(JSUMemoryInputStream&) { } -DEFINE_NERVE(TNerveBPWait, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPWait, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + + JGeometry::TVec3 toMario = boss->mPosition; + toMario -= *gpMarioPos; + f32 swingLength = boss->getBossPakkunParams()->mSLSwingLength.get(); + if (toMario.squared() < swingLength * swingLength) { + JGeometry::TVec3 facing(-toMario.x, -toMario.y, -toMario.z); + f32 desiredYaw = MsGetRotFromZaxisY(facing); + f32 angleDiff = MsAngleDiff(desiredYaw, boss->mRotation.y); + if (fabsf(angleDiff) < 60.0f) { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + spine->pushAfterCurrent(&TNerveBPSwing::theNerve()); + return true; + } + + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + spine->pushAfterCurrent(&TNerveBPVomit::theNerve()); + TPathNode goal(*gpMarioPos); + boss->unk114.push(boss->unkF4); + boss->unkF4 = goal; + spine->pushAfterCurrent(&TNerveBPPivot::theNerve()); + return true; + } + + if (spine->getTime() == 0) + boss->changeBck(25); + + if (spine->getTime() < boss->getBossPakkunParams()->mSLWaitFrameStg0.get() + || !boss->mMActor->isCurAnmAlreadyEnd(ANM_TYPE_BCK)) { + return false; + } + + if (gpMarDirector->mMap == 2 + && (gpMarDirector->unk7D == 0 || gpMarDirector->unk7D == 1)) { + JGeometry::TVec3* marioPos = gpMarioPos; + if (boss->unk188 == nullptr) { + boss->unk188 = static_cast( + gpConductor->search("ゲロエリアマネージャー")); + } + BOOL marioInArea = boss->unk188 == nullptr + ? false + : boss->unk188->contain(*marioPos); + if (marioInArea && !(*gpMarioGroundPlane)->isWaterSurface()) { + spine->pushAfterCurrent(&TNerveBPCannon::theNerve()); + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + } + return true; + } + + if (gpMarDirector->unk7D == 4) { + f32 tornadoProp = boss->getBossPakkunParams()->mSLTornadoProp.get(); + if (boss->mTornado->unk98 != 0 + || rand() * (1.0f / 32768.0f) < tornadoProp) { + spine->pushAfterCurrent(&TNerveBPTakeOff::theNerve()); + spine->pushAfterCurrent(&TNerveBPVomit::theNerve()); + } else if (boss->mTornado->unk98 == 0) { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + spine->pushAfterCurrent(&TNerveBPTornado::theNerve()); + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + } + return true; + } + + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + spine->pushAfterCurrent(&TNerveBPVomit::theNerve()); + JGeometry::TVec3 goalPosition = boss->mPosition; + f32 randomX = rand() * (1.0f / 32768.0f); + goalPosition.x += 10000.0f * (randomX - 0.5f); + f32 randomZ = rand() * (1.0f / 32768.0f); + goalPosition.z += 10000.0f * (randomZ - 0.5f); + TPathNode goal(goalPosition); + boss->unk114.push(boss->unkF4); + boss->unkF4 = goal; + spine->pushAfterCurrent(&TNerveBPPivot::theNerve()); + return true; +} DEFINE_NERVE(TNerveBPCannon, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPVomit, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPVomit, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(21); + + if (actor->checkCurBckFromIndex(21)) { + f32 frame = actor->getFrameCtrl(ANM_TYPE_BCK)->getFrame(); + if (frame > 25.0f && frame < 165.0f) + boss->unk16C = 2; + else + boss->unk16C = 0; + } + + if (actor->checkCurBckFromIndex(20) && rand() * (1.0f / 32768.0f) < 0.2f + && spine->getTime() == 500) { + JGeometry::TVec3 offset = fromPolar(boss->mRotation.y, 700.0f); + JGeometry::TVec3 appearOffset = offset; + gpItemManager->makeObjAppear( + boss->mPosition.x + appearOffset.x, boss->mPosition.y + 1.0f, + boss->mPosition.z + appearOffset.z, 0x20000002, false); + } + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + if (actor->checkCurBckFromIndex(21)) { + boss->unk16C = 0; + boss->changeBck(20); + boss->mVomit->vomit(); + boss->rumblePad(1, boss->mPosition); + } else { + if (!boss->is2ndFightNow()) { + if (!(boss->unk1C0 & 1)) + gpMarDirector->getConsole()->startAppearBalloon(0xE0000, + true); + boss->unk1C0 |= 1; + } + return true; + } + } + + if (actor->checkCurBckFromIndex(20)) { + JGeometry::TVec3 wind(JMASin(boss->mRotation.y), 0.0f, + JMACos(boss->mRotation.y)); + gpModelWaterManager->wind(wind); + } + return false; +} DEFINE_NERVE(TNerveBPTornado, TLiveActor) { return false; } @@ -260,13 +608,52 @@ DEFINE_NERVE(TNerveBPTumble, TLiveActor) { return false; } DEFINE_NERVE(TNerveBPTumbleOut, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPGetUp, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPGetUp, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->changeBck(14); + gpCameraShake->startShake(static_cast(0x10), 1.0f); + boss->rumblePad(0, boss->mPosition); + } + if (actor->curAnmEndsNext(0, nullptr)) { + if (actor->checkCurBckFromIndex(14)) { + boss->changeBck(19); + return false; + } + return true; + } + return false; +} -DEFINE_NERVE(TNerveBPSwing, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPSwing, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(15); + if (spine->getTime() == 0) { + gpMarioParticleManager->emitAndBindToSRTMtxPtr( + 0xAC, boss->getModel()->getAnmMtx(18), 0, boss); + } + if (actor->curAnmEndsNext(0, nullptr)) + return true; + return false; +} DEFINE_NERVE(TNerveBPStompReact, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPJumpReact, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPJumpReact, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(17); + if (actor->curAnmEndsNext(0, nullptr)) + return true; + return false; +} DEFINE_NERVE(TNerveBPPreDie, TLiveActor) { return false; } @@ -278,18 +665,110 @@ DEFINE_NERVE(TNerveBPFly, TLiveActor) { return false; } DEFINE_NERVE(TNerveBPTouchDown, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPFlyCannon, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPFlyCannon, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(12); + if (spine->getTime() == 168) + boss->launchPolDrop(); + if (actor->curAnmEndsNext(0, nullptr)) + return true; + return false; +} -DEFINE_NERVE(TNerveBPFlyPivot, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPFlyPivot, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) + boss->changeBck(11); + if (boss->turnToCurPathNode( + boss->getBossPakkunParams()->mSLPivotSpeed.get())) { + boss->switchNextGoalPath(); + return true; + } + return false; +} DEFINE_NERVE(TNerveBPHover, TLiveActor) { return false; } DEFINE_NERVE(TNerveBPFall, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPSleep, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPSleep, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) + boss->changeBck(23); + return false; +} -DEFINE_NERVE(TNerveBPBreakSleep, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPBreakSleep, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) { + boss->changeBck(14); + MSBgm::stopTrackBGMs(7, 10); + } + if (boss->mMActor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBPTakeOff::theNerve()); + return true; + } + return false; +} -DEFINE_NERVE(TNerveBPWaitL, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPWaitL, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) + actor->setBck("bosspaku_wait"); + + if (spine->getTime() + >= boss->getBossPakkunParams()->mSLWaitFrameStg0.get()) { + JGeometry::TVec3* marioPos = gpMarioPos; + if (boss->unk188 == nullptr) { + boss->unk188 = static_cast( + gpConductor->search("ゲロエリアマネージャー")); + } + + BOOL marioInArea = boss->unk188 == nullptr + ? false + : boss->unk188->contain(*marioPos); + if (marioInArea) { + if (!(*gpMarioGroundPlane)->isWaterSurface()) { + spine->pushAfterCurrent(&TNerveBPCannonL::theNerve()); + return true; + } + } + } + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBPWaitL::theNerve()); + return true; + } + + return false; +} -DEFINE_NERVE(TNerveBPCannonL, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPCannonL, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) + actor->setBck("bosspaku_pollut_start"); + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + if (actor->checkCurAnm("bosspaku_pollut_start", ANM_TYPE_BCK)) { + actor->setBck("bosspaku_ball_end"); + boss->launchPolDrop(); + } else { + spine->pushAfterCurrent(&TNerveBPWaitL::theNerve()); + return true; + } + } + + return false; +} From 244f02ef35b10c262db2b9c1fb50e900cece6465 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:39:19 -0400 Subject: [PATCH 03/23] Decompile more Boss Pakkun nerve behaviors --- include/Enemy/BossPakkun.hpp | 6 +- src/Enemy/bosspakkun.cpp | 356 +++++++++++++++++++++++++++++++++-- 2 files changed, 344 insertions(+), 18 deletions(-) diff --git a/include/Enemy/BossPakkun.hpp b/include/Enemy/BossPakkun.hpp index ef13aa122..1a9df1499 100644 --- a/include/Enemy/BossPakkun.hpp +++ b/include/Enemy/BossPakkun.hpp @@ -101,7 +101,7 @@ class TBossPakkun : public TSpineEnemy { void resetWaterMark(); void startTornadoBlur(); void ignoreWaterCheck(); - BOOL is2ndFightNow() const; + bool is2ndFightNow() const; void showMessage(u32); void rumblePad(int, const JGeometry::TVec3&); void startBGM(); @@ -124,7 +124,7 @@ class TBossPakkun : public TSpineEnemy { /* 0x170 */ s32 unk170; /* 0x174 */ s32 unk174; /* 0x178 */ s32 unk178; - /* 0x17C */ u8 unk17C; + /* 0x17C */ s8 unk17C; /* 0x180 */ MActor* unk180; /* 0x184 */ f32 unk184; /* 0x188 */ TAreaCylinderManager* unk188; @@ -136,7 +136,7 @@ class TBossPakkun : public TSpineEnemy { /* 0x1B8 */ s32 unk1B8; /* 0x1BC */ u8 unk1BC; /* 0x1C0 */ s32 unk1C0; - /* 0x1C4 */ u8 unk1C4; + /* 0x1C4 */ s8 unk1C4; /* 0x1C8 */ f32 unk1C8; /* 0x1CC */ u8 unk1CC; }; diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index e6e6e9832..a27492663 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -1,8 +1,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -12,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -167,7 +172,19 @@ void TBPTornado::vanish() { } void TBPTornado::perform(u32, JDrama::TGraphics*) { } -void TBPTornado::launch(const JGeometry::TVec3&) { } +void TBPTornado::launch(const JGeometry::TVec3& target) +{ + unk98 = 1; + unk70 = target; + mPosition = mOwner->mPosition; + unk7C = mOwner->mPosition; + unk94 = mOwner->getBossPakkunParams()->mSLTornadoMoveInit.get(); + offHitFlag(HIT_FLAG_NO_COLLISION); + + J3DFrameCtrl* frameCtrl = mActor->getFrameCtrl(ANM_TYPE_BRK); + frameCtrl->setFrame(0.0f); + frameCtrl->setRate(0.0f); +} TBPHeadHit::TBPHeadHit(TBossPakkun* owner, const char* name) : THitActor(name) @@ -333,9 +350,11 @@ void TBossPakkun::rumblePad(int type, const JGeometry::TVec3& position) void TBossPakkun::showMessage(u32) { } -BOOL TBossPakkun::is2ndFightNow() const +bool TBossPakkun::is2ndFightNow() const { - return gpMarDirector->mMap == 2 && gpMarDirector->unk7D == 4; + if (gpMarDirector->mMap == 2 && gpMarDirector->unk7D == 4) + return true; + return false; } void TBossPakkun::ignoreWaterCheck() { } @@ -545,7 +564,26 @@ DEFINE_NERVE(TNerveBPWait, TLiveActor) return true; } -DEFINE_NERVE(TNerveBPCannon, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPCannon, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) + boss->changeBck(21); + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + if (actor->checkCurBckFromIndex(21)) { + boss->changeBck(2); + boss->launchPolDrop(); + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + return true; + } + } + + return false; +} DEFINE_NERVE(TNerveBPVomit, TLiveActor) { @@ -596,17 +634,167 @@ DEFINE_NERVE(TNerveBPVomit, TLiveActor) return false; } -DEFINE_NERVE(TNerveBPTornado, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPTornado, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->changeBck(24); + gpMarioParticleManager->emitAndBindToSRTMtxPtr( + 0xab, boss->getModel()->getAnmMtx(3), 0, boss); + gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &boss->unk194, 0, + nullptr); + gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &boss->unk1A0, 0, + nullptr); + } + if (spine->getTime() == 150) + boss->mTornado->launch(*gpMarioPos); + if (actor->isCurAnmAlreadyEnd(ANM_TYPE_BCK)) + return true; + return false; +} + +DEFINE_NERVE(TNerveBPPivot, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) + boss->changeBck(25); -DEFINE_NERVE(TNerveBPPivot, TLiveActor) { return false; } + JGeometry::TVec3 delta = boss->mPosition; + delta -= SMS_GetMarioPos(); + f32 swingLength = boss->getBossPakkunParams()->mSLSwingLength.get(); + f32 pivotSpeed; + if (delta.squared() < swingLength * swingLength) + pivotSpeed = boss->getBossPakkunParams()->mSLPivotSpeedAware.get(); + else + pivotSpeed = boss->getBossPakkunParams()->mSLPivotSpeed.get(); -DEFINE_NERVE(TNerveBPSwallow, TLiveActor) { return false; } + if (!boss->turnToCurPathNode(pivotSpeed)) + return false; + boss->switchNextGoalPath(); + return true; +} + +DEFINE_NERVE(TNerveBPSwallow, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) + boss->changeBck(26); -DEFINE_NERVE(TNerveBPTumbleIn, TLiveActor) { return false; } + if (boss->unk178 >= boss->getBossPakkunParams()->mSLWaterMarkLimit.get()) { + spine->pushAfterCurrent(&TNerveBPTumbleIn::theNerve()); + boss->unk16C = 0; + boss->unk170 = 0; + return true; + } + + MtxPtr jointMtx = boss->getModel()->getAnmMtx(18); + gpMarioParticleManager->emitAndBindToMtxPtr(0x15d, jointMtx, 1, boss); + gpMarioParticleManager->emitAndBindToMtxPtr(0x15e, jointMtx, 1, boss); + + if (boss->unk170 != 0) { + boss->changeBck(26); + boss->unk170 = 0; + return false; + } + + boss->unk16C = 0; + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + return true; +} + +DEFINE_NERVE(TNerveBPTumbleIn, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(3); + + if (spine->getTime() == 336) { + gpMarioParticleManager->emitAndBindToMtxPtr( + 0xaa, boss->getModel()->getAnmMtx(14), 0, boss); + } + if (spine->getTime() == 348) { + gpCameraShake->startShake(static_cast(0xe), 1.0f); + boss->rumblePad(2, boss->mPosition); + } + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBPTumble::theNerve()); + return true; + } + return false; +} + +DEFINE_NERVE(TNerveBPTumble, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) { + boss->changeBck(6); + boss->unk16C = 1; + } + + gpMarioParticleManager->emitAndBindToMtxPtr( + 0x161, boss->getModel()->getAnmMtx(0), 1, boss); + gpCameraShake->keepShake(static_cast(0x11), 1.0f); + if ((spine->getTime() / 60) % 2 != 0) + boss->rumblePad(0, boss->mPosition); + + if (spine->getTime() >= boss->getBossPakkunParams()->mSLTumbleTime.get()) { + boss->unk16C = 0; + spine->pushAfterCurrent(&TNerveBPTumbleOut::theNerve()); + return true; + } + return false; +} + +DEFINE_NERVE(TNerveBPTumbleOut, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->changeBck(14); + gpCameraShake->startShake(static_cast(0x10), 1.0f); + boss->rumblePad(0, boss->mPosition); + } -DEFINE_NERVE(TNerveBPTumble, TLiveActor) { return false; } + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + if (actor->checkCurBckFromIndex(14)) { + boss->changeBck(22); + if (!boss->is2ndFightNow()) { + boss->unk1C4 += 1; + if (boss->unk1C4 >= 3) { + gpMarDirector->getConsole()->startAppearBalloon(0xe0001, + true); + boss->unk1C4 = 0; + } + } + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + return true; + } + } -DEFINE_NERVE(TNerveBPTumbleOut, TLiveActor) { return false; } + if (actor->checkCurBckFromIndex(22)) { + f32 frame = actor->getFrameCtrl(ANM_TYPE_BCK)->getFrame(); + if (140.0f < frame && frame < 160.0f && boss->unk17C == 0) { + boss->unk17C = 1; + boss->unk174 = 0; + boss->unk170 = 0; + boss->unk1B8 = 50; + if (boss->unk18C != nullptr) { + JGeometry::TVec3 position; + boss->getJointTransByIndex(18, &position); + position.y += 250.0f; + boss->unk18C->mPos.value = position; + gpModelWaterManager->emitRequest(*boss->unk18C); + } + } + if (35.0f < frame) + boss->unk1BC = 1; + } + return false; +} DEFINE_NERVE(TNerveBPGetUp, TLiveActor) { @@ -642,7 +830,41 @@ DEFINE_NERVE(TNerveBPSwing, TLiveActor) return false; } -DEFINE_NERVE(TNerveBPStompReact, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPStompReact, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) { + boss->changeBck(5); + boss->mHeadHit->onHitFlag(HIT_FLAG_NO_COLLISION); + } + + if (spine->getTime() == 30 && boss->unk17C == 0) { + boss->unk17C = 1; + boss->unk174 = 0; + boss->unk170 = 0; + boss->unk1B8 = 50; + + if (boss->unk18C != nullptr) { + JGeometry::TVec3 position; + boss->getJointTransByIndex(18, &position); + position.y += 250.0f; + boss->unk18C->mPos.value = position; + gpModelWaterManager->emitRequest(*boss->unk18C); + } + } + + if (spine->getTime() == 50) + boss->unk1BC = 1; + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + boss->mHeadHit->offHitFlag(HIT_FLAG_NO_COLLISION); + return true; + } + + return false; +} DEFINE_NERVE(TNerveBPJumpReact, TLiveActor) { @@ -655,15 +877,119 @@ DEFINE_NERVE(TNerveBPJumpReact, TLiveActor) return false; } -DEFINE_NERVE(TNerveBPPreDie, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPPreDie, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->changeBck(5); + boss->mHeadHit->onHitFlag(HIT_FLAG_NO_COLLISION); + if (boss->unk17C == 0) { + boss->unk17C = 1; + boss->unk174 = 0; + boss->unk170 = 0; + boss->unk1B8 = 50; + if (boss->unk18C != nullptr) { + JGeometry::TVec3 position; + boss->getJointTransByIndex(18, &position); + position.y += 250.0f; + boss->unk18C->mPos.value = position; + gpModelWaterManager->emitRequest(*boss->unk18C); + } + } -DEFINE_NERVE(TNerveBPDie, TLiveActor) { return false; } + TNameKuriManager* manager + = JDrama::TNameRefGen::search( + "ナメクリマネージャー"); + if (manager != nullptr) + manager->killChildren(); + MSBgm::stopTrackBGM(1, 10); + } -DEFINE_NERVE(TNerveBPTakeOff, TLiveActor) { return false; } + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBPDie::theNerve()); + return true; + } + return false; +} + +DEFINE_NERVE(TNerveBPDie, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + gpMSound->unk98->modBgm(0, 1); + + if (actor->checkCurBckFromIndex(7) && spine->getTime() == 680) + boss->mLiveFlag |= LIVE_FLAG_UNK8; + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr) + && actor->checkCurBckFromIndex(7)) { + boss->kill(); + gpItemManager->makeShineAppearWithDemo( + "シャイン(ボス用)", "ボスシャインカメラ", boss->mPosition.x, + boss->mPosition.y, boss->mPosition.z); + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveBPTakeOff, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->onLiveFlag(LIVE_FLAG_UNK10); + boss->onLiveFlag(LIVE_FLAG_AIRBORNE); + boss->changeBck(13); + } + + if (actor->checkCurBckFromIndex(13) + && actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) + boss->changeBck(11); + + if (actor->checkCurBckFromIndex(11)) { + boss->mPosition.y += 5.0f; + JGeometry::TVec3 goal = boss->getUnk104().getPoint(); + if (goal.y < boss->mPosition.y) { + boss->mPosition.y = goal.y; + if (boss->getTracer()->getGraph() != nullptr) + spine->pushAfterCurrent(&TNerveBPFly::theNerve()); + else + spine->pushAfterCurrent(&TNerveBPTouchDown::theNerve()); + return true; + } + } + return false; +} DEFINE_NERVE(TNerveBPFly, TLiveActor) { return false; } -DEFINE_NERVE(TNerveBPTouchDown, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPTouchDown, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) + boss->changeBck(11); + + if (actor->checkCurBckFromIndex(11)) { + boss->mPosition.y -= 5.0f; + JGeometry::TVec3 goal = boss->getUnk104().getPoint(); + if (goal.y > boss->mPosition.y) { + boss->mPosition.y = goal.y; + boss->changeBck(18); + boss->offLiveFlag(LIVE_FLAG_UNK10); + } + } + + if (actor->checkCurBckFromIndex(18) + && actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + return true; + } + return false; +} DEFINE_NERVE(TNerveBPFlyCannon, TLiveActor) { From 2c575105c6d875f3f7c9d06cb65840b7555d15d5 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:55:42 -0400 Subject: [PATCH 04/23] Complete Boss Pakkun nerve behaviors --- include/Enemy/BossPakkun.hpp | 2 +- src/Enemy/bosspakkun.cpp | 125 +++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/include/Enemy/BossPakkun.hpp b/include/Enemy/BossPakkun.hpp index 1a9df1499..61f49b7c5 100644 --- a/include/Enemy/BossPakkun.hpp +++ b/include/Enemy/BossPakkun.hpp @@ -138,7 +138,7 @@ class TBossPakkun : public TSpineEnemy { /* 0x1C0 */ s32 unk1C0; /* 0x1C4 */ s8 unk1C4; /* 0x1C8 */ f32 unk1C8; - /* 0x1CC */ u8 unk1CC; + /* 0x1CC */ s8 unk1CC; }; class TBPNavel : public THitActor { diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index a27492663..7d0ccc768 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -456,7 +456,18 @@ void TBossPakkun::changeBck(int index) setAnmSound(table == nullptr ? nullptr : table[index]); } -void TBossPakkun::flyToCurPathNode(f32, f32) { } +void TBossPakkun::flyToCurPathNode(f32 flySpeed, f32 turnSpeed) +{ + turnToCurPathNode(turnSpeed); + + JGeometry::TVec3 velocityStep = getUnkF4().getPoint(); + velocityStep -= mPosition; + PSVECNormalize(&velocityStep, &velocityStep); + velocityStep *= flySpeed; + JGeometry::TVec3 velocity = mLinearVelocity; + velocity += velocityStep; + mLinearVelocity = velocity; +} const char** TBossPakkun::getBasNameTable() const { @@ -918,10 +929,10 @@ DEFINE_NERVE(TNerveBPDie, TLiveActor) TBossPakkun* boss = static_cast(spine->getBody()); MActor* actor = boss->mMActor; - gpMSound->unk98->modBgm(0, 1); + SMSGetMSound()->unk98->modBgm(0, 1); if (actor->checkCurBckFromIndex(7) && spine->getTime() == 680) - boss->mLiveFlag |= LIVE_FLAG_UNK8; + boss->onLiveFlag(LIVE_FLAG_UNK8); if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr) && actor->checkCurBckFromIndex(7)) { @@ -964,7 +975,34 @@ DEFINE_NERVE(TNerveBPTakeOff, TLiveActor) return false; } -DEFINE_NERVE(TNerveBPFly, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPFly, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) { + boss->changeBck(11); + boss->goToRandomNextGraphNode(); + if (boss->unk1CC == 0) { + MSBgm::startBGM(MSD_BGM_MAP_SELECT); + boss->unk1CC = 1; + } + } + + JGeometry::TVec3 toNext = boss->getUnk104().getPoint(); + toNext -= boss->mPosition; + toNext.y = 0.0f; + if (PSVECMag(&toNext) < 100.0f) { + if (!boss->getTracer()->getCurrent().checkFlag(0x800)) { + boss->goToRandomNextGraphNode(); + } else { + spine->pushAfterCurrent(&TNerveBPHover::theNerve()); + return true; + } + } + + boss->flyToCurPathNode(boss->getBossPakkunParams()->mSLFlySpeed.get(), + boss->mTurnSpeed); + return false; +} DEFINE_NERVE(TNerveBPTouchDown, TLiveActor) { @@ -1017,9 +1055,84 @@ DEFINE_NERVE(TNerveBPFlyPivot, TLiveActor) return false; } -DEFINE_NERVE(TNerveBPHover, TLiveActor) { return false; } +DEFINE_NERVE(TNerveBPHover, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) { + boss->changeBck(16); + boss->unk16C = 3; + } + + f32 range = boss->getBossPakkunParams()->mSLPollBallRange.get(); + if (boss->inArea(*gpMarioPos) + && boss->getDistToMarioSquared() < range * range) { + spine->pushAfterCurrent(&TNerveBPHover::theNerve()); + spine->pushAfterCurrent(&TNerveBPFlyCannon::theNerve()); -DEFINE_NERVE(TNerveBPFall, TLiveActor) { return false; } + TPathNode goal(*gpMarioPos); + boss->unk114.push(boss->unkF4); + boss->unkF4 = goal; + + spine->pushAfterCurrent(&TNerveBPFlyPivot::theNerve()); + return true; + } + + if (spine->getTime() >= boss->getBossPakkunParams()->mSLHoverTimer.get()) { + spine->pushAfterCurrent(&TNerveBPFly::theNerve()); + boss->unk16C = 0; + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveBPFall, TLiveActor) +{ + TBossPakkun* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + if (spine->getTime() == 0) { + boss->offLiveFlag(LIVE_FLAG_UNK10); + boss->onLiveFlag(LIVE_FLAG_AIRBORNE); + boss->changeBck(10); + } + + if (actor->checkCurBckFromIndex(10)) { + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) + boss->changeBck(9); + } else if (actor->checkCurBckFromIndex(9)) { + if (!boss->isAirborne()) { + boss->changeBck(8); + gpCameraShake->startShake(static_cast(15), 1.0f); + boss->rumblePad(2, boss->mPosition); + } + } else if (actor->checkCurBckFromIndex(8)) { + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + boss->changeBck(14); + gpCameraShake->startShake(static_cast(16), 1.0f); + boss->rumblePad(0, boss->mPosition); + } + } else if (actor->checkCurBckFromIndex(14) + && actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + if (boss->is2ndFightNow()) { + f32 tornadoProp = boss->getBossPakkunParams()->mSLTornadoProp.get(); + if (boss->mTornado->unk98 != 0 + || rand() * (1.0f / 32768.0f) < tornadoProp) { + spine->pushAfterCurrent(&TNerveBPTakeOff::theNerve()); + spine->pushAfterCurrent(&TNerveBPVomit::theNerve()); + } else if (boss->mTornado->unk98 == 0) { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + spine->pushAfterCurrent(&TNerveBPTornado::theNerve()); + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + } + } else { + spine->pushAfterCurrent(&TNerveBPWait::theNerve()); + } + return true; + } + + return false; +} DEFINE_NERVE(TNerveBPSleep, TLiveActor) { From a2e6da3ade14eb4a37e41aed0354d8c9283ae16c Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:33:19 -0400 Subject: [PATCH 05/23] Decompile Boss Pakkun support actors --- src/Enemy/bosspakkun.cpp | 114 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 6 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 7d0ccc768..660b552ff 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -157,7 +159,26 @@ void TBPVomit::vomit() void TBPVomit::vomitFinished() { } -void TBPVomit::perform(u32, JDrama::TGraphics*) { } +void TBPVomit::perform(u32 flags, JDrama::TGraphics* graphics) +{ + if (unk14->getCurAnmIdx(ANM_TYPE_BCK) < 0) + return; + + u32 calcAnim = flags & CUE_CALC_ANIM; + if (calcAnim && unk14->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + unk14->setBckFromIndex(-1); + unk18->setBckFromIndex(-1); + return; + } + + if (calcAnim) + unk18->calcAnm(); + + if (flags & CUE_ENTRY) + gpPollution->stampModel(unk18->getModel()); + + unk14->perform(flags, graphics); +} TBPTornado::TBPTornado(TBossPakkun* owner, const char* name) : THitActor(name) @@ -204,9 +225,30 @@ TBPNavel::TBPNavel(TBossPakkun* owner, const char* name) { } -BOOL TBPNavel::receiveMessage(THitActor*, u32) { return false; } +BOOL TBPNavel::receiveMessage(THitActor* sender, u32 message) +{ + if (mOwner->mSpine->getLatestNerve() == &TNerveBPSleep::theNerve()) + return mOwner->receiveMessage(sender, message); + + if (sender->getActorType() == 0x1000001) + return false; + + if (mOwner->unk16C != 1) + return true; + + if (sender->getActorType() == 0x80000001 && message == HIT_MESSAGE_HIP_DROP) + mOwner->gotHipDropDamage(); + + return true; +} + +void TBPNavel::perform(u32 flags, JDrama::TGraphics* graphics) +{ + if (flags & 2) + mOwner->getJointTransByIndex(6, &mPosition); -void TBPNavel::perform(u32, JDrama::TGraphics*) { } + THitActor::perform(flags, graphics); +} TBossPakkunMtxCalc::TBossPakkunMtxCalc(TBossPakkun* owner) : M3UMtxCalcSIAnmBlendQuat(false) @@ -488,11 +530,71 @@ TBossPakkunManager::TBossPakkunManager(const char* name, int value) { } -void TBossPakkunManager::initJParticle() { } +void TBossPakkunManager::initJParticle() +{ + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_blur1.jpa", 0xa9); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_down.jpa", 0xaa); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_swing1.jpa", 0xab); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_swing2.jpa", 0xac); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_wathit.jpa", 0x15d); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_wathit_w.jpa", 0x15e); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_ase.jpa", 0x15f); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_blur2.jpa", 0x160); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_jita.jpa", 0x161); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_tr_rock.jpa", 0x162); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_tr_smoke.jpa", 0x163); + SMS_LoadParticle("/scene/bosspakkun/jpa/ms_bopa_tr_weed.jpa", 0x164); +} -void TBossPakkunManager::createModelData() { } +void TBossPakkunManager::createModelData() +{ + if (unk54 != 0) { + static const TModelDataLoadEntry entry[] = { + { "bosspaku_model.bmd", + J3DMLF_MaterialPEFull | (1 << J3DMLF_TevStageNumShift), 0 }, + { "pollut_ball.bmd", + J3DMLF_MaterialPEFull | J3DMLF_MaterialUseIndirect + | (4 << J3DMLF_TevStageNumShift), + 0 }, + { "pollut_ball_stamp.bmd", + J3DMLF_MaterialPEFull | (1 << J3DMLF_TevStageNumShift), 0 }, + { nullptr, 0, 0 }, + }; + createModelDataArray(entry); + } else { + static const TModelDataLoadEntry entry[] = { + { "bosspaku_model.bmd", + J3DMLF_MaterialPEFull | (1 << J3DMLF_TevStageNumShift), 0 }, + { "bosspaku_end.bmd", + J3DMLF_MaterialPEFull | (16 << J3DMLF_TevStageNumShift), 0 }, + { "pollut_ball.bmd", + J3DMLF_MaterialPEFull | J3DMLF_MaterialUseIndirect + | (4 << J3DMLF_TevStageNumShift), + 0 }, + { "pollut_ball_stamp.bmd", + J3DMLF_MaterialPEFull | (1 << J3DMLF_TevStageNumShift), 0 }, + { "bosspakuPollut.bmd", + J3DMLF_MaterialPEFull | J3DMLF_MaterialUseIndirect + | (2 << J3DMLF_TevStageNumShift), + 0 }, + { "bosspakuPollut_white.bmd", + J3DMLF_MaterialPEFull | (1 << J3DMLF_TevStageNumShift), 0 }, + { "trunade.bmd", + J3DMLF_MaterialPEFull | (2 << J3DMLF_TevStageNumShift), 0 }, + { nullptr, 0, 0 }, + }; + createModelDataArray(entry); + } +} -void TBossPakkunManager::load(JSUMemoryInputStream&) { } +void TBossPakkunManager::load(JSUMemoryInputStream& stream) +{ + unk38 = new TBossPakkunParams("/enemy/bosspakkun.prm"); + TEnemyManager::load(stream); + + if (unk54 == 0) + initJParticle(); +} DEFINE_NERVE(TNerveBPWait, TLiveActor) { From 5ac2779fcdd2f56d79813f2b1ff192d248cf2cbc Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 03:57:14 -0400 Subject: [PATCH 06/23] Decompile Boss Pakkun matrix and hit handling --- src/Enemy/bosspakkun.cpp | 197 +++++++++++++++++++++++++++++++++++---- 1 file changed, 177 insertions(+), 20 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 660b552ff..c97ccf3d9 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -211,18 +211,113 @@ TBPHeadHit::TBPHeadHit(TBossPakkun* owner, const char* name) : THitActor(name) , mOwner(owner) { + initHitActor(0x8000010, 5, 0x81000000, 300.0f, 500.0f, 300.0f, 500.0f); + offHitFlag(HIT_FLAG_NO_COLLISION); +} + +BOOL TBPHeadHit::receiveMessage(THitActor* sender, u32 message) +{ + TBossPakkun* boss = mOwner; + if (boss->mSpine->getLatestNerve() == &TNerveBPSleep::theNerve()) + return boss->receiveMessage(sender, message); + + u32 actorType = sender->getActorType(); + if (boss->unk16C == 3 + && (actorType == 0x1000000d || actorType == 0x1000001)) { + boss->unk16C = 0; + boss->mSpine->reset(); + boss->mSpine->setNext(&TNerveBPFall::theNerve()); + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_FALL, &boss->mPosition, + 0, nullptr, 0, 4); + return true; + } + + if (boss->unk16C != 2) { + if (boss->is2ndFightNow() + && boss->mSpine->getLatestNerve() == &TNerveBPFly::theNerve()) + boss->showMessage(0xe0002); + return actorType == 0x1000001; + } + + if (actorType == 0x1000001 && message == HIT_MESSAGE_SPRAYED_BY_WATER) { + JGeometry::TVec3 toMario = *gpMarioPos; + toMario -= mPosition; + + f32 angle = MsAngleWrap(MsGetRotFromZaxisY(toMario)); + angle = MsAngleDiff(MsGetRotFromZaxisY(toMario), boss->mRotation.y); + if (fabsf(angle) + < 0.5f * boss->getBossPakkunParams()->mSLDamageAngle.get()) { + if (boss->unk17C == 0) { + boss->unk170 += 1; + if (boss->unk178 + < boss->getBossPakkunParams()->mSLWaterMarkLimit.get()) + boss->unk178 += 1; + boss->unk174 + = boss->getBossPakkunParams()->mSLWaterHitTimer.get(); + + if (boss->mSpine->getLatestNerve() + != &TNerveBPSwallow::theNerve()) { + boss->mSpine->reset(); + boss->mSpine->setNext(&TNerveBPSwallow::theNerve()); + } + } + } + } + + return true; +} + +void TBPHeadHit::throwActor(THitActor* actor) +{ + if (actor->getActorType() != 0x80000001) + return; + if (!mOwner->mMActor->checkCurBckFromIndex(15)) + return; + + static JGeometry::TVec3 up(0.0f, 1.0f, 0.0f); + JGeometry::TVec3 direction = mOwner->mPosition; + direction -= actor->mPosition; + if (direction.isZero()) + direction.set(0.0f, 0.0f, 1.0f); + else + PSVECNormalize(&direction, &direction); + + JGeometry::TVec3 perpendicular; + perpendicular.cross(up, direction); + if (perpendicular.isZero()) + perpendicular.set(1.0f, 0.0f, 0.0f); + else + PSVECNormalize(&perpendicular, &perpendicular); + + perpendicular *= 2.0f; + perpendicular += up; + SMS_SendMessageToMario(this, HIT_MESSAGE_ATTACK); + SMS_SendMessageToMario(this, HIT_MESSAGE_THROWN); + SMS_ThrowMario(perpendicular, 100.0f); } -BOOL TBPHeadHit::receiveMessage(THitActor*, u32) { return false; } +void TBPHeadHit::perform(u32 flags, JDrama::TGraphics* graphics) +{ + if ((flags & CUE_MOVE) && mOwner->unk16C != 1) { + for (int i = 0; i < mColCount; ++i) { + THitActor* actor = mCollisions[i]; + if (actor->isActorType(0x80000001)) + throwActor(actor); + } + } -void TBPHeadHit::throwActor(THitActor*) { } + if (flags & CUE_CALC_ANIM) + mOwner->getJointTransByIndex(1, &mPosition); -void TBPHeadHit::perform(u32, JDrama::TGraphics*) { } + THitActor::perform(flags, graphics); +} TBPNavel::TBPNavel(TBossPakkun* owner, const char* name) : THitActor(name) , mOwner(owner) { + initHitActor(0x8000011, 1, 0x80000000, 200.0f, 300.0f, 200.0f, 300.0f); + offHitFlag(HIT_FLAG_NO_COLLISION); } BOOL TBPNavel::receiveMessage(THitActor* sender, u32 message) @@ -256,7 +351,44 @@ TBossPakkunMtxCalc::TBossPakkunMtxCalc(TBossPakkun* owner) { } -void TBossPakkunMtxCalc::calcBellyScale(u16) { } +void TBossPakkunMtxCalc::calcBellyScale(u16 jointIndex) +{ + if (jointIndex != 4 && jointIndex != 36) + return; + + f32 progress; + if (mOwner->unk17C != 0) { + progress = static_cast(mOwner->unk1B8) / 50.0f; + } else { + s32 limit = mOwner->getBossPakkunParams()->mSLWaterMarkLimit.get(); + s32 mark = mOwner->unk178; + if (mark > limit) + mark = limit; + progress = static_cast(mark) / static_cast(limit); + } + + f32 blend = JMAHermiteInterpolation(progress, 0.0f, 0.0f, 10.0f, 1.0f, 1.0f, + 0.0f); + MtxPtr jointMtx = mOwner->getModel()->getAnmMtx(jointIndex); + Mtx scaleMtx; + + if (jointIndex == 36) { + static JGeometry::TVec3 goal(1.4f, 1.4f, 1.6f); + static JGeometry::TVec3 start(1.0f, 0.8f, 0.8f); + MTXScale(scaleMtx, blend * (goal.x - start.x) + start.x, + blend * (goal.y - start.y) + start.y, + blend * (goal.z - start.z) + start.z); + } else { + static JGeometry::TVec3 goal(1.3f, 1.7f, 1.7f); + static JGeometry::TVec3 start(1.0f, 0.9f, 0.9f); + MTXScale(scaleMtx, blend * (goal.x - start.x) + start.x, + blend * (goal.y - start.y) + start.y, + blend * (goal.z - start.z) + start.z); + } + + MTXConcat(jointMtx, scaleMtx, jointMtx); + MTXCopy(jointMtx, J3DSys::mCurrentMtx); +} void TBossPakkunMtxCalc::calcHeadDir(u16 jointIndex) { @@ -270,31 +402,51 @@ void TBossPakkunMtxCalc::calcHeadDir(u16 jointIndex) toMario.z -= headMtx[2][3]; JGeometry::TVec3 headAxis(headMtx[0][1], 0.0f, headMtx[2][1]); - f32 headAngle = MsGetRotFromZaxisY(headAxis); + f32 headRotation = mOwner->unk184; + f32 headAngle = MsGetRotFromZaxisY(headAxis); f32 desiredAngle; if (mOwner->mMActor->checkCurBckFromIndex(25)) { - desiredAngle = mOwner->unk184 + MsGetRotFromZaxisY(toMario); - desiredAngle = MsWrap(desiredAngle, 0.0f, 360.0f); + desiredAngle + = MsWrap(headRotation + MsGetRotFromZaxisY(toMario), 0.0f, 360.0f); } else { desiredAngle = headAngle; } f32 delta = MsAngleDiff(desiredAngle, headAngle); f32 limit = mOwner->getBossPakkunParams()->mSLHeadHomingLimit.get(); - if (delta > limit) - delta = limit; - else if (delta < -limit) - delta = -limit; - - f32 turn = MsAngleDiff(delta, mOwner->unk184); - if (turn > 1.0f) - turn = 1.0f; - else if (turn < -1.0f) - turn = -1.0f; - mOwner->unk184 += turn; + + f32 limitedDelta; + if (0.0f < delta) + limitedDelta = limit > delta ? delta : limit; + else + limitedDelta = -limit > delta ? -limit : delta; + + f32 turn = MsAngleDiff(limitedDelta, headRotation); + if (0.0f < turn) + turn = 1.0f > turn ? turn : 1.0f; + else + turn = -1.0f > turn ? -1.0f : turn; + + headRotation += turn; + mOwner->unk184 = headRotation; + + f32 s = JMASin(headRotation); + f32 c = JMACos(headRotation); Mtx rotation; - MsMtxSetRotRPH(rotation, 0.0f, mOwner->unk184, 0.0f); + rotation[0][0] = 1.0f; + rotation[0][1] = 0.0f; + rotation[0][2] = 0.0f; + rotation[0][3] = 0.0f; + rotation[1][0] = 0.0f; + rotation[1][1] = c; + rotation[1][2] = -s; + rotation[1][3] = 0.0f; + rotation[2][0] = 0.0f; + rotation[2][1] = s; + rotation[2][2] = c; + rotation[2][3] = 0.0f; + MTXConcat(headMtx, rotation, headMtx); MTXConcat(J3DSys::mCurrentMtx, rotation, J3DSys::mCurrentMtx); } @@ -324,7 +476,12 @@ void TBossPakkunMtxCalc::setAnm(int index) mMotionBlendRatio = 0.0f; } -void TBossPakkunMtxCalc::calc(u16) { } +void TBossPakkunMtxCalc::calc(u16 jointIndex) +{ + M3UMtxCalcSIAnmBlendQuat::calc(jointIndex); + calcBellyScale(jointIndex); + calcHeadDir(jointIndex); +} TBossPakkun::TBossPakkun(const char* name) : TSpineEnemy(name) From f03b145c522e4823a4df6895a6e238112ba1bdaa Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:03:38 -0400 Subject: [PATCH 07/23] Improve Boss Pakkun vomit behavior --- src/Enemy/bosspakkun.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index c97ccf3d9..add893678 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -864,7 +864,7 @@ DEFINE_NERVE(TNerveBPVomit, TLiveActor) if (actor->checkCurBckFromIndex(21)) { f32 frame = actor->getFrameCtrl(ANM_TYPE_BCK)->getFrame(); - if (frame > 25.0f && frame < 165.0f) + if (25.0f < frame && frame < 165.0f) boss->unk16C = 2; else boss->unk16C = 0; @@ -872,7 +872,9 @@ DEFINE_NERVE(TNerveBPVomit, TLiveActor) if (actor->checkCurBckFromIndex(20) && rand() * (1.0f / 32768.0f) < 0.2f && spine->getTime() == 500) { - JGeometry::TVec3 offset = fromPolar(boss->mRotation.y, 700.0f); + JGeometry::TVec3 offset; + offset = fromPolar( + static_cast(DEG2SHORTANGLE(boss->mRotation.y)), 700.0f); JGeometry::TVec3 appearOffset = offset; gpItemManager->makeObjAppear( boss->mPosition.x + appearOffset.x, boss->mPosition.y + 1.0f, From ee914dbd6650755fdb4b81d8977c71ef721052a2 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:07:32 -0400 Subject: [PATCH 08/23] Decompile Boss Pakkun tornado construction --- src/Enemy/bosspakkun.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index add893678..a84aa80b9 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -183,10 +183,17 @@ void TBPVomit::perform(u32 flags, JDrama::TGraphics* graphics) TBPTornado::TBPTornado(TBossPakkun* owner, const char* name) : THitActor(name) , mOwner(owner) - , mActor(nullptr) , unk94(0.0f) , unk98(0) { + mActor = mOwner->getActorKeeper()->createMActor("trunade.bmd", 0); + initHitActor(0x8000010, 5, 0x81000000, 150.0f, 600.0f, 100.0f, + 600.0f); + onHitFlag(HIT_FLAG_NO_COLLISION); + mActor->setBtkFromIndex(2); + mActor->setBckFromIndex(29); + mActor->setBrkFromIndex(1); + mScaling.set(2.0f, 2.0f, 2.0f); } void TBPTornado::vanish() { } From df0cd829a408112d6b70746545130be6fe7eb8a3 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:13:47 -0400 Subject: [PATCH 09/23] Decompile Boss Pakkun tornado behavior --- src/Enemy/bosspakkun.cpp | 126 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 2 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index a84aa80b9..1e494baae 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -196,9 +198,129 @@ TBPTornado::TBPTornado(TBossPakkun* owner, const char* name) mScaling.set(2.0f, 2.0f, 2.0f); } -void TBPTornado::vanish() { } +void TBPTornado::vanish() +{ + onHitFlag(HIT_FLAG_NO_COLLISION); + unk98 = 2; + + J3DFrameCtrl* frameCtrl = mActor->getFrameCtrl(ANM_TYPE_BRK); + frameCtrl->setFrame(0.0f); + frameCtrl->setRate(SMSGetAnmFrameRate()); +} -void TBPTornado::perform(u32, JDrama::TGraphics*) { } +void TBPTornado::perform(u32 flags, JDrama::TGraphics* graphics) +{ + THitActor::perform(flags, graphics); + + if (mOwner->checkLiveFlag(LIVE_FLAG_DEAD)) + return; + if (unk98 == 0) + return; + + if (flags & CUE_MOVE) { + if (unk98 == 2) { + mPosition += unk88; + if (mActor->curAnmEndsNext(ANM_TYPE_BRK, nullptr)) { + unk98 = 0; + return; + } + } else { + unk94 += mOwner->getBossPakkunParams()->mSLTornadoMoveInc.get(); + if (unk94 + > mOwner->getBossPakkunParams()->mSLTornadoMoveLimit.get()) { + vanish(); + return; + } + + f32 angle = 360.0f - static_cast(unk94) % 360; + f32 radius + = unk94 + * mOwner->getBossPakkunParams()->mSLTornadoRollSpeed.get(); + JGeometry::TVec3 direction; + direction = unk70; + direction.sub(unk7C); + if (PSVECMag(&direction) < 100.0f) { + vanish(); + return; + } + + PSVECNormalize(&direction, &direction); + direction + *= mOwner->getBossPakkunParams()->mSLTornadoSpeed.get(); + unk7C += direction; + + s16 shortAngle = DEG2SHORTANGLE(angle); + JGeometry::TVec3 nextPosition( + unk7C.x + radius * JMASCos(shortAngle), unk7C.y, + unk7C.z + radius * JMASSin(shortAngle)); + + const TBGCheckData* ground; + f32 groundHeight + = gpMap->checkGround(nextPosition.x, nextPosition.y + 200.0f, + nextPosition.z, &ground); + if (!ground->isIllegalData()) + nextPosition.y = groundHeight; + + if (gpMap->isTouchedOneWallAndMoveXZ( + &nextPosition.x, nextPosition.y, &nextPosition.z, 80.0f)) + vanish(); + + unk88 = nextPosition; + unk88.sub(mPosition); + mPosition = nextPosition; + + for (s32 i = 0; i < mColCount; ++i) { + if (!mCollisions[i]->isActorType(0x80000001)) + continue; + + static const JGeometry::TVec3 up(0.0f, 1.0f, 0.0f); + SMS_SendMessageToMario(this, HIT_MESSAGE_ATTACK); + SMS_SendMessageToMario(this, HIT_MESSAGE_THROWN); + SMS_ThrowMario(up, 100.0f); + vanish(); + } + } + } + + if (flags & CUE_CALC_ANIM) { + J3DModel* model = mActor->getModel(); + MtxPtr mtx = model->getBaseTRMtx(); + MTXIdentity(mtx); + mtx[0][3] = mPosition.x; + mtx[1][3] = mPosition.y; + mtx[2][3] = mPosition.z; + model->setBaseScale(mScaling); + } + + if (flags & CUE_CALC_ANIM) { + MtxPtr mtx = mActor->getModel()->getBaseTRMtx(); + JPABaseEmitter* emitter = gpMarioParticleManager->emitAndBindToMtxPtr( + 0x162, mtx, 1, this); + if (emitter) + emitter->setGlobalScale(mScaling); + + emitter = gpMarioParticleManager->emitAndBindToMtxPtr( + 0x163, mtx, 1, reinterpret_cast(this) + 1); + if (emitter) + emitter->setGlobalScale(mScaling); + + emitter = gpMarioParticleManager->emitAndBindToMtxPtr( + 0x164, mtx, 1, reinterpret_cast(this) + 2); + if (emitter) + emitter->setGlobalScale(mScaling); + } + + if (flags & CUE_CALC_ANIM) { + JGeometry::TVec3 toMario; + toMario = mPosition; + toMario.sub(*gpMarioPos); + SMSGetMSound()->startSoundActorWithInfo( + MSD_SE_BS_BSPAKU_TORNADO, &mPosition, nullptr, toMario.length(), 0, + 0, nullptr, 0, 4); + } + + mActor->perform(flags, graphics); +} void TBPTornado::launch(const JGeometry::TVec3& target) { From 0bee1e96eb2f0cd6cb1af98bab31e62f0eb1ee3b Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:16:11 -0400 Subject: [PATCH 10/23] Decompile Boss Pakkun pollution drop movement --- src/Enemy/bosspakkun.cpp | 57 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 1e494baae..c1c0a532d 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -120,7 +120,62 @@ TBPPolDrop::TBPPolDrop(TBossPakkun* owner, const char* name) void TBPPolDrop::drop() { } -void TBPPolDrop::move() { } +void TBPPolDrop::move() +{ + if (unk80 == 0) { + onHitFlag(HIT_FLAG_NO_COLLISION); + return; + } + + JGeometry::TVec3 nextPosition = mPosition; + nextPosition += unk6C; + + if (unk80 == 1) { + if (unk78->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) + unk78->setBck("pollut_ball"); + + unk6C.y -= 0.1f; + if (unk84 >= 60 || mOwner->is2ndFightNow()) { + const TBGCheckData* ground; + f32 groundHeight = gpMap->checkGround( + nextPosition.x, mPosition.y, nextPosition.z, &ground); + groundHeight += 1.0f; + if (ground->isIllegalData()) + groundHeight = unk88; + unk88 = groundHeight; + + if (nextPosition.y < groundHeight) { + unk80 = 2; + unk6C.zero(); + unk7C->setBck("pollut_ball_stamp"); + gpMarioParticleManager->emit(0x52, &mPosition, 0, nullptr); + SMSGetMSound()->startSoundActor( + MSD_SE_BS_BSPAKU_POLLUT_GND, &mPosition, 0, nullptr, 0, 4); + mOwner->rumblePad(2, mPosition); + nextPosition.y = groundHeight; + onHitFlag(HIT_FLAG_NO_COLLISION); + return; + } + + offHitFlag(HIT_FLAG_NO_COLLISION); + if (gpMap->isTouchedOneWallAndMoveXZ( + &nextPosition.x, nextPosition.y, &nextPosition.z, 80.0f)) + unk80 = 0; + + SMSGetMSound()->startSoundActorWithInfo( + MSD_SE_BS_BSPAKU_POLLUT_IMI, &mPosition, nullptr, -unk6C.y, 0, + 0, nullptr, 0, 4); + SMSGetMSound()->startSoundActorWithInfo( + MSD_SE_BS_BSPAKU_POLLUT_FLY, &mPosition, nullptr, -unk6C.y, 0, + 0, nullptr, 0, 4); + } + } else if (unk80 == 2 + && unk7C->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + unk80 = 0; + } + + mPosition = nextPosition; +} void TBPPolDrop::launch(const JGeometry::TVec3& position, const JGeometry::TVec3& velocity) From 8aeffd88882691ca9e596f5775ffe3578f9156e9 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:18:10 -0400 Subject: [PATCH 11/23] Decompile Boss Pakkun pollution drop rendering --- src/Enemy/bosspakkun.cpp | 66 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index c1c0a532d..144b27eb0 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -192,7 +193,70 @@ void TBPPolDrop::launch(const JGeometry::TVec3& position, unk88 = position.y; } -void TBPPolDrop::perform(u32, JDrama::TGraphics*) { } +void TBPPolDrop::perform(u32 flags, JDrama::TGraphics* graphics) +{ + if (unk80 == 0) + return; + + if (flags & CUE_MOVE) { + move(); + ++unk84; + } + + if (flags & CUE_MOVE) { + for (s32 i = 0; i < mColCount; ++i) { + THitActor* collision = mCollisions[i]; + if (!collision->isActorType(0x80000001)) + continue; + + collision->receiveMessage(this, HIT_MESSAGE_ATTACK); + mOwner->rumblePad(2, mPosition); + if (SMS_IsMarioTouchGround4cm()) + unk80 = 2; + else + unk80 = 0; + } + } + + u32 calcAnim = flags & CUE_CALC_ANIM; + if (calcAnim) { + MtxPtr mtx = unk78->getModel()->getBaseTRMtx(); + MTXIdentity(mtx); + mtx[0][3] = mPosition.x; + mtx[1][3] = mPosition.y; + mtx[2][3] = mPosition.z; + unk78->getModel()->setBaseScale(mScaling); + + if (unk80 == 2) { + f32 scale + = mOwner->getBossPakkunParams()->mSLPollBallStampScale.get(); + JGeometry::TVec3 stampScale(scale, scale, scale); + unk7C->getModel()->setBaseScale(stampScale); + MTXCopy(mtx, unk7C->getModel()->getBaseTRMtx()); + } + } + + if (unk80 == 1) { + unk78->perform(flags, graphics); + + if (flags & CUE_CALC_VIEW) { + TCircleShadowRequest request; + request.unk0 = mPosition; + request.unkC = 400.0f; + request.unk10 = 400.0f; + request.unk14 = 0.0f; + request.unk1C = 0; + gpBindShadowManager->request(request, 0); + } + } + + if (unk80 == 2) { + if (calcAnim) + unk7C->calcAnm(); + if (flags & CUE_ENTRY) + gpPollution->stampModel(unk7C->getModel()); + } +} TBPVomit::TBPVomit(TBossPakkun* owner, const char* name) : JDrama::TViewObj(name) From a155a7d6e55d7d67cbffef1cbbb7e08ef008be02 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:21:20 -0400 Subject: [PATCH 12/23] Decompile Boss Pakkun pollution drop construction --- src/Enemy/bosspakkun.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 144b27eb0..4121669ae 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,12 @@ TBPPolDrop::TBPPolDrop(TBossPakkun* owner, const char* name) , unk84(0) , unk88(0.0f) { + unk6C.zero(); + initHitActor(0x800000F, 1, 0x80000000, 0.0f, 0.0f, 100.0f, 200.0f); + offHitFlag(HIT_FLAG_NO_COLLISION); + JDrama::TNameRefGen::search("敵グループ") + ->getChildren() + .push_back(this); } void TBPPolDrop::drop() { } @@ -138,8 +145,8 @@ void TBPPolDrop::move() unk6C.y -= 0.1f; if (unk84 >= 60 || mOwner->is2ndFightNow()) { const TBGCheckData* ground; - f32 groundHeight = gpMap->checkGround( - nextPosition.x, mPosition.y, nextPosition.z, &ground); + f32 groundHeight = gpMap->checkGround(nextPosition.x, mPosition.y, + nextPosition.z, &ground); groundHeight += 1.0f; if (ground->isIllegalData()) groundHeight = unk88; @@ -150,8 +157,8 @@ void TBPPolDrop::move() unk6C.zero(); unk7C->setBck("pollut_ball_stamp"); gpMarioParticleManager->emit(0x52, &mPosition, 0, nullptr); - SMSGetMSound()->startSoundActor( - MSD_SE_BS_BSPAKU_POLLUT_GND, &mPosition, 0, nullptr, 0, 4); + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_POLLUT_GND, + &mPosition, 0, nullptr, 0, 4); mOwner->rumblePad(2, mPosition); nextPosition.y = groundHeight; onHitFlag(HIT_FLAG_NO_COLLISION); @@ -170,8 +177,7 @@ void TBPPolDrop::move() MSD_SE_BS_BSPAKU_POLLUT_FLY, &mPosition, nullptr, -unk6C.y, 0, 0, nullptr, 0, 4); } - } else if (unk80 == 2 - && unk7C->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + } else if (unk80 == 2 && unk7C->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { unk80 = 0; } @@ -308,8 +314,7 @@ TBPTornado::TBPTornado(TBossPakkun* owner, const char* name) , unk98(0) { mActor = mOwner->getActorKeeper()->createMActor("trunade.bmd", 0); - initHitActor(0x8000010, 5, 0x81000000, 150.0f, 600.0f, 100.0f, - 600.0f); + initHitActor(0x8000010, 5, 0x81000000, 150.0f, 600.0f, 100.0f, 600.0f); onHitFlag(HIT_FLAG_NO_COLLISION); mActor->setBtkFromIndex(2); mActor->setBckFromIndex(29); @@ -364,8 +369,7 @@ void TBPTornado::perform(u32 flags, JDrama::TGraphics* graphics) } PSVECNormalize(&direction, &direction); - direction - *= mOwner->getBossPakkunParams()->mSLTornadoSpeed.get(); + direction *= mOwner->getBossPakkunParams()->mSLTornadoSpeed.get(); unk7C += direction; s16 shortAngle = DEG2SHORTANGLE(angle); @@ -413,8 +417,8 @@ void TBPTornado::perform(u32 flags, JDrama::TGraphics* graphics) if (flags & CUE_CALC_ANIM) { MtxPtr mtx = mActor->getModel()->getBaseTRMtx(); - JPABaseEmitter* emitter = gpMarioParticleManager->emitAndBindToMtxPtr( - 0x162, mtx, 1, this); + JPABaseEmitter* emitter + = gpMarioParticleManager->emitAndBindToMtxPtr(0x162, mtx, 1, this); if (emitter) emitter->setGlobalScale(mScaling); @@ -1121,8 +1125,8 @@ DEFINE_NERVE(TNerveBPVomit, TLiveActor) if (actor->checkCurBckFromIndex(20) && rand() * (1.0f / 32768.0f) < 0.2f && spine->getTime() == 500) { JGeometry::TVec3 offset; - offset = fromPolar( - static_cast(DEG2SHORTANGLE(boss->mRotation.y)), 700.0f); + offset = fromPolar(static_cast(DEG2SHORTANGLE(boss->mRotation.y)), + 700.0f); JGeometry::TVec3 appearOffset = offset; gpItemManager->makeObjAppear( boss->mPosition.x + appearOffset.x, boss->mPosition.y + 1.0f, From 447859605c605284e0edc9b4670c55e926110b09 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:03:07 -0400 Subject: [PATCH 13/23] Decompile Boss Pakkun support actor transitions --- src/Enemy/bosspakkun.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 4121669ae..dfb72ae03 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -126,7 +126,16 @@ TBPPolDrop::TBPPolDrop(TBossPakkun* owner, const char* name) .push_back(this); } -void TBPPolDrop::drop() { } +void TBPPolDrop::drop() +{ + unk80 = 2; + unk6C.zero(); + unk7C->setBck("pollut_ball_stamp"); + gpMarioParticleManager->emit(0x52, &mPosition, 0, nullptr); + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_POLLUT_GND, &mPosition, 0, + nullptr, 0, 4); + mOwner->rumblePad(2, mPosition); +} void TBPPolDrop::move() { @@ -153,13 +162,7 @@ void TBPPolDrop::move() unk88 = groundHeight; if (nextPosition.y < groundHeight) { - unk80 = 2; - unk6C.zero(); - unk7C->setBck("pollut_ball_stamp"); - gpMarioParticleManager->emit(0x52, &mPosition, 0, nullptr); - SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_POLLUT_GND, - &mPosition, 0, nullptr, 0, 4); - mOwner->rumblePad(2, mPosition); + drop(); nextPosition.y = groundHeight; onHitFlag(HIT_FLAG_NO_COLLISION); return; @@ -284,7 +287,11 @@ void TBPVomit::vomit() unk18->getModel()->setBaseScale(mOwner->mScaling); } -void TBPVomit::vomitFinished() { } +void TBPVomit::vomitFinished() +{ + unk14->setBckFromIndex(-1); + unk18->setBckFromIndex(-1); +} void TBPVomit::perform(u32 flags, JDrama::TGraphics* graphics) { @@ -293,8 +300,7 @@ void TBPVomit::perform(u32 flags, JDrama::TGraphics* graphics) u32 calcAnim = flags & CUE_CALC_ANIM; if (calcAnim && unk14->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { - unk14->setBckFromIndex(-1); - unk18->setBckFromIndex(-1); + vomitFinished(); return; } From 7d74a6733376377b68f7baee418a9c811cc98aa6 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:06:32 -0400 Subject: [PATCH 14/23] Decompile Boss Pakkun construction and collision checks --- include/Enemy/BossPakkun.hpp | 4 +-- src/Enemy/bosspakkun.cpp | 51 ++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/include/Enemy/BossPakkun.hpp b/include/Enemy/BossPakkun.hpp index 61f49b7c5..cf3988362 100644 --- a/include/Enemy/BossPakkun.hpp +++ b/include/Enemy/BossPakkun.hpp @@ -105,7 +105,7 @@ class TBossPakkun : public TSpineEnemy { void showMessage(u32); void rumblePad(int, const JGeometry::TVec3&); void startBGM(); - void checkMarioRiding(); + BOOL checkMarioRiding(); TBossPakkunParams* getBossPakkunParams() const { @@ -129,7 +129,7 @@ class TBossPakkun : public TSpineEnemy { /* 0x184 */ f32 unk184; /* 0x188 */ TAreaCylinderManager* unk188; /* 0x18C */ TWaterEmitInfo* unk18C; - /* 0x190 */ u8 unk190; + /* 0x190 */ s8 unk190; /* 0x194 */ JGeometry::TVec3 unk194; /* 0x1A0 */ JGeometry::TVec3 unk1A0; /* 0x1AC */ JGeometry::TVec3 unk1AC; diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index dfb72ae03..43c654dbe 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -743,7 +744,6 @@ void TBossPakkunMtxCalc::calc(u16 jointIndex) TBossPakkun::TBossPakkun(const char* name) : TSpineEnemy(name) - , mMtxCalc(nullptr) , unk154(0.0f) , mPolDrop(nullptr) , mVomit(nullptr) @@ -764,14 +764,33 @@ TBossPakkun::TBossPakkun(const char* name) , unk1BC(0) , unk1C0(0) , unk1C4(0) - , unk1C8(0.0f) , unk1CC(0) { + offLiveFlag(LIVE_FLAG_UNK100); + mBinder = new TWalker; } void TBossPakkun::init(TLiveManager*) { } -void TBossPakkun::checkMarioRiding() { } +BOOL TBossPakkun::checkMarioRiding() +{ + const TBGCheckData* ground = SMS_GetMarioGrPlane(); + if (unk190 == 0) { + if (ground != nullptr && ground->getActor() == this + && SMS_IsMarioTouchGround4cm()) { + u32 status = SMS_GetMarioStatus(); + if ((status & MARIO_STATUS_FLAG_UNK200) + && !(status & MARIO_STATUS_FLAG_UNK200000)) { + unk190 = 1; + return true; + } + } + } else if (ground == nullptr || ground->getActor() != this + || !SMS_IsMarioTouchGround4cm()) { + unk190 = 0; + } + return false; +} void TBossPakkun::startBGM() { } @@ -805,7 +824,19 @@ void TBossPakkun::rumblePad(int type, const JGeometry::TVec3& position) SMSRumbleMgr->start(8, &unk1C8); } -void TBossPakkun::showMessage(u32) { } +void TBossPakkun::showMessage(u32 message) +{ + u32 index = message - 0xe0000; + u32 mask; + if (index == 1) + mask = 0; + else + mask = 1 << index; + + if (!(unk1C0 & mask)) + gpMarDirector->getConsole()->startAppearBalloon(message, true); + unk1C0 |= mask; +} bool TBossPakkun::is2ndFightNow() const { @@ -933,7 +964,17 @@ const char** TBossPakkun::getBasNameTable() const void TBossPakkun::setGroundCollision() { } -void TBossPakkun::kill() { } +void TBossPakkun::kill() +{ + TLiveActor::kill(); + onHitFlag(HIT_FLAG_NO_COLLISION); + if (mHeadHit != nullptr) + mHeadHit->onHitFlag(HIT_FLAG_NO_COLLISION); + if (mNavel != nullptr) + mNavel->onHitFlag(HIT_FLAG_NO_COLLISION); + if (mPolDrop != nullptr) + mPolDrop->onHitFlag(HIT_FLAG_NO_COLLISION); +} BOOL TBossPakkun::receiveMessage(THitActor*, u32) { return false; } From 3ad47513f323e17ff5c6e7135f82d9e0aea6f359 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:13:40 -0400 Subject: [PATCH 15/23] Reconstruct Boss Pakkun damage and effect helpers --- src/Enemy/bosspakkun.cpp | 164 +++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 43c654dbe..3890aa232 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -483,11 +484,7 @@ BOOL TBPHeadHit::receiveMessage(THitActor* sender, u32 message) u32 actorType = sender->getActorType(); if (boss->unk16C == 3 && (actorType == 0x1000000d || actorType == 0x1000001)) { - boss->unk16C = 0; - boss->mSpine->reset(); - boss->mSpine->setNext(&TNerveBPFall::theNerve()); - SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_FALL, &boss->mPosition, - 0, nullptr, 0, 4); + boss->gotFlyingDamage(); return true; } @@ -506,20 +503,7 @@ BOOL TBPHeadHit::receiveMessage(THitActor* sender, u32 message) angle = MsAngleDiff(MsGetRotFromZaxisY(toMario), boss->mRotation.y); if (fabsf(angle) < 0.5f * boss->getBossPakkunParams()->mSLDamageAngle.get()) { - if (boss->unk17C == 0) { - boss->unk170 += 1; - if (boss->unk178 - < boss->getBossPakkunParams()->mSLWaterMarkLimit.get()) - boss->unk178 += 1; - boss->unk174 - = boss->getBossPakkunParams()->mSLWaterHitTimer.get(); - - if (boss->mSpine->getLatestNerve() - != &TNerveBPSwallow::theNerve()) { - boss->mSpine->reset(); - boss->mSpine->setNext(&TNerveBPSwallow::theNerve()); - } - } + boss->gotWaterDamage(); } } @@ -792,7 +776,13 @@ BOOL TBossPakkun::checkMarioRiding() return false; } -void TBossPakkun::startBGM() { } +void TBossPakkun::startBGM() +{ + if (unk1CC == 0) { + MSBgm::startBGM(MSD_BGM_MAP_SELECT); + unk1CC = 1; + } +} void TBossPakkun::rumblePad(int type, const JGeometry::TVec3& position) { @@ -847,9 +837,28 @@ bool TBossPakkun::is2ndFightNow() const void TBossPakkun::ignoreWaterCheck() { } -void TBossPakkun::startTornadoBlur() { } +void TBossPakkun::startTornadoBlur() +{ + gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &unk194, 0, nullptr); + gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &unk1A0, 0, nullptr); +} -void TBossPakkun::resetWaterMark() { } +void TBossPakkun::resetWaterMark() +{ + if (unk17C == 0) { + unk17C = 1; + unk174 = 0; + unk170 = 0; + unk1B8 = 50; + if (unk18C != nullptr) { + JGeometry::TVec3 position; + getJointTransByIndex(18, &position); + position.y += 250.0f; + unk18C->mPos.value = position; + gpModelWaterManager->emitRequest(*unk18C); + } + } +} BOOL TBossPakkun::inArea(const JGeometry::TVec3& position) { @@ -861,9 +870,29 @@ BOOL TBossPakkun::inArea(const JGeometry::TVec3& position) return unk188 == nullptr ? false : unk188->contain(position); } -void TBossPakkun::gotFlyingDamage() { } +void TBossPakkun::gotFlyingDamage() +{ + unk16C = 0; + mSpine->reset(); + mSpine->setNext(&TNerveBPFall::theNerve()); + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_FALL, &mPosition, 0, + nullptr, 0, 4); +} -void TBossPakkun::gotWaterDamage() { } +void TBossPakkun::gotWaterDamage() +{ + if (unk17C == 0) { + unk170 += 1; + if (unk178 < getBossPakkunParams()->mSLWaterMarkLimit.get()) + unk178 += 1; + unk174 = getBossPakkunParams()->mSLWaterHitTimer.get(); + + if (mSpine->getLatestNerve() != &TNerveBPSwallow::theNerve()) { + mSpine->reset(); + mSpine->setNext(&TNerveBPSwallow::theNerve()); + } + } +} void TBossPakkun::gotHipDropDamage() { } @@ -895,9 +924,15 @@ void TBossPakkun::launchPolDrop() mPolDrop->launch(launchPosition, velocity); } -void TBossPakkun::launchTornado() { } +void TBossPakkun::launchTornado() { mTornado->launch(*gpMarioPos); } -void TBossPakkun::killSmallEnemies() { } +void TBossPakkun::killSmallEnemies() +{ + TNameKuriManager* manager + = JDrama::TNameRefGen::search("ナメクリマネージャー"); + if (manager != nullptr) + manager->killChildren(); +} void TBossPakkun::changeBck(int index) { @@ -962,7 +997,18 @@ const char** TBossPakkun::getBasNameTable() const return bosspakkun_bastable; } -void TBossPakkun::setGroundCollision() { } +void TBossPakkun::setGroundCollision() +{ + const TNerveBase* dieNerve = &TNerveBPDie::theNerve(); + if (mSpine->getLatestNerve() != dieNerve + && mMapCollisionManager != nullptr) { + TPosition3f collisionMtx; + collisionMtx.set(getModel()->getAnmMtx(2)); + TMapCollisionBase* collision = mMapCollisionManager->unk8; + if (collision != nullptr) + collision->moveMtx(collisionMtx); + } +} void TBossPakkun::kill() { @@ -1213,13 +1259,10 @@ DEFINE_NERVE(TNerveBPTornado, TLiveActor) boss->changeBck(24); gpMarioParticleManager->emitAndBindToSRTMtxPtr( 0xab, boss->getModel()->getAnmMtx(3), 0, boss); - gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &boss->unk194, 0, - nullptr); - gpMarioParticleManager->emitAndBindToPosPtr(0xa9, &boss->unk1A0, 0, - nullptr); + boss->startTornadoBlur(); } if (spine->getTime() == 150) - boss->mTornado->launch(*gpMarioPos); + boss->launchTornado(); if (actor->isCurAnmAlreadyEnd(ANM_TYPE_BCK)) return true; return false; @@ -1348,19 +1391,8 @@ DEFINE_NERVE(TNerveBPTumbleOut, TLiveActor) if (actor->checkCurBckFromIndex(22)) { f32 frame = actor->getFrameCtrl(ANM_TYPE_BCK)->getFrame(); - if (140.0f < frame && frame < 160.0f && boss->unk17C == 0) { - boss->unk17C = 1; - boss->unk174 = 0; - boss->unk170 = 0; - boss->unk1B8 = 50; - if (boss->unk18C != nullptr) { - JGeometry::TVec3 position; - boss->getJointTransByIndex(18, &position); - position.y += 250.0f; - boss->unk18C->mPos.value = position; - gpModelWaterManager->emitRequest(*boss->unk18C); - } - } + if (140.0f < frame && frame < 160.0f) + boss->resetWaterMark(); if (35.0f < frame) boss->unk1BC = 1; } @@ -1411,20 +1443,8 @@ DEFINE_NERVE(TNerveBPStompReact, TLiveActor) boss->mHeadHit->onHitFlag(HIT_FLAG_NO_COLLISION); } - if (spine->getTime() == 30 && boss->unk17C == 0) { - boss->unk17C = 1; - boss->unk174 = 0; - boss->unk170 = 0; - boss->unk1B8 = 50; - - if (boss->unk18C != nullptr) { - JGeometry::TVec3 position; - boss->getJointTransByIndex(18, &position); - position.y += 250.0f; - boss->unk18C->mPos.value = position; - gpModelWaterManager->emitRequest(*boss->unk18C); - } - } + if (spine->getTime() == 30) + boss->resetWaterMark(); if (spine->getTime() == 50) boss->unk1BC = 1; @@ -1455,25 +1475,8 @@ DEFINE_NERVE(TNerveBPPreDie, TLiveActor) if (spine->getTime() == 0) { boss->changeBck(5); boss->mHeadHit->onHitFlag(HIT_FLAG_NO_COLLISION); - if (boss->unk17C == 0) { - boss->unk17C = 1; - boss->unk174 = 0; - boss->unk170 = 0; - boss->unk1B8 = 50; - if (boss->unk18C != nullptr) { - JGeometry::TVec3 position; - boss->getJointTransByIndex(18, &position); - position.y += 250.0f; - boss->unk18C->mPos.value = position; - gpModelWaterManager->emitRequest(*boss->unk18C); - } - } - - TNameKuriManager* manager - = JDrama::TNameRefGen::search( - "ナメクリマネージャー"); - if (manager != nullptr) - manager->killChildren(); + boss->resetWaterMark(); + boss->killSmallEnemies(); MSBgm::stopTrackBGM(1, 10); } @@ -1541,10 +1544,7 @@ DEFINE_NERVE(TNerveBPFly, TLiveActor) if (spine->getTime() == 0) { boss->changeBck(11); boss->goToRandomNextGraphNode(); - if (boss->unk1CC == 0) { - MSBgm::startBGM(MSD_BGM_MAP_SELECT); - boss->unk1CC = 1; - } + boss->startBGM(); } JGeometry::TVec3 toNext = boss->getUnk104().getPoint(); From 8c06e70e5dd7912e0c0531722376fdb22f11a78f Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:27:03 -0400 Subject: [PATCH 16/23] Decompile Boss Pakkun initialization --- src/Enemy/bosspakkun.cpp | 86 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 3890aa232..a6c933b85 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -754,7 +756,89 @@ TBossPakkun::TBossPakkun(const char* name) mBinder = new TWalker; } -void TBossPakkun::init(TLiveManager*) { } +void TBossPakkun::init(TLiveManager* manager) +{ + mManager = manager; + mManager->manageActor(this); + + mMActorKeeper = new TMActorKeeper(mManager, 7); + mMActor = mMActorKeeper->createMActor("bosspaku_model.bmd", 0); + + if (static_cast(mManager)->unk54 == 0) { + unk180 = mMActorKeeper->createMActor("bosspaku_end.bmd", 0); + unk180->setBckFromIndex(7); + unk180->setBrkFromIndex(0); + } + + TIdxGroupObj* group + = JDrama::TNameRefGen::search("敵グループ"); + initHitActor(0x800000F, 1, 0x80000000, 80.0f, 300.0f, 80.0f, 300.0f); + offHitFlag(HIT_FLAG_NO_COLLISION); + + if (static_cast(mManager)->unk54 == 0) { + mHeadHit = new TBPHeadHit(this, "ボスパックン頭部"); + mNavel = new TBPNavel(this, "ボスパックンへそ"); + group->getChildren().push_back(mHeadHit); + group->getChildren().push_back(mNavel); + + mMapCollisionManager + = new TMapCollisionManager(1, "/scene/bosspakkun", this); + mMapCollisionManager->init("col_body.col", 1, nullptr); + + mMapCollisionManager->setUpUnk8TRS(mPosition, mRotation, mScaling); + + mMtxCalc = new TBossPakkunMtxCalc(this); + mMActor->setCalcForBck(mMtxCalc); + mMActor->calc(); + } + + if (static_cast(mManager)->unk54 != 0) { + mSpine->initWith(&TNerveBPWaitL::theNerve()); + } else if (gpMarDirector->mMap == 0x37) { + mSpine->initWith(&TNerveBPFall::theNerve()); + } else if (gpMarDirector->unk7D == 4) { + mSpine->initWith(&TNerveBPSleep::theNerve()); + } else { + mSpine->initWith(&TNerveBPWait::theNerve()); + } + + mPolDrop = new TBPPolDrop(this); + MActor* stamp = mMActorKeeper->createMActor("pollut_ball_stamp.bmd", 0); + mPolDrop->unk78 = mMActorKeeper->createMActor("pollut_ball.bmd", 0); + mPolDrop->unk7C = stamp; + const ResTIMG* res = static_cast( + JKRFileLoader::getGlbResource("/scene/map/pollution/H_ma_rak.bti")); + if (res != nullptr) { + SMS_ChangeTextureAll(mPolDrop->unk78->getModel()->getModelData(), + "M_dummy", *res); + } + + if (static_cast(mManager)->unk54 == 0) { + mVomit = new TBPVomit(this, ""); + MActor* white + = mMActorKeeper->createMActor("bosspakuPollut_white.bmd", 0); + MActor* pollut = mMActorKeeper->createMActor("bosspakuPollut.bmd", 0); + mVomit->unk14 = pollut; + mVomit->unk18 = white; + + mTornado = new TBPTornado(this, ""); + group->getChildren().push_back(mTornado); + + unk18C = new TWaterEmitInfo("/enemy/bosspakuwater.prm"); + } + + initAnmSound(); + onLiveFlag(LIVE_FLAG_UNK400); + mScaledBodyRadius = 400.0f; + + unk124->setGraph(gpConductor->getGraphByName("bosspakkun")); + if (unk124->getGraph() != nullptr) { + unk124->reset(); + goToShortestNextGraphNode(); + } + + mHitPoints = getMaxHitPoints(); +} BOOL TBossPakkun::checkMarioRiding() { From 49ab4556b05b933dc47025d888e0e95754344937 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:41:36 -0400 Subject: [PATCH 17/23] Decompile Boss Pakkun performance updates --- src/Enemy/bosspakkun.cpp | 142 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index a6c933b85..5ed1c0b84 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -1108,7 +1110,145 @@ void TBossPakkun::kill() BOOL TBossPakkun::receiveMessage(THitActor*, u32) { return false; } -void TBossPakkun::perform(u32, JDrama::TGraphics*) { } +void TBossPakkun::perform(u32 cue, JDrama::TGraphics* graphics) +{ + if (mPolDrop != nullptr) + mPolDrop->perform(cue, graphics); + if (mVomit != nullptr) + mVomit->perform(cue, graphics); + if (mTornado != nullptr) + mTornado->perform(cue, graphics); + + if (checkLiveFlag(LIVE_FLAG_DEAD)) + return; + + if (mHeadHit != nullptr) + mHeadHit->perform(cue, graphics); + if (mNavel != nullptr) + mNavel->perform(cue, graphics); + + if (static_cast(mManager)->unk54 == 0 + && (cue & CUE_MOVE)) { + mMtxCalc->advanceMotionBlend(-unk154); + + if (unk17C) { + if (unk178 <= 0) { + unk17C = 0; + unk178 = 0; + } else { + s32 step = getBossPakkunParams()->mSLWaterMarkLimit.get() / 100; + if (step == 0) + step = 1; + unk178 -= step; + if (unk178 < 0) + unk178 = 0; + } + } + + if (unk17C == 0) { + if (unk174 > 0) { + unk174--; + unk170++; + } + } + + if (unk1BC) { + if (unk1B8 <= 0) { + unk1BC = 0; + unk1B8 = 0; + } else { + unk1B8--; + } + } + + if (unk16C == 1 && checkMarioRiding()) { + if (&TNerveBPJumpReact::theNerve() != mSpine->getLatestNerve()) { + mSpine->pushNerve(&TNerveBPJumpReact::theNerve()); + } + } + } + + if (static_cast(mManager)->unk54 == 0 + && (cue & CUE_CALC_ANIM)) { + if (mMActor->checkCurBckFromIndex(24)) { + MtxPtr mtx = mMActor->getModel()->getAnmMtx(43); + unk194.set(mtx[0][3], mtx[1][3], mtx[2][3]); + mtx = mMActor->getModel()->getAnmMtx(44); + unk1A0.set(mtx[0][3], mtx[1][3], mtx[2][3]); + } + + if (mMActor->checkCurBckFromIndex(23)) { + MtxPtr mtx = mMActor->getModel()->getAnmMtx(20); + unk1AC.set(mtx[0][3], mtx[1][3], mtx[2][3]); + JPABaseEmitter* emitter + = gpMarioParticleManager->emitAndBindToPosPtr( + PARTICLE_MS_POI_ZZZ, &unk1AC, 1, this); + if (emitter != nullptr) { + static JGeometry::TVec3 scale(2.5f, 2.5f, 2.5f); + emitter->setGlobalScale(scale); + } + } + + if (mMActor->checkCurBckFromIndex(11) + || mMActor->checkCurBckFromIndex(13) + || mMActor->checkCurBckFromIndex(12)) { + gpMarioParticleManager->emitAndBindToMtxPtr( + 0x160, getModel()->getAnmMtx(38), 1, this); + gpMarioParticleManager->emitAndBindToMtxPtr( + 0x160, getModel()->getAnmMtx(46), 1, (u8*)this + 1); + } + + if (mMActor->checkCurBckFromIndex(11) + || mMActor->checkCurBckFromIndex(6) + || mMActor->checkCurBckFromIndex(26)) { + gpMarioParticleManager->emitAndBindToMtxPtr( + 0x15F, getModel()->getAnmMtx(20), 1, (u8*)this + 1); + } + } + + if (static_cast(mManager)->unk54 == 0) { + if (&TNerveBPDie::theNerve() == mSpine->getLatestNerve()) { + MActor* origActor = mMActor; + mMActor = unk180; + TSpineEnemy::perform(cue, graphics); + mMActor = origActor; + return; + } + } + + if (static_cast(mManager)->unk54 == 0 + && (cue & CUE_CALC_ANIM)) { + updateSquareToMario(); + getModel()->getModelData()->getJointNodePointer(0)->setMtxCalc( + mMtxCalc); + } + + if (static_cast(mManager)->unk54 == 0 + && (cue & CUE_CALC_ANIM)) { + if (unk16C == 1) { + JGeometry::TVec3 pos = mNavel->mPosition; + pos.y += 100.0f; + gpTargetArrow->unk14 = 1; + gpTargetArrow->setPos(pos); + } else { + gpTargetArrow->unk14 = 0; + } + } + + if (static_cast(mManager)->unk54 == 0 + && (cue & CUE_ENTRY)) { + if (&TNerveBPPreDie::theNerve() == mSpine->getLatestNerve() + || &TNerveBPStompReact::theNerve() == mSpine->getLatestNerve()) { + mMActor->offMakeDL(); + SMS_AddDamageFogEffect(mMActor->getModel()->getModelData(), + mPosition, graphics); + } else { + SMS_ResetDamageFogEffect(mMActor->getModel()->getModelData()); + } + } + + TSpineEnemy::perform(cue, graphics); +} TBossPakkunManager::TBossPakkunManager(const char* name, int value) : TEnemyManager(name) From f11a21526a5d2f4ee5a97d009e751d70ec905d11 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:47:15 -0400 Subject: [PATCH 18/23] Decompile Boss Pakkun hip-drop damage --- src/Enemy/bosspakkun.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 5ed1c0b84..f00b145e3 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -980,7 +980,33 @@ void TBossPakkun::gotWaterDamage() } } -void TBossPakkun::gotHipDropDamage() { } +void TBossPakkun::gotHipDropDamage() +{ + decHitPoints(); + unk16C = 0; + if (getHitPoints() == 0) { + if (&TNerveBPPreDie::theNerve() != mSpine->getLatestNerve() + && &TNerveBPDie::theNerve() != mSpine->getLatestNerve()) { + mSpine->setNext(&TNerveBPPreDie::theNerve()); + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_DOWN, &mPosition, + 0, nullptr, 0, 4); + } + } else if (&TNerveBPTumbleOut::theNerve() != mSpine->getLatestNerve()) { + SMSGetMSound()->startSoundActor(MSD_SE_BS_BSPAKU_DAMAGE, &mPosition, 0, + nullptr, 0, 4); + if (gpMarDirector->getCurrentStage() == 4) { + mSpine->reset(); + mSpine->setNext(&TNerveBPTakeOff::theNerve()); + mSpine->pushNerve(&TNerveBPGetUp::theNerve()); + mSpine->pushNerve(&TNerveBPStompReact::theNerve()); + } else { + mSpine->reset(); + mSpine->setNext(&TNerveBPWait::theNerve()); + mSpine->pushNerve(&TNerveBPGetUp::theNerve()); + mSpine->pushNerve(&TNerveBPStompReact::theNerve()); + } + } +} void TBossPakkun::gotTrampleDamage() { } From 38750c7ed23708ebe1655c0b52a0ce28391e7b5d Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:49:51 -0400 Subject: [PATCH 19/23] Decompile Boss Pakkun message handling --- src/Enemy/bosspakkun.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index f00b145e3..81b8e849a 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -1134,7 +1134,30 @@ void TBossPakkun::kill() mPolDrop->onHitFlag(HIT_FLAG_NO_COLLISION); } -BOOL TBossPakkun::receiveMessage(THitActor*, u32) { return false; } +BOOL TBossPakkun::receiveMessage(THitActor* sender, u32) +{ + if (static_cast(mManager)->unk54 != 0) + return false; + + if (&TNerveBPSleep::theNerve() == mSpine->getLatestNerve() + && sender->getActorType() == 0x1000000d) { + mSpine->reset(); + mSpine->setNext(&TNerveBPBreakSleep::theNerve()); + return true; + } + + if (unk16C == 3 + && (sender->getActorType() == 0x1000000d + || sender->getActorType() == 0x1000001)) { + if (mPosition.y - 300.0f > sender->mPosition.y) + return true; + if (mPosition.y + 1500.0f < sender->mPosition.y) + return true; + gotFlyingDamage(); + return true; + } + return false; +} void TBossPakkun::perform(u32 cue, JDrama::TGraphics* graphics) { From 8789c29487713aa162ee67e46224376b618defed Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:09:03 -0400 Subject: [PATCH 20/23] Decompile Boss Pakkun damage messages and animation blending --- src/Enemy/bosspakkun.cpp | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 81b8e849a..dda92623e 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -80,12 +80,6 @@ static inline JGeometry::TVec3 fromPolar(s16 angle, f32 radius) radius * JMASCos(angle)); } -// fabricated -static inline JGeometry::TVec3 fromPolar(f32 angle, f32 radius) -{ - return fromPolar(static_cast(DEG2SHORTANGLE(angle)), radius); -} - TBossPakkunParams::TBossPakkunParams(const char* path) : TSpineEnemyParams(path) , PARAM_INIT(mSLWaitFrameStg0, 400) @@ -481,34 +475,38 @@ TBPHeadHit::TBPHeadHit(TBossPakkun* owner, const char* name) BOOL TBPHeadHit::receiveMessage(THitActor* sender, u32 message) { - TBossPakkun* boss = mOwner; - if (boss->mSpine->getLatestNerve() == &TNerveBPSleep::theNerve()) - return boss->receiveMessage(sender, message); + if (&TNerveBPSleep::theNerve() == mOwner->mSpine->getLatestNerve()) + return mOwner->receiveMessage(sender, message); - u32 actorType = sender->getActorType(); + TBossPakkun* boss = mOwner; if (boss->unk16C == 3 - && (actorType == 0x1000000d || actorType == 0x1000001)) { + && (sender->getActorType() == 0x1000000d + || sender->getActorType() == 0x1000001)) { boss->gotFlyingDamage(); return true; } if (boss->unk16C != 2) { if (boss->is2ndFightNow() - && boss->mSpine->getLatestNerve() == &TNerveBPFly::theNerve()) + && &TNerveBPFly::theNerve() == boss->mSpine->getLatestNerve()) boss->showMessage(0xe0002); - return actorType == 0x1000001; + if (sender->getActorType() == 0x1000001) + return true; + return false; } - if (actorType == 0x1000001 && message == HIT_MESSAGE_SPRAYED_BY_WATER) { + if (sender->getActorType() == 0x1000001 + && message == HIT_MESSAGE_SPRAYED_BY_WATER) { JGeometry::TVec3 toMario = *gpMarioPos; toMario -= mPosition; - f32 angle = MsAngleWrap(MsGetRotFromZaxisY(toMario)); - angle = MsAngleDiff(MsGetRotFromZaxisY(toMario), boss->mRotation.y); + f32 angle = MsWrap(MsGetRotFromZaxisY(toMario), 0.0f, 360.0f); + angle = MsAngleDiff(MsGetRotFromZaxisY(toMario), mOwner->mRotation.y); if (fabsf(angle) - < 0.5f * boss->getBossPakkunParams()->mSLDamageAngle.get()) { - boss->gotWaterDamage(); + < 0.5f * mOwner->getBossPakkunParams()->mSLDamageAngle.get()) { + mOwner->gotWaterDamage(); } + return true; } return true; @@ -569,7 +567,7 @@ TBPNavel::TBPNavel(TBossPakkun* owner, const char* name) BOOL TBPNavel::receiveMessage(THitActor* sender, u32 message) { - if (mOwner->mSpine->getLatestNerve() == &TNerveBPSleep::theNerve()) + if (&TNerveBPSleep::theNerve() == mOwner->mSpine->getLatestNerve()) return mOwner->receiveMessage(sender, message); if (sender->getActorType() == 0x1000001) @@ -973,7 +971,7 @@ void TBossPakkun::gotWaterDamage() unk178 += 1; unk174 = getBossPakkunParams()->mSLWaterHitTimer.get(); - if (mSpine->getLatestNerve() != &TNerveBPSwallow::theNerve()) { + if (&TNerveBPSwallow::theNerve() != mSpine->getLatestNerve()) { mSpine->reset(); mSpine->setNext(&TNerveBPSwallow::theNerve()); } @@ -1025,8 +1023,8 @@ void TBossPakkun::launchPolDrop() f32 marioYaw = gpMarioOriginal->mRotation.y; f32 front = getBossPakkunParams()->mSLPollBallFront.get(); - JGeometry::TVec3 targetOffset = fromPolar(marioYaw, front); - JGeometry::TVec3 target = targetOffset; + s16 angle = DEG2SHORTANGLE(marioYaw); + JGeometry::TVec3 target = fromPolar(angle, front); target += SMS_GetMarioPos(); JGeometry::TVec3 velocity; @@ -1062,21 +1060,21 @@ void TBossPakkun::changeBck(int index) } f32 blendTime = -1.0f; - switch (previous) { - case 25: + if (previous == 25) { if (index == 21 || index == 26) blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); - break; - case 18: - case 2: - case 20: + } else if (previous == 18) { if (index == 25) blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); - break; - case 26: + } else if (previous == 2) { + if (index == 25) + blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); + } else if (previous == 20) { + if (index == 25) + blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); + } else if (previous == 26) { if (index == 22) blendTime = getBossPakkunParams()->mSLAnmBlendTime0.get(); - break; } if (blendTime < 0.0f) { @@ -1085,7 +1083,10 @@ void TBossPakkun::changeBck(int index) blendTime = 0.1f * ctrl->getEnd(); } - unk154 = blendTime == 0.0f ? 1.0f : 1.0f / blendTime; + if (blendTime == 0.0f) + unk154 = 1.0f; + else + unk154 = 1.0f / blendTime; const char** table = getBasNameTable(); setAnmSound(table == nullptr ? nullptr : table[index]); From b65d71f57418c85e7431fcdf4713b03e9bc7f61a Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:14:09 -0400 Subject: [PATCH 21/23] Include the MActor mtx calc type names in Boss Pakkun --- src/Enemy/bosspakkun.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index dda92623e..2bc08f9e5 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include From 5845c12f14f0f384cd9433893ed7a90ae9f19a96 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:03:18 -0400 Subject: [PATCH 22/23] Match Boss Pakkun ground collision update --- src/Enemy/bosspakkun.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 2bc08f9e5..2f3a3ca22 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -1118,9 +1118,8 @@ void TBossPakkun::setGroundCollision() && mMapCollisionManager != nullptr) { TPosition3f collisionMtx; collisionMtx.set(getModel()->getAnmMtx(2)); - TMapCollisionBase* collision = mMapCollisionManager->unk8; - if (collision != nullptr) - collision->moveMtx(collisionMtx); + if (mMapCollisionManager->unk8 != nullptr) + mMapCollisionManager->unk8->moveMtx(collisionMtx); } } From 19f758195c74c7306ac6d310aa46500ceeff3c95 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:47:51 -0400 Subject: [PATCH 23/23] Boss Pakkun: name cue flag, unify bind-ID cast spelling --- src/Enemy/bosspakkun.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Enemy/bosspakkun.cpp b/src/Enemy/bosspakkun.cpp index 2f3a3ca22..c1d4278fe 100644 --- a/src/Enemy/bosspakkun.cpp +++ b/src/Enemy/bosspakkun.cpp @@ -429,13 +429,13 @@ void TBPTornado::perform(u32 flags, JDrama::TGraphics* graphics) if (emitter) emitter->setGlobalScale(mScaling); - emitter = gpMarioParticleManager->emitAndBindToMtxPtr( - 0x163, mtx, 1, reinterpret_cast(this) + 1); + emitter = gpMarioParticleManager->emitAndBindToMtxPtr(0x163, mtx, 1, + (u8*)this + 1); if (emitter) emitter->setGlobalScale(mScaling); - emitter = gpMarioParticleManager->emitAndBindToMtxPtr( - 0x164, mtx, 1, reinterpret_cast(this) + 2); + emitter = gpMarioParticleManager->emitAndBindToMtxPtr(0x164, mtx, 1, + (u8*)this + 2); if (emitter) emitter->setGlobalScale(mScaling); } @@ -585,7 +585,7 @@ BOOL TBPNavel::receiveMessage(THitActor* sender, u32 message) void TBPNavel::perform(u32 flags, JDrama::TGraphics* graphics) { - if (flags & 2) + if (flags & CUE_CALC_ANIM) mOwner->getJointTransByIndex(6, &mPosition); THitActor::perform(flags, graphics);