#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 {{1,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; /*****************************************************************************/ // The Prize Model class dSongPrize: public dEn_c { public: int onCreate(); int onDelete(); int onExecute(); int beforeDraw(); int onDraw(); mHeapAllocator_c allocator; nw4r::g3d::ResFile resFile; m3d::mdl_c bodyModel; m3d::anmChr_c aw; int queue; int timer; HermiteKey keysX[0x10]; unsigned int Xkey_count; HermiteKey keysY[0x10]; unsigned int Ykey_count; HermiteKey keysS[0x10]; unsigned int Skey_count; USING_STATES(dSongPrize); DECLARE_STATE(Wait); DECLARE_STATE(Shrink); static dSongPrize *build(); }; CREATE_STATE(dSongPrize, Wait); CREATE_STATE(dSongPrize, Shrink); dSongPrize *dSongPrize::build() { void *buffer = AllocFromGameHeap1(sizeof(dSongPrize)); return new(buffer) dSongPrize; } int dSongPrize::onCreate() { // Settings queue = this->settings & 0xF; int prize = this->settings >> 16; scale = (Vec){ 3.0, 3.0, 3.0 }; int p; p = PrizePacks[prize][queue]; // Model creation allocator.link(-1, GameHeaps[0], 0, 0x20); resFile.data = getResource(Prizes[p][0], Prizes[p][1]); nw4r::g3d::ResMdl mdl = resFile.GetResMdl(Prizes[p][2]); bodyModel.setup(mdl, &allocator, 0x224, 1, 0); SetupTextures_Item(&bodyModel, 0); // 800B42B0 // Animation Assignment nw4r::g3d::ResAnmChr anmChr = resFile.GetResAnmChr("wait2"); aw.setup(mdl, anmChr, &allocator, 0); aw.bind(&bodyModel, anmChr, 1); bodyModel.bindAnim(&aw, 0.0); aw.setUpdateRate(1.0); allocator.unlink(); // Change State doStateChange(&dSongPrize::StateID_Wait); return true; } int dSongPrize::onDelete() { return true; } int dSongPrize::onExecute() { acState.execute(); return true; } int dSongPrize::onDraw() { 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(); bodyModel._vf1C(); if(this->aw.isAnimationDone()) this->aw.setCurrentFrame(0.0); return true; } void dSongPrize::beginState_Wait() {} void dSongPrize::endState_Wait() {} void dSongPrize::executeState_Wait() {} void dSongPrize::beginState_Shrink() { this->timer = 0; 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, 3.0, 0.8 }; keysX[1] = (HermiteKey){ 60.0, pos.x + (30.0 * queue) - 172.0, 1.0 }; keysY[1] = (HermiteKey){ 60.0, pos.y + 64.0, 1.0 }; keysS[1] = (HermiteKey){ 60.0, 1.0, 1.0 }; } void dSongPrize::endState_Shrink() {} void dSongPrize::executeState_Shrink() { float modX = GetHermiteCurveValue(timer, keysX, Xkey_count); float modY = GetHermiteCurveValue(timer, keysY, Ykey_count); float modS = GetHermiteCurveValue(timer, keysS, Skey_count); pos = (Vec){ modX, modY, pos.z }; scale = (Vec){ modS, modS, modS }; if (timer == 60) { doStateChange(&StateID_Wait); } timer += 1; } /*****************************************************************************/ // Sing Along 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; dSongPrize *PrizeModel; // dSongBlock *SongBlocks[7]; dSongBlock *SBa; dSongBlock *SBb; dSongBlock *SBc; dSongBlock *SBd; dSongBlock *SBe; dSongBlock *SBf; dSongBlock *SBg; int song; int prize; int chorus; 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 = this->settings & 0xF; this->prize = (this->settings >> 4) & 0xF; this->chorus = -1; 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 // Create and prepare the blocks S16Vec rot = (S16Vec){0,0,0}; float x = pos.x; float y = pos.y - 40.0; float z = pos.z; // for (int i = 0; i < 7; i++) { // SongBlocks[i] = (dSongBlock*)create(WM_KILLER, i+1, &(Vec){x-96.0+(32.0*i), y, z}, &rot, 0); // } SBa = (dSongBlock*)create(WM_KILLER, 1, &(Vec){x-96.0+(32.0*0), y, z}, &rot, 0); SBb = (dSongBlock*)create(WM_KILLER, 2, &(Vec){x-96.0+(32.0*1), y, z}, &rot, 0); SBc = (dSongBlock*)create(WM_KILLER, 3, &(Vec){x-96.0+(32.0*2), y, z}, &rot, 0); SBd = (dSongBlock*)create(WM_KILLER, 4, &(Vec){x-96.0+(32.0*3), y, z}, &rot, 0); SBe = (dSongBlock*)create(WM_KILLER, 5, &(Vec){x-96.0+(32.0*4), y, z}, &rot, 0); SBf = (dSongBlock*)create(WM_KILLER, 6, &(Vec){x-96.0+(32.0*5), y, z}, &rot, 0); SBg = (dSongBlock*)create(WM_KILLER, 7, &(Vec){x-96.0+(32.0*6), y, z}, &rot, 0); // // Trigger the intro state // state.setState(&StateID_Intro); isResponding = 0; return true; } int dSingAlong::onExecute() { state.execute(); return true; } int dSingAlong::onDraw() { return true; } int dSingAlong::onDelete() { instance = 0; 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]) { PlaySoundAsync(this, Notes[Songs[song][chorus][currentNote][0]-1]); OSReport("Note was correct"); currentNote += 1; } else { OSReport("Player failed"); PlaySound(this, SE_MG_CMN_WIN_CLOSE); isResponding = 0; state.setState(&StateID_Failure); } } } /*****************************************************************************/ // Intro CREATE_STATE(dSingAlong, Intro); void dSingAlong::executeState_Intro() { state.setState(&StateID_Prize); } //*****************************************************************************/ // Prize CREATE_STATE(dSingAlong, Prize); void dSingAlong::beginState_Prize() { this->timer = 120; } void dSingAlong::executeState_Prize() { if ((timer == 120) && (chorus >= 0)) { // 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) && (chorus >= 0)) { OSReport("Switching PrizeModel States"); PrizeModel->doStateChange(&dSongPrize::StateID_Shrink); } if (timer == 0) { chorus += 1; if (chorus == 4) { state.setState(&StateID_Win); return; } SpawnEffect("Wm_en_blockcloud", 0, &(Vec){pos.x, pos.y+32.0, 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 OSReport("Making the prize model"); PrizeModel = (dSongPrize*)create(WM_SINKSHIP, chorus + (prize << 16), &pos, &rot, 0); } 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]); PlaySoundAsync(this, Notes[Songs[song][chorus][currentNote][0]-1]); currentNote += 1; 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_Prize); } } } /*****************************************************************************/ // Failure CREATE_STATE(dSingAlong, Failure); void dSingAlong::beginState_Failure() { this->timer = 0; } void dSingAlong::executeState_Failure() { if (timer == 60) { PlaySound(this, SE_MG_IH_NOPAIR_NG); } if (timer == 240) { this->addPowerups(); ExitStage(WORLD_MAP, 0, BEAT_LEVEL, MARIO_WIPE); } 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(WORLD_MAP, 0, BEAT_LEVEL, MARIO_WIPE); } 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 beforeDraw(); int onDraw(); mHeapAllocator_c allocator; nw4r::g3d::ResFile resFile; m3d::mdl_c bodyModel; StageActorLight light; mHeapAllocator_c allocatorB; 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 allocator.unlink(); // light.init(&allocatorB, 2); // 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(); // light.pos.x = pos.x; // light.pos.y = pos.y+8.0; // light.pos.z = pos.z; // light.size = 226.0; // light.update(); // now check zone bounds based on state if (acState.getCurrentState()->isEqual(&StateID_Wait)) { checkZoneBoundaries(0); } return true; } int dSongBlock::beforeDraw() { // light.draw(); return dStageActor_c::beforeDraw(); } int dSongBlock::onDraw() { 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; SpawnEffect("Wm_en_vshit_ring", 0, &(Vec){pos.x, pos.y+8.0, pos.z-100.0}, &(S16Vec){0,0,0}, &(Vec){1.0, 1.0, 1.0}); SpawnEffect("Wm_ob_keyget02_lighit", 0, &(Vec){pos.x, pos.y+8.0, pos.z-100.0}, &(S16Vec){0,0,0}, &(Vec){0.5, 0.5, 0.5}); dSingAlong::instance->RegisterNote(this->note); physics.setup(this, &physicsInfo, 3, currentLayerID); physics.addToList(); doStateChange(&StateID_Wait); } void dSongBlock::calledWhenUpMoveExecutes() { if (initialY >= pos.y) blockWasHit(false); } void dSongBlock::calledWhenDownMoveExecutes() { 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; } }