cgame.cpp

来自「symbian 的一个 二维飞行游戏 源码 及相关技术文章」· C++ 代码 · 共 477 行

CPP
477
字号
// Copyright 2002 Kenneth Guy,
// 
// CGame.cpp

/** \file CGame.cpp

    implementation of class CGame */

#include <e32std.h>
#include <e32base.h>

#include "CMap.h"
#include "CGame.h"
#include "TBadGuy.h"
#include "TPlayersShip.h"
#include "TBullet.h"
#include "TGameData.h"

/** Maximum number of bad guys on screen at one time.

    When all array slots are used adding a new bad guy
    doesn't do anything.
*/
const TInt KMaxBadGuys=125;

/** Maximum number of players bullets on screen at one time.

    When all array slots are used shooting (CGame::Shoot())
    doesn't do anything.
*/
const TInt KMaxBullets=8;


/** Standard leave safe construction */

CGame* CGame::NewL(TGameData &aData) {
  CGame *self= new (ELeave) CGame(aData);
  CleanupStack::PushL(self);
  self->ConstructL();
  CleanupStack::Pop(self);
  return(self);
}

/** constructor */

CGame::CGame(TGameData& aData) : iData(aData), iLastFired(0) {

};

/** closes the status screen and delete member objects */
CGame::~CGame() {
  iStatus.Close();
  delete iMap;
  delete [] iBadGuys;
  delete [] iBullets;
  delete iPlayersShip;
};


/** Second phase construtor.

    Creates status screen, creates players ship, creates array of slots for
    bad guys, creates array of bullets and sets them up as strength 1

*/

void CGame::ConstructL() {
  // create the status screen area
  User::LeaveIfError(iStatus.Open(iData.iLevel.iStatus));

  // create the players ship as defined by the level data
  iPlayersShip = new (ELeave) TPlayersShip(iData.iLevel.iShipStartXPos,
                                           iData.iLevel.iShipStartYPos,
                                           iData.iLevel.iShipStartHealth,
                                           iData.iLevel.iShipSprite,
                                           iData.iLevel.iShipDeathStartSprite,
                                           iData.iLevel.iShipDeathEndSprite);  

  // create slots for KMaxBadGuy bad guys
  // (TBadGuy is used for bonuses and sheilds as well as bad guys)
  iBadGuys= new (ELeave) TBadGuy[KMaxBadGuys];

  // set up players bullets at power one
  iBullets= new (ELeave) TBullet[KMaxBullets];
  for(TInt i=0;i<KMaxBullets;i++) {
    iBullets[i].Set(iData.iLevel.iBulletPower1_StartSprite+i,1);
  }

  TInt16 count=iData.iLevel.iShipPowerups;
  while(count--) {
    Bonus(EBonusPowerup);
  }

  // create the offscreen buffer, and plot the first screen full of
  // the map
  iMap=CMap::NewL(*this,iData.iLevel.iBackgrounds,iData.iLevel.iMapData);
  // CMap may calls back into CGame to add bad guys, if there
  // are any bad guys in the first 11 lines of the map this will
  // happen during construction, so CGame should be valid when
  // constructing iMap,
};


/** Main game loop.

    This loop runs while the player hasn't died, finished the level,
    or quit.

    The player dies when players ship health has reached zero, and the
    explosion animation has finished.

    The player finishes the level when the map reaches the end and the
    player flies off the right hand side of the screen.

    The game quits if the TGameData::EQuit bit is set in TGameData::iKeys

    The loop consists of:
    <ul>
    <li> Handle key presses - move ship/shoot </li>
    <li> Move bad guys and bullets </li>
    <li> Draw sprites onto offscreen map (player, badguys, bullets) </li>
    <li> Delay so that each loop takes at least 2 ticks </li>
    <li> Copy offscreen map to screen </li>
    <li> Erase sprites from offscreen map </li>
    <li> Handle collisions between badguys and bullets or players ship </li>
    <li> Handle collisions between players ship and landscape </li>
    <li> return is player is dead </li>
    <li> return is player has finished </li>
    <li> Update status pane </li>
    <li> Scroll the map, (may add new bad guys) </li>
    </ul>

*/

