From 83c675f5c43012ddbac482cc3ccb19af4071587c Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:59:57 -0400 Subject: [PATCH 1/8] Point the strings rogue includes at the right header Now that the dummy pair has its own header, the TUs that retail shows carrying only the pair should take System/DummyStrings.hpp rather than dragging the mtx calc type names in with it: PauseMenu2, MSound, MapWire and Application. CameraJetCoaster and MapEventMare carry neither set in retail, so their include is just wrong and goes away. Found by diffing what each retail object actually contains against what our sources include. Co-Authored-By: Claude Opus 4.8 --- src/Camera/CameraJetCoaster.cpp | 1 - src/GC2D/PauseMenu2.cpp | 2 +- src/MSound/MSound.cpp | 2 +- src/Map/MapEventMare.cpp | 1 - src/Map/MapWire.cpp | 2 +- src/System/Application.cpp | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Camera/CameraJetCoaster.cpp b/src/Camera/CameraJetCoaster.cpp index c379f86a3..37446399a 100644 --- a/src/Camera/CameraJetCoaster.cpp +++ b/src/Camera/CameraJetCoaster.cpp @@ -20,7 +20,6 @@ // rogue includes needed for matching sinit & bss #include #include -#include // NOTE: it is weak but I don't want to put it in the header since it's pretty // big and only used in one function. Also, dunno even if the size is correct diff --git a/src/GC2D/PauseMenu2.cpp b/src/GC2D/PauseMenu2.cpp index 0d1ea52c8..0b90a4c3f 100644 --- a/src/GC2D/PauseMenu2.cpp +++ b/src/GC2D/PauseMenu2.cpp @@ -33,7 +33,7 @@ extern JPAEmitterManager* gpEmitterManager4D2; // rogue includes needed for matching sinit & bss #include #include -#include +#include // fabricated void TPauseMenu2::draw(JDrama::TGraphics* gfx) diff --git a/src/MSound/MSound.cpp b/src/MSound/MSound.cpp index 62f9efcdd..d56c901c3 100644 --- a/src/MSound/MSound.cpp +++ b/src/MSound/MSound.cpp @@ -23,7 +23,7 @@ #include // rogue -#include +#include // TODO: place in correct header template static inline T min(T a, T b) { return a < b ? a : b; } diff --git a/src/Map/MapEventMare.cpp b/src/Map/MapEventMare.cpp index ae6f2551e..cdb2837c1 100644 --- a/src/Map/MapEventMare.cpp +++ b/src/Map/MapEventMare.cpp @@ -19,7 +19,6 @@ // rogue includes needed for matching sinit & bss #include #include -#include f32 TMareWallRock::mAppearSpeed = 3.0f; f32 TMareWallRock::mDepressSpeed = 3.0f; diff --git a/src/Map/MapWire.cpp b/src/Map/MapWire.cpp index 8cb2b176f..ced7923bd 100644 --- a/src/Map/MapWire.cpp +++ b/src/Map/MapWire.cpp @@ -18,7 +18,7 @@ // rogue includes needed for matching sinit & bss #include #include -#include +#include TMapWirePoint::TMapWirePoint() { diff --git a/src/System/Application.cpp b/src/System/Application.cpp index afc323ea3..f401e4af1 100644 --- a/src/System/Application.cpp +++ b/src/System/Application.cpp @@ -49,7 +49,7 @@ // rogue includes needed for matching sinit & bss #include #include -#include +#include TMarDirector* gpMarDirector; MSound* gpMSound; From 0f4229cccd7740d2d7bce8e45111d6ad863e27cc Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:57:31 -0400 Subject: [PATCH 2/8] Enemy/bossgesso: is2ndFightNow returns BOOL, not bool The ternary yielded a bool that MWCC then normalised with clrlwi. Retail returns straight out of both branches with no normalisation, which is what the explicit TRUE/FALSE returns give. 78.8% -> 100%. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 7ff4572a0..36273dcc8 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -726,7 +726,10 @@ f32 TBossGesso::inSight() BOOL TBossGesso::is2ndFightNow() const { - return gpMarDirector->unk7D == 4 ? true : false; + if (gpMarDirector->unk7D == 4) + return TRUE; + + return FALSE; } void TBossGesso::stopIfRoll() From a93483fb1c81167c985babc7f2aea059c50d880e Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 06:48:46 -0400 Subject: [PATCH 3/8] Enemy/bossgesso: start reconstructing TBossGesso::perform Was an empty stub against 2528 bytes of retail code. The first two cue blocks are in: the CUE_CALC_ANIM pull sound, gated on the beak or a tentacle being held and on Mario pulling hard enough, and the CUE_MOVE idle-nag timer that shows message 0xE0004 once Mario has loitered within 2000 units for 1200 frames while the boss sits in attack mode 6. Both blocks match retail instruction for instruction; the only diffs are register numbers, which should settle once the rest of the function is written and the frame grows to match. 0.2% -> 19.5%. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 36273dcc8..d58943312 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -1249,7 +1249,38 @@ void TBossGesso::calcRootMatrix() void TBossGesso::performInContainer(u32, JDrama::TGraphics*) { } -void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) { } +void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) +{ + if (cue & CUE_CALC_ANIM) { + if (mBeak->getHolder() != nullptr || tentacleHeld()) { + if (gpMarioOriginal->mIntendedMag > 0.1f) { + SMSGetMSound()->startSoundActor(MSD_SE_BS_GESO_PULL, &mPosition, + 0, nullptr, 0, 4); + } + } + } + + if (cue & CUE_MOVE) { + if (!unk1A0 && !is2ndFightNow() && mAttackMode == 6) { + JGeometry::TVec3 toMario = *gpMarioPos; + toMario.x -= mPosition.x; + toMario.y -= mPosition.y; + toMario.z -= mPosition.z; + + if (toMario.x * toMario.x + toMario.y * toMario.y + + toMario.z * toMario.z + < 4000000.0f) { + unk19C++; + if (unk19C >= 1200) + showMessage(0xE0004); + } + } + } + + // TODO: the rest of the cue handling is still unwritten -- see the + // remaining blocks from 0x35ec onwards in the retail function. + TSpineEnemy::perform(cue, graphics); +} TBossGessoManager::TBossGessoManager(const char* name) : TEnemyManager(name) From c337ef30beb2014fdef728e2be98931564792a6e Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 07:12:27 -0400 Subject: [PATCH 4/8] Enemy/bossgesso: more of TBossGesso::perform Adds the rest of the CUE_MOVE block (beak-held rumble every fourth frame of the attack mode, and the two countdown timers), the attack mode 6 branch that hands off to the first tentacle and returns early after parking its first two spline nodes, and the CUE_ENTRY damage fog toggle keyed on the beak damage nerve, followed by the base perform and the pol drop dispatch. 19.5% -> 34.9%. Everything written so far lines up with retail instruction for instruction; the diffs are register and stack offsets that come from the frame still being smaller than retail's. Two TODOs mark where the reconstruction stops. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 54 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index d58943312..4a8a47b57 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1271,15 +1272,62 @@ void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) + toMario.z * toMario.z < 4000000.0f) { unk19C++; - if (unk19C >= 1200) + if (unk19C >= 1200) { showMessage(0xE0004); + unk1A0 = 1; + } } } + + if (mBeak->getHolder() != nullptr + && mTimeInCurrentAttackMode % 4 == 0) { + rumblePad(1, mBeak->mPosition); + } + + if (unk1AC > 0) + unk1AC--; + + if (unk1AE > 0) + unk1AE--; + } + + if (mAttackMode == 6) { + if (cue & CUE_CALC_ANIM) { + if (JDrama::TNameRefGen::search("container") + == nullptr) { + changeAttackMode(0); + } else if (mTentacles[0]->mState != 4) { + JGeometry::TVec3 pos(11603.0f, 2114.3f, 2411.4f); + mTentacles[0]->mNodes[0].setPosition(pos); + pos.x = 11510.0f; + mTentacles[0]->mNodes[1].setPosition(pos); + } + } + + mTentacles[0]->testPerform(cue, graphics); + return; + } + + if (cue & CUE_ENTRY) { + if (mSpine->getLatestNerve() == &TNerveBGBeakDamage::theNerve()) { + SMS_AddDamageFogEffect(getModel()->getModelData(), mPosition, + graphics); + } else { + SMS_ResetDamageFogEffect(getModel()->getModelData()); + } + + // TODO: a virtual call on the model data's material follows here, + // passing unk190 -- slot 0x38, class not pinned down yet. } - // TODO: the rest of the cue handling is still unwritten -- see the - // remaining blocks from 0x35ec onwards in the retail function. TSpineEnemy::perform(cue, graphics); + mPolDrop->testPerform(cue, graphics); + + if (mLiveFlag & LIVE_FLAG_DEAD) + return; + + // TODO: the remaining cue handling from 0x381c onwards is still + // unwritten -- particle emission, shadow request, tentacle dispatch. } TBossGessoManager::TBossGessoManager(const char* name) From b99bc6c0094b74047c7fda7ea9e7b88dd0e8ae83 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 07:33:22 -0400 Subject: [PATCH 5/8] Enemy/bossgesso: perform gains the ink particle and cork dispatch The CUE_CALC_ANIM ink burst runs down unk194 one frame at a time while bound to the body joint matrix, and the cork picks one of its two MActors depending on unkC, copying joint 27 into the cork model base matrix on the way through when the intact one is in play. 34.9% -> 39.2%. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 4a8a47b57..323b58677 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -1326,8 +1326,29 @@ void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) if (mLiveFlag & LIVE_FLAG_DEAD) return; - // TODO: the remaining cue handling from 0x381c onwards is still - // unwritten -- particle emission, shadow request, tentacle dispatch. + if (cue & CUE_CALC_ANIM) { + if (unk194 > 0) { + gpMarioParticleManager->emitAndBindToMtxPtr( + 0x13B, getModel()->getAnmMtx(0), 1, this); + unk194--; + } + } + + MActor* cork; + if (mCork->unkC == 0) { + cork = mCork->unk4; + if (cue & CUE_CALC_ANIM) { + PSMTXCopy(mCork->mOwner->getModel()->getAnmMtx(27), + mCork->unk4->getModel()->getBaseTRMtx()); + } + } else { + cork = mCork->unk8; + } + + cork->perform(cue, graphics); + + // TODO: the remaining cue handling from 0x38ac onwards is still + // unwritten -- CUE_CALC_VIEW shadow request and tentacle dispatch. } TBossGessoManager::TBossGessoManager(const char* name) From bcb403a707c9b7edd3d585113dc1f6b5bfa969e3 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:00:32 -0400 Subject: [PATCH 6/8] Enemy/bossgesso: finish the body of TBossGesso::perform All of the retail cue handling is now present: the shadow request built from the joint 1 basis vectors, the sub-actor dispatch, the mtx calc blend decay, the cloud model stamp, the two tentacle root modes, the per-tentacle damage fog loop, the roll sound driven by the toe-to-Mario distance, and the tail that nags through the console balloon when the beak is held but the outer tentacles are not. 0.16% -> 70.6%. Retail reaches the model through mMActor->getModel() in most places and only calls TLiveActor::getModel() twice, which is worth a couple of points on its own. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 111 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 323b58677..2c75b0909 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1310,10 +1312,10 @@ void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) if (cue & CUE_ENTRY) { if (mSpine->getLatestNerve() == &TNerveBGBeakDamage::theNerve()) { - SMS_AddDamageFogEffect(getModel()->getModelData(), mPosition, - graphics); + SMS_AddDamageFogEffect(mMActor->getModel()->getModelData(), + mPosition, graphics); } else { - SMS_ResetDamageFogEffect(getModel()->getModelData()); + SMS_ResetDamageFogEffect(mMActor->getModel()->getModelData()); } // TODO: a virtual call on the model data's material follows here, @@ -1347,8 +1349,107 @@ void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) cork->perform(cue, graphics); - // TODO: the remaining cue handling from 0x38ac onwards is still - // unwritten -- CUE_CALC_VIEW shadow request and tentacle dispatch. + if (cue & CUE_CALC_VIEW) { + TCircleShadowRequest request; + + MtxPtr joint = mMActor->getModel()->getAnmMtx(1); + request.unk0 + = JGeometry::TVec3(joint[0][3], mPosition.y, joint[2][3]); + + JGeometry::TVec3 right(joint[0][0], joint[1][0], joint[2][0]); + JGeometry::TVec3 front(joint[0][2], joint[1][2], joint[2][2]); + + request.unkC = PSVECMag(right); + request.unk10 = PSVECMag(front); + request.unkC *= mScaledBodyRadius; + request.unk10 *= mScaledBodyRadius; + request.unk1C = getShadowType(); + request.unk14 = mRotation.y; + + gpBindShadowManager->request(request, getActorType()); + } + + mBeak->testPerform(cue, graphics); + mLeftEye->testPerform(cue, graphics); + mRightEye->testPerform(cue, graphics); + mBody->testPerform(cue, graphics); + + if (cue & CUE_MOVE) { + mMtxCalc->unk50 -= unk188; + if (mMtxCalc->unk50 < 0.0f) + mMtxCalc->unk50 = 0.0f; + else if (mMtxCalc->unk50 > 1.0f) + mMtxCalc->unk50 = 1.0f; + } + + if (unk17C) { + if (cue & CUE_CALC_ANIM) { + PSMTXCopy(mMActor->getModel()->getBaseTRMtx(), + unk178->getModel()->getBaseTRMtx()); + unk178->calcAnm(); + } + + if (cue & CUE_ENTRY) + gpPollution->stampModel(unk178->getModel()); + } + + if (cue & CUE_CALC_ANIM) { + if (mLiveFlag & LIVE_FLAG_CLIPPED_OUT) { + for (int i = 0; i < TENTACLE_NUM; ++i) { + if (mTentacles[i]->mState != 4) + mTentacles[i]->mNodes[0].setPosition(mPosition); + } + } else { + static const int rootJoints[] = { 2, 3, 5, 6 }; + + for (int i = 0; i < TENTACLE_NUM; ++i) { + if (mTentacles[i]->mState == 4) + continue; + + JGeometry::TVec3 trans; + if (getJointTransByIndex(rootJoints[i], &trans) >= 0) + mTentacles[i]->mNodes[0].setPosition(trans); + } + } + } + + for (int i = 0; i < TENTACLE_NUM; ++i) { + if (cue & CUE_ENTRY) { + if (mSpine->getLatestNerve() == &TNerveBGBeakDamage::theNerve()) { + mTentacles[i]->unk2C->offMakeDL(); + SMS_AddDamageFogEffect( + mTentacles[i]->unk2C->getModel()->getModelData(), mPosition, + graphics); + } else { + SMS_ResetDamageFogEffect( + mTentacles[i]->unk2C->getModel()->getModelData()); + } + } + + mTentacles[i]->testPerform(cue, graphics); + } + + if (cue & CUE_CALC_ANIM) { + if (mMActor->checkCurBckFromIndex(14) + || mMActor->checkCurBckFromIndex(15)) { + f32 len = lenFromToeToMario(); + SMSGetMSound()->startSoundActorWithInfo(MSD_SE_BS_GESO_ROLL, + &mPosition, nullptr, len, 0, + 0, nullptr, 0, 4); + } + } + + if (cue & CUE_MOVE) { + if (mBeak->getHolder() != nullptr && unk190.color.a == 0) { + int left = mTentacles[1]->mState; + int right = mTentacles[3]->mState; + + if (!((left == 4 || left == 6 || left == 3) + && (right == 4 || right == 6 || right == 3))) { + gpMarDirector->mConsole->startAppearBalloon(0xE0003, true); + } + } + } } TBossGessoManager::TBossGessoManager(const char* name) From 7c664a2640cf087f59695515bdbddb211955c336 Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:02:18 -0400 Subject: [PATCH 7/8] Enemy/bossgesso: reach Mario through SMS_GetMarioPos Same codegen as the raw gpMarioPos deref, but it is the accessor the rest of the game uses and the map wants the weak copy in this TU. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 2c75b0909..69b5cfd0a 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -1265,7 +1265,7 @@ void TBossGesso::perform(u32 cue, JDrama::TGraphics* graphics) if (cue & CUE_MOVE) { if (!unk1A0 && !is2ndFightNow() && mAttackMode == 6) { - JGeometry::TVec3 toMario = *gpMarioPos; + JGeometry::TVec3 toMario = SMS_GetMarioPos(); toMario.x -= mPosition.x; toMario.y -= mPosition.y; toMario.z -= mPosition.z; From ad82b1b995cc65eeea643eb467db756d0137406d Mon Sep 17 00:00:00 2001 From: KakarotCake <61133727+KakarottoCake@users.noreply.github.com> Date: Sat, 1 Aug 2026 16:35:57 -0400 Subject: [PATCH 8/8] Enemy/bossgesso: reuse the mario position copy in doAttackSingle The height guard read SMS_GetMarioPos().y afresh where retail reads the y out of the delta copy it just made. The rest of this function -- the per-tentacle aiming loop that computes the yaw to Mario with MsGetRotFromZaxisY and wraps it against the boss rotation -- is still the unfinished angle math marked with the existing TODO. Co-Authored-By: Claude Opus 4.8 --- src/Enemy/bossgesso.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Enemy/bossgesso.cpp b/src/Enemy/bossgesso.cpp index 69b5cfd0a..d2f6a017a 100644 --- a/src/Enemy/bossgesso.cpp +++ b/src/Enemy/bossgesso.cpp @@ -924,7 +924,7 @@ void TBossGesso::doAttackSingle() JGeometry::TVec3 delta = SMS_GetMarioPos(); - if (SMS_GetMarioPos().y + 20.0f < mPosition.y) + if (delta.y + 20.0f < mPosition.y) return; delta -= mPosition;