| 1 |
/* |
| 2 |
* The Mana World |
| 3 |
* Copyright (C) 2004 The Mana World Development Team |
| 4 |
* |
| 5 |
* This file is part of The Mana World. |
| 6 |
* |
| 7 |
* This program is free software; you can redistribute it and/or modify |
| 8 |
* it under the terms of the GNU General Public License as published by |
| 9 |
* the Free Software Foundation; either version 2 of the License, or |
| 10 |
* any later version. |
| 11 |
* |
| 12 |
* This program is distributed in the hope that it will be useful, |
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 |
* GNU General Public License for more details. |
| 16 |
* |
| 17 |
* You should have received a copy of the GNU General Public License |
| 18 |
* along with this program; if not, write to the Free Software |
| 19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 20 |
*/ |
| 21 |
|
| 22 |
#include <queue> |
| 23 |
|
| 24 |
#include "beingmanager.h" |
| 25 |
#include "configuration.h" |
| 26 |
#include "game.h" |
| 27 |
#include "graphics.h" |
| 28 |
#include "map.h" |
| 29 |
#include "particle.h" |
| 30 |
#include "simpleanimation.h" |
| 31 |
#include "sprite.h" |
| 32 |
#include "tileset.h" |
| 33 |
|
| 34 |
#include "resources/ambientoverlay.h" |
| 35 |
#include "resources/image.h" |
| 36 |
#include "resources/resourcemanager.h" |
| 37 |
|
| 38 |
#include "utils/dtor.h" |
| 39 |
#include "utils/stringutils.h" |
| 40 |
|
| 41 |
extern volatile int tick_time; |
| 42 |
|
| 43 |
/** |
| 44 |
* The used side-length for tiles |
| 45 |
*/ |
| 46 |
const int DEFAULT_TILE_SIDE_LENGTH = 32; |
| 47 |
|
| 48 |
/** |
| 49 |
* A location on a tile map. Used for pathfinding, open list. |
| 50 |
*/ |
| 51 |
struct Location |
| 52 |
{ |
| 53 |
/** |
| 54 |
* Constructor. |
| 55 |
*/ |
| 56 |
Location(int px, int py, MetaTile *ptile): |
| 57 |
x(px), y(py), tile(ptile) |
| 58 |
{} |
| 59 |
|
| 60 |
/** |
| 61 |
* Comparison operator. |
| 62 |
*/ |
| 63 |
bool operator< (const Location &loc) const |
| 64 |
{ |
| 65 |
return tile->Fcost > loc.tile->Fcost; |
| 66 |
} |
| 67 |
|
| 68 |
int x, y; |
| 69 |
MetaTile *tile; |
| 70 |
}; |
| 71 |
|
| 72 |
TileAnimation::TileAnimation(Animation *ani): |
| 73 |
mLastImage(NULL) |
| 74 |
{ |
| 75 |
mAnimation = new SimpleAnimation(ani); |
| 76 |
} |
| 77 |
|
| 78 |
TileAnimation::~TileAnimation() |
| 79 |
{ |
| 80 |
delete mAnimation; |
| 81 |
} |
| 82 |
|
| 83 |
void TileAnimation::update(int ticks) |
| 84 |
{ |
| 85 |
if (!mAnimation) |
| 86 |
return; |
| 87 |
|
| 88 |
// update animation |
| 89 |
mAnimation->update(ticks); |
| 90 |
|
| 91 |
// exchange images |
| 92 |
Image *img = mAnimation->getCurrentImage(); |
| 93 |
if (img != mLastImage) |
| 94 |
{ |
| 95 |
for (std::list<std::pair<MapLayer*, int> >::iterator i = |
| 96 |
mAffected.begin(); i != mAffected.end(); i++) |
| 97 |
{ |
| 98 |
i->first->setTile(i->second, img); |
| 99 |
} |
| 100 |
mLastImage = img; |
| 101 |
} |
| 102 |
} |
| 103 |
|
| 104 |
MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer): |
| 105 |
mX(x), mY(y), |
| 106 |
mWidth(width), mHeight(height), |
| 107 |
mIsFringeLayer(isFringeLayer) |
| 108 |
{ |
| 109 |
const int size = mWidth * mHeight; |
| 110 |
mTiles = new Image*[size]; |
| 111 |
std::fill_n(mTiles, size, (Image*) 0); |
| 112 |
} |
| 113 |
|
| 114 |
MapLayer::~MapLayer() |
| 115 |
{ |
| 116 |
delete[] mTiles; |
| 117 |
} |
| 118 |
|
| 119 |
void MapLayer::setTile(int x, int y, Image *img) |
| 120 |
{ |
| 121 |
setTile(x + y * mWidth, img); |
| 122 |
} |
| 123 |
|
| 124 |
Image* MapLayer::getTile(int x, int y) const |
| 125 |
{ |
| 126 |
return mTiles[x + y * mWidth]; |
| 127 |
} |
| 128 |
|
| 129 |
void MapLayer::draw(Graphics *graphics, int startX, int startY, |
| 130 |
int endX, int endY, int scrollX, int scrollY, |
| 131 |
const MapSprites &sprites) const |
| 132 |
{ |
| 133 |
startX -= mX; |
| 134 |
startY -= mY; |
| 135 |
endX -= mX; |
| 136 |
endY -= mY; |
| 137 |
|
| 138 |
if (startX < 0) startX = 0; |
| 139 |
if (startY < 0) startY = 0; |
| 140 |
if (endX > mWidth) endX = mWidth; |
| 141 |
if (endY > mHeight) endY = mHeight; |
| 142 |
|
| 143 |
MapSprites::const_iterator si = sprites.begin(); |
| 144 |
|
| 145 |
for (int y = startY; y < endY; y++) |
| 146 |
{ |
| 147 |
// If drawing the fringe layer, make sure all sprites above this row of |
| 148 |
// tiles have been drawn |
| 149 |
if (mIsFringeLayer) |
| 150 |
{ |
| 151 |
while (si != sprites.end() && (*si)->getPixelY() <= y * 32) |
| 152 |
{ |
| 153 |
(*si)->setAlpha(1.0f); |
| 154 |
(*si)->draw(graphics, -scrollX, -scrollY); |
| 155 |
si++; |
| 156 |
} |
| 157 |
} |
| 158 |
|
| 159 |
for (int x = startX; x < endX; x++) |
| 160 |
{ |
| 161 |
Image *img = getTile(x, y); |
| 162 |
if (img) |
| 163 |
{ |
| 164 |
const int px = (x + mX) * 32 - scrollX; |
| 165 |
const int py = (y + mY) * 32 - scrollY + 32 - img->getHeight(); |
| 166 |
graphics->drawImage(img, px, py); |
| 167 |
} |
| 168 |
} |
| 169 |
} |
| 170 |
|
| 171 |
// Draw any remaining sprites |
| 172 |
if (mIsFringeLayer) |
| 173 |
{ |
| 174 |
while (si != sprites.end()) |
| 175 |
{ |
| 176 |
(*si)->setAlpha(1.0f); |
| 177 |
(*si)->draw(graphics, -scrollX, -scrollY); |
| 178 |
si++; |
| 179 |
} |
| 180 |
} |
| 181 |
} |
| 182 |
|
| 183 |
Map::Map(int width, int height, int tileWidth, int tileHeight): |
| 184 |
mWidth(width), mHeight(height), |
| 185 |
mTileWidth(tileWidth), mTileHeight(tileHeight), |
| 186 |
mMaxTileHeight(height), |
| 187 |
mOnClosedList(1), mOnOpenList(2), |
| 188 |
mLastScrollX(0.0f), mLastScrollY(0.0f) |
| 189 |
{ |
| 190 |
const int size = mWidth * mHeight; |
| 191 |
|
| 192 |
mMetaTiles = new MetaTile[size]; |
| 193 |
for (int i = 0; i < NB_BLOCKTYPES; i++) |
| 194 |
{ |
| 195 |
mOccupation[i] = new int[size]; |
| 196 |
memset(mOccupation[i], 0, size * sizeof(int)); |
| 197 |
} |
| 198 |
} |
| 199 |
|
| 200 |
Map::~Map() |
| 201 |
{ |
| 202 |
// delete metadata, layers, tilesets and overlays |
| 203 |
delete[] mMetaTiles; |
| 204 |
for (int i = 0; i < NB_BLOCKTYPES; i++) |
| 205 |
{ |
| 206 |
delete[] mOccupation[i]; |
| 207 |
} |
| 208 |
delete_all(mLayers); |
| 209 |
delete_all(mTilesets); |
| 210 |
delete_all(mOverlays); |
| 211 |
delete_all(mTileAnimations); |
| 212 |
} |
| 213 |
|
| 214 |
void Map::initializeOverlays() |
| 215 |
{ |
| 216 |
ResourceManager *resman = ResourceManager::getInstance(); |
| 217 |
|
| 218 |
for (int i = 0; |
| 219 |
hasProperty("overlay" + toString(i) + "image"); |
| 220 |
i++) |
| 221 |
{ |
| 222 |
const std::string name = "overlay" + toString(i); |
| 223 |
|
| 224 |
Image *img = resman->getImage(getProperty(name + "image")); |
| 225 |
const float speedX = getFloatProperty(name + "scrollX"); |
| 226 |
const float speedY = getFloatProperty(name + "scrollY"); |
| 227 |
const float parallax = getFloatProperty(name + "parallax"); |
| 228 |
const bool keepRatio = getBoolProperty(name + "keepratio"); |
| 229 |
|
| 230 |
if (img) |
| 231 |
{ |
| 232 |
mOverlays.push_back( |
| 233 |
new AmbientOverlay(img, parallax, speedX, speedY, keepRatio)); |
| 234 |
|
| 235 |
// The AmbientOverlay takes control over the image. |
| 236 |
img->decRef(); |
| 237 |
} |
| 238 |
} |
| 239 |
} |
| 240 |
|
| 241 |
void Map::addLayer(MapLayer *layer) |
| 242 |
{ |
| 243 |
mLayers.push_back(layer); |
| 244 |
} |
| 245 |
|
| 246 |
void Map::addTileset(Tileset *tileset) |
| 247 |
{ |
| 248 |
mTilesets.push_back(tileset); |
| 249 |
|
| 250 |
if (tileset->getHeight() > mMaxTileHeight) |
| 251 |
mMaxTileHeight = tileset->getHeight(); |
| 252 |
} |
| 253 |
|
| 254 |
bool spriteCompare(const Sprite *a, const Sprite *b) |
| 255 |
{ |
| 256 |
return a->getPixelY() < b->getPixelY(); |
| 257 |
} |
| 258 |
|
| 259 |
void Map::update(int ticks) |
| 260 |
{ |
| 261 |
//update animated tiles |
| 262 |
for (std::map<int, TileAnimation*>::iterator iAni = mTileAnimations.begin(); |
| 263 |
iAni != mTileAnimations.end(); |
| 264 |
iAni++) |
| 265 |
{ |
| 266 |
iAni->second->update(ticks); |
| 267 |
} |
| 268 |
} |
| 269 |
|
| 270 |
void Map::draw(Graphics *graphics, int scrollX, int scrollY) |
| 271 |
{ |
| 272 |
int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1; |
| 273 |
|
| 274 |
// TODO: Do this per-layer |
| 275 |
endPixelY += mMaxTileHeight - mTileHeight; |
| 276 |
|
| 277 |
int startX = scrollX / mTileWidth; |
| 278 |
int startY = scrollY / mTileHeight; |
| 279 |
int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth; |
| 280 |
int endY = endPixelY / mTileHeight; |
| 281 |
|
| 282 |
// Make sure sprites are sorted |
| 283 |
mSprites.sort(spriteCompare); |
| 284 |
|
| 285 |
// draw the game world |
| 286 |
Layers::const_iterator layeri = mLayers.begin(); |
| 287 |
for (; layeri != mLayers.end(); ++layeri) |
| 288 |
{ |
| 289 |
(*layeri)->draw(graphics, |
| 290 |
startX, startY, endX, endY, |
| 291 |
scrollX, scrollY, |
| 292 |
mSprites); |
| 293 |
} |
| 294 |
|
| 295 |
// Draws beings with a lower opacity to make them visible |
| 296 |
// even when covered by a wall or some other elements... |
| 297 |
MapSprites::const_iterator si = mSprites.begin(); |
| 298 |
while (si != mSprites.end()) |
| 299 |
{ |
| 300 |
if (Sprite *sprite = *si) |
| 301 |
{ |
| 302 |
// For now, just draw sprites with only one layer. |
| 303 |
if (sprite->getNumberOfLayers() == 1) |
| 304 |
{ |
| 305 |
sprite->setAlpha(0.3f); |
| 306 |
sprite->draw(graphics, -scrollX, -scrollY); |
| 307 |
} |
| 308 |
} |
| 309 |
si++; |
| 310 |
} |
| 311 |
|
| 312 |
drawOverlay(graphics, scrollX, scrollY, |
| 313 |
(int) config.getValue("OverlayDetail", 2)); |
| 314 |
} |
| 315 |
|
| 316 |
void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY) |
| 317 |
{ |
| 318 |
int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1; |
| 319 |
int startX = scrollX / mTileWidth; |
| 320 |
int startY = scrollY / mTileHeight; |
| 321 |
int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth; |
| 322 |
int endY = endPixelY / mTileHeight; |
| 323 |
|
| 324 |
if (startX < 0) startX = 0; |
| 325 |
if (startY < 0) startY = 0; |
| 326 |
if (endX > mWidth) endX = mWidth; |
| 327 |
if (endY > mHeight) endY = mHeight; |
| 328 |
|
| 329 |
for (int y = startY; y < endY; y++) |
| 330 |
{ |
| 331 |
for (int x = startX; x < endX; x++) |
| 332 |
{ |
| 333 |
graphics->setColor(gcn::Color(0, 0, 0, 64)); |
| 334 |
graphics->drawRectangle(gcn::Rectangle( |
| 335 |
x * mTileWidth - scrollX, |
| 336 |
y * mTileWidth - scrollY, |
| 337 |
33, 33)); |
| 338 |
|
| 339 |
if (!getWalk(x, y, BLOCKMASK_WALL)) |
| 340 |
{ |
| 341 |
graphics->setColor(gcn::Color(0, 0, 200, 64)); |
| 342 |
graphics->fillRectangle(gcn::Rectangle( |
| 343 |
x * mTileWidth - scrollX, |
| 344 |
y * mTileWidth - scrollY, |
| 345 |
32, 32)); |
| 346 |
} |
| 347 |
|
| 348 |
if (!getWalk(x, y, BLOCKMASK_MONSTER)) |
| 349 |
{ |
| 350 |
graphics->setColor(gcn::Color(200, 0, 0, 64)); |
| 351 |
graphics->fillRectangle(gcn::Rectangle( |
| 352 |
x * mTileWidth - scrollX, |
| 353 |
y * mTileWidth - scrollY, |
| 354 |
32, 32)); |
| 355 |
} |
| 356 |
|
| 357 |
if (!getWalk(x, y, BLOCKMASK_CHARACTER)) |
| 358 |
{ |
| 359 |
graphics->setColor(gcn::Color(0, 200, 0, 64)); |
| 360 |
graphics->fillRectangle(gcn::Rectangle( |
| 361 |
x * mTileWidth - scrollX, |
| 362 |
y * mTileWidth - scrollY, |
| 363 |
32, 32)); |
| 364 |
} |
| 365 |
} |
| 366 |
} |
| 367 |
} |
| 368 |
|
| 369 |
void Map::drawOverlay(Graphics *graphics, |
| 370 |
float scrollX, float scrollY, int detail) |
| 371 |
{ |
| 372 |
static int lastTick = tick_time; |
| 373 |
|
| 374 |
// Detail 0: no overlays |
| 375 |
if (detail <= 0) return; |
| 376 |
|
| 377 |
if (mLastScrollX == 0.0f && mLastScrollY == 0.0f) |
| 378 |
{ |
| 379 |
// First call - initialisation |
| 380 |
mLastScrollX = scrollX; |
| 381 |
mLastScrollY = scrollY; |
| 382 |
} |
| 383 |
|
| 384 |
// Update Overlays |
| 385 |
int timePassed = get_elapsed_time(lastTick); |
| 386 |
float dx = scrollX - mLastScrollX; |
| 387 |
float dy = scrollY - mLastScrollY; |
| 388 |
|
| 389 |
std::list<AmbientOverlay*>::iterator i; |
| 390 |
for (i = mOverlays.begin(); i != mOverlays.end(); i++) |
| 391 |
{ |
| 392 |
(*i)->update(timePassed, dx, dy); |
| 393 |
} |
| 394 |
mLastScrollX = scrollX; |
| 395 |
mLastScrollY = scrollY; |
| 396 |
lastTick = tick_time; |
| 397 |
|
| 398 |
// Draw overlays |
| 399 |
for (i = mOverlays.begin(); i != mOverlays.end(); i++) |
| 400 |
{ |
| 401 |
(*i)->draw(graphics, graphics->getWidth(), graphics->getHeight()); |
| 402 |
|
| 403 |
// Detail 1: only one overlay, higher: all overlays |
| 404 |
if (detail == 1) |
| 405 |
break; |
| 406 |
} |
| 407 |
} |
| 408 |
|
| 409 |
class ContainsGidFunctor |
| 410 |
{ |
| 411 |
public: |
| 412 |
bool operator() (const Tileset *set) const |
| 413 |
{ |
| 414 |
return (set->getFirstGid() <= gid && |
| 415 |
gid - set->getFirstGid() < (int)set->size()); |
| 416 |
} |
| 417 |
int gid; |
| 418 |
} containsGid; |
| 419 |
|
| 420 |
Tileset *Map::getTilesetWithGid(int gid) const |
| 421 |
{ |
| 422 |
containsGid.gid = gid; |
| 423 |
|
| 424 |
Tilesets::const_iterator i = find_if(mTilesets.begin(), mTilesets.end(), |
| 425 |
containsGid); |
| 426 |
|
| 427 |
return (i == mTilesets.end()) ? NULL : *i; |
| 428 |
} |
| 429 |
|
| 430 |
void Map::blockTile(int x, int y, BlockType type) |
| 431 |
{ |
| 432 |
if (type == BLOCKTYPE_NONE || !contains(x, y)) |
| 433 |
return; |
| 434 |
|
| 435 |
const int tileNum = x + y * mWidth; |
| 436 |
|
| 437 |
if ((++mOccupation[type][tileNum]) > 0) |
| 438 |
{ |
| 439 |
switch (type) |
| 440 |
{ |
| 441 |
case BLOCKTYPE_WALL: |
| 442 |
mMetaTiles[tileNum].blockmask |= BLOCKMASK_WALL; |
| 443 |
break; |
| 444 |
case BLOCKTYPE_CHARACTER: |
| 445 |
mMetaTiles[tileNum].blockmask |= BLOCKMASK_CHARACTER; |
| 446 |
break; |
| 447 |
case BLOCKTYPE_MONSTER: |
| 448 |
mMetaTiles[tileNum].blockmask |= BLOCKMASK_MONSTER; |
| 449 |
break; |
| 450 |
default: |
| 451 |
// shut up! |
| 452 |
break; |
| 453 |
} |
| 454 |
} |
| 455 |
} |
| 456 |
|
| 457 |
bool Map::getWalk(int x, int y, unsigned char walkmask) const |
| 458 |
{ |
| 459 |
// You can't walk outside of the map |
| 460 |
if (!contains(x, y)) |
| 461 |
return false; |
| 462 |
|
| 463 |
// Check if the tile is walkable |
| 464 |
return !(mMetaTiles[x + y * mWidth].blockmask & walkmask); |
| 465 |
} |
| 466 |
|
| 467 |
#ifdef EATHENA_SUPPORT |
| 468 |
bool Map::occupied(int x, int y) const |
| 469 |
{ |
| 470 |
const Beings &beings = beingManager->getAll(); |
| 471 |
for (Beings::const_iterator i = beings.begin(); i != beings.end(); i++) |
| 472 |
{ |
| 473 |
const Being *being = *i; |
| 474 |
|
| 475 |
// job 45 is a portal, they don't collide |
| 476 |
if (being->getTileX() == x && being->getTileY() == y && being->mJob != 45) |
| 477 |
return true; |
| 478 |
} |
| 479 |
|
| 480 |
return false; |
| 481 |
} |
| 482 |
#endif |
| 483 |
|
| 484 |
bool Map::contains(int x, int y) const |
| 485 |
{ |
| 486 |
return x >= 0 && y >= 0 && x < mWidth && y < mHeight; |
| 487 |
} |
| 488 |
|
| 489 |
MetaTile *Map::getMetaTile(int x, int y) const |
| 490 |
{ |
| 491 |
return &mMetaTiles[x + y * mWidth]; |
| 492 |
} |
| 493 |
|
| 494 |
MapSprite Map::addSprite(Sprite *sprite) |
| 495 |
{ |
| 496 |
mSprites.push_front(sprite); |
| 497 |
return mSprites.begin(); |
| 498 |
} |
| 499 |
|
| 500 |
void Map::removeSprite(MapSprite iterator) |
| 501 |
{ |
| 502 |
mSprites.erase(iterator); |
| 503 |
} |
| 504 |
|
| 505 |
const std::string &Map::getMusicFile() const |
| 506 |
{ |
| 507 |
return getProperty("music"); |
| 508 |
} |
| 509 |
|
| 510 |
const std::string &Map::getName() const |
| 511 |
{ |
| 512 |
if (hasProperty("name")) |
| 513 |
return getProperty("name"); |
| 514 |
|
| 515 |
return getProperty("mapname"); |
| 516 |
} |
| 517 |
|
| 518 |
static int const basicCost = 100; |
| 519 |
|
| 520 |
Path Map::findPath(int startX, int startY, int destX, int destY, |
| 521 |
unsigned char walkmask, int maxCost) |
| 522 |
{ |
| 523 |
// Path to be built up (empty by default) |
| 524 |
Path path; |
| 525 |
|
| 526 |
// Declare open list, a list with open tiles sorted on F cost |
| 527 |
std::priority_queue<Location> openList; |
| 528 |
|
| 529 |
// Return when destination not walkable |
| 530 |
if (!getWalk(destX, destY, walkmask)) return path; |
| 531 |
|
| 532 |
// Reset starting tile's G cost to 0 |
| 533 |
MetaTile *startTile = getMetaTile(startX, startY); |
| 534 |
startTile->Gcost = 0; |
| 535 |
|
| 536 |
// Add the start point to the open list |
| 537 |
openList.push(Location(startX, startY, startTile)); |
| 538 |
|
| 539 |
bool foundPath = false; |
| 540 |
|
| 541 |
// Keep trying new open tiles until no more tiles to try or target found |
| 542 |
while (!openList.empty() && !foundPath) |
| 543 |
{ |
| 544 |
// Take the location with the lowest F cost from the open list. |
| 545 |
Location curr = openList.top(); |
| 546 |
openList.pop(); |
| 547 |
|
| 548 |
// If the tile is already on the closed list, this means it has already |
| 549 |
// been processed with a shorter path to the start point (lower G cost) |
| 550 |
if (curr.tile->whichList == mOnClosedList) |
| 551 |
{ |
| 552 |
continue; |
| 553 |
} |
| 554 |
|
| 555 |
// Put the current tile on the closed list |
| 556 |
curr.tile->whichList = mOnClosedList; |
| 557 |
|
| 558 |
// Check the adjacent tiles |
| 559 |
for (int dy = -1; dy <= 1; dy++) |
| 560 |
{ |
| 561 |
for (int dx = -1; dx <= 1; dx++) |
| 562 |
{ |
| 563 |
// Calculate location of tile to check |
| 564 |
const int x = curr.x + dx; |
| 565 |
const int y = curr.y + dy; |
| 566 |
|
| 567 |
// Skip if if we're checking the same tile we're leaving from, |
| 568 |
// or if the new location falls outside of the map boundaries |
| 569 |
if ((dx == 0 && dy == 0) || !contains(x, y)) |
| 570 |
{ |
| 571 |
continue; |
| 572 |
} |
| 573 |
|
| 574 |
MetaTile *newTile = getMetaTile(x, y); |
| 575 |
|
| 576 |
// Skip if the tile is on the closed list or is not walkable |
| 577 |
// unless its the destination tile |
| 578 |
if (newTile->whichList == mOnClosedList || |
| 579 |
((newTile->blockmask & walkmask) |
| 580 |
&& !(x == destX && y == destY))) |
| 581 |
{ |
| 582 |
continue; |
| 583 |
} |
| 584 |
|
| 585 |
// When taking a diagonal step, verify that we can skip the |
| 586 |
// corner. |
| 587 |
if (dx != 0 && dy != 0) |
| 588 |
{ |
| 589 |
MetaTile *t1 = getMetaTile(curr.x, curr.y + dy); |
| 590 |
MetaTile *t2 = getMetaTile(curr.x + dx, curr.y); |
| 591 |
|
| 592 |
if ((t1->blockmask | t2->blockmask) & BLOCKMASK_WALL) |
| 593 |
continue; |
| 594 |
} |
| 595 |
|
| 596 |
// Calculate G cost for this route, ~sqrt(2) for moving diagonal |
| 597 |
int Gcost = curr.tile->Gcost + |
| 598 |
(dx == 0 || dy == 0 ? basicCost : basicCost * 362 / 256); |
| 599 |
|
| 600 |
/* Demote an arbitrary direction to speed pathfinding by |
| 601 |
adding a defect (TODO: change depending on the desired |
| 602 |
visual effect, e.g. a cross-product defect toward |
| 603 |
destination). |
| 604 |
Important: as long as the total defect along any path is |
| 605 |
less than the basicCost, the pathfinder will still find one |
| 606 |
of the shortest paths! */ |
| 607 |
if (dx == 0 || dy == 0) |
| 608 |
{ |
| 609 |
// Demote horizontal and vertical directions, so that two |
| 610 |
// consecutive directions cannot have the same Fcost. |
| 611 |
++Gcost; |
| 612 |
} |
| 613 |
|
| 614 |
#ifdef EATHENA_SUPPORT |
| 615 |
// It costs extra to walk through a being (needs to be enough |
| 616 |
// to make it more attractive to walk around). |
| 617 |
if (occupied(x, y)) |
| 618 |
{ |
| 619 |
Gcost += 3 * basicCost; |
| 620 |
} |
| 621 |
#endif |
| 622 |
|
| 623 |
// Skip if Gcost becomes too much |
| 624 |
// Warning: probably not entirely accurate |
| 625 |
if (Gcost > maxCost * basicCost) |
| 626 |
{ |
| 627 |
continue; |
| 628 |
} |
| 629 |
|
| 630 |
if (newTile->whichList != mOnOpenList) |
| 631 |
{ |
| 632 |
// Found a new tile (not on open nor on closed list) |
| 633 |
|
| 634 |
/* Update Hcost of the new tile. The pathfinder does not |
| 635 |
work reliably if the heuristic cost is higher than the |
| 636 |
real cost. In particular, using Manhattan distance is |
| 637 |
forbidden here. */ |
| 638 |
int dx = std::abs(x - destX), dy = std::abs(y - destY); |
| 639 |
newTile->Hcost = std::abs(dx - dy) * basicCost + |
| 640 |
std::min(dx, dy) * (basicCost * 362 / 256); |
| 641 |
|
| 642 |
// Set the current tile as the parent of the new tile |
| 643 |
newTile->parentX = curr.x; |
| 644 |
newTile->parentY = curr.y; |
| 645 |
|
| 646 |
// Update Gcost and Fcost of new tile |
| 647 |
newTile->Gcost = Gcost; |
| 648 |
newTile->Fcost = Gcost + newTile->Hcost; |
| 649 |
|
| 650 |
if (x != destX || y != destY) { |
| 651 |
// Add this tile to the open list |
| 652 |
newTile->whichList = mOnOpenList; |
| 653 |
openList.push(Location(x, y, newTile)); |
| 654 |
} |
| 655 |
else { |
| 656 |
// Target location was found |
| 657 |
foundPath = true; |
| 658 |
} |
| 659 |
} |
| 660 |
else if (Gcost < newTile->Gcost) |
| 661 |
{ |
| 662 |
// Found a shorter route. |
| 663 |
// Update Gcost and Fcost of the new tile |
| 664 |
newTile->Gcost = Gcost; |
| 665 |
newTile->Fcost = Gcost + newTile->Hcost; |
| 666 |
|
| 667 |
// Set the current tile as the parent of the new tile |
| 668 |
newTile->parentX = curr.x; |
| 669 |
newTile->parentY = curr.y; |
| 670 |
|
| 671 |
// Add this tile to the open list (it's already |
| 672 |
// there, but this instance has a lower F score) |
| 673 |
openList.push(Location(x, y, newTile)); |
| 674 |
} |
| 675 |
} |
| 676 |
} |
| 677 |
} |
| 678 |
|
| 679 |
// Two new values to indicate whether a tile is on the open or closed list, |
| 680 |
// this way we don't have to clear all the values between each pathfinding. |
| 681 |
mOnClosedList += 2; |
| 682 |
mOnOpenList += 2; |
| 683 |
|
| 684 |
// If a path has been found, iterate backwards using the parent locations |
| 685 |
// to extract it. |
| 686 |
if (foundPath) |
| 687 |
{ |
| 688 |
int pathX = destX; |
| 689 |
int pathY = destY; |
| 690 |
|
| 691 |
while (pathX != startX || pathY != startY) |
| 692 |
{ |
| 693 |
// Add the new path node to the start of the path list |
| 694 |
path.push_front(Position(pathX, pathY)); |
| 695 |
|
| 696 |
// Find out the next parent |
| 697 |
MetaTile *tile = getMetaTile(pathX, pathY); |
| 698 |
pathX = tile->parentX; |
| 699 |
pathY = tile->parentY; |
| 700 |
} |
| 701 |
} |
| 702 |
|
| 703 |
return path; |
| 704 |
} |
| 705 |
|
| 706 |
void Map::addParticleEffect(const std::string &effectFile, int x, int y) |
| 707 |
{ |
| 708 |
ParticleEffectData newEffect; |
| 709 |
newEffect.file = effectFile; |
| 710 |
newEffect.x = x; |
| 711 |
newEffect.y = y; |
| 712 |
particleEffects.push_back(newEffect); |
| 713 |
} |
| 714 |
|
| 715 |
void Map::initializeParticleEffects(Particle *particleEngine) |
| 716 |
{ |
| 717 |
if (config.getValue("particleeffects", 1)) |
| 718 |
{ |
| 719 |
for (std::list<ParticleEffectData>::iterator i = particleEffects.begin(); |
| 720 |
i != particleEffects.end(); |
| 721 |
i++ |
| 722 |
) |
| 723 |
{ |
| 724 |
particleEngine->addEffect(i->file, i->x, i->y); |
| 725 |
} |
| 726 |
} |
| 727 |
} |
| 728 |
|
| 729 |
TileAnimation *Map::getAnimationForGid(int gid) const |
| 730 |
{ |
| 731 |
std::map<int, TileAnimation*>::const_iterator i = mTileAnimations.find(gid); |
| 732 |
return (i == mTileAnimations.end()) ? NULL : i->second; |
| 733 |
} |