#include #include #include #include #include #include "effects.h" #include "player.h" void poodleCollisionCallback(ActivePhysics *apThis, ActivePhysics *apOther); void poodleCollisionCallback(ActivePhysics *apThis, ActivePhysics *apOther) { if (apOther->owner->name == BROS_FIREBALL) { return; } else if (apOther->owner->name == BROS_ICEBALL) { return; } else { dEn_c::collisionCallback(apThis, apOther); } } class daPodouble : public dEn_c { int onCreate(); int onDelete(); int onExecute(); int onDraw(); mHeapAllocator_c allocator; nw4r::g3d::ResFile resFile; m3d::mdl_c bodyModel; m3d::mdl_c fogModel; m3d::mdl_c fog2Model; m3d::anmTexSrt_c body; m3d::anmChr_c fogChr; m3d::anmTexSrt_c fogSRT; m3d::anmChr_c fog2Chr; m3d::anmTexSrt_c fog2SRT; char isFire; char goingUp; int timer; float dying; int damage; float Baseline; char isInvulnerable; int countdown; dEn_c *Kameck; static daPodouble *build(); // void spriteCollision(ActivePhysics *apThis, ActivePhysics *apOther); void playerCollision(ActivePhysics *apThis, ActivePhysics *apOther); void yoshiCollision(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat3_StarPower(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat14_YoshiFire(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCatD_GroundPound(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat7_WMWaggleWater(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat7_WMWaggleWaterYoshi(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat9_RollingObject(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat1_Fireball_E_Explosion(ActivePhysics *apThis, ActivePhysics *apOther); bool collisionCat2_IceBall_15_YoshiIce(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCat13_Hammer(ActivePhysics *apThis, ActivePhysics *apOther); void collisionCatA_PenguinMario(ActivePhysics *apThis, ActivePhysics *apOther); USING_STATES(daPodouble); DECLARE_STATE(Bounce); DECLARE_STATE(Spit); DECLARE_STATE(Damage); DECLARE_STATE(Grow); DECLARE_STATE(Outro); DECLARE_STATE(SyncDie); }; daPodouble *daPodouble::build() { void *buffer = AllocFromGameHeap1(sizeof(daPodouble)); return new(buffer) daPodouble; } /////////////////////// // Externs and States /////////////////////// extern "C" void *PlaySound(dStageActor_c *, int soundID); extern "C" void *PlaySoundAsync(dStageActor_c *, int soundID); extern "C" u32 GenerateRandomNumber(int max); extern "C" dStageActor_c *GetSpecificPlayerActor(int num); extern "C" int SmoothRotation(short* rot, u16 amt, int unk2); extern "C" void* WaterManager; extern "C" u32 CheckforLiquid(void*, Vec, int, int, int, int layer); extern "C" void *StopBGMMusic(); extern "C" void *StartBGMMusic(); extern "C" void *MakeMarioEnterDemoMode(); extern "C" void *MakeMarioExitDemoMode(); extern "C" void *UpdateGameMgr(); CREATE_STATE(daPodouble, Bounce); CREATE_STATE(daPodouble, Spit); CREATE_STATE(daPodouble, Damage); CREATE_STATE(daPodouble, Grow); CREATE_STATE(daPodouble, Outro); CREATE_STATE(daPodouble, SyncDie); //////////////////////// // Collision Functions //////////////////////// void daPodouble::playerCollision(ActivePhysics *apThis, ActivePhysics *apOther) { this->dEn_c::playerCollision(apThis, apOther); this->_vf220(apOther->owner); // fix multiple player collisions via megazig this->isDead = 0; this->flags_4FC |= (1<<(31-7)); if(apOther->owner->which_player < 3) { this->counter_504[apOther->owner->which_player] = 0; } } void daPodouble::yoshiCollision(ActivePhysics *apThis, ActivePhysics *apOther) { this->playerCollision(apThis, apOther); } void daPodouble::collisionCat7_WMWaggleWater(ActivePhysics *apThis, ActivePhysics *apOther) { this->playerCollision(apThis, apOther); } void daPodouble::collisionCat7_WMWaggleWaterYoshi(ActivePhysics *apThis, ActivePhysics *apOther) { this->playerCollision(apThis, apOther); } void daPodouble::collisionCatA_PenguinMario(ActivePhysics *apThis, ActivePhysics *apOther) { this->playerCollision(apThis, apOther); } void daPodouble::collisionCatD_GroundPound(ActivePhysics *apThis, ActivePhysics *apOther) { return; } void daPodouble::collisionCat9_RollingObject(ActivePhysics *apThis, ActivePhysics *apOther) { return; } void daPodouble::collisionCat3_StarPower(ActivePhysics *apThis, ActivePhysics *apOther) { return; } void daPodouble::collisionCat13_Hammer(ActivePhysics *apThis, ActivePhysics *apOther) { apOther->owner->kill(); if (this->isInvulnerable) { return; } this->damage += 2; if (this->damage < 12) { doStateChange(&StateID_Damage); } else { doStateChange(&StateID_Outro); } } void daPodouble::collisionCat14_YoshiFire(ActivePhysics *apThis, ActivePhysics *apOther){ apOther->owner->Delete(1); if (this->isInvulnerable) { return; } if (this->isFire == 0) { this->damage += 3; if (this->damage < 12) { doStateChange(&StateID_Damage); } else { doStateChange(&StateID_Outro); } } } void daPodouble::collisionCat1_Fireball_E_Explosion(ActivePhysics *apThis, ActivePhysics *apOther) { if (this->isInvulnerable) { return; } if (this->isFire == 0) { this->damage += 2; if (this->damage < 12) { doStateChange(&StateID_Damage); } else { doStateChange(&StateID_Outro); } } } bool daPodouble::collisionCat2_IceBall_15_YoshiIce(ActivePhysics *apThis, ActivePhysics *apOther) { apOther->owner->Delete(1); if (this->isInvulnerable) { return true; } if (this->isFire == 1) { if (apOther->owner->name == 104) { this->damage += 2; } else { this->damage += 3; } if (this->damage < 12) { doStateChange(&StateID_Damage); } else { doStateChange(&StateID_Outro); } return true; } else { return false; } } int daPodouble::onCreate() { this->isFire = this->settings >> 28; this->Baseline = this->pos.y - (float)((this->settings & 0xFF) * 0.8); OSReport("Creating the Podouble Model\n"); allocator.link(-1, GameHeaps[0], 0, 0x20); // Fire or Ice if (this->isFire == 1) { this->resFile.data = getResource("bubble", "g3d/t00.brres"); } else { this->resFile.data = getResource("bubble", "g3d/t05.brres"); } // Body and anms nw4r::g3d::ResMdl mdl = this->resFile.GetResMdl("bubble"); bodyModel.setup(mdl, &allocator, 0x224, 1, 0); nw4r::g3d::ResAnmTexSrt anmRes = this->resFile.GetResAnmTexSrt("bubble"); this->body.setup(mdl, anmRes, &this->allocator, 0, 1); this->body.bindEntry(&this->bodyModel, anmRes, 0, 0); this->bodyModel.bindAnim(&this->body, 0.0); // Fog up and anms mdl = this->resFile.GetResMdl("bubble_fog"); fogModel.setup(mdl, &allocator, 0x124, 1, 0); nw4r::g3d::ResAnmChr anmChr = this->resFile.GetResAnmChr("bubble_fog"); this->fogChr.setup(mdl, anmChr, &this->allocator, 0); this->fogChr.bind(&this->fogModel, anmChr, 1); this->fogModel.bindAnim(&this->fogChr, 0.0); this->fogChr.setUpdateRate(1.0); anmRes = this->resFile.GetResAnmTexSrt("bubble_fog"); this->fogSRT.setup(mdl, anmRes, &this->allocator, 0, 1); this->fogSRT.bindEntry(&this->fogModel, anmRes, 0, 0); this->fogModel.bindAnim(&this->fogSRT, 0.0); // Fog down and anms mdl = this->resFile.GetResMdl("bubble_fog2"); fog2Model.setup(mdl, &allocator, 0x124, 1, 0); anmChr = this->resFile.GetResAnmChr("bubble_fog2"); this->fog2Chr.setup(mdl, anmChr, &this->allocator, 0); this->fog2Chr.bind(&this->fog2Model, anmChr, 1); this->fogModel.bindAnim(&this->fog2Chr, 0.0); this->fog2Chr.setUpdateRate(1.0); anmRes = this->resFile.GetResAnmTexSrt("bubble_fog"); this->fog2SRT.setup(mdl, anmRes, &this->allocator, 0, 1); this->fog2SRT.bindEntry(&this->fog2Model, anmRes, 0, 0); this->fog2Model.bindAnim(&this->fog2SRT, 0.0); allocator.unlink(); // Stuff I do understand OSReport("Setting Podouble's Size to 1.0\n"); this->scale = (Vec){1.0, 1.0, 1.0}; this->direction = (this->isFire) ? 0 : 1; this->countdown = (this->isFire) ? 90 : 0; this->rot.x = 0; // X is vertical axis this->rot.y = (direction) ? 0xD800 : 0x2800; this->rot.z = 0; // Z is ... an axis >.> this->speed.x = 0.0; this->speed.y = 0.0; this->max_speed.x = 0.0; this->x_speed_inc = 0.0; this->goingUp = 0; OSReport("Creating Podouble's Physics Struct\n"); ActivePhysics::Info HitMeBaby; HitMeBaby.xDistToCenter = 0.0; HitMeBaby.yDistToCenter = 0.0; HitMeBaby.xDistToEdge = 40.0; HitMeBaby.yDistToEdge = 40.0; HitMeBaby.category1 = 0x3; HitMeBaby.category2 = 0x0; HitMeBaby.bitfield1 = 0x4F; HitMeBaby.bitfield2 = (this->isFire) ? 0x388626 : 0x388626; HitMeBaby.unkShort1C = 0; HitMeBaby.callback = &poodleCollisionCallback; OSReport("Making the Physics Class and adding to the list\n"); this->aPhysics.initWithStruct(this, &HitMeBaby); this->aPhysics.addToList(); this->disableEatIn(); doStateChange(&StateID_Grow); OSReport("Going to Execute Podouble\n"); this->onExecute(); return true; } int daPodouble::onDelete() { return true; } int daPodouble::onExecute() { acState.execute(); return true; } int daPodouble::onDraw() { if (this->speed.y >= 0) { matrix.translation(pos.x, pos.y, pos.z); fogModel.setDrawMatrix(matrix); fogModel.setScale(&scale); fogModel.calcWorld(false); fogModel.scheduleForDrawing(); fogModel._vf1C(); if(this->fogChr.isAnimationDone()) this->fogChr.setCurrentFrame(0.0); this->fogSRT.process(); } else { matrix.translation(pos.x, pos.y, pos.z); fog2Model.setDrawMatrix(matrix); fog2Model.setScale(&scale); fog2Model.calcWorld(false); fog2Model.scheduleForDrawing(); fog2Model._vf1C(); if(this->fog2Chr.isAnimationDone()) this->fog2Chr.setCurrentFrame(0.0); this->fog2SRT.process(); } matrix.translation(pos.x, pos.y, pos.z); matrix.applyRotationYXZ(&rot.x, &rot.y, &rot.z); bodyModel.setDrawMatrix(matrix); bodyModel.setScale(&scale); bodyModel.calcWorld(false); bodyModel.scheduleForDrawing(); this->body.process(); return true; } /////////////// // Grow State /////////////// void daPodouble::beginState_Grow() { this->timer = 0; if (isFire) { return; } // Stop the BGM Music StopBGMMusic(); // Set the necessary Flags and make Mario enter Demo Mode dStage32C_c::instance->freezeMarioBossFlag = 1; WLClass::instance->_4 = 4; WLClass::instance->_8 = 0; MakeMarioEnterDemoMode(); // Make sure to use the correct position Vec pos = (Vec){this->pos.x - 124.0, this->pos.y + 104.0, 3564.0}; S16Vec rot = (S16Vec){0, 0, 0}; // Create And use Kameck Kameck = (daKameckDemo*)createChild(KAMECK_FOR_CASTLE_DEMO, (dStageActor_c*)this, 0, &pos, &rot, 0); Kameck->doStateChange(&daKameckDemo::StateID_DemoWait); this->scale = (Vec){1.0, 1.0, 1.0}; } void daPodouble::executeState_Grow() { this->timer = this->timer + 1; float scaleSpeed, yPosScaling; if ((this->timer > 150) && (this->timer < 230)) { scaleSpeed = 0.03125; yPosScaling = 18; float modifier; modifier = 1.0 + ((this->timer - 150) * scaleSpeed); this->scale = (Vec){modifier, modifier, modifier}; this->pos.y = this->pos.y + (yPosScaling/80); } if (this->timer > 420) { PlaySound(this, SE_EMY_CS_MOVE_W8_BUBBLE_APP); doStateChange(&StateID_Bounce); } if (isFire) { return; } if (this->timer == 130) { Kameck->doStateChange(&daKameckDemo::StateID_DemoSt); } if (this->timer == 150) { PlaySound(this, SE_BOSS_IGGY_WANWAN_TO_L); } if (this->timer == 400) { Kameck->doStateChange(&daKameckDemo::StateID_DemoSt2); } if (this->timer == 360) { Vec tempPos = (Vec){this->pos.x - 40.0, this->pos.y + 120.0, 3564.0}; CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 175); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 400); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 401); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 564); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 583); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 754); // 400 & 401 // 564 // 583 // 754 // 958 CreateEffect(&tempPos, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}, 958); // 400 & 401 // 564 // 583 // 754 // 958 } } void daPodouble::endState_Grow() { this->Baseline = this->pos.y; if (isFire) { return; } // Clean up the flags and Kameck dStage32C_c::instance->freezeMarioBossFlag = 0; WLClass::instance->_8 = 1; MakeMarioExitDemoMode(); StartBGMMusic(); Kameck->Delete(1); } /////////////// // Bounce State /////////////// void daPodouble::beginState_Bounce() { this->rot.y = (direction) ? 0xD800 : 0x2800; this->rot.x = 0; this->max_speed.y = -5.0; this->speed.y = -1.0; this->y_speed_inc = -0.1875; this->goingUp = 0; } void daPodouble::executeState_Bounce() { if (this->countdown) { this->countdown--; return; } HandleYSpeed(); doSpriteMovement(); if (this->pos.y < this->Baseline) { this->speed.y = 8.0; this->goingUp = 1; } if (-0.1 < this->speed.y < 0.1) { if (this->goingUp == 1) { doStateChange(&StateID_Spit); } } // Check for stupid liquid junk float ydest; ydest = (this->pos.y > this->last_pos.y) ? this->last_pos.y : this->pos.y; int liquid = CheckforLiquid(WaterManager, this->pos, 0, 0, 0, this->currentLayerID); OSReport("Liquid type %d", liquid); if (liquid == 1) { CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){3.0, 3.0, 3.0}, 415); PlaySoundAsync(this, SE_EMY_BUBBLE_SPLASH); } if (liquid == 0) { CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){3.0, 3.0, 3.0}, 422); PlaySoundAsync(this, SE_OBJ_CMN_SPLASH); } } void daPodouble::endState_Bounce() { this->speed.y = 0.0; this->y_speed_inc = 0.0; } /////////////// // Spit State /////////////// void daPodouble::beginState_Spit() { this->timer = 0; } void daPodouble::executeState_Spit() { if (this->timer < 0x40) { this->rot.x -= 0x80; } else if (this->timer < 0x48) { this->rot.x += 0x400; } else if (this->timer == 0x48) { if (this->isFire == 0) { dStageActor_c *spawner = create(BROS_ICEBALL, 0x10, &this->pos, &(S16Vec){0,0,0}, 0); *((u32 *) (((char *) spawner) + 0x3DC)) = this->id; PlaySoundAsync(this, SE_EMY_ICE_BROS_ICE); doStateChange(&StateID_Bounce); } else { create(BROS_FIREBALL, 0, &this->pos, &(S16Vec){0,0,0}, 0); PlaySoundAsync(this, SE_EMY_FIRE_BROS_FIRE); doStateChange(&StateID_Bounce); } } this->timer += 1; } void daPodouble::endState_Spit() { this->speed.y = -1.0; this->y_speed_inc = -0.1875; } /////////////// // Damage State /////////////// void daPodouble::beginState_Damage() { this->timer = 0; this->isInvulnerable = 1; if (this->isFire == 0) { PlaySoundAsync(this, SE_OBJ_PNGN_ICE_THAW); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 243); } else { PlaySoundAsync(this, SE_EMY_FIRE_SNAKE_EXTINCT); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){3.0, 3.0, 3.0}, 252); } } void daPodouble::executeState_Damage() { int amt; amt = sin(this->timer * 3.14 / 4.0) * 0x2000; this->rot.y = amt; this->rot.y += (direction) ? 0xD800 : 0x2800; if (this->timer > 180) { doStateChange(&StateID_Bounce); } if ((this->timer == 60) || (this->timer == 80) || (this->timer == 100)) { if (this->isFire == 0) { dStageActor_c *spawner = create(BROS_ICEBALL, 0x10, &this->pos, &(S16Vec){0,0,0}, 0); *((u32 *) (((char *) spawner) + 0x3DC)) = this->id; PlaySoundAsync(this, SE_EMY_ICE_BROS_ICE); } else { create(BROS_FIREBALL, 0, &this->pos, &(S16Vec){0,0,0}, 0); PlaySoundAsync(this, SE_EMY_FIRE_BROS_FIRE); } } this->timer += 1; } void daPodouble::endState_Damage() { this->isInvulnerable = 0; } /////////////// // Outro State /////////////// void daPodouble::beginState_Outro() { daPodouble *other = (daPodouble*)FindActorByType(SHIP_WINDOW, (Actor*)this); if (other != 0) { other->doStateChange(&StateID_SyncDie); } WLClass::instance->_4 = 5; WLClass::instance->_8 = 0; this->removeMyActivePhysics(); this->timer = 0; StopBGMMusic(); dStage32C_c::instance->freezeMarioBossFlag = 1; } void daPodouble::executeState_Outro() { if (this->dying == 1) { if (this->timer > 180) { ExitStage(WORLD_MAP, 0, BEAT_LEVEL, MARIO_WIPE); } if (this->timer == 60) { UpdateGameMgr(); if (GetSpecificPlayerActor(0) != 0) { PlaySound(this, SE_VOC_MA_CLEAR_BOSS); // Send PlBase into DemoGoal State here, kthxbai } if (GetSpecificPlayerActor(1) != 0) { PlaySound(this, SE_VOC_LU_CLEAR_BOSS); // Send PlBase into DemoGoal State here, kthxbai } if (GetSpecificPlayerActor(2) != 0) { PlaySound(this, SE_VOC_KO_CLEAR_BOSS); // Send PlBase into DemoGoal State here, kthxbai } if (GetSpecificPlayerActor(3) != 0) { PlaySound(this, SE_VOC_KO2_CLEAR_BOSS); // Send PlBase into DemoGoal State here, kthxbai } } this->timer += 1; return; } if (this->scale.x > 0.1) { PlaySound(this, SE_EMY_BUBBLE_EXTINCT); // Adjust this to equal the scale of your boss / 80. this->scale.x -= 0.04375; this->scale.y -= 0.04375; this->scale.z -= 0.04375; if (this->timer == 30) { CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 756); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 801); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 957); this->timer = 0; } this->timer += 1; } else { this->scale.x = 0.0; this->scale.y = 0.0; this->scale.z = 0.0; CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 588); this->dying = 1; this->timer = 0; PlaySound(this, STRM_BGM_SHIRO_BOSS_CLEAR); MakeMarioEnterDemoMode(); } } void daPodouble::endState_Outro() { } /////////////// // SyncDie State /////////////// void daPodouble::beginState_SyncDie() { this->removeMyActivePhysics(); this->timer = 0; } void daPodouble::executeState_SyncDie() { if (this->dying == 1) { return; } if (this->scale.x > 0.1) { PlaySound(this, SE_EMY_BUBBLE_EXTINCT); // Adjust this to equal the scale of your boss / 80. this->scale.x -= 0.04375; this->scale.y -= 0.04375; this->scale.z -= 0.04375; if (this->timer == 30) { CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 756); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 801); CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 957); this->timer = 0; } this->timer += 1; } else { this->scale.x = 0.0; this->scale.y = 0.0; this->scale.z = 0.0; CreateEffect(&this->pos, &(S16Vec){0,0,0}, &(Vec){2.0, 2.0, 2.0}, 588); this->dying = 1; this->timer = 0; } } void daPodouble::endState_SyncDie() { }