#include #include #include #include #include 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 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; } }