Hosting courtesy of Sourceforge

SourceForge Logo
Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

GameMap.cpp

Go to the documentation of this file.
00001 //
00002 //      Copyright (C) 2002 Robert Renaud
00003 //
00004 //      This program is free software; you can redistribute it and/or
00005 //      modify it under the terms of the GNU General Public License
00006 //      as published by the Free Software Foundation; either version 2
00007 //      of the License, or (at your option) any later version.
00008 //
00009 //      This program is distributed in the hope that it will be useful,
00010 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00012 //
00013 //      See the GNU General Public License for more details.
00014 //
00015 //      You should have received a copy of the GNU General Public License
00016 //      along with this program; if not, write to the Free Software
00017 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00018 //
00019 
00020 
00021 #include "GameMap.h"
00022 #include <cstdlib>
00023 #include <algorithm>
00024 
00025 using namespace Game;
00026 
00027 GameMap::BombPairAtPred::BombPairAtPred(GameMap* mapArg, int mapX, int mapY) :
00028 gameMap(mapArg),
00029 x(mapX),
00030 y(mapY)
00031 { }
00032 
00033 bool GameMap::BombPairAtPred::operator()(const GameMap::BombPlayerPair* b) {
00034         return x == b->bombLoc.getMapX() && y == b->bombLoc.getMapY();
00035 }
00036 
00037 GameMap::MapTile::MapTile() :
00038 bombOwnerPair(NULL),
00039 powerup(NULL),
00040 tile(NULL)
00041 { }
00042 
00043 GameMap::MapTile::~MapTile() {
00044         delete bombOwnerPair;
00045         delete powerup;
00046         delete tile;
00047 }
00048 
00049 GameMap::BombPlayerPair::BombPlayerPair(AbstractPlayer* bombOwner) :
00050 owner(bombOwner),
00051 bomb(new Bomb(getNearestHorizTile(bombOwner->getXpixel())*Constants::TILE_WIDTH,
00052                           getNearestVertTile(bombOwner->getYpixel())*Constants::TILE_HEIGHT,
00053                           bombOwner->getBombRadius())
00054         ),
00055 bombLoc (       getNearestHorizTile(bombOwner->getXpixel()),  // "round" to nearest tile
00056                         getNearestVertTile (bombOwner->getYpixel())
00057                 ),
00058 markedForDeletion(false)
00059 {       
00060         bombOwner->decreaseBombsLeft(); 
00061 }
00062 
00063 bool GameMap::BombPlayerPair::operator <( const BombPlayerPair& other) const {
00064         return this->bombLoc < other.bombLoc;
00065 }
00066 
00067 bool GameMap::BombPlayerPair::operator ==(const BombPlayerPair& other) const {
00068         return this->bombLoc == other.bombLoc;
00069 }
00070 
00071 GameMap::BombPlayerPair::~BombPlayerPair() {
00072         if (owner != NULL) owner->increaseBombsLeft();
00073         delete bomb;
00074         bomb = 0;
00075 }
00076 
00077 GameMap::GameMap()
00078 : tileMatrix(Constants::MAP_NUM_ROWS, Constants::MAP_NUM_COLS, NULL)
00079 { 
00080         for (int i = 0; i < tileMatrix.numRows(); i++) {
00081                 for (int j = 0; j < tileMatrix.numCols(); j++) {
00082                         tileMatrix(i,j) = new MapTile();
00083                 }
00084         }       
00085 }
00086 
00087 GameMap::~GameMap() {
00088         for (int i = 0; i < tileMatrix.numRows(); i++) {
00089                 for (int j = 0; j < tileMatrix.numCols(); j++) {
00090                         delete tileMatrix(i,j);
00091                 }
00092         }
00093         Util::freeDynamicContainer(playerList);
00094         Util::freeDynamicContainer(openStartLocations);
00095         Util::freeDynamicContainer(tilesToExplode);
00096         FlameImageManager::destroy();
00097 }
00098 
00099 void GameMap::removePowerupAt(int x, int y) {
00100         Powerup** p = &tileMatrix(x,y)->powerup;
00101         delete *p;
00102         *p = NULL;
00103 }
00104 
00105 void GameMap::readMap(std::istream &input) {
00106         
00107         Parse::SimpleParser parser(input);
00108         
00109         Util::Matrix<int> tileIndicies(tileMatrix.numRows(), tileMatrix.numCols(), -1);
00110         TileSetReader tileSet;
00111         PowerupFactory puFact;
00112         
00113         parser.addAction("TileSet", new Parse::DelegateToSubParser(tileSet.makeParserToParseMe(input)));
00114         parser.addAction("MapData", new Parse::StdRead<Util::Matrix<int> >(tileIndicies));
00115         parser.addAction("StartLocation", new Parse::NaturalListParser<MapLocation>(openStartLocations));
00116         
00117         parser.read();
00118         
00119         for (int i = 0; i < tileIndicies.numRows() ; i++) {
00120                 for (int j = 0; j < tileIndicies.numCols(); j++) {              
00121                         tileMatrix(i,j)->tile = tileSet.copyTile(tileIndicies(i,j));
00122                         if (!tileMatrix(i,j)->tile->isPassable()) tileMatrix(i,j)->powerup = puFact.getPowerup();
00123                 }
00124         }       
00125 }
00126 
00127 bool GameMap::tileIsPassable(int x, int y) const {
00128 #ifdef DEBUG_GAME_MAP
00129         debugOut << "GameMap::tileIsPassable(" << x << " , " << y << ")" << endl;
00130 #endif
00131         
00132         if (!isValidLocation(x,y)) {
00133 #ifdef DEBUG_GAME_MAP
00134                 debugOut << "Tried to access out of range tile, returning false " << endl;
00135 #endif
00136                 return false;
00137         }
00138 #ifdef DEBUG_GAME_MAP
00139         debugOut << "Valid tile, querying tile for passability and returning " << tileMatrix(x,y)->tile->isPassable() << endl;
00140 #endif
00141         return tileMatrix(x,y)->tile->isPassable();
00142 }
00143 
00144 void GameMap::occupyTile(int x, int y) {
00145         if (isValidLocation(x,y)) tileMatrix(x,y)->tile->occupy();
00146 }
00147 
00148 void GameMap::deOccupyTile(int x, int y) {
00149         if (isValidLocation(x,y)) tileMatrix(x,y)->tile->deOccupy();
00150 }
00151 
00152 void GameMap::addPlayer(const std::string& blpFileName, const std::string& cfgFileName) {
00153         MapLocation loc = getEmptyStartSpot();
00154         std::ifstream blpFile(blpFileName.c_str());
00155         std::ifstream cfgFile(cfgFileName.c_str());
00156         
00157         if (!blpFile.good() || !cfgFile.good()) throw Util::GeneralException("Invalid file, loading player with player file " +
00158                 blpFileName + " and cfg file " + cfgFileName, __FILE__, __LINE__);
00159 
00160         AbstractPlayer* player = new LocalPlayer(loc.getMapX() * Constants::TILE_WIDTH, loc.getMapY() * Constants::TILE_HEIGHT);
00161 
00162         Parse::Parser* blpParser = player->makeParserToParseMe(blpFile);
00163         blpParser->read();
00164 
00165         Parse::Parser* cfgParser = player->makeParserToParseMe(cfgFile);
00166         cfgParser->read();
00167 
00168         playerList.push_back(player);
00169 
00170         delete blpParser;
00171         delete cfgParser;
00172 }
00173 
00174 MapLocation GameMap::getEmptyStartSpot() {
00175         size_t numSpots = openStartLocations.size();
00176         if (numSpots == 0) throw Util::GeneralException("No more player spots", __FILE__, __LINE__);
00177 
00178         size_t randSpot = rand() % numSpots;
00179 
00180         std::list<MapLocation*>::iterator locIterator = openStartLocations.begin();
00181         for (size_t i = 0; i < randSpot; i++) {
00182                 locIterator++;
00183         }
00184 
00185         MapLocation spot(**locIterator);
00186         delete *locIterator;
00187         openStartLocations.erase(locIterator);
00188 
00189         return spot;
00190 }
00191 
00192 bool GameMap::isValidLocation(int x, int y) const {
00193         return !(       x < 0
00194                 ||      x >= tileMatrix.numRows()
00195                 ||      y < 0
00196                 ||      y >= tileMatrix.numCols()
00197                 );
00198 }
00199 
00200 void GameMap::explodeTile(int x, int y) {
00201 #ifdef DEBUG_GAME_MAP
00202         debugOut << "GameMap::explodeTile(" << x << ", " << y << ")" << endl;
00203 #endif
00204         if (Constants::ALLOW_BOMB_STACKING) {
00205                 tileMatrix(x,y)->tile->hitWithExplosion();
00206         } else {
00207                 tilesToExplode.push_back(new MapLocation(x,y));
00208         }       
00209 }       
00210 
00211 GameMap::FlameStatus GameMap::handleBombChain(int x, int y, FlameType sparkType) {
00212         if (!isValidLocation(x, y)) return FLAME_STOP;
00213         MapTile* currentTile = tileMatrix(x,y);
00214         if (currentTile->bombOwnerPair != NULL && 
00215                 !currentTile->bombOwnerPair->markedForDeletion) { // flame hit a bomb, start chain
00216                 blowUpBomb(currentTile->bombOwnerPair);
00217         }
00218         if (!currentTile->tile->isPassable()) {
00219                 explodeTile(x,y);
00220                 return FLAME_STOP;
00221         } else if (currentTile->powerup != NULL) {
00222                 removePowerupAt(x,y);
00223         }
00224         currentTile->flame.addSpark(sparkType);
00225         return FLAME_KEEP_GOING;
00226 }
00227 
00228 void GameMap::blowUpBomb(GameMap::BombPlayerPair* b) {
00229         int x = b->bombLoc.getMapX();
00230         int y = b->bombLoc.getMapY();
00231 
00232         size_t bombRadius = (size_t) b->bomb->getBombRadius();  
00233         deOccupyTile(x,y);
00234 
00235         std::set<BombPlayerPair*>::iterator deadBombIter =
00236                 find_if(bombPlayerSet.begin(), bombPlayerSet.end(), BombPairAtPred(this, x,y));
00237 
00238         if (deadBombIter == bombPlayerSet.end()) {
00239                 throw Util::GeneralException("Couldn't find BombPlayerPair in set that should be there", __FILE__, __LINE__);
00240         }
00241 
00242         tileMatrix(x,y)->bombOwnerPair->markedForDeletion=true;
00243         tileMatrix(x,y)->flame.addSpark(Game::FLAME_MIDDLE_CONNECTOR);
00244         
00245         bool hitSomething=false;
00246         size_t left, right, up, down;
00247         
00248         for (left = 1; left < bombRadius && !hitSomething; left++) {
00249                 if (handleBombChain(x - left, y, FLAME_HORIZ_CONNECTOR) == FLAME_STOP) {
00250                         hitSomething=true;
00251                         break;
00252                 }
00253         }
00254         if (!hitSomething)      handleBombChain(x - left, y, FLAME_LEFT_END);
00255         
00256         hitSomething=false;
00257         for (right = 1; right < bombRadius && !hitSomething; right++) {
00258                 if (handleBombChain(x + right, y, FLAME_HORIZ_CONNECTOR) == FLAME_STOP) {
00259                         hitSomething=true;
00260                         break;
00261                 }
00262         }
00263         if (!hitSomething) handleBombChain(x + right, y, FLAME_RIGHT_END);
00264                                 
00265         hitSomething=false;     
00266         for (down = 1; down < bombRadius && !hitSomething; down++) {
00267                 if (handleBombChain(x, y + down, FLAME_VERT_CONNECTOR) == FLAME_STOP) {
00268                         hitSomething = true;
00269                         break;
00270                 }
00271         }
00272     if (!hitSomething) handleBombChain(x, y + down, FLAME_DOWN_END);
00273         
00274     hitSomething=false;
00275         for (up = 1; up < bombRadius && !hitSomething; up++) {
00276                 if (handleBombChain(x, y - up, FLAME_VERT_CONNECTOR) == FLAME_STOP) {
00277                         hitSomething=true;
00278                         break;
00279                 }
00280         }
00281         if (!hitSomething) handleBombChain(x, y - up, FLAME_UP_END);
00282 }
00283 
00284 void GameMap::putBomb(AbstractPlayer* bombOwner) {
00285         
00286         BombPlayerPair* bpp= new BombPlayerPair(bombOwner);
00287         int bombX = bpp->bombLoc.getMapX();
00288         int bombY = bpp->bombLoc.getMapY();
00289 
00290         if (tileMatrix(bombX, bombY)->bombOwnerPair != NULL) { // bomb here already
00291                 delete bpp;  // can't place a bomb on top of each other... so just get rid of it
00292                 return;
00293         }
00294         occupyTile(bombX, bombY);
00295         tileMatrix(bombX, bombY)->bombOwnerPair = bpp;
00296         bombPlayerSet.insert(bpp);
00297 }
00298 
00299 bool GameMap::runGame(SDL_Surface * screen) {
00300         bool done=false;
00301 
00302         SDL_PumpEvents();
00303         SDL_Event event;
00304         while (SDL_PollEvent(&event)) {
00305                 switch (event.type) {
00306                 case SDL_QUIT: done = true; break;
00307                 case SDL_KEYDOWN:
00308                 switch (event.key.keysym.sym) {
00309                         case SDLK_ESCAPE: done=true; break;
00310                         case SDLK_PRINT: SDL_SaveBMP(screen, "Screenshot.bmp"); break;
00311                         }                               
00312                 }
00313         }
00314 
00315         SDL_Rect tileArea;
00316         tileArea.x = 0;
00317         tileArea.y = 0;
00318         
00319         for (int i = 0; i < tileMatrix.numRows(); i++) {
00320                 tileArea.y=0;
00321                 for (int j = 0; j < tileMatrix.numCols(); j++) {
00322                         MapTile* mapTile = tileMatrix(i,j);
00323                         mapTile->tile->drawAt(screen, &tileArea);
00324                         mapTile->flame.drawAt(screen, &tileArea);
00325                         if (mapTile->tile->isPassable() && mapTile->powerup != NULL) mapTile->powerup->drawAt(screen, &tileArea);       
00326                         tileArea.y += Constants::TILE_HEIGHT;
00327                 }
00328                 tileArea.x += Constants::TILE_WIDTH;
00329         }
00330 
00331         for (std::set<BombPlayerPair*>::iterator it = bombPlayerSet.begin(); it != bombPlayerSet.end(); it++) {
00332                 if (!(*it)->markedForDeletion) {
00333                         if      ((*it)->bomb->tick()) {
00334                                 blowUpBomb(*it);
00335                         }
00336                 }
00337         }
00338         
00339         deleteMarkedBombs();
00340         
00341         if (!Constants::ALLOW_BOMB_STACKING) {
00342                 for (std::list<MapLocation*>::iterator tileIt=tilesToExplode.begin(); tileIt != tilesToExplode.end(); tileIt++) {
00343                         tileMatrix((*tileIt)->getMapX(), (*tileIt)->getMapY())->tile->hitWithExplosion();
00344                 }
00345                 Util::freeDynamicContainer(tilesToExplode);
00346         }                       
00347         
00348         for (std::set<BombPlayerPair*>::iterator jt = bombPlayerSet.begin(); jt != bombPlayerSet.end(); jt++) {
00349                 (*jt)->bomb->draw(screen);
00350         }
00351 
00352         for (std::list<AbstractPlayer*>::iterator curPlayer = playerList.begin(); curPlayer != playerList.end(); curPlayer++) {
00353                 if (playerHitByBomb(*curPlayer)) {
00354                         // unmark bombs owned by current player
00355                         for (std::set<BombPlayerPair*>::iterator bombIt = bombPlayerSet.begin(); bombIt != bombPlayerSet.end(); bombIt++) {
00356                                 if ((*bombIt)->owner == *curPlayer) (*bombIt)->owner=NULL;
00357                         }
00358                         delete *curPlayer;
00359                         if (Util::safeIteratorRemove<AbstractPlayer*>(playerList, curPlayer) == Util::LIST_END) break;
00360                 }
00361 
00362                 Sint16 playerX = getNearestHorizTile((*curPlayer)->getXpixel());
00363                 Sint16 playerY = getNearestVertTile((*curPlayer)->getYpixel());
00364 
00365                 Powerup* powerupBelowPlayer = tileMatrix(playerX, playerY)->powerup;
00366                 if (powerupBelowPlayer != NULL) {
00367                         powerupBelowPlayer->effect(*curPlayer);
00368                         removePowerupAt(playerX, playerY);
00369                 }
00370 
00371                 bool playerMoved= false;
00372                 PlayerAction pa = (*curPlayer)->getInput();
00373                 
00374                 // try to move the player in the direction it was already moving before checking if it wants
00375         // to move in a different direction
00376                 if (pa & getMovementFromDirection((*curPlayer)->getDirection())) {
00377                         playerMoved = moveObject(**curPlayer, (*curPlayer)->getDirection() ,(*curPlayer)->getSpeed());
00378                 }
00379                 
00380                 // try the rest of the directions
00381                 if ( (!playerMoved) && (pa & Constants::MOVE_RIGHT)) { //
00382                         playerMoved = moveObject(**curPlayer, RIGHT, (*curPlayer)->getSpeed());
00383                 }
00384                 if ( (!playerMoved) && (pa & Constants::MOVE_LEFT)) {
00385                         playerMoved = moveObject(**curPlayer, LEFT, (*curPlayer)->getSpeed());
00386                 }
00387                 if ( (!playerMoved) && (pa & Constants::MOVE_DOWN)) {
00388                         playerMoved = moveObject(**curPlayer, DOWN, (*curPlayer)->getSpeed());
00389                 }
00390                 if ( (!playerMoved) && (pa & Constants::MOVE_UP)) {
00391                         playerMoved = moveObject(**curPlayer, UP ,(*curPlayer)->getSpeed());
00392                 }       
00393                 if (!playerMoved) {
00394                         (*curPlayer)->setDirection(NO_DIRECTION);
00395                         (*curPlayer)->unlockMovement();
00396                 }
00397                 if ( pa & Constants::DROP_BOMB) {
00398                         putBomb(*curPlayer);    
00399                 }
00400 
00401                 MovementEffect effect = tileMatrix(playerX,playerY)->tile->effect(**curPlayer);
00402                 if (effect.isRelative()) {
00403         
00404                         if (effect.getXchange() > 0) {
00405                                 moveObject(**curPlayer, RIGHT, effect.getXchange(), false, 0);
00406                         } else {
00407                                 moveObject(**curPlayer, LEFT, -effect.getXchange(), false, 0);
00408                         }
00409                                                                                                 
00410                         if (effect.getYchange() > 0) {
00411                                 moveObject(**curPlayer, DOWN, effect.getYchange(), false, 0);
00412                         } else {
00413                                 moveObject(**curPlayer, UP, -effect.getYchange(), false, 0);
00414                         }
00415                 }       
00416         // need to handle non relative motions as well...
00417 
00418                 (*curPlayer)->draw(screen);             
00419         }
00420         return done;
00421 }
00422 
00423 void GameMap::deleteMarkedBombs() {
00424         bool moreBombsToDelete=true;
00425         while (moreBombsToDelete) {
00426                 for (std::set<BombPlayerPair*>::iterator eraser = bombPlayerSet.begin(); eraser != bombPlayerSet.end(); eraser++) {
00427                         if ((*eraser)->markedForDeletion) {
00428                                 bombPlayerSet.erase(eraser);
00429                                 int mapX = (*eraser)->bombLoc.getMapX();
00430                                 int mapY = (*eraser)->bombLoc.getMapY();
00431                                 delete tileMatrix(mapX,mapY)->bombOwnerPair;
00432                                 tileMatrix(mapX,mapY)->bombOwnerPair = NULL;
00433                                 moreBombsToDelete=true;
00434                                 break;
00435                         }
00436                 }
00437                 moreBombsToDelete=false;
00438         }
00439 }
00440 
00441 bool GameMap::playerHitByBomb(AbstractPlayer* player) {
00442   return (tileMatrix(getNearestHorizTile(player->getXpixel()),getNearestVertTile(player->getYpixel()))->flame.isOnFire());      
00443 }
00444 
00445 bool GameMap::moveHoriz(MoveableItem& m, int amount, int questionableHorizTileLoc, int slack, int slackTolerance) {
00446         bool movedSigAmount=true;
00447 
00448         if(abs(amount) <= slack) { // have enough space move so that moving whole distance won't cause collision
00449                 m.move(amount);
00450         } else {
00451                 
00452                 bool upPassable= tileIsPassable(questionableHorizTileLoc, getUpTile(m.getYpixel()));
00453                 bool downPassable= tileIsPassable(questionableHorizTileLoc, getDownTile(m.getYpixel()));
00454                 
00455                 if (upPassable && downPassable) {
00456                         m.move(amount);
00457                 } else {
00458                         
00459                         bool trivialUpSlack = getUpSlack(m.getYpixel()) < slackTolerance;
00460                         bool trivialDownSlack = getDownSlack(m.getYpixel()) < slackTolerance;
00461                         
00462                         if (upPassable && trivialUpSlack) {
00463                                 m.inch(getUpSlack(m.getYpixel()), UP);
00464                                 m.move(amount);
00465                         } else if (downPassable && trivialDownSlack) {
00466                                 m.inch(getDownSlack(m.getYpixel()), DOWN);
00467                                 m.move(amount);
00468                         } else {
00469                                 m.move(slack);
00470                                 movedSigAmount=false;
00471                         }
00472                 }
00473         }
00474 
00475         return movedSigAmount;
00476 }
00477 
00478 bool GameMap::moveVert(MoveableItem& m, int amount, int questionableVertTileLoc, int slack, int slackTolerance) {
00479         bool movedSigAmount=true;
00480         
00481         // amount to move is less then the number of pixels to next tile
00482         if(abs(amount) <= slack) {
00483                 m.move(amount);
00484         } else {
00485 
00486                 bool rightPassable= tileIsPassable(getRightTile(m.getXpixel()), questionableVertTileLoc);
00487                 bool leftPassable= tileIsPassable(getLeftTile(m.getXpixel()), questionableVertTileLoc);
00488                 
00489                 if (rightPassable && leftPassable) {
00490                         m.move(amount);
00491                 } else {
00492                         
00493                         bool trivialRightSlack = getRightSlack(m.getXpixel()) < slackTolerance;
00494                         bool trivialLeftSlack = getLeftSlack(m.getXpixel()) <   slackTolerance;
00495                         
00496                         if (leftPassable && trivialLeftSlack) {                         
00497                                 m.inch(getLeftSlack(m.getXpixel()), LEFT);
00498                                 m.move(amount);
00499                         } else if (rightPassable && trivialRightSlack) {
00500                                 m.inch(getRightSlack(m.getXpixel()), RIGHT);
00501                                 m.move(amount);
00502                         } else {
00503                                 m.move(slack);
00504                                 movedSigAmount=false;
00505                         }
00506                 }
00507         }
00508         return movedSigAmount;
00509 }
00510 
00511 bool GameMap::moveObject(MoveableItem& m, Direction d, int speed, bool allowDirectionChange, int slackTol) {
00512         bool changedDir=false;
00513         Direction origDir = m.getDirection(); 
00514         m.setDirection(d);
00515         switch (d) {
00516         case LEFT: 
00517                 changedDir = moveHoriz(m, speed, getLeftTile(m.getXpixel(), -speed), getLeftSlack(m.getXpixel()), slackTol);
00518                 break;
00519         case RIGHT:
00520                 changedDir = moveHoriz(m, speed, getRightTile(m.getXpixel(), speed), getRightSlack(m.getXpixel()), slackTol);
00521                 break;          
00522         case UP:                
00523                 changedDir = moveVert(m, speed, getUpTile(m.getYpixel(), -speed), getUpSlack(m.getYpixel()), slackTol);
00524                 break;  
00525         case DOWN:
00526                 changedDir = moveVert(m, speed, getDownTile(m.getYpixel(), speed), getDownSlack(m.getYpixel()), slackTol);
00527                 break;
00528         }               
00529         if (!changedDir || !allowDirectionChange) m.setDirection(origDir);
00530         return changedDir;
00531 }

Generated on Tue May 21 07:26:51 2002 for BomberLAN by doxygen1.2.12 written by Dimitri van Heesch, © 1997-2001