📄 cengine.cpp
字号:
#include <eikenv.h>
#include <fbs.h>
#include <aknutils.h>
#include <s32file.h>
#include <aknquerydialog.h>
#include <bautils.h>
#include "CEngine.h"
#include "CGameTimer.h"
#include "CSplash.h"
#include "CMenu.h"
#include "CGameTetris.h"
#include "TetrisAppUi.h"
#include <Tetris.rsg>
#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<TScore>(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<CTetrisAppUi*> (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<TUint8> ((iMenu->iSetting[4] - 10) * 5);
// Difficulty
iGame->iStateDifficulty = static_cast<TUint8> (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<TInt8> (*index & 0x00f);
TInt8 g = static_cast<TInt8> ((*index & 0x0f0) >> 4);
TInt8 b = static_cast<TInt8> ((*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());
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -