00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <queue>
00023
00024 #include "beingmanager.h"
00025 #include "configuration.h"
00026 #include "game.h"
00027 #include "graphics.h"
00028 #include "map.h"
00029 #include "particle.h"
00030 #include "simpleanimation.h"
00031 #include "sprite.h"
00032 #include "tileset.h"
00033
00034 #include "resources/ambientoverlay.h"
00035 #include "resources/image.h"
00036 #include "resources/resourcemanager.h"
00037
00038 #include "utils/dtor.h"
00039 #include "utils/stringutils.h"
00040
00041 extern volatile int tick_time;
00042
00046 struct Location
00047 {
00051 Location(int px, int py, MetaTile *ptile):
00052 x(px), y(py), tile(ptile)
00053 {}
00054
00058 bool operator< (const Location &loc) const
00059 {
00060 return tile->Fcost > loc.tile->Fcost;
00061 }
00062
00063 int x, y;
00064 MetaTile *tile;
00065 };
00066
00067 TileAnimation::TileAnimation(Animation *ani):
00068 mLastImage(NULL)
00069 {
00070 mAnimation = new SimpleAnimation(ani);
00071 }
00072
00073 TileAnimation::~TileAnimation()
00074 {
00075 delete mAnimation;
00076 }
00077
00078 void TileAnimation::update(int ticks)
00079 {
00080 if (!mAnimation)
00081 return;
00082
00083
00084 mAnimation->update(ticks);
00085
00086
00087 Image *img = mAnimation->getCurrentImage();
00088 if (img != mLastImage)
00089 {
00090 for (std::list<std::pair<MapLayer*, int> >::iterator i =
00091 mAffected.begin(); i != mAffected.end(); i++)
00092 {
00093 i->first->setTile(i->second, img);
00094 }
00095 mLastImage = img;
00096 }
00097 }
00098
00099 MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer):
00100 mX(x), mY(y),
00101 mWidth(width), mHeight(height),
00102 mIsFringeLayer(isFringeLayer)
00103 {
00104 const int size = mWidth * mHeight;
00105 mTiles = new Image*[size];
00106 std::fill_n(mTiles, size, (Image*) 0);
00107 }
00108
00109 MapLayer::~MapLayer()
00110 {
00111 delete[] mTiles;
00112 }
00113
00114 void MapLayer::setTile(int x, int y, Image *img)
00115 {
00116 setTile(x + y * mWidth, img);
00117 }
00118
00119 Image* MapLayer::getTile(int x, int y) const
00120 {
00121 return mTiles[x + y * mWidth];
00122 }
00123
00124 void MapLayer::draw(Graphics *graphics, int startX, int startY,
00125 int endX, int endY, int scrollX, int scrollY,
00126 const Sprites &sprites) const
00127 {
00128 startX -= mX;
00129 startY -= mY;
00130 endX -= mX;
00131 endY -= mY;
00132
00133 if (startX < 0) startX = 0;
00134 if (startY < 0) startY = 0;
00135 if (endX > mWidth) endX = mWidth;
00136 if (endY > mHeight) endY = mHeight;
00137
00138 Sprites::const_iterator si = sprites.begin();
00139
00140 for (int y = startY; y < endY; y++)
00141 {
00142
00143
00144 if (mIsFringeLayer)
00145 {
00146 while (si != sprites.end() && (*si)->getPixelY() <= y * 32)
00147 {
00148 (*si)->draw(graphics, -scrollX, -scrollY);
00149 si++;
00150 }
00151 }
00152
00153 for (int x = startX; x < endX; x++)
00154 {
00155 Image *img = getTile(x, y);
00156 if (img)
00157 {
00158 const int px = (x + mX) * 32 - scrollX;
00159 const int py = (y + mY) * 32 - scrollY + 32 - img->getHeight();
00160 graphics->drawImage(img, px, py);
00161 }
00162 }
00163 }
00164
00165
00166 if (mIsFringeLayer)
00167 {
00168 while (si != sprites.end())
00169 {
00170 (*si)->draw(graphics, -scrollX, -scrollY);
00171 si++;
00172 }
00173 }
00174 }
00175
00176 Map::Map(int width, int height, int tileWidth, int tileHeight):
00177 mWidth(width), mHeight(height),
00178 mTileWidth(tileWidth), mTileHeight(tileHeight),
00179 mMaxTileHeight(height),
00180 mOnClosedList(1), mOnOpenList(2),
00181 mLastScrollX(0.0f), mLastScrollY(0.0f)
00182 {
00183 const int size = mWidth * mHeight;
00184
00185 mMetaTiles = new MetaTile[size];
00186 for (int i = 0; i < NB_BLOCKTYPES; i++)
00187 {
00188 mOccupation[i] = new int[size];
00189 memset(mOccupation[i], 0, size * sizeof(int));
00190 }
00191 }
00192
00193 Map::~Map()
00194 {
00195
00196 delete[] mMetaTiles;
00197 for (int i = 0; i < NB_BLOCKTYPES; i++)
00198 {
00199 delete[] mOccupation[i];
00200 }
00201 delete_all(mLayers);
00202 delete_all(mTilesets);
00203 delete_all(mOverlays);
00204 delete_all(mTileAnimations);
00205 }
00206
00207 void Map::initializeOverlays()
00208 {
00209 ResourceManager *resman = ResourceManager::getInstance();
00210
00211 for (int i = 0;
00212 hasProperty("overlay" + toString(i) + "image");
00213 i++)
00214 {
00215 const std::string name = "overlay" + toString(i);
00216
00217 Image *img = resman->getImage(getProperty(name + "image"));
00218 const float speedX = getFloatProperty(name + "scrollX");
00219 const float speedY = getFloatProperty(name + "scrollY");
00220 const float parallax = getFloatProperty(name + "parallax");
00221
00222 if (img)
00223 {
00224 mOverlays.push_back(
00225 new AmbientOverlay(img, parallax, speedX, speedY));
00226
00227
00228 img->decRef();
00229 }
00230 }
00231 }
00232
00233 void Map::addLayer(MapLayer *layer)
00234 {
00235 mLayers.push_back(layer);
00236 }
00237
00238 void Map::addTileset(Tileset *tileset)
00239 {
00240 mTilesets.push_back(tileset);
00241
00242 if (tileset->getHeight() > mMaxTileHeight)
00243 mMaxTileHeight = tileset->getHeight();
00244 }
00245
00246 bool spriteCompare(const Sprite *a, const Sprite *b)
00247 {
00248 return a->getPixelY() < b->getPixelY();
00249 }
00250
00251 void Map::update(int ticks)
00252 {
00253
00254 for (std::map<int, TileAnimation*>::iterator iAni = mTileAnimations.begin();
00255 iAni != mTileAnimations.end();
00256 iAni++)
00257 {
00258 iAni->second->update(ticks);
00259 }
00260 }
00261
00262 void Map::draw(Graphics *graphics, int scrollX, int scrollY)
00263 {
00264 int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1;
00265
00266
00267 endPixelY += mMaxTileHeight - mTileHeight;
00268
00269 int startX = scrollX / mTileWidth;
00270 int startY = scrollY / mTileHeight;
00271 int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth;
00272 int endY = endPixelY / mTileHeight;
00273
00274
00275 mSprites.sort(spriteCompare);
00276
00277
00278 Layers::const_iterator layeri = mLayers.begin();
00279 for (; layeri != mLayers.end(); ++layeri)
00280 {
00281 (*layeri)->draw(graphics,
00282 startX, startY, endX, endY,
00283 scrollX, scrollY,
00284 mSprites);
00285 }
00286
00287 drawOverlay(graphics, scrollX, scrollY,
00288 (int) config.getValue("OverlayDetail", 2));
00289 }
00290
00291 void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY)
00292 {
00293 int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1;
00294 int startX = scrollX / mTileWidth;
00295 int startY = scrollY / mTileHeight;
00296 int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth;
00297 int endY = endPixelY / mTileHeight;
00298
00299 if (startX < 0) startX = 0;
00300 if (startY < 0) startY = 0;
00301 if (endX > mWidth) endX = mWidth;
00302 if (endY > mHeight) endY = mHeight;
00303
00304 for (int y = startY; y < endY; y++)
00305 {
00306 for (int x = startX; x < endX; x++)
00307 {
00308 graphics->setColor(gcn::Color(0, 0, 0, 64));
00309 graphics->drawRectangle(gcn::Rectangle(
00310 x * mTileWidth - scrollX,
00311 y * mTileWidth - scrollY,
00312 33, 33));
00313
00314 if (!getWalk(x, y, BLOCKMASK_WALL))
00315 {
00316 graphics->setColor(gcn::Color(0, 0, 200, 64));
00317 graphics->fillRectangle(gcn::Rectangle(
00318 x * mTileWidth - scrollX,
00319 y * mTileWidth - scrollY,
00320 32, 32));
00321 }
00322
00323 if (!getWalk(x, y, BLOCKMASK_MONSTER))
00324 {
00325 graphics->setColor(gcn::Color(200, 0, 0, 64));
00326 graphics->fillRectangle(gcn::Rectangle(
00327 x * mTileWidth - scrollX,
00328 y * mTileWidth - scrollY,
00329 32, 32));
00330 }
00331
00332 if (!getWalk(x, y, BLOCKMASK_CHARACTER))
00333 {
00334 graphics->setColor(gcn::Color(0, 200, 0, 64));
00335 graphics->fillRectangle(gcn::Rectangle(
00336 x * mTileWidth - scrollX,
00337 y * mTileWidth - scrollY,
00338 32, 32));
00339 }
00340 }
00341 }
00342 }
00343
00344 void Map::drawOverlay(Graphics *graphics,
00345 float scrollX, float scrollY, int detail)
00346 {
00347 static int lastTick = tick_time;
00348
00349
00350 if (detail <= 0) return;
00351
00352 if (mLastScrollX == 0.0f && mLastScrollY == 0.0f)
00353 {
00354
00355 mLastScrollX = scrollX;
00356 mLastScrollY = scrollY;
00357 }
00358
00359
00360 int timePassed = get_elapsed_time(lastTick);
00361 float dx = scrollX - mLastScrollX;
00362 float dy = scrollY - mLastScrollY;
00363
00364 std::list<AmbientOverlay*>::iterator i;
00365 for (i = mOverlays.begin(); i != mOverlays.end(); i++)
00366 {
00367 (*i)->update(timePassed, dx, dy);
00368 }
00369 mLastScrollX = scrollX;
00370 mLastScrollY = scrollY;
00371 lastTick = tick_time;
00372
00373
00374 for (i = mOverlays.begin(); i != mOverlays.end(); i++)
00375 {
00376 (*i)->draw(graphics, graphics->getWidth(), graphics->getHeight());
00377
00378
00379 if (detail == 1)
00380 break;
00381 };
00382 }
00383
00384 class ContainsGidFunctor
00385 {
00386 public:
00387 bool operator() (const Tileset* set) const
00388 {
00389 return (set->getFirstGid() <= gid &&
00390 gid - set->getFirstGid() < (int)set->size());
00391 }
00392 int gid;
00393 } containsGid;
00394
00395 Tileset* Map::getTilesetWithGid(int gid) const
00396 {
00397 containsGid.gid = gid;
00398
00399 Tilesets::const_iterator i = find_if(mTilesets.begin(), mTilesets.end(),
00400 containsGid);
00401
00402 return (i == mTilesets.end()) ? NULL : *i;
00403 }
00404
00405 void Map::blockTile(int x, int y, BlockType type)
00406 {
00407 if (type == BLOCKTYPE_NONE || !contains(x, y))
00408 return;
00409
00410 int tileNum = x + y * mWidth;
00411
00412 if ((++mOccupation[type][tileNum]) > 0)
00413 {
00414 switch (type)
00415 {
00416 case BLOCKTYPE_WALL:
00417 mMetaTiles[tileNum].blockmask |= BLOCKMASK_WALL;
00418 break;
00419 case BLOCKTYPE_CHARACTER:
00420 mMetaTiles[tileNum].blockmask |= BLOCKMASK_CHARACTER;
00421 break;
00422 case BLOCKTYPE_MONSTER:
00423 mMetaTiles[tileNum].blockmask |= BLOCKMASK_MONSTER;
00424 break;
00425 default:
00426
00427 break;
00428 }
00429 }
00430 }
00431
00432 bool Map::getWalk(int x, int y, char walkmask) const
00433 {
00434
00435 if (!contains(x, y))
00436 return false;
00437
00438
00439 return !(mMetaTiles[x + y * mWidth].blockmask & walkmask);
00440 }
00441
00442 bool Map::contains(int x, int y) const
00443 {
00444 return x >= 0 && y >= 0 && x < mWidth && y < mHeight;
00445 }
00446
00447 MetaTile* Map::getMetaTile(int x, int y) const
00448 {
00449 return &mMetaTiles[x + y * mWidth];
00450 }
00451
00452 SpriteIterator Map::addSprite(Sprite *sprite)
00453 {
00454 mSprites.push_front(sprite);
00455 return mSprites.begin();
00456 }
00457
00458 void Map::removeSprite(SpriteIterator iterator)
00459 {
00460 mSprites.erase(iterator);
00461 }
00462
00463 static int const basicCost = 100;
00464
00465 Path Map::findPath(int startX, int startY, int destX, int destY, unsigned char walkmask, int maxCost)
00466 {
00467
00468 Path path;
00469
00470
00471 std::priority_queue<Location> openList;
00472
00473
00474 if (!getWalk(destX, destY, walkmask)) return path;
00475
00476
00477 MetaTile *startTile = getMetaTile(startX, startY);
00478 startTile->Gcost = 0;
00479
00480
00481 openList.push(Location(startX, startY, startTile));
00482
00483 bool foundPath = false;
00484
00485
00486 while (!openList.empty() && !foundPath)
00487 {
00488
00489 Location curr = openList.top();
00490 openList.pop();
00491
00492
00493
00494 if (curr.tile->whichList == mOnClosedList)
00495 {
00496 continue;
00497 }
00498
00499
00500 curr.tile->whichList = mOnClosedList;
00501
00502
00503 for (int dy = -1; dy <= 1; dy++)
00504 {
00505 for (int dx = -1; dx <= 1; dx++)
00506 {
00507
00508 const int x = curr.x + dx;
00509 const int y = curr.y + dy;
00510
00511
00512
00513 if ((dx == 0 && dy == 0) || !contains(x, y))
00514 {
00515 continue;
00516 }
00517
00518 MetaTile *newTile = getMetaTile(x, y);
00519
00520
00521
00522 if (newTile->whichList == mOnClosedList ||
00523 ((newTile->blockmask & walkmask) && !(x == destX && y == destY)))
00524 {
00525 continue;
00526 }
00527
00528
00529
00530 if (dx != 0 && dy != 0)
00531 {
00532 MetaTile *t1 = getMetaTile(curr.x, curr.y + dy);
00533 MetaTile *t2 = getMetaTile(curr.x + dx, curr.y);
00534
00535 if (t1->blockmask & walkmask && !(t2->blockmask & walkmask))
00536 {
00537 continue;
00538 }
00539 }
00540
00541
00542 int Gcost = curr.tile->Gcost +
00543 (dx == 0 || dy == 0 ? basicCost : basicCost * 362 / 256);
00544
00545
00546
00547
00548
00549
00550
00551
00552 if (dx == 0 || dy == 0)
00553 {
00554
00555
00556 ++Gcost;
00557 }
00558
00559
00560
00561 if (!getWalk(x, y, BLOCKMASK_CHARACTER | BLOCKMASK_MONSTER))
00562 {
00563 Gcost += 3 * basicCost;
00564 }
00565
00566
00567
00568 if (Gcost > maxCost * basicCost)
00569 {
00570 continue;
00571 }
00572
00573 if (newTile->whichList != mOnOpenList)
00574 {
00575
00576
00577
00578
00579
00580
00581 int dx = std::abs(x - destX), dy = std::abs(y - destY);
00582 newTile->Hcost = std::abs(dx - dy) * basicCost +
00583 std::min(dx, dy) * (basicCost * 362 / 256);
00584
00585
00586 newTile->parentX = curr.x;
00587 newTile->parentY = curr.y;
00588
00589
00590 newTile->Gcost = Gcost;
00591 newTile->Fcost = Gcost + newTile->Hcost;
00592
00593 if (x != destX || y != destY) {
00594
00595 newTile->whichList = mOnOpenList;
00596 openList.push(Location(x, y, newTile));
00597 }
00598 else {
00599
00600 foundPath = true;
00601 }
00602 }
00603 else if (Gcost < newTile->Gcost)
00604 {
00605
00606
00607 newTile->Gcost = Gcost;
00608 newTile->Fcost = Gcost + newTile->Hcost;
00609
00610
00611 newTile->parentX = curr.x;
00612 newTile->parentY = curr.y;
00613
00614
00615
00616 openList.push(Location(x, y, newTile));
00617 }
00618 }
00619 }
00620 }
00621
00622
00623
00624 mOnClosedList += 2;
00625 mOnOpenList += 2;
00626
00627
00628
00629 if (foundPath)
00630 {
00631 int pathX = destX;
00632 int pathY = destY;
00633
00634 while (pathX != startX || pathY != startY)
00635 {
00636
00637 path.push_front(Position(pathX, pathY));
00638
00639
00640 MetaTile *tile = getMetaTile(pathX, pathY);
00641 pathX = tile->parentX;
00642 pathY = tile->parentY;
00643 }
00644 }
00645
00646 return path;
00647 }
00648
00649 void Map::addParticleEffect(const std::string &effectFile, int x, int y)
00650 {
00651 ParticleEffectData newEffect;
00652 newEffect.file = effectFile;
00653 newEffect.x = x;
00654 newEffect.y = y;
00655 particleEffects.push_back(newEffect);
00656 }
00657
00658 void Map::initializeParticleEffects(Particle* particleEngine)
00659 {
00660 if (config.getValue("particleeffects", 1))
00661 {
00662 for (std::list<ParticleEffectData>::iterator i = particleEffects.begin();
00663 i != particleEffects.end();
00664 i++
00665 )
00666 {
00667 particleEngine->addEffect(i->file, i->x, i->y);
00668 }
00669 }
00670 }
00671
00672 TileAnimation* Map::getAnimationForGid(int gid)
00673 {
00674 std::map<int, TileAnimation*>::iterator i = mTileAnimations.find(gid);
00675 if (i == mTileAnimations.end())
00676 {
00677 return NULL;
00678 } else {
00679 return i->second;
00680 }
00681 }