From dc47aaf9dc94ecd7f46e859e165e7ede9849116f Mon Sep 17 00:00:00 2001 From: Treeki Date: Mon, 25 Feb 2013 21:58:44 +0100 Subject: tons and tons of final boss fixes, the usual --- bossCaptainBowser.yaml | 4 + src/bossCaptainBowser.cpp | 318 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 256 insertions(+), 66 deletions(-) diff --git a/bossCaptainBowser.yaml b/bossCaptainBowser.yaml index 354d0dd..7544c56 100644 --- a/bossCaptainBowser.yaml +++ b/bossCaptainBowser.yaml @@ -32,6 +32,10 @@ hooks: # That offset stores a pointer to a table of pointers to strings, followed by four bytes of padding # Each string is an arcname to load + - name: NeverLetCarDismountPlayerIfItTouchesTheGround + type: patch + addr_pal: 0x80812C84 + data: '38600000' ################################################ # HOOKS FOR KOOPA THROW diff --git a/src/bossCaptainBowser.cpp b/src/bossCaptainBowser.cpp index 5eb8ac7..7b80576 100644 --- a/src/bossCaptainBowser.cpp +++ b/src/bossCaptainBowser.cpp @@ -7,6 +7,10 @@ extern "C" void *StageScreen; +extern u32 GameTimer; + +#define time_macro *(u32*)((GameTimer) + 0x4) + // const char* effects_name_list [] = { // "Wm_jr_electricline", // cool @@ -99,12 +103,22 @@ public: float sinTimerY; bool sinTimerXRunning, sinTimerYRunning, stopMoving; - bool wereEntirelyDone; + bool shipAnmFinished; float explosionBottomBound; bool deathSequenceRunning; + bool forceClownEnds; + int timerWhenCrashHappens; + int timerForVictoryDance; void initiateDeathSequence(); + int clownCount; + dEn_c *clownPointers[4]; + VEC2 clownDests[4]; + + int saveTimer; + bool exitedFlag; + static daCaptainBowser *build(); void bindAnimChr_and_setUpdateRate(const char* name, int unk, float unk2, float rate); @@ -126,6 +140,7 @@ public: DECLARE_STATE(Intro); DECLARE_STATE(FinalAttack); DECLARE_STATE(Outro); + DECLARE_STATE(PanToExit); }; @@ -149,6 +164,7 @@ daCaptainBowser *daCaptainBowser::build() { CREATE_STATE(daCaptainBowser, Intro); CREATE_STATE(daCaptainBowser, FinalAttack); CREATE_STATE(daCaptainBowser, Outro); + CREATE_STATE(daCaptainBowser, PanToExit); @@ -249,7 +265,7 @@ int daCaptainBowser::onCreate() { // Prep the goods this->playerCount = GetActivePlayerCount(); - this->maxDamage = (20 * this->playerCount) + 10; + this->maxDamage = 24; pos.z = 8000.0; this->scale = (Vec){0.57, 0.57, 0.57}; @@ -292,14 +308,12 @@ int daCaptainBowser::onCreate() { this->isIntro = 3; doStateChange(&StateID_Intro); - // THIS IS FOR TESTING!!! - damage = 0; - return true; } int daCaptainBowser::onDelete() { - prplSound.Stop(0); + if (prplSound.Exists()) + prplSound.Stop(0); return true; } @@ -308,9 +322,22 @@ int daCaptainBowser::afterExecute(int param) { } int daCaptainBowser::onExecute() { + if (forceClownEnds) { + dEn_c *clownIter = 0; + while (clownIter = (dEn_c*)dEn_c::search(JR_CLOWN_FOR_PLAYER, clownIter)) { + clownIter->counter_500 = 120; // what a terrible hack. + float *pPropRotationIncrement = (float*)(((u32)clownIter) + 0x740); + *pPropRotationIncrement = 30.0f; + + SmoothRotation((s16*)(((u32)clownIter) + 0xD4A), 0, 0x300); + } + } + acState.execute(); + if (saveTimer > 0) + time_macro = saveTimer; - if (!prplSound.Exists() && acState.getCurrentState() != &StateID_Outro) { + if (!prplSound.Exists() && acState.getCurrentState() != &StateID_Outro && acState.getCurrentState() != &StateID_PanToExit) { PlaySoundWithFunctionB4(SoundRelatedClass, &prplSound, SE_BOSS_SHIP_PRPL, 1); } @@ -345,7 +372,7 @@ int daCaptainBowser::onExecute() { } } - if(this->shipAnm.isAnimationDone() && acState.getCurrentState() != &StateID_Outro) { + if(this->shipAnm.isAnimationDone() && acState.getCurrentState() != &StateID_Outro && acState.getCurrentState() != &StateID_PanToExit) { this->shipAnm.setCurrentFrame(0.0); } @@ -744,97 +771,256 @@ void daCaptainBowser::beginState_Outro() { explosionBottomBound = 0.0f; timer = 0; + timerWhenCrashHappens = 1000000; + timerForVictoryDance = 1000000; } +extern void *_8042A788; +extern void playFanfare(void *, int type); extern dStateBase_c JrClownEndDemoState; void daCaptainBowser::executeState_Outro() { - if (wereEntirelyDone) - return; - timer++; - float frame = shipAnm.getCurrentFrame(); + if (!shipAnmFinished) { + float frame = shipAnm.getCurrentFrame(); + + // nuke the things! + if (frame == 35.0f) { + dActor_c *iter = 0; + + while (iter = (dActor_c*)dActor_c::searchByBaseType(2, iter)) { + dStageActor_c *sa = (dStageActor_c*)iter; + + if (sa->name == EN_BIRIKYU_MAKER || sa->name == KAZAN_MGR) { + sa->Delete(1); + } + + if (sa->name == EN_LINE_BIRIKYU || + sa->name == EN_STAR_COIN || + sa->name == EN_HATENA_BALLOON || + sa->name == EN_ITEM || + sa->name == EN_TARZANROPE || // Meteor + sa->name == WM_ANCHOR) { // Koopa Throw + sa->killedByLevelClear(); + sa->Delete(1); + } + } - // nuke the things! - if (frame == 35.0f) { - dActor_c *iter = 0; + // freeze ye olde clowne + dEn_c *clownIter = 0; + while (clownIter = (dEn_c*)dEn_c::search(JR_CLOWN_FOR_PLAYER, clownIter)) { + clownIter->doStateChange(&JrClownEndDemoState); + } + forceClownEnds = true; + saveTimer = time_macro; - while (iter = (dActor_c*)dActor_c::searchByBaseType(2, iter)) { - dStageActor_c *sa = (dStageActor_c*)iter; + // remove all ship collisions + for (int i = 0; i < SHIP_SCOLL_COUNT; i++) + shipSColls[i].removeFromList(); - if (sa->name == EN_BIRIKYU_MAKER || sa->name == KAZAN_MGR) { - sa->Delete(1); + } else if (frame == 150.0f) { + nw4r::snd::SoundHandle handle; + PlaySoundWithFunctionB4(SoundRelatedClass, &handle, SE_BOSS_KOOPA_CRASH, 1); + timerWhenCrashHappens = timer; + prplSound.Stop(10); + + ClassWithCameraInfo *cwci = ClassWithCameraInfo::instance; + Vec efPos = {cwci->screenCentreX+168.0f, cwci->screenTop-cwci->screenHeight, pos.z}; + SpawnEffect("Wm_bg_volcano", 0, &efPos, &(S16Vec){0,0,0}, &(Vec){4.0, 4.0, 4.0}); + + ShakeScreen(StageScreen, 17, 7, 0, 0); + + } else if (shipAnm.isAnimationDone()) { + shipAnmFinished = true; + } + + if (frame > 30.0f) { + if (timer & 4 && explosionBottomBound > -249.0f) { + static const char *efs[] = {"Wm_en_explosion", "Wm_ob_cmnboxpiece"}; + + int id = MakeRandomNumber(2); + Vec efPos = { + pos.x - 150.0f + MakeRandomNumber(300), + pos.y + explosionBottomBound + MakeRandomNumber(250 + explosionBottomBound), + pos.z + 200.0f + }; + float efScale = (float(MakeRandomNumber(30)) / 20.0f); + + SpawnEffect(efs[id], 0, &efPos, &(S16Vec){0,0,0}, &(Vec){efScale,efScale,efScale}); } - if (sa->name == EN_LINE_BIRIKYU || - sa->name == EN_STAR_COIN || - sa->name == EN_HATENA_BALLOON || - sa->name == EN_ITEM || - sa->name == EN_TARZANROPE || // Meteor - sa->name == WM_ANCHOR) { // Koopa Throw - sa->killedByLevelClear(); - sa->Delete(1); + if ((timer % 12) == 0 && (frame < 150.0f)) { + nw4r::snd::SoundHandle handle; + PlaySoundWithFunctionB4(SoundRelatedClass, &handle, SE_DEMO_OP_CAKE_CLASH_1210f, 1); } + + explosionBottomBound -= 0.85f; } - // freeze ye olde clowne + // handleYSpeed(): + bowserYSpeed += bowserYAccel; + if (bowserYSpeed < bowserMaxYSpeed) + bowserYSpeed = bowserMaxYSpeed; + + // doMovement() + bowserX += bowserXSpeed; + bowserY += bowserYSpeed; + + bowserRotX -= 0xC00; + bowserRotY -= 0x100; + } + + + // FUN SHITS. + if (timer == (timerWhenCrashHappens + 90)) { + playFanfare(_8042A788, 4); + timerForVictoryDance = timer + (7*60); + + // Prepare for moving the clowns + clownCount = 0; + + dEn_c *unsortedClownPointers[4]; + dAcPy_c *unsortedClownPlayers[4]; + dEn_c *clownIter = 0; while (clownIter = (dEn_c*)dEn_c::search(JR_CLOWN_FOR_PLAYER, clownIter)) { - clownIter->doStateChange(&JrClownEndDemoState); + dAcPy_c *player = *((dAcPy_c**)(((u32)clownIter) + 0x738)); + if (player) { + unsortedClownPointers[clownCount] = clownIter; + unsortedClownPlayers[clownCount] = player; + clownCount++; + } } - // remove all ship collisions - for (int i = 0; i < SHIP_SCOLL_COUNT; i++) - shipSColls[i].removeFromList(); + // Now sort them by ID + int sortedClownID = 0; + for (int playerID = 0; playerID < 4; playerID++) { + dAcPy_c *player = (dAcPy_c*)GetSpecificPlayerActor(playerID); - } else if (frame == 150.0f) { - nw4r::snd::SoundHandle handle; - PlaySoundWithFunctionB4(SoundRelatedClass, &handle, SE_BOSS_KOOPA_CRASH, 1); - prplSound.Stop(10); + for (int searchClown = 0; searchClown < clownCount; searchClown++) { + if (player == unsortedClownPlayers[searchClown]) { + clownPointers[sortedClownID] = unsortedClownPointers[searchClown]; + sortedClownID++; + break; + } + } + } ClassWithCameraInfo *cwci = ClassWithCameraInfo::instance; - Vec efPos = {cwci->screenCentreX+168.0f, cwci->screenTop-cwci->screenHeight, pos.z}; - SpawnEffect("Wm_bg_volcano", 0, &efPos, &(S16Vec){0,0,0}, &(Vec){4.0, 4.0, 4.0}); - ShakeScreen(StageScreen, 17, 7, 0, 0); + // calculate their end positions + // Assuming each clown takes up ~32 width, and we have 16 padding between them ... + float clownSpan = (clownCount * 48.0f) - 16.0f; + float clownX = cwci->screenCentreX - (clownSpan / 2.0f); - } else if (shipAnm.isAnimationDone()) { - wereEntirelyDone = true; + for (int i = 0; i < clownCount; i++) { + clownDests[i].x = clownX; + clownDests[i].y = cwci->screenCentreY; + clownX += 48.0f; + } } - if (frame > 30.0f) { - if (timer & 4 && explosionBottomBound > -249.0f) { - static const char *efs[] = {"Wm_en_explosion", "Wm_ob_cmnboxpiece"}; + if (timer > (timerWhenCrashHappens + 90)) { + // Let's move the clowns + int playersAtEnd = 0; - int id = MakeRandomNumber(2); - Vec efPos = { - pos.x - 150.0f + MakeRandomNumber(300), - pos.y + explosionBottomBound + MakeRandomNumber(250 + explosionBottomBound), - pos.z + 200.0f - }; - float efScale = (float(MakeRandomNumber(30)) / 20.0f); + for (int i = 0; i < clownCount; i++) { + dEn_c *clown = clownPointers[i]; + + float moveSpeed = 1.2f; + + // Are we there already? + float xDiff = abs(clown->pos.x - clownDests[i].x); + float yDiff = abs(clown->pos.y - clownDests[i].y); + + int check = 0; + if (xDiff < moveSpeed) { + clown->pos.x = clownDests[i].x; + check |= 1; + } + if (yDiff < moveSpeed) { + clown->pos.y = clownDests[i].y; + check |= 2; + } + if (check == 3) { + playersAtEnd++; + continue; + } + + // Otherwise, move them a bit further + VEC3 direction = {clownDests[i].x - clown->pos.x, clownDests[i].y - clown->pos.y, 0.0f}; + VECNormalize(&direction, &direction); + VECScale(&direction, &direction, moveSpeed); - SpawnEffect(efs[id], 0, &efPos, &(S16Vec){0,0,0}, &(Vec){efScale,efScale,efScale}); + clown->pos.x += direction.x; + clown->pos.y += direction.y; } - if ((timer % 12) == 0 && (frame < 150.0f)) { - nw4r::snd::SoundHandle handle; - PlaySoundWithFunctionB4(SoundRelatedClass, &handle, SE_DEMO_OP_CAKE_CLASH_1210f, 1); + if (playersAtEnd >= clownCount) { + //if (timerForVictoryDance == 1000000) + // timerForVictoryDance = timer + 45; + } + + if (timer == timerForVictoryDance) { + static const int vocs[4] = { + SE_VOC_MA_CLEAR_LAST_BOSS, + SE_VOC_LU_CLEAR_LAST_BOSS, + SE_VOC_KO_CLEAR_LAST_BOSS, + SE_VOC_KO2_CLEAR_LAST_BOSS + }; + for (int i = 0; i < clownCount; i++) { + dAcPy_c *player = *((dAcPy_c**)(((u32)clownPointers[i]) + 0x738)); + // let's be hacky + dPlayerModelHandler_c *pmh = (dPlayerModelHandler_c*)(((u32)player) + 0x2A60); + int whatAnim = goal_puton_capB; + if (pmh->mdlClass->powerup_id == 4) + whatAnim = goal_puton_capB; + else if (pmh->mdlClass->powerup_id == 5) + whatAnim = goal_puton_capC; + pmh->mdlClass->startAnimation(whatAnim, 1.0f, 0.0f, 0.0f); + + nw4r::snd::SoundHandle handle; + PlaySoundWithFunctionB4(SoundRelatedClass, &handle, vocs[pmh->mdlClass->player_id_1], 1); + } } - explosionBottomBound -= 0.85f; + if (timer == (timerForVictoryDance + 180)) { + doStateChange(&StateID_PanToExit); + nw4r::snd::SoundHandle handle; + PlaySoundWithFunctionB4(SoundRelatedClass, &handle, SE_PLY_CROWN_ACC, 1); + } + //dAcPy_c *splayer = *((dAcPy_c**)(((u32)clownPointers[0]) + 0x738)); + //OSReport("Player has %s\n", splayer->states2.getCurrentState()->getName()); } +} +void daCaptainBowser::endState_Outro() { } - // handleYSpeed(): - bowserYSpeed += bowserYAccel; - if (bowserYSpeed < bowserMaxYSpeed) - bowserYSpeed = bowserMaxYSpeed; - // doMovement() - bowserX += bowserXSpeed; - bowserY += bowserYSpeed; +void daCaptainBowser::beginState_PanToExit() { + timer = 0; +} +void daCaptainBowser::endState_PanToExit() { +} - bowserRotX -= 0xC00; - bowserRotY -= 0x100; +void daCaptainBowser::executeState_PanToExit() { + float targetClownY = ClassWithCameraInfo::instance->screenCentreY - 160.0f; + + for (int i = 0; i < clownCount; i++) { + dEn_c *clown = clownPointers[i]; + clown->pos.y -= 1.5f; + if (clown->pos.y < targetClownY && !exitedFlag) { + RESTART_CRSIN_LevelStartStruct.purpose = 0; + RESTART_CRSIN_LevelStartStruct.world1 = 7; + RESTART_CRSIN_LevelStartStruct.world2 = 7; + RESTART_CRSIN_LevelStartStruct.level1 = 40; + RESTART_CRSIN_LevelStartStruct.level2 = 40; + RESTART_CRSIN_LevelStartStruct.areaMaybe = 0; + RESTART_CRSIN_LevelStartStruct.entrance = 0xFF; + RESTART_CRSIN_LevelStartStruct.unk4 = 0; // load replay + DontShowPreGame = true; + ExitStage(RESTART_CRSIN, 0, BEAT_LEVEL, MARIO_WIPE); + exitedFlag = true; + } + } } -void daCaptainBowser::endState_Outro() { } + -- cgit v1.2.3