void CGame::Play() {
  TUint lasttick=0;

  while(!(iData.iKeys & TGameData::EQuit)) {
    // handle key presses
    HandleKeys();

    // move bullets and bad guys
    MoveThings();
    // plot the ship/bullets & badguys into offscreen buffer
    DrawEveryThing();
    
    // make sure each loop takes at least 2 ticks
    TUint tick = User::TickCount();
    while((tick-lasttick)<2)
      tick = User::TickCount();
    lasttick=tick;   

    // copy the offscreen buffer to the screen
    iMap->BlitAsm();

    // remove all the sprites we ploted in reverse order
    RemoveEveryThing();
    
    // handle bad guy collisions
    Collisions();
    
    // crash if player hit the landscape
    if(iPlayersShip->Hit()==TSprite32x24::EHitLandScape)
      iPlayersShip->SetHealth(0);

    
    // Handle crashing ship
    if(iPlayersShip->Dead()!=EFalse) {
      iData.iLives--;
      return;
    }

    // handle end of level (when map has finished, and player flies
    // off the right hand side of the screen)
    if(iMap->Finished()!=EFalse && iPlayersShip->iX==304 &&
       iPlayersShip->Dieing()==EFalse) {
      iData.iLevelCompleted=ETrue;
      // update level data so that next level can start with same
      // health / powerups
      iData.iLevel.iShipPowerups=iPowerups;
      iData.iLevel.iShipStartHealth=iPlayersShip->Health();
      return;
    }

    
    // plot status window
    if(iData.iForceRedraw==EFalse) {
      iStatus.Update(iData.iScore,iData.iLives,iPlayersShip->Health());
    } else {
      iStatus.Draw(iData.iScore,iData.iLives,iPlayersShip->Health());
      iData.iForceRedraw=EFalse;
    }
    
    // scroll the offset screen map, plot another line of map sprites
    // if nessesary.
    iMap->ScrollMap();
  }
};


/** Collect bonuses.

    This is called to add an extra life, or powerup a bullet when
    the players ship collides with a bonus sprite.

    \param aBonus Type of bonus
*/
void CGame::Bonus(TGameBonus aBonus) {
  switch(aBonus) {
  case EBonusLife:
    iData.iLives++;
    break;

  case EBonusPowerup: {    
    // remember the number of these so that if the player completes
    // the level they can be carried to the next level
    iPowerups++;
    // find the bullet with the least power and increase
    // its power by one, changing its sprite at the same time.
    // if all bullets are at full strength nothing happens.
    TInt16 minPower=iBullets[0].Strength();
    TInt powerUp=0;
    for(TInt i=1;i<KMaxBullets;i++) {
      const TInt16 strength=iBullets[i].Strength();
      if(strength < minPower) {
        powerUp=i;
        minPower = strength;
        }
    }
    switch(minPower) {
    case 0:
       iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower1_StartSprite,1);
       break;
    case 1:
       iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower2_StartSprite,2);
       break;
    case 2:
       iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower3_StartSprite,3);
       break;
    case 3:
       iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower4_StartSprite,4);
       break;
    }
  }
  break;  

  }
}

/** First bad guy explosion sprite.

    \return Sprite number of first sprite to use for bad guy explossions

*/

TInt16 CGame::FirstExplosionSprite() {
  return iData.iLevel.iBadGuyDeathStartSprite;
}

/** Last bad guy explosion sprite.

    \return Sprite number of last sprite to use for bad guy explossions (inclusive)
*/

TInt16 CGame::LastExplosionSprite() {
  return iData.iLevel.iBadGuyDeathEndSprite;
}

/** Give the player some points */
void CGame::AddScore(TInt aScore) {
  iData.iScore+=aScore;
}

/** Damage bad guys with correct id.

    \param aId Id of bad guys to damage
    \param aHealth amount of damage to do
*/

void CGame::DamageBadGuys(TInt16 aId,TInt16 aHealth) {
  for(TInt i=0;i<KMaxBadGuys;i++) {
    if(iBadGuys[i].Used()!= EFalse && aId == iBadGuys[i].Id()) {
      iBadGuys[i].Damage(aHealth,*this);
    }
  }      
}


/** Work out what hit what.

    Check the TSprite32x24::Hit for each of the bad guys to see what is
    was plotted over. Call the HitShip() or HitBullet() members as nessesary.
*/

void CGame::Collisions() {
  for(TInt i=KMaxBadGuys-1;i>=0;--i) {
    if(iBadGuys[i].Used()!=EFalse) {
      TSprite32x24::THit what=iBadGuys[i].Hit();
      
      if(what==TSprite32x24::EHitShip) {
        // badguy has hit player
        iBadGuys[i].HitShip(*iPlayersShip,*this);

      } else if(what >=TSprite32x24::EHitBullet1 &&
                what <=TSprite32x24::EHitBullet8) {
        // badguy has hit a bullet
        
        TInt bulletNo = what - TSprite32x24::EHitBullet1;
        iBadGuys[i].HitBullet(iBullets[bulletNo],*this);
      }
    }
  }
}

