/* * The ManaPlus Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2012 The ManaPlus Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "maplayer.h" #include "configuration.h" #include "graphicsvertexes.h" #include "logger.h" #include "localplayer.h" #include "resources/image.h" #include "resources/resourcemanager.h" #include "gui/gui.h" #include "gui/sdlfont.h" #include "utils/dtor.h" #include "debug.h" MapLayer::MapLayer(int x, int y, int width, int height, bool fringeLayer): mX(x), mY(y), mWidth(width), mHeight(height), mIsFringeLayer(fringeLayer), mHighlightAttackRange(config.getBoolValue("highlightAttackRange")), mTiles(new Image*[mWidth * mHeight]) { std::fill_n(mTiles, mWidth * mHeight, static_cast<Image*>(nullptr)); config.addListener("highlightAttackRange", this); } MapLayer::~MapLayer() { config.removeListener("highlightAttackRange", this); delete [] mTiles; delete_all(mTempRows); mTempRows.clear(); } void MapLayer::optionChanged(const std::string &value) { if (value == "highlightAttackRange") { mHighlightAttackRange = config.getBoolValue("highlightAttackRange"); } } void MapLayer::setTile(int x, int y, Image *img) { mTiles[x + y * mWidth] = img; } void MapLayer::draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, int debugFlags) const { if (!player_node) return; startX -= mX; startY -= mY; endX -= mX; endY -= mY; if (startX < 0) startX = 0; if (startY < 0) startY = 0; if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; const int dx = (mX * 32) - scrollX; const int dy = (mY * 32) - scrollY + 32; const bool flag = (debugFlags != Map::MAP_SPECIAL && debugFlags != Map::MAP_SPECIAL2); for (int y = startY; y < endY; y++) { const int y32 = y * 32; const int yWidth = y * mWidth; const int py0 = y32 + dy; Image **tilePtr = mTiles + startX + yWidth; for (int x = startX; x < endX; x++, tilePtr++) { const int x32 = x * 32; int c = 0; Image *img = *tilePtr; if (img) { const int px = x32 + dx; const int py = py0 - img->mBounds.h; if (flag || img->mBounds.h <= 32) { int width = 0; // here need not draw over player position c = getTileDrawWidth(img, endX - x, width); if (!c) { graphics->drawImage(img, px, py); } else { graphics->drawImagePattern(img, px, py, width, img->mBounds.h); } } } x += c; } } } void MapLayer::updateSDL(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, int debugFlags) { delete_all(mTempRows); mTempRows.clear(); startX -= mX; startY -= mY; endX -= mX; endY -= mY; if (startX < 0) startX = 0; if (startY < 0) startY = 0; if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; const int dx = (mX * 32) - scrollX; const int dy = (mY * 32) - scrollY + 32; const bool flag = (debugFlags != Map::MAP_SPECIAL && debugFlags != Map::MAP_SPECIAL2); for (int y = startY; y < endY; y++) { MapRowVertexes *row = new MapRowVertexes(); mTempRows.push_back(row); Image *lastImage = nullptr; ImageVertexes *imgVert = nullptr; const int yWidth = y * mWidth; const int py0 = y * 32 + dy; Image **tilePtr = mTiles + startX + yWidth; for (int x = startX; x < endX; x++, tilePtr++) { Image *img = *tilePtr; if (img) { const int px = x * 32 + dx; const int py = py0 - img->mBounds.h; if (flag || img->mBounds.h <= 32) { if (lastImage != img) { imgVert = new ImageVertexes(); imgVert->image = img; row->images.push_back(imgVert); lastImage = img; } graphics->calcTile(imgVert, px, py); } } } } } void MapLayer::drawSDL(Graphics *graphics) { MapRows::const_iterator rit = mTempRows.begin(); MapRows::const_iterator rit_end = mTempRows.end(); while (rit != rit_end) { MepRowImages *images = &(*rit)->images; MepRowImages::const_iterator iit = images->begin(); MepRowImages::const_iterator iit_end = images->end(); while (iit != iit_end) { graphics->drawTile(*iit); ++ iit; } ++ rit; } } void MapLayer::updateOGL(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, int debugFlags) { delete_all(mTempRows); mTempRows.clear(); startX -= mX; startY -= mY; endX -= mX; endY -= mY; if (startX < 0) startX = 0; if (startY < 0) startY = 0; if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; const int dx = (mX * 32) - scrollX; const int dy = (mY * 32) - scrollY + 32; const bool flag = (debugFlags != Map::MAP_SPECIAL && debugFlags != Map::MAP_SPECIAL2); for (int y = startY; y < endY; y++) { MapRowVertexes *row = new MapRowVertexes(); mTempRows.push_back(row); Image *lastImage = nullptr; ImageVertexes *imgVert = nullptr; const int yWidth = y * mWidth; const int py0 = y * 32 + dy; std::map<Image*, ImageVertexes*> imgSet; Image **tilePtr = mTiles + startX + yWidth; for (int x = startX; x < endX; x++, tilePtr++) { Image *img = *tilePtr; if (img) { const int px = x * 32 + dx; const int py = py0 - img->mBounds.h; if (flag || img->mBounds.h <= 32) { if (lastImage != img) { if (img->mBounds.w > 32) imgSet.clear(); imgSet[lastImage] = imgVert; if (imgSet.find(img) != imgSet.end()) { imgVert = imgSet[img]; } else { imgVert = new ImageVertexes(); imgVert->image = img; row->images.push_back(imgVert); } lastImage = img; } graphics->calcTile(imgVert, px, py); } } } } } void MapLayer::drawOGL(Graphics *graphics) { MapRows::const_iterator rit = mTempRows.begin(); MapRows::const_iterator rit_end = mTempRows.end(); while (rit != rit_end) { MepRowImages *images = &(*rit)->images; MepRowImages::const_iterator iit = images->begin(); MepRowImages::const_iterator iit_end = images->end(); while (iit != iit_end) { graphics->drawTile(*iit); ++ iit; } ++ rit; } } void MapLayer::drawFringe(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, const Actors *actors, int debugFlags, int yFix) const { if (!player_node || !mSpecialLayer || !mTempLayer) return; startX -= mX; startY -= mY; endX -= mX; endY -= mY; if (startX < 0) startX = 0; if (startY < 0) startY = 0; if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; ActorsCIter ai = actors->begin(); ActorsCIter ai_end = actors->end(); const int dx = (mX * 32) - scrollX; const int dy = (mY * 32) - scrollY + 32; int specialWidth = mSpecialLayer->mWidth; int specialHeight = mSpecialLayer->mHeight; for (int y = startY; y < endY; y++) { const int y32 = y * 32; const int y32s = (y + yFix) * 32; const int yWidth = y * mWidth; // If drawing the fringe layer, make sure all actors above this row of // tiles have been drawn while (ai != ai_end && (*ai)->getPixelY() <= y32s) { (*ai)->draw(graphics, -scrollX, -scrollY); ++ ai; } if (debugFlags == Map::MAP_SPECIAL3 || debugFlags == Map::MAP_BLACKWHITE) { if (y < specialHeight) { int ptr = y * specialWidth; const int py1 = y32 - scrollY; int endX1 = endX; if (endX1 > specialWidth) endX1 = specialWidth; if (endX1 < 0) endX1 = 0; for (int x = startX; x < endX1; x++) { const int px1 = x * 32 - scrollX; MapItem *item = mSpecialLayer->mTiles[ptr + x]; if (item) item->draw(graphics, px1, py1, 32, 32); item = mTempLayer->mTiles[ptr + x]; if (item) item->draw(graphics, px1, py1, 32, 32); } } } else { const int py0 = y32 + dy; const int py1 = y32 - scrollY; Image **tilePtr = mTiles + startX + yWidth; for (int x = startX; x < endX; x++, tilePtr++) { const int x32 = x * 32; const int px1 = x32 - scrollX; int c = 0; Image *img = *tilePtr; if (img) { const int px = x32 + dx; const int py = py0 - img->mBounds.h; if ((debugFlags != Map::MAP_SPECIAL && debugFlags != Map::MAP_SPECIAL2) || img->mBounds.h <= 32) { int width = 0; // here need not draw over player position c = getTileDrawWidth(img, endX - x, width); if (!c) { graphics->drawImage(img, px, py); } else { graphics->drawImagePattern(img, px, py, width, img->mBounds.h); } } } if (y < specialHeight) { int c1 = c; if (c1 + x + 1 > specialWidth) c1 = specialWidth - x - 1; if (c1 < 0) c1 = 0; int ptr = y * specialWidth + x; for (int x1 = 0; x1 < c1 + 1; x1 ++) { MapItem *item1 = mSpecialLayer->mTiles[ptr + x1]; MapItem *item2 = mTempLayer->mTiles[ptr + x1]; if (item1 || item2) { const int px2 = px1 + (x1 * 32); if (item1 && item1->mType != MapItem::EMPTY) item1->draw(graphics, px2, py1, 32, 32); if (item2 && item2->mType != MapItem::EMPTY) item2->draw(graphics, px2, py1, 32, 32); } } } x += c; } } } // Draw any remaining actors if (debugFlags != Map::MAP_SPECIAL3) { while (ai != ai_end) { (*ai)->draw(graphics, -scrollX, -scrollY); ++ai; } if (mHighlightAttackRange && player_node) { const int px = player_node->getPixelX() - scrollX - 16; const int py = player_node->getPixelY() - scrollY - 32; const int attackRange = player_node->getAttackRange() * 32; int x = px - attackRange; int y = py - attackRange; int w = 2 * attackRange + 32; int h = w; if (attackRange <= 32) { x -= 16; y -= 16; w += 32; h += 32; } if (userPalette) { graphics->setColor(userPalette->getColorWithAlpha( UserPalette::ATTACK_RANGE)); graphics->fillRectangle(gcn::Rectangle( x, y, w, h)); graphics->setColor(userPalette->getColorWithAlpha( UserPalette::ATTACK_RANGE_BORDER)); graphics->drawRectangle(gcn::Rectangle( x, y, w, h)); } } } } int MapLayer::getTileDrawWidth(Image *img, int endX, int &width) const { Image *img1 = img; int c = 0; if (!img1) { width = 0; return c; } width = img1->mBounds.w; for (int x = 1; x < endX; x++) { img ++; if (img != img1) break; c ++; if (img) width += img->mBounds.w; } return c; } SpecialLayer::SpecialLayer(int width, int height, bool drawSprites): mWidth(width), mHeight(height), mDrawSprites(drawSprites) { const int size = mWidth * mHeight; mTiles = new MapItem*[size]; std::fill_n(mTiles, size, static_cast<MapItem*>(nullptr)); } SpecialLayer::~SpecialLayer() { for (int f = 0; f < mWidth * mHeight; f ++) { delete mTiles[f]; mTiles[f] = nullptr; } delete [] mTiles; } MapItem* SpecialLayer::getTile(int x, int y) const { if (x < 0 || x >= mWidth || y < 0 || y >= mHeight) { return nullptr; } return mTiles[x + y * mWidth]; } void SpecialLayer::setTile(int x, int y, MapItem *item) { if (x < 0 || x >= mWidth || y < 0 || y >= mHeight) { return; } int idx = x + y * mWidth; delete mTiles[idx]; if (item) item->setPos(x, y); mTiles[idx] = item; } void SpecialLayer::setTile(int x, int y, int type) { if (x < 0 || x >= mWidth || y < 0 || y >= mHeight) { return; } int idx = x + y * mWidth; if (mTiles[idx]) mTiles[idx]->setType(type); else mTiles[idx] = new MapItem(type); mTiles[idx]->setPos(x, y); } void SpecialLayer::addRoad(Path road) { for (Path::const_iterator i = road.begin(), i_end = road.end(); i != i_end; ++i) { Position pos = (*i); MapItem *item = getTile(pos.x, pos.y); if (!item) { item = new MapItem(MapItem::ROAD); setTile(pos.x, pos.y, item); } else { item->setType(MapItem::ROAD); } } } void SpecialLayer::clean() { if (!mTiles) return; for (int f = 0; f < mWidth * mHeight; f ++) { MapItem *item = mTiles[f]; if (item) item->setType(MapItem::EMPTY); } } void SpecialLayer::draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY) { if (startX < 0) startX = 0; if (startY < 0) startY = 0; if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; for (int y = startY; y < endY; y++) { for (int x = startX; x < endX; x++) itemDraw(graphics, x, y, scrollX, scrollY); } } void SpecialLayer::itemDraw(Graphics *graphics, int x, int y, int scrollX, int scrollY) { MapItem *item = getTile(x, y); if (item) { const int px = x * 32 - scrollX; const int py = y * 32 - scrollY; item->draw(graphics, px, py, 32, 32); } } MapItem::MapItem(): mImage(nullptr), mComment(""), mName(""), mX(-1), mY(-1) { setType(EMPTY); } MapItem::MapItem(int type): mImage(nullptr), mComment(""), mName(""), mX(-1), mY(-1) { setType(type); } MapItem::MapItem(int type, std::string comment): mImage(nullptr), mComment(comment), mName(""), mX(-1), mY(-1) { setType(type); } MapItem::MapItem(int type, std::string comment, int x, int y): mImage(nullptr), mComment(comment), mName(""), mX(x), mY(y) { setType(type); } MapItem::~MapItem() { if (mImage) { mImage->decRef(); mImage = nullptr; } } void MapItem::setType(int type) { std::string name(""); mType = type; if (mImage) mImage->decRef(); switch (type) { case ARROW_UP: name = "graphics/sprites/arrow_up.gif"; break; case ARROW_DOWN: name = "graphics/sprites/arrow_down.gif"; break; case ARROW_LEFT: name = "graphics/sprites/arrow_left.gif"; break; case ARROW_RIGHT: name = "graphics/sprites/arrow_right.gif"; break; default: break; } if (name != "") { ResourceManager *resman = ResourceManager::getInstance(); mImage = resman->getImage(name); } else { mImage = nullptr; } } void MapItem::setPos(int x, int y) { mX = x; mY = y; } void MapItem::draw(Graphics *graphics, int x, int y, int dx, int dy) { if (mImage) graphics->drawImage(mImage, x, y); switch (mType) { case ROAD: case CROSS: graphics->setColor(userPalette->getColorWithAlpha( UserPalette::ROAD_POINT)); graphics->fillRectangle(gcn::Rectangle(x + dx / 3, y + dy / 3, dx / 3, dy / 3)); break; case HOME: { graphics->setColor(userPalette->getColorWithAlpha( UserPalette::HOME_PLACE)); graphics->fillRectangle(gcn::Rectangle( x, y, dx, dy)); graphics->setColor(userPalette->getColorWithAlpha( UserPalette::HOME_PLACE_BORDER)); graphics->drawRectangle(gcn::Rectangle( x, y, dx, dy)); break; } default: break; } if (!mName.empty() && mType != PORTAL && mType != EMPTY) { gcn::Font *font = gui->getFont(); if (font) { graphics->setColor(userPalette->getColor(UserPalette::BEING)); font->drawString(graphics, mName, x, y); } } } ObjectsLayer::ObjectsLayer(unsigned width, unsigned height) : mWidth(width), mHeight(height) { const unsigned size = width * height; mTiles = new MapObjectList*[size]; std::fill_n(mTiles, size, static_cast<MapObjectList*>(nullptr)); } ObjectsLayer::~ObjectsLayer() { const unsigned size = mWidth * mHeight; for (unsigned f = 0; f < size; f ++) delete mTiles[f]; delete [] mTiles; mTiles = nullptr; } void ObjectsLayer::addObject(std::string name, int type, unsigned x, unsigned y, unsigned dx, unsigned dy) { if (!mTiles) return; if (x + dx > mWidth) dx = mWidth - x; if (y + dy > mHeight) dy = mHeight - y; for (unsigned y1 = y; y1 < y + dy; y1 ++) { unsigned idx1 = x + y1 * mWidth; unsigned idx2 = idx1 + dx; for (unsigned i = idx1; i < idx2; i ++) { if (!mTiles[i]) mTiles[i] = new MapObjectList(); mTiles[i]->objects.push_back(MapObject(type, name)); } } } MapObjectList *ObjectsLayer::getAt(unsigned x, unsigned y) { if (x >= mWidth || y >= mHeight) return nullptr; return mTiles[x + y * mWidth]; } MapRowVertexes::~MapRowVertexes() { delete_all(images); images.clear(); }