summaryrefslogtreecommitdiff
path: root/src/map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map.cpp')
-rw-r--r--src/map.cpp323
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();