Enemy/bosstelesa: decompile Boss Telesa behavior - #134
Conversation
Report for GMSJ01 (5a8c71e - 60fcf3f)📈 Matched code: 35.52% (+0.21%, +7504 bytes) ✅ 65 new matches
...and 35 more new matches 📈 42 improvements in unmatched items
...and 12 more improvements in unmatched items |
|
Follow-up on the latest commit ( Header
Source changes
Effect
TU fuzzy match: 90.38% → 93.18%. Remaining residuals are MWCC stack-frame padding, register-allocation shifts, and inlining depth (e.g. |
- route sounds through SMSGetMSound()->startSoundActor + SoundEffects enum - add boss telesa particle enum entries, use them in emit/load - use MsMtxSetXYZRPH f32 overload in TBubble::calcRootMatrix - drop fabricated isActorTypeOf, use direct actor-type comparisons - rename MActor::getAnmBck -> getCurBckAnmPtr (real map name) - unk198/unk1A8 -> bool[3] arrays (remove pointer fakematch) - rolling/allStopped -> bool, MsWrap-free 360 wraps restored - fabsf instead of __fabsf, CAM_SHAKE_MODE enum, SMSGetMarDirector
|
Addressed most of the review in Done
Pushed back on
Still TODO (not attempted / couldn't resolve cleanly)
One thing to flag: routing the sounds through the inline |
|
|
||
| void TBubble::setDeadAnm() { setBckAnm(9); } | ||
|
|
||
| MtxPtr TBubble::getTakingMtx() { return mMActor->unk4->unk20; } |
| } | ||
| } | ||
|
|
||
| BOOL TNerveBubbleLive::execute(TSpineBase<TLiveActor>* spine) const |
There was a problem hiding this comment.
Nerve definitions/declarations should use the macros
|
|
||
| if (message == HIT_MESSAGE_TRAMPLE) { | ||
| bool isMario; | ||
| if (sender->mActorType == (ACTOR_TYPE_PLAYER | 1)) |
There was a problem hiding this comment.
ACTOR_TYPE_PLAYER etc macros should not be used like this -- just hardcode the actor type. This part is to be refactored later on.
| if (boss->checkCurAnmEnd(0)) { | ||
| boss->onHitFlag(HIT_FLAG_NO_COLLISION); | ||
| boss->unk16C->onHitFlag(HIT_FLAG_NO_COLLISION); | ||
| boss->unk170->onHitFlag(HIT_FLAG_NO_COLLISION); |
There was a problem hiding this comment.
this is offAllCollisions. Check the entire code for other other functions from the same file being inlined
|
|
||
| void TBossTelesaBody::checkHit() { } | ||
|
|
||
| void TBossTelesaTongue::checkHit() { } |
There was a problem hiding this comment.
these need to be reconstructed
| bubble->unk198 = nullptr; | ||
| TMapObjBase* item = gpItemManager->makeObjAppear( | ||
| bubble->mPosition.x, bubble->mPosition.y, bubble->mPosition.z, | ||
| 0x20000008, true); |
There was a problem hiding this comment.
This place should be the appendItem inline -- reconstruct it based on size. It's symmetric to appendEnemy it seems
|
|
||
| ((TMapObjBase*)unk2F8[i])->mVelocity = velocity; | ||
| ((TMapObjBase*)unk2F8[i])->offLiveFlag(LIVE_FLAG_UNK10); | ||
| ((TMapObjBase*)unk2F8[i])->mRotation.set(0.0f, 90.0f, 0.0f); |
There was a problem hiding this comment.
Here and in a bunch of places casts are used for zero reason -- use the exact type in the field instead (forward-declare it in the header so as not to include a bunch of stuff)
| velocity.y = dir.y; | ||
| velocity.z = dir.z * speed * itemSpeedRange.rand(); | ||
|
|
||
| TMapObjBase* item = gpItemManager->makeObjAppeared(0x2000000E); |
There was a problem hiding this comment.
Better to cast to TItem* here, that makes more sense as code that humans would've wrote
| enemy->mVelocity = velocity; | ||
| enemy->mPosition.y += 10.0f; | ||
| enemy->onLiveFlag(LIVE_FLAG_AIRBORNE); | ||
| PSMTXCopy(rootMtx, enemy->mMActor->unk4->unk20); |
There was a problem hiding this comment.
Here and everywhere else, the wrapper macros should be used: MTXCopy instead of PSMTXCopy
| bool TBossTelesa::checkAllItemDead() | ||
| { | ||
| for (int i = 0; i < unk274; ++i) | ||
| if (!(unk1AC[i]->mLiveFlag & LIVE_FLAG_DEAD)) |
There was a problem hiding this comment.
checkLiveFlag inline should be used here and everywhere else -- validate that appropriate inlines are used
Replace 42 hand-expanded copies of the bck motion-blend boilerplate
with the MActor wrappers that already exist for them, matching the
idiom Mrkol used when cleaning up bosseel:
actor->setBckOldMotionBlendAnmPtr(actor->getBckAnm());
actor->setBckFromIndex(n);
actor->setMotionBlendRatioForBck(ratio);
No raw ->unkC accesses are left in the TU. Net -183 lines, and the
match improves 92.242% -> 92.414% (TNerveBossTelesaAppear 99.5 -> 99.9,
TNerveBossTelesaHide 96.3 -> 99.0, setSpicy 95.7 -> 97.7,
moveObject 78.2 -> 80.1).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TBossTelesaBody::checkHit and TBossTelesaTongue::checkHit are UNUSED in mario.MAP -- they are real functions that the compiler fully inlined and the linker then stripped. Their bodies were open-coded inside TBossTelesa::moveObject instead of living in the methods themselves. Move each loop into its own method and let moveObject just call them, the same way TBGCork::perform was pulled out of TBossGesso::perform. Body's out-of-line copy now matches the map size exactly (0xa0); Tongue is at 0xec of 0x23c, so it still has code left to recover. Also use the actor-type inlines rather than open-coded comparisons: checkActorType(ACTOR_TYPE_ENEMY) for the "is an enemy" test Mrkol pointed at, and isActorType(0x80000001) for the Mario checks -- the boss's own collision loop was comparing against ACTOR_TYPE_PLAYER (0x80000000) rather than the 0x80000001 the game actually uses. unk16C/unk170/unk174 now carry their real types instead of THitActor*. moveObject 78.2% -> 85.3%; TU 92.414% -> 92.712%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed two more commits addressing feedback:
Still open from the review: the 6 remaining |
The 0x20000 live flag is set inside the nerve check, not after it. The ROM's early-exit branch lands on the 0x40 flag block and skips the 0x20000 one, so when the current nerve is already TNerveSmallEnemyDie only 0x40 gets set. TBubble::kill() now matches 100%.
…ppers Both collision-actor receiveMessage overrides forward to a TBossTelesa method rather than touching the spine directly. Both of those methods are UNUSED in mario.MAP, so they are fully inlined back into the caller and the emitted code is unchanged in size. The extra layer is what makes the nerve singletons match. Reaching pushNerve directly put TNerveBase<TLiveActor>::TNerveBase() at inline pass 3, where its 2 statements fit the budget of 3 and it was inlined. Going through the wrapper pushes it to pass 4, which never runs, so it stays an out-of-line call exactly as the ROM has it. TBossTelesaTongue::receiveMessage and TBossTelesaBody::receiveMessage both now match 100%.
The tail of the SlotStart nerve is a bool-returning TBossTelesa method, not an inline early return. checkSlot is UNUSED in mario.MAP at 0xd4, which is large enough for the isRollDrum test, the unk18C reset and the whole SpitSlotItem nerve singleton block. Routing through it fixes two things at once: the inlined bool result is materialised and then tested, which is the li/b/li/clrlwi./beq sequence the ROM emits, and the extra call layer pushes TNerveBase<TLiveActor>::TNerveBase() out to inline pass 4 so it stays an out-of-line call. TNerveBossTelesaSlotStart::execute now differs only in stack frame size.
Summary
Matching
.data,.rodata,.sdata,.sdata2, and.sbssTBossTelesa::loadAfteris 96.8%,generateSlotItemis 77.6%,TNerveBossTelesaSlotStart::executeis 79.5%, and compiler-generated__sinit_bosstelesa_cppis now an exact 100% matchTU structure
UNUSEDsymbolsVerification
ninja baselineninja changes_allninja all_source progress build/GMSJ01/report.jsonbuild/GMSJ01/mario.dol: OKpython tools/check-changed-symbol-order.py src/Enemy/bosstelesa.cpppython tools/validate-symbol-order.py -u mario/Enemy/bosstelesagit diff --checkThis is the full BossTelesa decompilation effort.