00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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()),
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) {
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) {
00291 delete bpp;
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
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
00375
00376 if (pa & getMovementFromDirection((*curPlayer)->getDirection())) {
00377 playerMoved = moveObject(**curPlayer, (*curPlayer)->getDirection() ,(*curPlayer)->getSpeed());
00378 }
00379
00380
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
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) {
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
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 }