www.pudn.com > Tetris.rar > CEngine.cpp
#include#include #include #include #include #include #include "CEngine.h" #include "CGameTimer.h" #include "CSplash.h" #include "CMenu.h" #include "CGameTetris.h" #include "TetrisAppUi.h" #include #include "Tetris.hrh" #include "TScore.h" // sound #include "CWaveLoader.h" #include "CSoundMixer.h" _LIT(KFileStore,"save.dat"); // construct and destruct // NewL() CEngine* CEngine::NewL( CGameTetris* aGame, CWindowGc& aGc, RWindow& aWindow, TDisplayMode aDisplayMode ) { CEngine* self = new( ELeave )CEngine(aGame, aGc, aWindow, aDisplayMode ); CleanupStack::PushL( self ); self->ConstructL(); CleanupStack::Pop( self ); return self; } // ~ CEngine::~CEngine() { if(iTimer) { iTimer->Cancel(); delete iTimer; } delete iBackBufferGc; delete iBackBufferDevice; delete iBackBufferBitmap; delete iMenu; delete iBitmapUtil; delete iScoreTable; // sound delete iSound; delete iSoundBg.iData; delete iSoundGameOver.iData; delete iSoundOnClick.iData; delete iSoundMenu.iData; delete iSoundClearRow.iData; delete iSoundWellDone.iData; delete iSoundLevelUp.iData; } // ConstructL() void CEngine::ConstructL() { // get the draw region TPoint pos = iWindow.Position(); TSize size = iWindow.Size(); iDrawRect.SetRect(pos, size); // Create a new bitmap with size of viewĄŻs rect and color depth of // screen iBackBufferBitmap = new(ELeave) CFbsBitmap(); User::LeaveIfError(iBackBufferBitmap->Create(iDrawRect.Size(), iDisplayMode)); // Create bitmap device for the bitmap iBackBufferDevice = CFbsBitmapDevice::NewL(iBackBufferBitmap); // Create graphics context for the bitmap User::LeaveIfError(iBackBufferDevice->CreateContext(iBackBufferGc)); iBitmapUtil = new(ELeave)TBitmapUtil(iBackBufferBitmap); // Create a menu iMenu = CMenu::NewL(iBackBufferGc); iTimer = CGameTimer::NewL(*this); iPause = ETrue; // Load sound CWavLoader* wavLoader = CWavLoader::NewLC(); iSoundBg = wavLoader->LoadL(_L("bg.wav")); iSoundGameOver = wavLoader->LoadL(_L("gameover.wav")); iSoundOnClick = wavLoader->LoadL(_L("click.wav")); iSoundMenu = wavLoader->LoadL(_L("menu.wav")); iSoundClearRow = wavLoader->LoadL(_L("cleanrow.wav")); iSoundLevelUp = wavLoader->LoadL(_L("levelup.wav")); iSoundWellDone = wavLoader->LoadL(_L("welldone.wav")); CleanupStack::PopAndDestroy( wavLoader ); // create sound mixer iSound = CSoundMixer::NewL(); // make iSoundBg repeat itself iSoundBg.iRepEnd = iSoundBg.iLength; //iSound->Play(iSoundBg, 0, 16000, 256); // create score table iScoreTable = new (ELeave) CArrayFixSeg (1); ScoreLoadL(); iGameState = ENewSplash; //iGameState = EMenu; } // CEngine() CEngine::CEngine( CGameTetris* aGame, CWindowGc& aGc, RWindow& aWindow, TDisplayMode aDisplayMode ) : iGame(aGame) , iGc( aGc ) , iWindow( aWindow ) , iDisplayMode( aDisplayMode ) {} /////////////////////////////////////////////////////////////////////////////////// // Other Method // DoGameFrameL() TInt CEngine::DoGameFrameL() { // Prepare to Splash if(iGameState == ENewSplash) { if(!ShowSplash()) iGameState = EMenu; } if(iGameState == EExit) { if(!ShowSplash()) { CTetrisAppUi* appUi = static_cast (CEikonEnv::Static()->AppUi()); appUi->Quit(); //User::Exit(0); // this cann't check memory leak ,why create it ,i'm confused! } } // Prepare for showing the menu, used for state changing. if(iGameState == EMenu) { // Stop bg music iSound->Stop(0); // set Menu running frequency iTimer->SetTickTime(40000); iGameState = EMenuRunning; } // Menu running if(iGameState == EMenuRunning) { // show main menu TInt32 signal = iMenu->Command(iKey); switch(signal) { case 1 : // quit { iGameState = EExit; }break; case 2 : // Start new game { if(!iMusicOff) iSound->Play(iSoundBg, 0, 16000, 64); iGameState = ERunning; iGame->iStateRunning = 0; iTimer->SetTickTime(50000); }break; case 3 : // continue game { if(!iMusicOff) iSound->Play(iSoundBg, 0, 16000, 64); if(!iGame->iStateRunning) return 1; iGameState = ERunning; iTimer->SetTickTime(50000); }break; case 4: { iDisplayTop5 = 1; }break; case 5: { iDisplayTop5 = 0; }break; case 11 : // Set brick rotation { if(iMenu->iSetting[0] == 0) iGame->iStateRotateDir = -1; else iGame->iStateRotateDir = 1; }break; case 12 : // Set Sound { if(iMenu->iSetting[1] == 2) // set sound effect iSoundEffectOff = 0; else iSoundEffectOff = 1; if(iMenu->iSetting[2] == 4) // set background music iMusicOff = 0; else iMusicOff = 1; iSound->SetVolume( (iMenu->iSetting[3] - 5) * 256 / 4 ); }break; case 13 : // Set Difficult { // start lines, scratching iGame->iStateStartLines = static_cast ((iMenu->iSetting[4] - 10) * 5); // Difficulty iGame->iStateDifficulty = static_cast (iMenu->iSetting[5] - 14); }break; default: { iGc.Activate( iWindow ); iMenu->Draw(); iGc.BitBlt(iDrawRect.iTl, iBackBufferBitmap); if(iDisplayTop5) ScoreDisplay(); iGc.Deactivate(); }break; } iKey = 0; } // game over if(iGameState == EGameOver) { // show Txt "game over" and some other effects // check if it is a new high score // if it is, then prompt to input user's name // else goto main menu directly iBitmapUtil->Begin(TPoint(0, 0)); // lock the heap TUint16* addr = (TUint16*)iBackBufferBitmap->DataAddress(); // width and height in pixel TInt width = iBackBufferBitmap->SizeInPixels().iWidth; TInt height = iBackBufferBitmap->SizeInPixels().iHeight; if(!iControl) DipBlackwhite(addr, width, height); if(++iControl > 15) DipEffect(addr, width, height); iBitmapUtil->End(); // unlock the heap if(iControl == 35) { iControl = 0; iGameState = EMenu; ScoreAddL(); return 1; } iGc.Activate( iWindow ); iGc.BitBlt(TPoint(0, 0), iBackBufferBitmap); const CFont* font; font = CEikonEnv::Static()->LegendFont(); iGc.UseFont(font); iGc.SetPenColor(KRgbRed); iGc.DrawText(_L("Game Over"), TPoint(65, 100)); iGc.DiscardFont(); iGc.Deactivate(); iKey = 0; } // running if(iGameState == ERunning) { TInt8 state = iGame->Run(); if(state == 1) // game over { if(!iSoundEffectOff) iSound->Play(iSoundGameOver, 1, 16000, 64); iGameState = EGameOver; return 1; } // draw screen if(iGame->Command(iKey) || state == 2) { iGc.Activate( iWindow ); iGame->Draw(iBackBufferGc); iGc.BitBlt(iDrawRect.iTl, iBackBufferBitmap); iGc.Deactivate(); } iKey = 0; // play sound effect if(!iSoundEffectOff) { // only play one sound effect ,there is a priority if(iGame->iStateLevelUp) { iSound->Play(iSoundLevelUp, 1, 16000, 64); iGame->iStateLevelUp = 0; //return 1; } if(iGame->iStateWellDone) { iSound->Play(iSoundWellDone, 2, 16000, 64); iGame->iStateWellDone = 0; } if(iGame->iStateClearRow) { iSound->Play(iSoundClearRow, 3, 16000, 64); iGame->iStateClearRow = 0; //return 1; } } } return ETrue; } // Start() void CEngine::Start() { if( !iPause ) return; iPause = EFalse; iTimer->Start(); iSound->Resume(); } // Stop() void CEngine::Stop() { if( iPause ) return; iPause = ETrue; iTimer->Cancel(); iSound->Pause(); } // Pause() void CEngine::Pause() { if(iPause) Start(); else Stop(); } // Command() void CEngine::Command(TInt aCommand) { if(aCommand == 'q') { if(iGameState == ERunning || iGameState == EMenuRunning) iGameState = EMenu; else return; } if(aCommand == '0' && iGameState == ERunning) Pause(); iKey = aCommand; if(iSoundEffectOff) return; if(iGameState == EMenuRunning) { switch(iKey) { case EStdKeyDevice5: case '5': case 63557: { iSound->Play(iSoundOnClick, 2, 16000, 64); }break; case EKeyUpArrow : // UP case '2' : case EKeyDownArrow : // Down case '8' : case EKeyLeftArrow : // Left case '4' : case EKeyRightArrow : // Right case '6' : { iSound->Play(iSoundMenu, 3, 16000, 64); }break; default: break; } } } // ShowSplash() TBool CEngine::ShowSplash() { if(!iSplash) { iSplash = CSplash::NewL(iGc, iWindow); iTimer->SetTickTime(100000); } else if(!iSplash->Draw()) { delete iSplash; iSplash = NULL; return 0; // stop splashing } return 1; // keep splashing } // DipEffect() void CEngine::DipEffect(TUint16* aAddr, TInt aWidthInPixel, TInt aHeightInPixel) { TInt i, j; TUint16* index; for(i = 0; i < aHeightInPixel; i++) { index = aAddr + aWidthInPixel * i; // get address of each row for(j = 0; j < aWidthInPixel; j++) { TInt8 r = static_cast (*index & 0x00f); TInt8 g = static_cast ((*index & 0x0f0) >> 4); TInt8 b = static_cast ((*index & 0xf00) >> 8); r--; g--; b--; (r < 0) ? *index = 0 : *index = r; (g < 0) ? *index &= 0xf0f : *index |= g << 4; (b < 0) ? *index &= 0x0ff : *index |= b << 8; //(r > 0xf) ? *index = 0xf : *index = r; //(g > 0xf) ? *index |= 0x0f0 : *index |= g << 4; //(b > 0xf) ? *index |= 0xf00 : *index |= b << 8; index ++; } } } void CEngine::DipBlackwhite(TUint16* aAddr, TInt aWidthInPixel, TInt aHeightInPixel) { TInt i, j; TUint16* index; for(i = 0; i < aHeightInPixel; i++) { index = aAddr + aWidthInPixel * i; // get address of each row for(j = 0; j < aWidthInPixel; j++) { *index = *index > 0x800 ? 0xfff : 0; index ++; } } } // method deal with score void CEngine::ScoreLoadL() { // in my opinion, this can only be used in real handset, not emulator TFileName fileName( KFileStore ); CompleteWithAppPath( fileName ); if(!BaflUtils::FileExists(CEikonEnv::Static()->FsSession(), fileName)) { ScoreSaveL(); return; } CFileStore* store = CDirectFileStore::OpenLC(CEikonEnv::Static()->FsSession(), fileName, EFileRead); RStoreReadStream stream; stream.OpenLC(*store,store->Root()); TScore sc; TInt i, count=stream.ReadInt32L(); for(i = 0; i < count; i++) { stream >> sc; iScoreTable->AppendL(sc); } CleanupStack::PopAndDestroy(2); // (store and stream) } void CEngine::ScoreAddL() { // first clear score table ScoreReset(); // load score ScoreLoadL(); // check if the score is a new Top5 score if(iScoreTable->Count() == TopNum) // not full if(iGame->iStateScore <= -iScoreTable->At(TopNum - 1).iValue) return; TBuf<15> name; CAknTextQueryDialog* dialog = CAknTextQueryDialog::NewL(name); if(!dialog->ExecuteLD(R_INPUT_USERNAME_DIALOG)) return; // new score TScore newScore(iGame->iStateScore, name); // Insert the element in the Score table (using an Integer key on iValue) TKeyArrayFix byScore(_FOFF(TScore, iValue), ECmpTInt); iScoreTable->InsertIsqAllowDuplicatesL(newScore, byScore); // Remove the last player from the table if necessary and compress the table if(iScoreTable->Count() > TopNum) iScoreTable->Delete(TopNum); iScoreTable->Compress(); // save the score ScoreSaveL(); } void CEngine::ScoreSaveL() { // in my opinion, this can only be used in real handset, not emulator TFileName fileName( KFileStore ); CompleteWithAppPath( fileName ); CFileStore* store = CDirectFileStore::ReplaceLC(CEikonEnv::Static()->FsSession(), fileName, EFileWrite); store->SetTypeL(KDirectFileStoreLayoutUid); // Create the hi-score stream RStoreWriteStream stream; TStreamId id = stream.CreateLC(*store); // Write the number of score table entries in the store TInt count= iScoreTable->Count(); stream.WriteInt32L(count); // Then write each entry for(TInt i = 0; i < count; i++) { stream << iScoreTable->At(i); } // Commit the changes to the stream stream.CommitL(); CleanupStack::PopAndDestroy(); // Set the stream in the store and commit the store store->SetRootL(id); store->CommitL(); CleanupStack::PopAndDestroy(); } void CEngine::ScoreDisplay() { // note this don't has iGc.Activate( iWindow ); & iGc.Deactivate(); //iGc.Activate( iWindow ); const CFont* font; font = CEikonEnv::Static()->TitleFont(); iGc.UseFont(font); iGc.SetPenColor(KRgbWhite); iGc.DrawText(_L("Top 5"), TPoint(75, 30)); for(TInt i = 0; i < iScoreTable->Count(); i++) { iGc.DrawText(iScoreTable->At(i).iName, TPoint(30, 60 + 30*i)); TBuf16<10> score; score.Num(-iScoreTable->At(i).iValue); iGc.DrawText(score, TPoint(130, 60 + 30*i)); } iGc.DiscardFont(); //iGc.Deactivate(); } void CEngine::ScoreReset() { iScoreTable->Delete(0, iScoreTable->Count()); }