diff options
Diffstat (limited to 'src/map.cpp')
-rw-r--r-- | src/map.cpp | 323 |
1 files changed, 223 insertions, 100 deletions
diff --git a/src/map.cpp b/src/map.cpp index 9e6d865d..3e5e8e12 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -21,32 +21,31 @@ #include "map.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "client.h" #include "configuration.h" #include "graphics.h" #include "particle.h" #include "simpleanimation.h" -#include "sprite.h" #include "tileset.h" #include "resources/ambientlayer.h" #include "resources/image.h" #include "resources/resourcemanager.h" +#include "net/net.h" + #include "utils/dtor.h" #include "utils/stringutils.h" #include <queue> +#include <limits.h> /** * A location on a tile map. Used for pathfinding, open list. */ struct Location { - /** - * Constructor. - */ Location(int px, int py, MetaTile *ptile): x(px), y(py), tile(ptile) {} @@ -95,10 +94,12 @@ void TileAnimation::update(int ticks) } } -MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer): +MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer, + Map *map): mX(x), mY(y), mWidth(width), mHeight(height), - mIsFringeLayer(isFringeLayer) + mIsFringeLayer(isFringeLayer), + mMap(map) { const int size = mWidth * mHeight; mTiles = new Image*[size]; @@ -124,7 +125,7 @@ void MapLayer::draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, - const MapSprites &sprites, int debugFlags) const + const Actors &actors, int debugFlags) const { startX -= mX; startY -= mY; @@ -136,47 +137,86 @@ void MapLayer::draw(Graphics *graphics, if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; - MapSprites::const_iterator si = sprites.begin(); + Actors::const_iterator ai = actors.begin(); + + int dx = (mX * mMap->getTileWidth()) - scrollX; + int dy = (mY * mMap->getTileHeight()) - scrollY + mMap->getTileHeight(); for (int y = startY; y < endY; y++) { - // If drawing the fringe layer, make sure all sprites above this row of + int pixelY = y * mMap->getTileHeight(); + + // If drawing the fringe layer, make sure all actors above this row of // tiles have been drawn if (mIsFringeLayer) { - while (si != sprites.end() && (*si)->getPixelY() <= y * 32) + while (ai != actors.end() && (*ai)->getPixelY() + <= y * mMap->getTileHeight()) { - (*si)->setAlpha(1.0f); - (*si)->draw(graphics, -scrollX, -scrollY); - si++; + (*ai)->draw(graphics, -scrollX, -scrollY); + ++ai; } } - for (int x = startX; x < endX; x++) + if (!(debugFlags & Map::MAP_SPECIAL3)) { - Image *img = getTile(x, y); - if (img) + const int py0 = pixelY + dy; + + for (int x = startX; x < endX; x++) { - const int px = (x + mX) * 32 - scrollX; - const int py = (y + mY) * 32 - scrollY + 32 - img->getHeight(); - if (debugFlags != Map::MAP_SPECIAL || img->getHeight() <= 32) - graphics->drawImage(img, px, py); + Image *img = getTile(x, y); + if (img) + { + const int px = (x * mMap->getTileWidth()) + dx; + const int py = py0 - img->getHeight(); + if (!(debugFlags & (Map::MAP_SPECIAL1 | Map::MAP_SPECIAL2)) + || img->getHeight() <= mMap->getTileHeight()) + { + int width = 0; + int c = getTileDrawWidth(x, y, endX, width); + if (!c) + { + graphics->drawImage(img, px, py); + } + else + { + graphics->drawImagePattern(img, px, py, + width, img->getHeight()); + } + x += c; + } + } } } } - // Draw any remaining sprites + // Draw any remaining actors if (mIsFringeLayer) { - while (si != sprites.end()) + while (ai != actors.end()) { - (*si)->setAlpha(1.0f); - (*si)->draw(graphics, -scrollX, -scrollY); - si++; + (*ai)->draw(graphics, -scrollX, -scrollY); + ai++; } } } +int MapLayer::getTileDrawWidth(int x1, int y1, int endX, int &width) const +{ + Image *img1 = getTile(x1, y1); + int c = 0; + width = img1->getWidth(); + for (int x = x1 + 1; x < endX; x++) + { + Image *img = getTile(x, y1); + if (img != img1) + break; + c ++; + width += img->getWidth(); + } + return c; +} + Map::Map(int width, int height, int tileWidth, int tileHeight): mWidth(width), mHeight(height), mTileWidth(tileWidth), mTileHeight(tileHeight), @@ -191,8 +231,8 @@ Map::Map(int width, int height, int tileWidth, int tileHeight): mMetaTiles = new MetaTile[size]; for (int i = 0; i < NB_BLOCKTYPES; i++) { - mOccupation[i] = new int[size]; - memset(mOccupation[i], 0, size * sizeof(int)); + mOccupation[i] = new unsigned[size]; + memset(mOccupation[i], 0, size * sizeof(unsigned)); } } @@ -288,9 +328,9 @@ void Map::addTileset(Tileset *tileset) mMaxTileWidth = tileset->getWidth(); } -bool spriteCompare(const Sprite *a, const Sprite *b) +bool actorCompare(const Actor *a, const Actor *b) { - return a->getPixelY() < b->getPixelY(); + return a->getDrawOrder() < b->getDrawOrder(); } void Map::update(int ticks) @@ -314,25 +354,47 @@ 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 + // Make sure actors are sorted ascending by Y-coordinate // so that they overlap correctly - mSprites.sort(spriteCompare); + mActors.sort(actorCompare); // update scrolling of all ambient layers updateAmbientLayers(scrollX, scrollY); // Draw backgrounds drawAmbientLayers(graphics, BACKGROUND_LAYERS, scrollX, scrollY, - (int) config.getValue("OverlayDetail", 2)); + config.getIntValue("OverlayDetail")); // draw the game world Layers::const_iterator layeri = mLayers.begin(); - for (; layeri != mLayers.end(); ++layeri) + + bool overFringe = false; + + if (mDebugFlags & MAP_SPECIAL3) + { + for (; layeri != mLayers.end(); ++layeri) + { + if ((*layeri)->isFringeLayer()) + { + (*layeri)->draw(graphics, + startX, startY, endX, endY, + scrollX, scrollY, + mActors, mDebugFlags); + } + } + } + else { - (*layeri)->draw(graphics, - startX, startY, endX, endY, - scrollX, scrollY, - mSprites, mDebugFlags); + for (; layeri != mLayers.end() && !overFringe; ++layeri) + { + if ((*layeri)->isFringeLayer() && (mDebugFlags & MAP_SPECIAL2)) + overFringe = true; + + (*layeri)->draw(graphics, + startX, startY, endX, endY, + scrollX, scrollY, + mActors, mDebugFlags); + } } // If the transparency hasn't been disabled, @@ -340,24 +402,25 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) { // We draw beings with a lower opacity to make them visible // even when covered by a wall or some other elements... - MapSprites::const_iterator si = mSprites.begin(); - while (si != mSprites.end()) + Actors::const_iterator ai = mActors.begin(); + while (ai != mActors.end()) { - if (Sprite *sprite = *si) + if (Actor *actor = *ai) { - // For now, just draw sprites with only one layer. - if (sprite->getNumberOfLayers() == 1) + // For now, just draw actors with only one layer. + if (actor->drawnWhenBehind()) { - sprite->setAlpha(0.3f); - sprite->draw(graphics, -scrollX, -scrollY); + actor->setAlpha(0.3f); + actor->draw(graphics, -scrollX, -scrollY); + actor->setAlpha(1.0f); } } - si++; + ai++; } } drawAmbientLayers(graphics, FOREGROUND_LAYERS, scrollX, scrollY, - (int) config.getValue("OverlayDetail", 2)); + config.getIntValue("OverlayDetail")); } void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, @@ -380,21 +443,24 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, { graphics->setColor(gcn::Color(0, 0, 0, 64)); - if (debugFlags < MAP_SPECIAL) + if (debugFlags & MAP_GRID) { graphics->drawRectangle(gcn::Rectangle( x * mTileWidth - scrollX, y * mTileHeight - scrollY, - 33, 33)); + mTileWidth + 1, mTileHeight + 1)); } + if (!(debugFlags & MAP_COLLISION_TILES)) + continue; + if (!getWalk(x, y, BLOCKMASK_WALL)) { graphics->setColor(gcn::Color(0, 0, 200, 64)); graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, y * mTileHeight - scrollY, - 32, 32)); + mTileWidth, mTileHeight)); } if (!getWalk(x, y, BLOCKMASK_MONSTER)) @@ -403,7 +469,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, y * mTileHeight - scrollY, - 32, 32)); + mTileWidth, mTileHeight)); } if (!getWalk(x, y, BLOCKMASK_CHARACTER)) @@ -412,7 +478,7 @@ void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, graphics->fillRectangle(gcn::Rectangle( x * mTileWidth - scrollX, y * mTileHeight - scrollY, - 32, 32)); + mTileWidth, mTileHeight)); } } } @@ -501,7 +567,8 @@ void Map::blockTile(int x, int y, BlockType type) const int tileNum = x + y * mWidth; - if ((++mOccupation[type][tileNum]) > 0) + if (mOccupation[type][tileNum] < UINT_MAX && + (++mOccupation[type][tileNum]) > 0) { switch (type) { @@ -533,37 +600,53 @@ bool Map::getWalk(int x, int y, unsigned char walkmask) const bool Map::occupied(int x, int y) const { - const Beings &beings = beingManager->getAll(); - for (Beings::const_iterator i = beings.begin(); i != beings.end(); i++) + const ActorSprites &actors = actorSpriteManager->getAll(); + ActorSpritesConstIterator it, it_end; + for (it = actors.begin(), it_end = actors.end(); it != it_end; it++) { - const Being *being = *i; + const ActorSprite *actor = *it; - if (being->getTileX() == x && being->getTileY() == y) + if (actor->getTileX() == x && actor->getTileY() == y && + actor->getType() != ActorSprite::FLOOR_ITEM) return true; } return false; } +Vector Map::getTileCenter(int x, int y) +{ + Vector tileCenterPos; + + tileCenterPos.x = x * mTileWidth + mTileWidth / 2; + tileCenterPos.y = y * mTileHeight + mTileHeight / 2; + return tileCenterPos; +} + bool Map::contains(int x, int y) const { return x >= 0 && y >= 0 && x < mWidth && y < mHeight; } +bool Map::containsPixel(int x, int y) const +{ + return contains(x / mTileWidth, y / mTileHeight); +} + MetaTile *Map::getMetaTile(int x, int y) const { return &mMetaTiles[x + y * mWidth]; } -MapSprite Map::addSprite(Sprite *sprite) +Actors::iterator Map::addActor(Actor *actor) { - mSprites.push_front(sprite); - return mSprites.begin(); + mActors.push_front(actor); + return mActors.begin(); } -void Map::removeSprite(MapSprite iterator) +void Map::removeActor(Actors::iterator iterator) { - mSprites.erase(iterator); + mActors.erase(iterator); } const std::string Map::getMusicFile() const @@ -592,20 +675,20 @@ 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; + const int tx = position.x / mTileWidth; + const int ty = position.y / mTileHeight; // Pre-computing character's position offsets. - int fx = position.x % 32; - int fy = position.y % 32; + int fx = position.x % mTileWidth; + int fy = position.y % mTileHeight; // 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; + if (radius > mTileWidth / 2) radius = mTileWidth / 2; // set a default value if no value returned. - if (radius < 1) radius = 32 / 3; + if (radius < 1) radius = mTileWidth / 3; // We check diagonal first as they are more restrictive. // Top-left border check @@ -616,56 +699,82 @@ Position Map::checkNodeOffsets(int radius, unsigned char walkMask, } // Top-right border check if (!getWalk(tx + 1, ty - 1, walkMask) - && (fy < radius) && fx > (32 - radius)) + && (fy < radius) && fx > (mTileWidth - radius)) { - fx = 32 -radius; + fx = mTileWidth - radius; fy = radius; } // Bottom-left border check if (!getWalk(tx - 1, ty + 1, walkMask) - && fy > (32 - radius) && fx < radius) + && fy > (mTileHeight - radius) && fx < radius) { fx = radius; - fy = 32 - radius; + fy = mTileHeight - radius; } // Bottom-right border check if (!getWalk(tx + 1, ty + 1, walkMask) - && fy > (32 - radius) && fx > (32 - radius)) + && fy > (mTileHeight - radius) && fx > (mTileWidth - radius)) { - fx = fy = 32 -radius; + fx = mTileWidth - radius; + fy = mTileHeight - 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; + if (fx > (mTileWidth - radius) && !getWalk(tx + 1, ty, walkMask)) + fx = mTileWidth - 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 > (mTileHeight - radius) && !getWalk(tx, ty + 1, walkMask)) + fy = mTileHeight - radius; else if (fy < radius && !getWalk(tx, ty - 1, walkMask)) fy = radius; - return Position(tx * 32 + fx, ty * 32 + fy); + return Position(tx * mTileWidth + fx, ty * mTileHeight + fy); +} + +Path Map::findTilePath(int startPixelX, int startPixelY, int endPixelX, + int endPixelY, unsigned char walkMask, int maxCost) +{ + Path myPath = findPath(startPixelX / mTileWidth, startPixelY / mTileHeight, + endPixelX / mTileWidth, endPixelY / mTileHeight, + walkMask, maxCost); + + // Don't compute empty coordinates. + if (myPath.empty()) + return myPath; + + // Convert the map path to pixels from the tile position + Path::iterator it = myPath.begin(); + while (it != myPath.end()) + { + // The new pixel position will be the tile center. + *it = Position(it->x * mTileWidth + mTileWidth / 2, + it->y * mTileHeight + mTileHeight / 2); + ++it; + } + + return myPath; } 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); + Path myPath = findPath(startPixelX / mTileWidth, startPixelY / mTileHeight, + endPixelX / mTileWidth, endPixelY / mTileHeight, + walkMask, maxCost); // Don't compute empty coordinates. if (myPath.empty()) return myPath; // Find the starting offset - float startOffsetX = (startPixelX % 32); - float startOffsetY = (startPixelY % 32); + float startOffsetX = (startPixelX % mTileWidth); + float startOffsetY = (startPixelY % mTileHeight); // Find the ending offset - float endOffsetX = (endPixelX % 32); - float endOffsetY = (endPixelY % 32); + float endOffsetX = (endPixelX % mTileWidth); + float endOffsetY = (endPixelY % mTileHeight); // Find the distance, and divide it by the number of steps int changeX = (int)((endOffsetX - startOffsetX) / myPath.size()); @@ -680,10 +789,10 @@ Path Map::findPixelPath(int startPixelX, int startPixelY, int endPixelX, // 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++; + it->x * mTileWidth + startOffsetX + changeX * i, + it->y * mTileHeight + startOffsetY + changeY * i); + ++i; + ++it; } // Remove the last path node, as it's more clever to go to the destination. @@ -700,7 +809,8 @@ Path Map::findPixelPath(int startPixelX, int startPixelY, int endPixelX, Path Map::findPath(int startX, int startY, int destX, int destY, unsigned char walkmask, int maxCost) { - static int const basicCost = 100; + // The basic walking cost of a tile. + const int basicCost = 100; // Path to be built up (empty by default) Path path; @@ -731,9 +841,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY, // If the tile is already on the closed list, this means it has already // been processed with a shorter path to the start point (lower G cost) if (curr.tile->whichList == mOnClosedList) - { continue; - } // Put the current tile on the closed list curr.tile->whichList = mOnClosedList; @@ -750,9 +858,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY, // Skip if if we're checking the same tile we're leaving from, // or if the new location falls outside of the map boundaries if ((dx == 0 && dy == 0) || !contains(x, y)) - { continue; - } MetaTile *newTile = getMetaTile(x, y); @@ -772,7 +878,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY, MetaTile *t1 = getMetaTile(curr.x, curr.y + dy); MetaTile *t2 = getMetaTile(curr.x + dx, curr.y); - if ((t1->blockmask | t2->blockmask) & BLOCKMASK_WALL) + if ((t1->blockmask | t2->blockmask) & walkmask) continue; } @@ -796,7 +902,9 @@ Path Map::findPath(int startX, int startY, int destX, int destY, // It costs extra to walk through a being (needs to be enough // to make it more attractive to walk around). - if (occupied(x, y)) + // N.B.: Specific to TmwAthena for now. + if (Net::getNetworkType() == ServerInfo::TMWATHENA && + occupied(x, y)) { Gcost += 3 * basicCost; } @@ -861,8 +969,22 @@ Path Map::findPath(int startX, int startY, int destX, int destY, // Two new values to indicate whether a tile is on the open or closed list, // this way we don't have to clear all the values between each pathfinding. - mOnClosedList += 2; - mOnOpenList += 2; + if (mOnOpenList > UINT_MAX - 2) + { + // We reset the list memebers value. + mOnClosedList = 1; + mOnOpenList = 2; + + // Clean up the metaTiles + const int size = mWidth * mHeight; + for (int i = 0; i < size; ++i) + mMetaTiles[i].whichList = 0; + } + else + { + mOnClosedList += 2; + mOnOpenList += 2; + } // If a path has been found, iterate backwards using the parent locations // to extract it. @@ -886,7 +1008,8 @@ Path Map::findPath(int startX, int startY, int destX, int destY, return path; } -void Map::addParticleEffect(const std::string &effectFile, int x, int y, int w, int h) +void Map::addParticleEffect(const std::string &effectFile, int x, int y, int w, + int h) { ParticleEffectData newEffect; newEffect.file = effectFile; @@ -902,7 +1025,7 @@ void Map::initializeParticleEffects(Particle *particleEngine) { Particle *p; - if (config.getValue("particleeffects", 1)) + if (config.getBoolValue("particleeffects")) { for (std::list<ParticleEffectData>::iterator i = particleEffects.begin(); i != particleEffects.end(); |