Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

CGameFramework.cpp

Go to the documentation of this file.
00001 // Copyright 2002 Kenneth Guy,
00002 // 
00003 // You are free to take the source and do as you wish with it.
00004 // However it would be nice if you let me know if the code was useful
00005 // to you and give me an acknowledgement if you use a significant portion
00006 // of the code in an application.
00007 // 
00008 // CGameFramework.cpp
00009 
00013 //
00014 
00015 #include <e32std.h>
00016 #include "CGameFramework.h"
00017 #include "CGame.h"
00018 #include "TGamePanics.h"
00019 
00020 
00025 CGameFramework* CGameFramework::NewL(){
00026   CGameFramework* self= new (ELeave) CGameFramework;
00027   return(self);
00028 };
00029 
00031 CGameFramework::~CGameFramework() {
00032   delete iLevel;
00033   delete iMap;
00034 };
00035 
00041 void CGameFramework::CancelL() {
00042   iData.iKeys |= TGameData::EQuit;
00043   RunL(EFalse);
00044 }
00045 
00061 TInt CGameFramework::GameThread(TAny* aGameData) {
00062   // create a cleanup stack
00063   CTrapCleanup* cleanup=CTrapCleanup::New();  
00064   if(cleanup==NULL)
00065     return KErrNoMemory;
00066 
00067   TInt error=KErrNone;
00068 
00069   TGameData& data= (*STATIC_CAST(TGameData*,aGameData));
00070 
00071   // look while we have lives, haven't got to the end of the level,
00072   // 'Q' hasn't been pressed, and haven't got an error creating the game.
00073   while(data.iLives!=0 && !(data.iKeys & TGameData::EQuit) &&
00074         data.iLevelCompleted==EFalse && error==KErrNone) {    
00075     // create a game and call play on it.
00076     CGame *game=NULL;
00077     TRAPD(error,game=CGame::NewL(data));
00078     if(error==KErrNone) {
00079       game->Play();
00080       delete game;
00081     }
00082   }
00083   delete cleanup;
00084   return error;
00085 };
00086 
00088 TInt CGameFramework::Score() {
00089   return(iData.iScore);
00090 }
00091 
00092 
00106 CGameFramework::TGameState CGameFramework::RunL(TBool aReset) {  
00107   // assert that LoadLevelL has been called and succeded in
00108   // loading a level
00109   __ASSERT_ALWAYS(iLevel!=NULL && iMap!=NULL,TGamePanics::Panic(TGamePanics::ENoLevelLoaded));
00110 
00111   // if this is a new game reset the score and lives
00112   if(aReset!=EFalse) {
00113     iData.iLives=3;
00114     iData.iScore=0;
00115   }
00116 
00117   // connect to the window server.
00118   RWsSession winSession;
00119   User::LeaveIfError(winSession.Connect());
00120   CleanupClosePushL(winSession);
00121   
00122   // these can be whatever we like, as long as they are unique,
00123   // as we have only one window and one windowgroup we don't
00124   // actually use them.
00125   const TUint32 KWindowHandle=1;
00126   const TUint32  KWindowGroupHandle=2;
00127 
00128   // create a window group so we can create a window
00129   RWindowGroup winGroup(winSession);
00130   User::LeaveIfError(winGroup.Construct(KWindowGroupHandle));
00131   CleanupClosePushL(winGroup);
00132   
00133   // create a blank window, blank windows can't be drawn on,
00134   // however as the game code writes directly to the screen this
00135   // isn't a problem.
00136   RBlankWindow window(winSession);
00137   User::LeaveIfError(window.Construct(winGroup,KWindowHandle));
00138   CleanupClosePushL(window);
00139   window.Activate();
00140   window.SetSize(TSize(640,200));
00141   // request focus events so we can pause the game thread when
00142   // we move to the background, this stops us writing over some other
00143   // apps screen.
00144   window.EnableFocusChangeEvents();
00145 
00146   // first time in we want want to redraw everything,
00147   iData.iForceRedraw=ETrue;
00148 
00149   // logon to the thread so we get informed when it exits.
00150   iGameThread.Logon(iGameStatus);
00151   iGameThread.Resume();
00152 
00153   // play the game
00154   TBool finished=Play(winSession);
00155 
00156   // cancel any outstanding request for key strokes/focus changes etc
00157   winSession.EventReadyCancel();
00158   // and watching the state of the thread
00159   iGameThread.LogonCancel(iGameStatus);
00160 
00161   // I think this is the EventReady completing.
00162   User::WaitForAnyRequest();
00163 
00164   // remove our window
00165   CleanupStack::PopAndDestroy(3,&winSession); //winGroup, window
00166   
00167 
00168   if(finished==EFalse) {
00169     // this is the thread notifying that it has suspended
00170     User::WaitForAnyRequest();
00171     // we have just paused the thread/game
00172     return EPaused;
00173   } else {
00174     // game finished, so close the thread, and delete the level
00175     iGameThread.Close();
00176     delete iLevel;
00177     iLevel=NULL;
00178     delete iMap;
00179     iMap=NULL;
00180 
00181     User::Heap().Compress();
00182 
00183     // did the player complete the level or die
00184     if(iData.iLevelCompleted==EFalse) {
00185       return EDied;
00186     } else {
00187       return ELevelCompleted;
00188     }
00189   }    
00190 };
00191 
00192 
00210 TBool CGameFramework::Play(RWsSession &aWindSession) {
00211   TRequestStatus normalEvent;
00212 
00213   // listen for key strokes/focus changes etc.
00214   aWindSession.EventReady(&normalEvent);
00215 
00216   for(;;) {
00217     User::WaitForAnyRequest();
00218     if(normalEvent!=KRequestPending) {
00219       TWsEvent event;
00220       aWindSession.GetEvent(event);
00221       aWindSession.EventReady(&normalEvent);        
00222       switch(event.Type()) {
00223       case EEventKeyDown: {
00224         TKeyEvent *key=event.Key();
00225         switch(key->iScanCode) {            
00226         case EStdKeyLeftArrow:   iData.iKeys |= TGameData::ELeft;    break;
00227         case EStdKeyRightArrow:  iData.iKeys |= TGameData::ERight;   break;
00228         case EStdKeyUpArrow:     iData.iKeys |= TGameData::EUp;      break;
00229         case EStdKeyDownArrow:   iData.iKeys |= TGameData::EDown;    break;
00230         case 'Q':                iData.iKeys |= TGameData::EQuit;    break;
00231         case 'A':                iData.iKeys |= TGameData::EAction1; break;
00232         case EStdKeySpace:       iData.iKeys |= TGameData::EAction2; break;
00233         case EStdKeyLeftShift:   iData.iKeys |= TGameData::EAction3; break;
00234         case EStdKeyEnter:       iData.iKeys |= TGameData::EAction4; break;
00235         case 'P': {
00236           iGameThread.Suspend();
00237           return EFalse;
00238         }
00239         }
00240       }
00241       break;
00242       case EEventKeyUp: {
00243         TKeyEvent *key=event.Key();
00244         switch(key->iScanCode) {            
00245         case EStdKeyLeftArrow:   iData.iKeys &= ~TGameData::ELeft;    break;
00246         case EStdKeyRightArrow:  iData.iKeys &= ~TGameData::ERight;   break;
00247         case EStdKeyUpArrow:     iData.iKeys &= ~TGameData::EUp;      break;
00248         case EStdKeyDownArrow:   iData.iKeys &= ~TGameData::EDown;    break;
00249         case 'A':                iData.iKeys &= ~TGameData::EAction1; break;
00250         case EStdKeySpace:       iData.iKeys &= ~TGameData::EAction2; break;
00251         case EStdKeyLeftShift:   iData.iKeys &= ~TGameData::EAction3; break;
00252         case EStdKeyEnter:       iData.iKeys &= ~TGameData::EAction4; break;
00253         }
00254       }        
00255       break;   
00256       case EEventFocusLost:
00257         // pause game if we go into the background
00258         iGameThread.Suspend();
00259         return EFalse;
00260         break;
00261       }
00262     } else if(iGameStatus!=KRequestPending) {
00263       // thread ended
00264       return ETrue;
00265     } else { 
00266       // event meant for active scheduler if there is one
00267       // I don't think it should never get here
00268       CActiveScheduler *sched=CActiveScheduler::Current();
00269       if(sched!=NULL) {
00270         TInt error;
00271         sched->RunIfReady(error,0);
00272       }
00273     }
00274   }
00275 }
00276 
00289 void CGameFramework::LoadLevelL(const TDesC& aLevelName,const TDesC& aMapName, TBool aResetShip) {
00290   __ASSERT_ALWAYS(iLevel==NULL && iMap==NULL,TGamePanics::Panic(TGamePanics::ELoadLevelWithLevelLoaded));
00291   
00292   RFs fs;
00293   User::LeaveIfError(fs.Connect());  
00294   CleanupClosePushL(fs);
00295 
00296 
00297   // load level file
00298   RFile levelFile;
00299   User::LeaveIfError(levelFile.Open(fs,aLevelName,EFileRead));  
00300   CleanupClosePushL(levelFile);                    
00301 
00302   TInt size;
00303   User::LeaveIfError(levelFile.Size(size));
00304   size-=16; //skip uids on front of file
00305   HBufC8 *levelDes= HBufC8::NewLC(size);
00306   TPtr8 levelPtr=levelDes->Des();
00307   User::LeaveIfError(levelFile.Read(16,levelPtr,size));
00308 
00309 
00310   // load map file
00311   RFile mapFile;
00312   User::LeaveIfError(mapFile.Open(fs,aMapName,EFileRead));
00313   CleanupClosePushL(mapFile);
00314 
00315   User::LeaveIfError(mapFile.Size(size));
00316   size-=16; //skip uids on front of file
00317   HBufC8 *mapDes= HBufC8::NewLC(size);
00318   TPtr8 mapPtr=mapDes->Des();
00319   User::LeaveIfError(mapFile.Read(16,mapPtr,size));
00320 
00321 
00322 
00323 
00324   // create a thread within which to run the game,
00325   _LIT(KGameThread,"RealGame");
00326   // 4k stack,
00327   const TInt KStackSize=4*1024;
00328   
00329   // create but don't resume as the iData isn't set up yet.
00330   User::LeaveIfError(iGameThread.Create(KGameThread,GameThread,KStackSize,NULL,&iData));
00331   // as we want to respond to key presses the game thread
00332   // has a lower priority than getting events
00333   iGameThread.SetPriority(EPriorityLess);
00334   
00335 
00336 
00337 
00338   
00339   CleanupStack::Pop(mapDes);
00340   iMap=mapDes;
00341   CleanupStack::PopAndDestroy(&mapFile);
00342   CleanupStack::Pop(levelDes);
00343   iLevel=levelDes;
00344   CleanupStack::PopAndDestroy(2,&fs); // levelFile
00345 
00346 
00347   // setup level data
00348   TInt32* level= (TInt32*) (iLevel->Ptr());
00349   TInt32* map= (TInt32*) (iMap->Ptr());
00350   
00351   iData.iKeys=0;
00352   iData.iLevelCompleted=EFalse;
00353 
00354   iData.iLevel.iMapData=(TInt16*) (iMap->Ptr()+map[0]);
00355   iData.iLevel.iPaths=(TInt16*) (iMap->Ptr()+map[1]);
00356 
00357   iData.iLevel.iBackgrounds=(TUint16*) (iLevel->Ptr()+level[0]);
00358   iData.iLevel.iSprites=(TUint16*) (iLevel->Ptr()+level[1]);
00359   iData.iLevel.iStatus=(TUint16*) (iLevel->Ptr()+level[2]);
00360 
00361   iData.iLevel.iShipSprite=(TInt16) map[2];
00362   iData.iLevel.iShipDeathStartSprite=(TInt16) map[3];
00363   iData.iLevel.iShipDeathEndSprite=(TInt16) map[4];
00364   iData.iLevel.iBadGuyDeathStartSprite=(TInt16) map[5];
00365   iData.iLevel.iBadGuyDeathEndSprite=(TInt16) map[6];
00366   iData.iLevel.iBulletPower1_StartSprite=(TInt16) map[7];
00367   iData.iLevel.iBulletPower2_StartSprite=(TInt16) map[8];
00368   iData.iLevel.iBulletPower3_StartSprite=(TInt16) map[9];
00369   iData.iLevel.iBulletPower4_StartSprite=(TInt16) map[10];
00370   iData.iLevel.iShipStartXPos=(TInt16) map[11];
00371   iData.iLevel.iShipStartYPos=(TInt16) map[12];
00372   iData.iLevel.iShipStartHealth=(TInt16) map[13];
00373   if(aResetShip!=EFalse) {
00374     iData.iLevel.iShipPowerups=(TInt16) map[14];
00375   }
00376 }
00377 

Documentation for Game (Beta) version 1.44.