diff options
author | Bertram <bertram@cegetel.net> | 2010-03-02 23:20:50 +0100 |
---|---|---|
committer | Bertram <yohanndotferreiraatorange.fr> | 2010-04-12 23:50:21 +0200 |
commit | dd2b559ea0d467db7fec522a63a66a82c1415b36 (patch) | |
tree | 06925dcb5d47d631a1425e4ced8ec1cf8975f88b /src/map.cpp | |
parent | 98967ec85b48fcc85d1468b7fa02b847389431fb (diff) | |
download | mana-dd2b559ea0d467db7fec522a63a66a82c1415b36.tar.gz mana-dd2b559ea0d467db7fec522a63a66a82c1415b36.tar.bz2 mana-dd2b559ea0d467db7fec522a63a66a82c1415b36.tar.xz mana-dd2b559ea0d467db7fec522a63a66a82c1415b36.zip |
Move path finding related code to the Map class and small fixes.
It permits two things:
1. It simplifies and demystifies Being::SetDestination() code.
2. It will permit to show the *real* calulated path when using
the drawDebugPath feature for ManaServ.
Diffstat (limited to 'src/map.cpp')
-rw-r--r-- | src/map.cpp | 136 |
1 files changed, 125 insertions, 11 deletions
diff --git a/src/map.cpp b/src/map.cpp index 9f0a901b..01a71375 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -301,7 +301,7 @@ void Map::update(int ticks) void Map::draw(Graphics *graphics, int scrollX, int scrollY) { - //Calculate range of tiles which are on-screen + // Calculate range of tiles which are on-screen int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1; endPixelY += mMaxTileHeight - mTileHeight; int startX = scrollX / mTileWidth; @@ -309,7 +309,8 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth; int endY = endPixelY / mTileHeight; - // Make sure sprites are sorted ascending by Y-coordinate so that they overlap correctly + // Make sure sprites are sorted ascending by Y-coordinate + // so that they overlap correctly mSprites.sort(spriteCompare); // update scrolling of all ambient layers @@ -350,7 +351,8 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) (int) config.getValue("OverlayDetail", 2)); } -void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugFlags) +void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, + int debugFlags) { int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1; int startX = scrollX / mTileWidth; @@ -373,7 +375,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugF { graphics->drawRectangle(gcn::Rectangle( x * mTileWidth - scrollX, - y * mTileWidth - scrollY, + y * mTileHeight - scrollY, 33, 33)); } @@ -382,7 +384,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugF graphics->setColor(gcn::Color(0, 0, 200, 64)); graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, - y * mTileWidth - scrollY, + y * mTileHeight - scrollY, 32, 32)); } @@ -391,7 +393,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugF graphics->setColor(gcn::Color(200, 0, 0, 64)); graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, - y * mTileWidth - scrollY, + y * mTileHeight - scrollY, 32, 32)); } @@ -400,7 +402,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugF graphics->setColor(gcn::Color(0, 200, 0, 64)); graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, - y * mTileWidth - scrollY, + y * mTileHeight - scrollY, 32, 32)); } } @@ -454,12 +456,15 @@ void Map::drawAmbientLayers(Graphics *graphics, LayerType type, layers = &mBackgrounds; break; default: - assert(false); // you noob, you added a new type of ambient layers without adding it to Map::drawAmbientLayers + // New type of ambient layers added here without adding it + // to Map::drawAmbientLayers. + assert(false); break; } // Draw overlays - for (std::list<AmbientLayer*>::iterator i = layers->begin(); i != layers->end(); i++) + for (std::list<AmbientLayer*>::iterator i = layers->begin(); + i != layers->end(); i++) { (*i)->draw(graphics, graphics->getWidth(), graphics->getHeight()); @@ -511,7 +516,7 @@ void Map::blockTile(int x, int y, BlockType type) mMetaTiles[tileNum].blockmask |= BLOCKMASK_MONSTER; break; default: - // shut up! + // Do nothing. break; } } @@ -534,7 +539,7 @@ bool Map::occupied(int x, int y) const { const Being *being = *i; - // job 45 is a portal, they don't collide + // Eathena: The Job 45 is a portal, so they don't collide. if (being->getTileX() == x && being->getTileY() == y && being->getJob() != 45) return true; @@ -589,6 +594,115 @@ const std::string *Map::getFilename() const return sub; } +Position Map::checkNodeOffsets(int radius, unsigned char walkMask, + const Position &position) const +{ + // Pre-computing character's position in tiles + const int tx = position.x / 32; + const int ty = position.y / 32; + + // Pre-computing character's position offsets. + int fx = position.x % 32; + int fy = position.y % 32; + + // Compute the being radius: + // FIXME: Hande beings with more than 1/2 tile radius by not letting them + // go or spawn in too narrow places. The server will have to be aware + // of being's radius value (in tiles) to handle this gracefully. + if (radius > 32 / 2) radius = 32 / 2; + // set a default value if no value returned. + if (radius < 1) radius = 32 / 3; + + // We check diagonal first as they are more restrictive. + // Top-left border check + if (!getWalk(tx - 1, ty - 1, walkMask) + && fy < radius && fx < radius) + { + fx = fy = radius; + } + // Top-right border check + if (!getWalk(tx + 1, ty - 1, walkMask) + && (fy < radius) && fx > (32 - radius)) + { + fx = 32 -radius; + fy = radius; + } + // Bottom-left border check + if (!getWalk(tx - 1, ty + 1, walkMask) + && fy > (32 - radius) && fx < radius) + { + fx = radius; + fy = 32 - radius; + } + // Bottom-right border check + if (!getWalk(tx + 1, ty + 1, walkMask) + && fy > (32 - radius) && fx > (32 - radius)) + { + fx = fy = 32 -radius; + } + + // Fix coordinates so that the player does not seem to dig into walls. + if (fx > (32 - radius) && !getWalk(tx + 1, ty, walkMask)) + fx = 32 - radius; + else if (fx < radius && !getWalk(tx - 1, ty, walkMask)) + fx = radius; + else if (fy > (32 - radius) && !getWalk(tx, ty + 1, walkMask)) + fy = 32 - radius; + else if (fy < radius && !getWalk(tx, ty - 1, walkMask)) + fy = radius; + + return Position(tx * 32 + fx, ty * 32 + fy); +} + +Path Map::findPixelPath(int startPixelX, int startPixelY, int endPixelX, + int endPixelY, + int radius, unsigned char walkMask, int maxCost) +{ + Path myPath = findPath(startPixelX / 32, startPixelY / 32, + endPixelX / 32, endPixelY / 32, walkMask, maxCost); + + // Don't compute empty coordinates. + if (myPath.empty()) + return myPath; + + // Find the starting offset + float startOffsetX = (startPixelX % 32); + float startOffsetY = (startPixelY % 32); + + // Find the ending offset + float endOffsetX = (endPixelX % 32); + float endOffsetY = (endPixelY % 32); + + // Find the distance, and divide it by the number of steps + int changeX = (int)((endOffsetX - startOffsetX) / myPath.size()); + int changeY = (int)((endOffsetY - startOffsetY) / myPath.size()); + + // Convert the map path to pixels over tiles + // And add interpolation between the starting and ending offsets + Path::iterator it = myPath.begin(); + int i = 0; + while (it != myPath.end()) + { + // A position that is valid on the start and end tile is not + // necessarily valid on all the tiles in between, so check the offsets. + *it = checkNodeOffsets(radius, walkMask, + it->x * 32 + startOffsetX + changeX * i, + it->y * 32 + startOffsetY + changeY * i); + i++; + it++; + } + + // Remove the last path node, as it's more clever to go to the destination. + // It also permit to avoid zigzag at the end of the path, + // especially with mouse. + Position destination = checkNodeOffsets(radius, walkMask, + endPixelX, endPixelY); + myPath.pop_back(); + myPath.push_back(destination); + + return myPath; +} + static int const basicCost = 100; Path Map::findPath(int startX, int startY, int destX, int destY, |