/** Erase all sprites.

    Put back the saved backgrounds to erase the sprites.
    This is done in the reverse of the order they were
    plotted so that the background is restored correctly
*/
void CGame::RemoveEveryThing() {
  TInt i;
  for(i=KMaxBadGuys-1;i>=0;--i) {
    if(iBadGuys[i].Used()!=EFalse)
      iBadGuys[i].Remove();
  }
  for(i=KMaxBullets-1;i>=0;--i) {
    if(iBullets[i].Used()!=EFalse)
      iBullets[i].Remove();
  }
  iPlayersShip->Remove();
}


/** Draw all sprites.

    Draw the badguys, bullets and players ship.
    The order is players ship and bullets first so that when the
    bad guys are plotted the collision information for the bad
    guy will include either bullets or the players ship if the badguy's
    position intersects with a bullet or the player.

*/
void CGame::DrawEveryThing() {
  const TUint16 *sprites=iData.iLevel.iSprites;

  iPlayersShip->Plot(*iMap,sprites);
  
  TInt i;
  for(i=0;i<KMaxBullets;++i) {
    if(iBullets[i].Used()!=EFalse)
      iBullets[i].Plot(*iMap,sprites);
  }
  
  for(i=0;i<KMaxBadGuys;++i) {
    if(iBadGuys[i].Used()!=EFalse)
      iBadGuys[i].Plot(*iMap,sprites);
  }
}

/** Move bullets and bad guys.
 */
void CGame::MoveThings() {
  for(TInt k=0;k<KMaxBullets;k++) {
    if(iBullets[k].Used()!=EFalse) {
      iBullets[k].Move();
    }
  }
  
  for(TInt i=0;i<KMaxBadGuys;++i) {
    if(iBadGuys[i].Used()!=EFalse) {
      iBadGuys[i].Move(iPlayersShip->iX,iPlayersShip->iY,iData.iLevel.iPaths,*this);
    }
  }
};


/** Add a bad guy to the bad guy array.

    If an empty slot is available create a new bad guy with
    these settings.

    \param aBadGuy bad guy settings to copy.
*/
void CGame::AddBadGuy(const TBadGuy &aBadGuy) {
  TInt empty=0;
  for(;empty<KMaxBadGuys && iBadGuys[empty].Used()!=EFalse;empty++) {};
  if(empty>=KMaxBadGuys) return;
  iBadGuys[empty]=aBadGuy;
}

/** Add a bad guy to the bad guy array.

    If an empty slot is available create a new bad guy with
    these settings.

    \param aType first map data TInt16
    \param aArgs 7 more map data TInt16s see TBadGuy::Init()
*/

void CGame::AddBadGuy(TInt16 aType,const  TInt16 aArgs[]) {
  TInt empty=0;
  for(;empty<KMaxBadGuys && iBadGuys[empty].Used()!=EFalse;empty++) {};
  if(empty>=KMaxBadGuys) return;
  iBadGuys[empty].Init(aType,aArgs);
};

/** Handle movement and action keys.

    Moves the players ship and fires the ships guns.

    It will allow the player to move while dieing but not to shoot
    while dieing.

*/
void CGame::HandleKeys() {
  //movement
  TInt x=0;
  TInt y=0;
  if(iData.iKeys & TGameData::EUp)    y-=6;
  if(iData.iKeys & TGameData::EDown)  y+=6;
  if(iData.iKeys & TGameData::ELeft)  x-=6;
  if(iData.iKeys & TGameData::ERight) x+=6;
  iPlayersShip->MoveBy(x,y,iMap->Finished());


  // shooting  
  // can't shoot while dieing,
  if(iData.iKeys & TGameData::EAction3 && iPlayersShip->Dieing()==EFalse) {    
    // de-bounce fire key
    iData.iKeys = iData.iKeys & ~TGameData::EAction3;
    Shoot();
  }        
}

/** Shoot.

    Make one of the bullet slots live. The function tries to rotate
    the bullet slots so that if one is a greater power than the others
    it is used once every KMaxBullets shots.
*/

void CGame::Shoot() {
  // add another bullet if there is a free slot
  // try and rotate bullet slots,
  TInt i;
  for(i=iLastFired+1;i<KMaxBullets;i++) {
    if(iBullets[i].Used()==EFalse) {
      iLastFired=i;
      iBullets[i].Shoot(iPlayersShip->iX+12,iPlayersShip->iY);
      return;
    }
  }
  for(i=0;i<=iLastFired && i<KMaxBullets;i++) {
    if(iBullets[i].Used()==EFalse) {
      iLastFired=i;
      iBullets[i].Shoot(iPlayersShip->iX+12,iPlayersShip->iY);
      return;
    } 
  } 
}

/** Speed map is scrolling at.

    The map has only two speeds, 2 words a second (4 pixels) or
    stopped;

*/

TInt16 CGame::MapSpeed() {
  if(iMap->Finished()==EFalse) {
    return -2;
  } else {
    return 0;
  }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?