diff options
Diffstat (limited to 'src/bonusRoom.cpp')
-rw-r--r-- | src/bonusRoom.cpp | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/src/bonusRoom.cpp b/src/bonusRoom.cpp new file mode 100644 index 0000000..6b61f27 --- /dev/null +++ b/src/bonusRoom.cpp @@ -0,0 +1,708 @@ +#include <common.h> +#include <game.h> +#include <g3dhax.h> +#include <sfx.h> +#include <stage.h> + +extern "C" bool SpawnEffect(const char*, int, Vec*, S16Vec*, Vec*); +extern "C" void *PlaySound(dStageActor_c *, int soundID); +extern "C" void *PlaySoundAsync(dStageActor_c *, int soundID); +extern "C" void *StopBGMMusic(); +extern "C" void *StartBGMMusic(); + +int Songs[2][4][15][2] = { + + { // Song 1 - + {{3,0},{3,15},{3,45},{1,75},{3,90},{5,120},{0,0}}, // First number is the note, Second is timing: 30 is one quarter note at 120 bpm, 0,0 ends the sequence + {{8,0},{5,22},{3,45},{6,67},{7,82},{6,97},{6,105},{0,0}}, + {{1,0},{1,7},{1,22},{1,37},{2,45},{3,60},{1,67},{7,82},{6,90},{0,0}}, + {{1,0},{1,7},{1,22},{1,37},{2,45},{3,52},{0,0}} + }, + + { // Song 2 - + {{1,30},{2,60},{3,90},{4,120}}, + {}, + {}, + {} + } + +}; + + +const char* Prizes[10][4] = { + { "I_kinoko", "g3d/I_kinoko.brres", "I_kinoko", "wait2" }, + { "I_fireflower", "g3d/I_fireflower.brres", "I_fireflower", "wait2" }, + { "I_iceflower", "g3d/I_iceflower.brres", "I_iceflower", "wait2" }, + { "I_penguin", "g3d/I_penguin.brres", "I_penguin", "wait2" }, + { "I_propeller", "g3d/I_propeller_model.brres", "I_propeller_model", "wait2" }, + { "I_kinoko_bundle","g3d/I_mini_kinoko.brres", "I_mini_kinoko", "wait2" }, + { "I_hammer", "g3d/I_hammer.brres", "I_hammer", "wait2" }, + { "I_star", "g3d/I_star.brres", "I_star", "wait2" }, + { "I_kinoko_bundle","g3d/I_life_kinoko.brres", "I_life_kinoko", "wait2" }, + { "obj_coin", "g3d/obj_coin.brres", "obj_coin", "wait2" } +}; + +int PrizePacks[2][4] = { // Numbers list prizes for each level + // 0 = Mushroom + // 1 = Fireflower + // 2 = Iceflower + // 3 = Penguin + // 4 = Propeller + // 5 = MiniShroom + // 6 = Starman + // 7 = Hammer + // 8 = 1-ups + // 9 = Coins + + {9, 0, 8, 1}, + {0, 0, 8, 5} +}; + +int Notes[9] = { + SE_EMY_PATAMET_STEP, + SE_EMY_PATAMET_STEP_2, + SE_EMY_PATAMET_STEP_3, + SE_EMY_PATAMET_STEP_4, + SE_EMY_PATAMET_STEP_5, + SE_EMY_PATAMET_STEP_6, + SE_EMY_PATAMET_STEP_7, + SE_EMY_PATAMET_STEP_8, + SE_EMY_PATAMET_COMPLETE +}; + + + +const char* SAarcNameList [] = { + "obj_coin", + "I_hammer", + "I_star", + "light_block", + "light_block_color", + "I_kinoko_bundle", + NULL +}; + +class dSongBlock; + +class dSingAlong : public dStageActor_c { + public: + static dSingAlong *instance; + static dSingAlong *build(); + + void RegisterNote(int note); + void addPowerups(); + + int onCreate(); + int onDelete(); + int onExecute(); + int onDraw(); + + int beforeExecute() { return true; } + int afterExecute(int) { return true; } + + dSingAlong() : state(this, &StateID_Intro) { } + + mHeapAllocator_c allocator; + nw4r::g3d::ResFile resFile[4]; + m3d::mdl_c pModel[4]; + m3d::anmChr_c pAnim[4]; + Vec prizePos[4]; + Vec prizeScale[4]; + + HermiteKey keysX[0x10]; + unsigned int Xkey_count; + HermiteKey keysY[0x10]; + unsigned int Ykey_count; + HermiteKey keysS[0x10]; + unsigned int Skey_count; + + + dSongBlock *Cblock; + dSongBlock *Dblock; + dSongBlock *Eblock; + dSongBlock *Fblock; + dSongBlock *Gblock; + dSongBlock *Ablock; + dSongBlock *Bblock; + + + // you might get casting issues. due to C++. blah = (int (*)[15][2])BigArray; fixes it + // int (*song)[4][15][2]; + // int (*prizes)[4]; + int song; + int prize; + int chorus; + // int (*currentNote)[2]; + int currentNote; + int endNote; + int timer; + int counter; + int Powerups[10]; + int isResponding; + + dStateWrapper_c<dSingAlong> state; + + // Intro, Call, Response, Display Prize, Failure, Win, Collect Prizes + USING_STATES(dSingAlong); + DECLARE_STATE(Intro); + DECLARE_STATE(Call); + DECLARE_STATE(Response); + DECLARE_STATE(Prize); + DECLARE_STATE(Failure); + DECLARE_STATE(Win); +}; + +dSingAlong *dSingAlong::instance = 0; +dSingAlong *dSingAlong::build() { + OSReport("Building Sing Along."); + void *buffer = AllocFromGameHeap1(sizeof(dSingAlong)); + dSingAlong *c = new(buffer) dSingAlong; + + instance = c; + return c; +} + + +/*****************************************************************************/ +// Events +int dSingAlong::onCreate() { + + OSReport("Creating the Sing Along gang."); + // Load in the settings + // this->song = Songs; + this->song = this->settings & 0xF; + // this->prizes = PrizePacks; + this->prize = (this->settings >> 4) & 0xF; + this->chorus = -1; + // this->currentNote = song[chorus][0]; + this->currentNote = 0; + + this->Powerups[0] = 0; // Mushroom + this->Powerups[1] = 0; // Fireflower + this->Powerups[2] = 0; // Iceflower + this->Powerups[3] = 0; // Penguin + this->Powerups[4] = 0; // Propeller + this->Powerups[5] = 0; // MiniShroom + this->Powerups[6] = 0; // Starman + this->Powerups[7] = 0; // Hammer + this->Powerups[8] = 0; // 1-ups + this->Powerups[9] = 0; // Coins + + // Load in the prize models + allocator.link(-1, GameHeaps[0], 0, 0x20); + + int p; + nw4r::g3d::ResMdl mdl; + nw4r::g3d::ResAnmChr anmChr; + + for (int i = 0; i < 4; i++) { + p = PrizePacks[prize][i]; + + // nw4r::g3d::ResFile file; + // resFile[i] = file; + // m3d::mdl_c model; + // pModel[i] = model; + // m3d::anmChr_c anim; + // pAnim[i] = anim; + + OSReport("Model %d: %s, %s, %s", i, Prizes[p][0], Prizes[p][1], Prizes[p][2]); + resFile[i].data = getResource(Prizes[p][0], Prizes[p][1]); + mdl = resFile[i].GetResMdl(Prizes[p][2]); + pModel[i].setup(mdl, &allocator, 0x224, 1, 0); + SetupTextures_Item(&pModel[i], 0); // 800B43D0 + + anmChr = resFile[i].GetResAnmChr("wait2"); + pAnim[i].setup(mdl, anmChr, &allocator, 0); + // pAnim[i].bind(&pModel[i], anmChr, 1); + // pModel[i].bindAnim(&pAnim[i], 0.0); + pAnim[i].setUpdateRate(1.0); + + prizePos[i] = (Vec){ pos.x, pos.y, pos.z }; + prizeScale[i] = (Vec){ 5.0, 5.0, 5.0 }; + } + + allocator.unlink(); + + // Create and prepare the blocks + float x = pos.x; + float y = pos.y - 40.0; + float z = pos.z; + S16Vec rot = (S16Vec){0,0,0}; + + Cblock = (dSongBlock*)create(WM_KILLER, 1, &(Vec){x-96.0, y, z}, &rot, 0); + Dblock = (dSongBlock*)create(WM_KILLER, 2, &(Vec){x-64.0, y, z}, &rot, 0); + Eblock = (dSongBlock*)create(WM_KILLER, 3, &(Vec){x-32.0, y, z}, &rot, 0); + Fblock = (dSongBlock*)create(WM_KILLER, 4, &(Vec){x , y, z}, &rot, 0); + Gblock = (dSongBlock*)create(WM_KILLER, 5, &(Vec){x+32.0, y, z}, &rot, 0); + Ablock = (dSongBlock*)create(WM_KILLER, 6, &(Vec){x+64.0, y, z}, &rot, 0); + Bblock = (dSongBlock*)create(WM_KILLER, 7, &(Vec){x+96.0, y, z}, &rot, 0); + + // // Trigger the intro state + // state.setState(&StateID_Intro); + + isResponding = 0; + OSReport("Is now responding: %d", isResponding); + OSReport("Daddy is at: %x", this); + + return true; +} + +int dSingAlong::onExecute() { + state.execute(); + + return true; +} + +int dSingAlong::onDraw() { + + if (chorus == -1) { return true; } + + for (int i = 0; i < (chorus + 1); i++) { + matrix.translation(prizePos[i].x, prizePos[i].y, prizePos[i].z); + matrix.applyRotationYXZ(&rot.x, &rot.y, &rot.z); + + pModel[i].setDrawMatrix(matrix); + + pModel[i].setScale(&prizeScale[i]); + + // OSReport("Calc World"); + // pModel[i].calcWorld(false); + + // OSReport("Schedule"); + // pModel[i].scheduleForDrawing(); + + // if (pAnim[i].isAnimationDone()) { + // pAnim[i].setCurrentFrame(0.0); } + + // OSReport("vf1C"); + // pModel[i]._vf1C(); + } + + return true; +} + +int dSingAlong::onDelete() { + return 1; +} + +/*****************************************************************************/ +// Register a Note being played by the players +void dSingAlong::RegisterNote(int note) { + OSReport("Register Note begins: %d", note); + OSReport("Responding is: %d", isResponding); + + if (isResponding == 1) { + OSReport("State was checked"); + + if (note == Songs[song][chorus][currentNote][0]) { + OSReport("Note was correct"); + + currentNote += 1; + } + else { + OSReport("Player failed"); + isResponding = 0; + state.setState(&StateID_Failure); + } + } +} + + +// Game Flow +// +// 1) Intro, maybe a banner 'Match the music' +// 2) Present the prize +// 3) Play the music +// 4) Wait for responses +// 6) If failure, make prize disappear, award banked prizes, end stage, play failure music +// 7) If successful, place the prize in the bank, and proceed to step 3 on the next segment +// 8) If all things are successful, play the victory theme and award the banked prizes, end stage + + +/*****************************************************************************/ +// Intro +CREATE_STATE(dSingAlong, Intro); + +void dSingAlong::executeState_Intro() { + state.setState(&StateID_Prize); +} + +//*****************************************************************************/ +// Prize +CREATE_STATE(dSingAlong, Prize); + +void dSingAlong::beginState_Prize() { + this->timer = 0; + + if (chorus != 0) { + this->timer = 120; + } + + Xkey_count = 2; + Ykey_count = 2; + Skey_count = 2; + + // /* keysX[i] = { frame, value, slope }; */ + keysX[0] = (HermiteKey){ 0.0, pos.x, 0.8 }; + keysY[0] = (HermiteKey){ 0.0, pos.y, 0.8 }; + keysS[0] = (HermiteKey){ 0.0, 5.0, 0.8 }; + + keysX[1] = (HermiteKey){ 60.0, pos.x + (30.0 * (chorus + 1)) - 320.0, 0.8 }; + keysY[1] = (HermiteKey){ 60.0, pos.y - 80.0, 0.8 }; + keysS[1] = (HermiteKey){ 60.0, 1.0, 0.8 }; +} +void dSingAlong::executeState_Prize() { + + if (timer == 120) { // Play a nice success sound, and wait a second + PlaySound(this, SE_MG_IH_PAIR_OK); // SE_MG_IH_NICE or SE_MG_UH_NICE + + int p; + p = PrizePacks[prize][chorus]; + this->Powerups[p] += 1; + } + + if (timer < 60 && timer >= 0) { // Move last time's model to the corner. + float modX = GetHermiteCurveValue(60.0 - timer, keysX, Xkey_count); + float modY = GetHermiteCurveValue(60.0 - timer, keysY, Ykey_count); + float modS = GetHermiteCurveValue(60.0 - timer, keysS, Skey_count); + + + prizePos[chorus] = (Vec){ modX, modY, prizePos[chorus].z }; + prizeScale[chorus] = (Vec){ modS, modS, modS }; + } + + if (timer == 0) { + chorus += 1; + + SpawnEffect("Wm_en_blockcloud", 0, &(Vec){pos.x, pos.y, pos.z+500.0}, &(S16Vec){0,0,0}, &(Vec){1.5, 1.5, 1.5}); + PlaySound(this, SE_OBJ_ITEM_APPEAR); // SE_OBJ_GOOD_ITEM_APPEAR + } + + if (timer == -90) { + state.setState(&StateID_Call); + } + + timer -= 1; +} + +//*****************************************************************************/ +// Call +CREATE_STATE(dSingAlong, Call); + +void dSingAlong::beginState_Call() { + timer = 0; + currentNote = 0; + + OSReport("SONG: {%d, %d} {%d %d} {%d %d}", Songs[song][chorus][0][0], Songs[song][chorus][0][1], Songs[song][chorus][1][0], Songs[song][chorus][1][1], Songs[song][chorus][2][0], Songs[song][chorus][2][1]); +} +void dSingAlong::executeState_Call() { + + // OSReport("%d: Waiting for timer %d", timer, *currentNote[1]); + + if (timer == Songs[song][chorus][currentNote][1]) { + OSReport("Playing Note %d", Songs[song][chorus][currentNote][0]); + + // int play = Notes[Songs[song][chorus][currentNote][0]-1]; + + // if ((currentNote > 1) && + // (Notes[Songs[song][chorus][currentNote][0]-1] == Notes[Songs[song][chorus][currentNote-1][0]-1]) && + // (Notes[Songs[song][chorus][currentNote][0]-1] == Notes[Songs[song][chorus][currentNote-2][0]-1])) + // { + // SoundPlayingClass::instance3->PlaySoundAtPosition(play, &(Vec2){pos.x,pos.y}, 0); + // } + + // else if ((currentNote > 0) && (Notes[Songs[song][chorus][currentNote][0]-1] == Notes[Songs[song][chorus][currentNote-1][0]-1])) { + // SoundPlayingClass::instance2->PlaySoundAtPosition(play, &(Vec2){pos.x,pos.y}, 0); + // } + // else { + // SoundPlayingClass::instance1->PlaySoundAtPosition(play, &(Vec2){pos.x,pos.y}, 0); + // } + + SoundPlayingClass::instance1->PlaySoundAtPosition(Notes[Songs[song][chorus][currentNote][0]-1], &(Vec2){pos.x,pos.y}, 0); + + // PlaySoundAsync(this, Notes[Songs[song][chorus][currentNote][0]-1]); + + + + currentNote += 1; + // currentNote = song[chorus][counter]; + OSReport("Next Note %d", Songs[song][chorus][currentNote][0]); + + if (Songs[song][chorus][currentNote][0] == 0) { + OSReport("Switching to Response Mode"); + state.setState(&StateID_Response); + } + } + + timer += 1; +} + +/*****************************************************************************/ +// Response +CREATE_STATE(dSingAlong, Response); + +void dSingAlong::beginState_Response() { + timer = 0; + currentNote = 0; + isResponding = 1; + OSReport("Is now responding: %d", isResponding); +} +void dSingAlong::executeState_Response() { + if (Songs[song][chorus][currentNote][0] == 0) { + isResponding = 0; + OSReport("Switching to some other mode: %d", isResponding); + if (chorus == 3) { + state.setState(&StateID_Win); + } + else { + state.setState(&StateID_Prize); + } + } +} + +/*****************************************************************************/ +// Failure +CREATE_STATE(dSingAlong, Failure); + +void dSingAlong::beginState_Failure() { + this->timer = 0; + + PlaySound(this, SE_MG_CMN_WIN_CLOSE); +} +void dSingAlong::executeState_Failure() { + if (timer == 240) { + this->addPowerups(); + ExitStage(3, 0, 0, 4); + } + timer += 1; +} + +/*****************************************************************************/ +// Win +CREATE_STATE(dSingAlong, Win); + +void dSingAlong::beginState_Win() { + this->timer = 0; + + PlaySound(this, SE_MG_CMN_FANFARE_GREAT); +} +void dSingAlong::executeState_Win() { + if (timer == 240) { + this->addPowerups(); + ExitStage(3, 0, 0, 4); + } + timer += 1; +} + +/*****************************************************************************/ +// Add Powerups at the End of the Stage +void dSingAlong::addPowerups() { + SaveFile *file = GetSaveFile(); + SaveBlock *block = file->GetBlock(file->header.current_file); + + for (int i = 0; i < 7; i++) { // Change this to 8 to support hammers + + block->powerups_available[i] = block->powerups_available[i] + this->Powerups[i]; + + if (block->powerups_available[i] > 99) { block->powerups_available[i] = 99; } + } + + + for (int i = 0; i < 4; i++) { // Make sure all players get the reward! + block->player_coins[i] = (this->Powerups[9] * 50) + block->player_coins[i]; + + for (;block->player_coins[i] < 100; block->player_coins[i] - 100) { + block->player_lives[i] = 1 + block->player_lives[i]; + } + + block->player_lives[i] = this->Powerups[8] + block->player_lives[i]; + if (block->player_lives[i] > 99) { block->player_lives[i] = 99; } + } + + return; +} + + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +// Replaces: Nothing yet + +class dSongBlock : public daEnBlockMain_c { +public: + Physics::Info physicsInfo; + + int onCreate(); + int onDelete(); + int onExecute(); + int onDraw(); + + mHeapAllocator_c allocator; + nw4r::g3d::ResFile resFile; + + m3d::mdl_c bodyModel; + m3d::anmChr_c glow; + + int note; + + void calledWhenUpMoveExecutes(); + void calledWhenDownMoveExecutes(); + void blockWasHit(bool isDown); + USING_STATES(dSongBlock); + DECLARE_STATE(Wait); + + static dSongBlock *build(); +}; + + +CREATE_STATE(dSongBlock, Wait); + + +dSongBlock *dSongBlock::build() { + void *buffer = AllocFromGameHeap1(sizeof(dSongBlock)); + return new(buffer) dSongBlock; +} + + +int dSongBlock::onCreate() { + + // Settings + this->note = this->settings; + + // Model creation + allocator.link(-1, GameHeaps[0], 0, 0x20); + + this->resFile.data = getResource("light_block", "g3d/light_block.brres"); + nw4r::g3d::ResMdl mdl = this->resFile.GetResMdl("light_block"); + bodyModel.setup(mdl, &allocator, 0x224, 1, 0); + SetupTextures_MapObj(&bodyModel, 0); // 800B42B0 + + // Animation Assignment + nw4r::g3d::ResAnmChr anmChr = this->resFile.GetResAnmChr("light_block"); + this->glow.setup(mdl, anmChr, &this->allocator, 0); + glow.bind(&bodyModel, anmChr, 1); + bodyModel.bindAnim(&glow, 0.0); + glow.setUpdateRate(1.0); + allocator.unlink(); + + // Block Physics + blockInit(pos.y); + + physicsInfo.x1 = -8; + physicsInfo.y1 = 16; + physicsInfo.x2 = 8; + physicsInfo.y2 = 0; + + physicsInfo.otherCallback1 = &daEnBlockMain_c::OPhysicsCallback1; + physicsInfo.otherCallback2 = &daEnBlockMain_c::OPhysicsCallback2; + physicsInfo.otherCallback3 = &daEnBlockMain_c::OPhysicsCallback3; + + physics.setup(this, &physicsInfo, 3, currentLayerID); + physics.flagsMaybe = 0x260; + physics.callback1 = &daEnBlockMain_c::PhysicsCallback1; + physics.callback2 = &daEnBlockMain_c::PhysicsCallback2; + physics.callback3 = &daEnBlockMain_c::PhysicsCallback3; + physics.addToList(); + + // Change State + doStateChange(&dSongBlock::StateID_Wait); + + return true; +} + + +int dSongBlock::onDelete() { + physics.removeFromList(); + return true; +} + + +int dSongBlock::onExecute() { + acState.execute(); + physics.update(); + blockUpdate(); + + // now check zone bounds based on state + if (acState.getCurrentState()->isEqual(&StateID_Wait)) { + checkZoneBoundaries(0); + } + + return true; +} + + +int dSongBlock::onDraw() { + + // tile.x = pos.x - 8; + // tile.y = -(16 + pos.y); + + matrix.translation(pos.x, pos.y+8.0, pos.z); + matrix.applyRotationYXZ(&rot.x, &rot.y, &rot.z); + + bodyModel.setDrawMatrix(matrix); + Vec myScale = (Vec){scale.x * 0.5, scale.y * 0.5, scale.z * 0.5}; + bodyModel.setScale(&myScale); + bodyModel.calcWorld(false); + + bodyModel.scheduleForDrawing(); + return true; +} + + +void dSongBlock::blockWasHit(bool isDown) { + pos.y = initialY; + + PlaySoundAsync(this, Notes[this->note-1]); + // Play an effect? + OSReport("Note is: %d", this->note); + dSingAlong::instance->RegisterNote(this->note); + OSReport("Note successfully registered"); + + physics.setup(this, &physicsInfo, 3, currentLayerID); + physics.addToList(); + + doStateChange(&StateID_Wait); +} + + +void dSongBlock::calledWhenUpMoveExecutes() { + this->bodyModel._vf1C(); + + if (this->glow.isAnimationDone()) { + this->glow.setCurrentFrame(0.0); } + + if (initialY >= pos.y) + blockWasHit(false); +} + +void dSongBlock::calledWhenDownMoveExecutes() { + this->bodyModel._vf1C(); + + if (this->glow.isAnimationDone()) { + this->glow.setCurrentFrame(0.0); } + + if (initialY <= pos.y) + blockWasHit(true); +} + + +void dSongBlock::beginState_Wait() {} +void dSongBlock::endState_Wait() {} +void dSongBlock::executeState_Wait() { + int result = blockResult(); + + if (result == 0) + return; + + if (result == 1) { + doStateChange(&daEnBlockMain_c::StateID_UpMove); + anotherFlag = 2; + isGroundPound = false; + } else { + doStateChange(&daEnBlockMain_c::StateID_DownMove); + anotherFlag = 1; + isGroundPound = true; + } +} + + |