Mana
Loading...
Searching...
No Matches
map.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "map.h"
23
24#include "actorspritemanager.h"
25#include "configuration.h"
26#include "graphics.h"
27#include "log.h"
28#include "particle.h"
29#include "simpleanimation.h"
30#include "tileset.h"
31
33#include "resources/image.h"
35
36#include "utils/time.h"
37
38#include "net/net.h"
39
40#include "utils/dtor.h"
41#include "utils/stringutils.h"
42
43#include <cassert>
44#include <queue>
45#include <climits>
46
51{
52 Location(int px, int py, MetaTile *ptile):
53 x(px), y(py), tile(ptile)
54 {}
55
59 bool operator< (const Location &loc) const
60 {
61 return tile->Fcost > loc.tile->Fcost;
62 }
63
64 int x, y;
66};
67
69 : mAnimation(std::move(animation))
70{
71}
72
74{
76
77 // exchange images
79 if (img != mLastImage)
80 {
81 for (auto &[layer, index] : mAffected)
82 {
83 layer->setTile(index, img);
84 }
85 mLastImage = img;
86 }
87}
88
89MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer,
90 Map *map):
91 mX(x), mY(y),
92 mWidth(width), mHeight(height),
93 mIsFringeLayer(isFringeLayer),
94 mMap(map)
95{
96 const int size = mWidth * mHeight;
97 mTiles = new Image*[size];
98 std::fill_n(mTiles, size, (Image*) nullptr);
99}
100
102{
103 delete[] mTiles;
104}
105
106void MapLayer::setTile(int x, int y, Image *img)
107{
108 setTile(x + y * mWidth, img);
109}
110
112 int startX, int startY,
113 int endX, int endY,
114 int scrollX, int scrollY,
115 const Actors &actors, int debugFlags) const
116{
117 startX -= mX;
118 startY -= mY;
119 endX -= mX;
120 endY -= mY;
121
122 if (startX < 0) startX = 0;
123 if (startY < 0) startY = 0;
124 if (endX > mWidth) endX = mWidth;
125 if (endY > mHeight) endY = mHeight;
126
127 auto ai = actors.begin();
128
129 int dx = (mX * mMap->getTileWidth()) - scrollX;
130 int dy = (mY * mMap->getTileHeight()) - scrollY + mMap->getTileHeight();
131
132 for (int y = startY; y < endY; y++)
133 {
134 int pixelY = y * mMap->getTileHeight();
135
136 // If drawing the fringe layer, make sure all actors above this row of
137 // tiles have been drawn
138 if (mIsFringeLayer)
139 {
140 while (ai != actors.end() && (*ai)->getDrawOrder()
141 <= y * mMap->getTileHeight())
142 {
143 (*ai)->draw(graphics, -scrollX, -scrollY);
144 ++ai;
145 }
146 }
147
148 if (!(debugFlags & Map::DEBUG_SPECIAL3))
149 {
150 const int py0 = pixelY + dy;
151
152 for (int x = startX; x < endX; x++)
153 {
154 if (Image *img = getTile(x, y))
155 {
156 if (!(debugFlags & (Map::DEBUG_SPECIAL1 | Map::DEBUG_SPECIAL2))
157 || img->getHeight() <= mMap->getTileHeight())
158 {
159 const int px = (x * mMap->getTileWidth()) + dx;
160 const int py = py0 - img->getHeight();
161 int width = 0;
162 int c = getTileDrawWidth(x, y, endX, width);
163 if (!c)
164 {
165 graphics->drawImage(img, px, py);
166 }
167 else
168 {
169 graphics->drawImagePattern(img, px, py,
170 width, img->getHeight());
171 }
172 x += c;
173 }
174 }
175 }
176 }
177 }
178
179 // Draw any remaining actors
180 if (mIsFringeLayer)
181 {
182 for (; ai != actors.end(); ++ai)
183 {
184 (*ai)->draw(graphics, -scrollX, -scrollY);
185 }
186 }
187}
188
189int MapLayer::getTileDrawWidth(int x1, int y1, int endX, int &width) const
190{
191 Image *img1 = getTile(x1, y1);
192 int c = 0;
193 width = img1->getWidth();
194
195 // Images that don't match the tile width can't be drawn as a pattern
196 if (width != mMap->getTileWidth())
197 return c;
198
199 for (int x = x1 + 1; x < endX; x++)
200 {
201 Image *img = getTile(x, y1);
202 if (img != img1)
203 break;
204 c++;
205 width += img->getWidth();
206 }
207 return c;
208}
209
210Map::Map(int width, int height, int tileWidth, int tileHeight):
211 mWidth(width), mHeight(height),
212 mTileWidth(tileWidth), mTileHeight(tileHeight),
213 mMaxTileHeight(tileHeight),
214 mMaxTileWidth(tileWidth),
215 mDebugFlags(DEBUG_NONE),
216 mOnClosedList(1), mOnOpenList(2),
217 mLastScrollX(0.0f), mLastScrollY(0.0f)
218{
219 const int size = mWidth * mHeight;
220
221 mMetaTiles = new MetaTile[size];
222 for (auto &occupation : mOccupation)
223 {
224 occupation = new unsigned[size];
225 memset(occupation, 0, size * sizeof(unsigned));
226 }
227}
228
230{
231 // delete metadata, layers, tilesets and overlays
232 delete[] mMetaTiles;
233 for (auto &occupation : mOccupation)
234 {
235 delete[] occupation;
236 }
239}
240
242{
244
245 auto addAmbientLayer = [=](const std::string &name, std::vector<AmbientLayer> &list)
246 {
247 if (auto img = resman->getImage(getProperty(name + "image")))
248 {
249 auto &ambientLayer = list.emplace_back(img);
250 ambientLayer.mParallax = getFloatProperty(name + "parallax");
251 ambientLayer.mSpeedX = getFloatProperty(name + "scrollX") / MILLISECONDS_IN_A_TICK;
252 ambientLayer.mSpeedY = getFloatProperty(name + "scrollY") / MILLISECONDS_IN_A_TICK;
253 ambientLayer.mMask = getIntProperty(name + "mask", 1);
254 ambientLayer.mKeepRatio = getBoolProperty(name + "keepratio");
255 }
256 };
257
258 // search for "foreground*" or "overlay*" (old term) in map properties
259 for (int i = 0; /* terminated by a break */; i++)
260 {
261 if (hasProperty("foreground" + toString(i) + "image"))
262 {
263 addAmbientLayer("foreground" + toString(i), mForegrounds);
264 }
265 else if (hasProperty("overlay" + toString(i) + "image"))
266 {
267 addAmbientLayer("overlay" + toString(i), mForegrounds);
268 }
269 else
270 {
271 break; // the FOR loop
272 }
273 }
274
275 // search for "background*" in map properties
276 for (int i = 0;
277 hasProperty("background" + toString(i) + "image");
278 i++)
279 {
280 addAmbientLayer("background" + toString(i), mBackgrounds);
281 }
282}
283
285{
286 mLayers.push_back(layer);
287}
288
290{
291 mTilesets.push_back(tileset);
292
293 if (tileset->getHeight() > mMaxTileHeight)
294 mMaxTileHeight = tileset->getHeight();
295 if (tileset->getWidth() > mMaxTileWidth)
296 mMaxTileWidth = tileset->getWidth();
297}
298
299void Map::update(int dt)
300{
301 // Update animated tiles
302 for (auto &[_, tileAnimation] : mTileAnimations)
303 {
304 tileAnimation.update(dt);
305 }
306}
307
308void Map::draw(Graphics *graphics, int scrollX, int scrollY)
309{
310 // Calculate range of tiles which are on-screen
311 int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1;
312 endPixelY += mMaxTileHeight - mTileHeight;
313 int startX = (scrollX - mMaxTileWidth + mTileWidth) / mTileWidth;
314 int startY = scrollY / mTileHeight;
315 int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth;
316 int endY = endPixelY / mTileHeight;
317
318 // Make sure actors are sorted ascending by Y-coordinate
319 // so that they overlap correctly
320 mActors.sort([] (const Actor *a, const Actor *b) {
321 return a->getDrawOrder() < b->getDrawOrder();
322 });
323
324 // update scrolling of all ambient layers
325 updateAmbientLayers(scrollX, scrollY);
326
327 // Draw backgrounds
330
331 // draw the game world
332 for (auto &layer : mLayers)
333 {
334 if ((layer->getMask() & mMask) == 0)
335 continue;
336
337 if (!layer->isFringeLayer() && (mDebugFlags & DEBUG_SPECIAL3))
338 continue;
339
340 layer->draw(graphics,
341 startX, startY, endX, endY,
342 scrollX, scrollY,
344
345 if (layer->isFringeLayer() && (mDebugFlags & (DEBUG_SPECIAL2 |
347 break;
348 }
349
350 // If the transparency hasn't been disabled,
352 {
353 // We draw beings with a lower opacity to make them visible
354 // even when covered by a wall or some other elements...
355 for (auto actor : mActors)
356 {
357 // For now, just draw actors with only one layer.
358 if (actor->drawnWhenBehind())
359 {
360 actor->setAlpha(0.3f);
361 actor->draw(graphics, -scrollX, -scrollY);
362 actor->setAlpha(1.0f);
363 }
364 }
365 }
366
369}
370
371void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY,
372 int debugFlags) const
373{
374 int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1;
375 int startX = scrollX / mTileWidth;
376 int startY = scrollY / mTileHeight;
377 int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth;
378 int endY = endPixelY / mTileHeight;
379
380 if (startX < 0) startX = 0;
381 if (startY < 0) startY = 0;
382 if (endX > mWidth) endX = mWidth;
383 if (endY > mHeight) endY = mHeight;
384
385 for (int y = startY; y < endY; y++)
386 {
387 for (int x = startX; x < endX; x++)
388 {
389
390 graphics->setColor(gcn::Color(0, 0, 0, 64));
391 if (debugFlags & DEBUG_GRID)
392 {
393 graphics->drawRectangle(gcn::Rectangle(
394 x * mTileWidth - scrollX,
395 y * mTileHeight - scrollY,
396 mTileWidth + 1, mTileHeight + 1));
397 }
398
399 if (!(debugFlags & DEBUG_COLLISION_TILES))
400 continue;
401
402 if (!getWalk(x, y, BLOCKMASK_WALL))
403 {
404 graphics->setColor(gcn::Color(0, 0, 200, 64));
405 graphics->fillRectangle(gcn::Rectangle(
406 x * mTileWidth - scrollX,
407 y * mTileHeight - scrollY,
409 }
410
411 if (!getWalk(x, y, BLOCKMASK_MONSTER))
412 {
413 graphics->setColor(gcn::Color(200, 0, 0, 64));
414 graphics->fillRectangle(gcn::Rectangle(
415 x * mTileWidth - scrollX,
416 y * mTileHeight - scrollY,
418 }
419
420 if (!getWalk(x, y, BLOCKMASK_CHARACTER))
421 {
422 graphics->setColor(gcn::Color(0, 200, 0, 64));
423 graphics->fillRectangle(gcn::Rectangle(
424 x * mTileWidth - scrollX,
425 y * mTileHeight - scrollY,
427 }
428 }
429 }
430}
431
432void Map::updateAmbientLayers(float scrollX, float scrollY)
433{
434 if (mLastScrollX == 0.0f && mLastScrollY == 0.0f)
435 {
436 // First call - initialisation
437 mLastScrollX = scrollX;
438 mLastScrollY = scrollY;
439 }
440
441 // Update Overlays
442 float dx = scrollX - mLastScrollX;
443 float dy = scrollY - mLastScrollY;
444
445 for (auto &background : mBackgrounds)
446 {
447 if ((background.mMask & mMask) == 0)
448 continue;
449
450 background.update(Time::deltaTimeMs(), dx, dy);
451 }
452 for (auto &foreground : mForegrounds)
453 {
454 if ((foreground.mMask & mMask) == 0)
455 continue;
456
457 foreground.update(Time::deltaTimeMs(), dx, dy);
458 }
459 mLastScrollX = scrollX;
460 mLastScrollY = scrollY;
461}
462
464 float scrollX, float scrollY, int detail)
465{
466 // Detail 0 = no ambient effects except background image
467 if (detail <= 0 && type != BACKGROUND_LAYERS) return;
468
469 // find out which layer list to draw
470 std::vector<AmbientLayer> *layers;
471 switch (type)
472 {
474 layers = &mForegrounds;
475 break;
477 layers = &mBackgrounds;
478 break;
479 default:
480 // New type of ambient layers added here without adding it
481 // to Map::drawAmbientLayers.
482 assert(false);
483 return;
484 }
485
486 // Draw overlays
487 for (auto &layer : *layers)
488 {
489 if ((layer.mMask & mMask) == 0)
490 continue;
491
492 layer.draw(graphics);
493
494 // Detail 1: only one overlay, higher: all overlays
495 if (detail == 1)
496 break;
497 }
498}
499
500Tileset *Map::getTilesetWithGid(unsigned gid) const
501{
502 Tileset *s = nullptr;
503 for (auto tileset : mTilesets)
504 {
505 if (tileset->getFirstGid() > gid)
506 break;
507 s = tileset;
508 }
509
510 return s;
511}
512
513void Map::blockTile(int x, int y, BlockType type)
514{
515 if (type == BLOCKTYPE_NONE || !contains(x, y))
516 return;
517
518 const int tileNum = x + y * mWidth;
519
520 if (mOccupation[type][tileNum] < UINT_MAX &&
521 (++mOccupation[type][tileNum]) > 0)
522 {
523 switch (type)
524 {
525 case BLOCKTYPE_WALL:
527 break;
530 break;
533 break;
534 default:
535 // Do nothing.
536 break;
537 }
538 }
539}
540
541bool Map::getWalk(int x, int y, unsigned char walkmask) const
542{
543 // You can't walk outside of the map
544 if (!contains(x, y))
545 return false;
546
547 // Check if the tile is walkable
548 return !(mMetaTiles[x + y * mWidth].blockmask & walkmask);
549}
550
551bool Map::occupied(int x, int y) const
552{
553 for (auto actor : actorSpriteManager->getAll())
554 {
555 if (actor->getTileX() == x && actor->getTileY() == y &&
556 actor->getType() != ActorSprite::FLOOR_ITEM)
557 return true;
558 }
559
560 return false;
561}
562
563Vector Map::getTileCenter(int x, int y) const
564{
565 Vector tileCenterPos;
566
567 tileCenterPos.x = x * mTileWidth + mTileWidth / 2;
568 tileCenterPos.y = y * mTileHeight + mTileHeight / 2;
569 return tileCenterPos;
570}
571
572bool Map::contains(int x, int y) const
573{
574 return x >= 0 && y >= 0 && x < mWidth && y < mHeight;
575}
576
577MetaTile *Map::getMetaTile(int x, int y) const
578{
579 return &mMetaTiles[x + y * mWidth];
580}
581
582Actors::iterator Map::addActor(Actor *actor)
583{
584 mActors.push_front(actor);
585 return mActors.begin();
586}
587
588void Map::removeActor(Actors::iterator iterator)
589{
590 mActors.erase(iterator);
591}
592
593std::string Map::getMusicFile() const
594{
595 return getProperty("music");
596}
597
598std::string Map::getName() const
599{
600 if (hasProperty("name"))
601 return getProperty("name");
602
603 return getProperty("mapname");
604}
605
606std::string Map::getFilename() const
607{
608 std::string fileName = getProperty("_filename");
609 int lastSlash = fileName.rfind("/") + 1;
610 int lastDot = fileName.rfind(".");
611
612 return fileName.substr(lastSlash, lastDot - lastSlash);
613}
614
615Position Map::checkNodeOffsets(int radius, unsigned char walkMask,
616 const Position &position) const
617{
618 // Pre-computing character's position in tiles
619 const int tx = position.x / mTileWidth;
620 const int ty = position.y / mTileHeight;
621
622 // Pre-computing character's position offsets.
623 int fx = position.x % mTileWidth;
624 int fy = position.y % mTileHeight;
625
626 // Compute the being radius:
627 // FIXME: Hande beings with more than 1/2 tile radius by not letting them
628 // go or spawn in too narrow places. The server will have to be aware
629 // of being's radius value (in tiles) to handle this gracefully.
630 if (radius > mTileWidth / 2) radius = mTileWidth / 2;
631 // set a default value if no value returned.
632 if (radius < 1) radius = mTileWidth / 3;
633
634 // We check diagonal first as they are more restrictive.
635 // Top-left border check
636 if (!getWalk(tx - 1, ty - 1, walkMask)
637 && fy < radius && fx < radius)
638 {
639 fx = fy = radius;
640 }
641 // Top-right border check
642 if (!getWalk(tx + 1, ty - 1, walkMask)
643 && (fy < radius) && fx > (mTileWidth - radius))
644 {
645 fx = mTileWidth - radius;
646 fy = radius;
647 }
648 // Bottom-left border check
649 if (!getWalk(tx - 1, ty + 1, walkMask)
650 && fy > (mTileHeight - radius) && fx < radius)
651 {
652 fx = radius;
653 fy = mTileHeight - radius;
654 }
655 // Bottom-right border check
656 if (!getWalk(tx + 1, ty + 1, walkMask)
657 && fy > (mTileHeight - radius) && fx > (mTileWidth - radius))
658 {
659 fx = mTileWidth - radius;
660 fy = mTileHeight - radius;
661 }
662
663 // Fix coordinates so that the player does not seem to dig into walls.
664 if (fx > (mTileWidth - radius) && !getWalk(tx + 1, ty, walkMask))
665 fx = mTileWidth - radius;
666 else if (fx < radius && !getWalk(tx - 1, ty, walkMask))
667 fx = radius;
668 else if (fy > (mTileHeight - radius) && !getWalk(tx, ty + 1, walkMask))
669 fy = mTileHeight - radius;
670 else if (fy < radius && !getWalk(tx, ty - 1, walkMask))
671 fy = radius;
672
673 return Position(tx * mTileWidth + fx, ty * mTileHeight + fy);
674}
675
676Path Map::findTilePath(int startPixelX, int startPixelY, int endPixelX,
677 int endPixelY, unsigned char walkMask, int maxCost)
678{
679 Path myPath = findPath(startPixelX / mTileWidth, startPixelY / mTileHeight,
680 endPixelX / mTileWidth, endPixelY / mTileHeight,
681 walkMask, maxCost);
682
683 // Don't compute empty coordinates.
684 if (myPath.empty())
685 return myPath;
686
687 // Convert the map path to pixels from the tile position
688 auto it = myPath.begin();
689 while (it != myPath.end())
690 {
691 // The new pixel position will be the tile center.
692 *it = Position(it->x * mTileWidth + mTileWidth / 2,
693 it->y * mTileHeight + mTileHeight / 2);
694 ++it;
695 }
696
697 return myPath;
698}
699
700Path Map::findPixelPath(int startPixelX, int startPixelY, int endPixelX,
701 int endPixelY,
702 int radius, unsigned char walkMask, int maxCost)
703{
704 Path myPath = findPath(startPixelX / mTileWidth, startPixelY / mTileHeight,
705 endPixelX / mTileWidth, endPixelY / mTileHeight,
706 walkMask, maxCost);
707
708 // Don't compute empty coordinates.
709 if (myPath.empty())
710 return myPath;
711
712 // Find the starting offset
713 float startOffsetX = (startPixelX % mTileWidth);
714 float startOffsetY = (startPixelY % mTileHeight);
715
716 // Find the ending offset
717 float endOffsetX = (endPixelX % mTileWidth);
718 float endOffsetY = (endPixelY % mTileHeight);
719
720 // Find the distance, and divide it by the number of steps
721 int changeX = (int)((endOffsetX - startOffsetX) / myPath.size());
722 int changeY = (int)((endOffsetY - startOffsetY) / myPath.size());
723
724 // Convert the map path to pixels over tiles
725 // And add interpolation between the starting and ending offsets
726 auto it = myPath.begin();
727 int i = 0;
728 while (it != myPath.end())
729 {
730 // A position that is valid on the start and end tile is not
731 // necessarily valid on all the tiles in between, so check the offsets.
732 *it = checkNodeOffsets(radius, walkMask,
733 it->x * mTileWidth + startOffsetX + changeX * i,
734 it->y * mTileHeight + startOffsetY + changeY * i);
735 ++i;
736 ++it;
737 }
738
739 // Remove the last path node, as it's more clever to go to the destination.
740 // It also permit to avoid zigzag at the end of the path,
741 // especially with mouse.
742 Position destination = checkNodeOffsets(radius, walkMask,
743 endPixelX, endPixelY);
744 myPath.pop_back();
745 myPath.push_back(destination);
746
747 return myPath;
748}
749
750Path Map::findPath(int startX, int startY, int destX, int destY,
751 unsigned char walkmask, int maxCost)
752{
753 // The basic walking cost of a tile.
754 const int basicCost = 100;
755
756 // Path to be built up (empty by default)
757 Path path;
758
759 // Declare open list, a list with open tiles sorted on F cost
760 std::priority_queue<Location> openList;
761
762 // Return when destination not walkable
763 if (!getWalk(destX, destY, walkmask))
764 return path;
765
766 // Reset starting tile's G cost to 0
767 MetaTile *startTile = getMetaTile(startX, startY);
768 startTile->Gcost = 0;
769
770 // Add the start point to the open list
771 openList.emplace(startX, startY, startTile);
772
773 bool foundPath = false;
774
775 // Keep trying new open tiles until no more tiles to try or target found
776 while (!openList.empty() && !foundPath)
777 {
778 // Take the location with the lowest F cost from the open list.
779 Location curr = openList.top();
780 openList.pop();
781
782 // If the tile is already on the closed list, this means it has already
783 // been processed with a shorter path to the start point (lower G cost)
784 if (curr.tile->whichList == mOnClosedList)
785 continue;
786
787 // Put the current tile on the closed list
789
790 // Check the adjacent tiles
791 for (int dy = -1; dy <= 1; dy++)
792 {
793 for (int dx = -1; dx <= 1; dx++)
794 {
795 // Calculate location of tile to check
796 const int x = curr.x + dx;
797 const int y = curr.y + dy;
798
799 // Skip if if we're checking the same tile we're leaving from,
800 // or if the new location falls outside of the map boundaries
801 if ((dx == 0 && dy == 0) || !contains(x, y))
802 continue;
803
804 MetaTile *newTile = getMetaTile(x, y);
805
806 // Skip if the tile is on the closed list or is not walkable
807 // unless its the destination tile
808 if (newTile->whichList == mOnClosedList ||
809 ((newTile->blockmask & walkmask)
810 && !(x == destX && y == destY)))
811 {
812 continue;
813 }
814
815 // When taking a diagonal step, verify that we can skip the
816 // corner.
817 if (dx != 0 && dy != 0)
818 {
819 MetaTile *t1 = getMetaTile(curr.x, curr.y + dy);
820 MetaTile *t2 = getMetaTile(curr.x + dx, curr.y);
821
822 if ((t1->blockmask | t2->blockmask) & walkmask)
823 continue;
824 }
825
826 // Calculate G cost for this route, ~sqrt(2) for moving diagonal
827 int Gcost = curr.tile->Gcost +
828 (dx == 0 || dy == 0 ? basicCost : basicCost * 362 / 256);
829
830 /* Demote an arbitrary direction to speed pathfinding by
831 adding a defect (TODO: change depending on the desired
832 visual effect, e.g. a cross-product defect toward
833 destination).
834 Important: as long as the total defect along any path is
835 less than the basicCost, the pathfinder will still find one
836 of the shortest paths! */
837 if (dx == 0 || dy == 0)
838 {
839 // Demote horizontal and vertical directions, so that two
840 // consecutive directions cannot have the same Fcost.
841 ++Gcost;
842 }
843
844 // It costs extra to walk through a being (needs to be enough
845 // to make it more attractive to walk around).
846 // N.B.: Specific to TmwAthena for now.
848 occupied(x, y))
849 {
850 Gcost += 3 * basicCost;
851 }
852
853 // Skip if Gcost becomes too much
854 // Warning: probably not entirely accurate
855 if (Gcost > maxCost * basicCost)
856 {
857 continue;
858 }
859
860 if (newTile->whichList != mOnOpenList)
861 {
862 // Found a new tile (not on open nor on closed list)
863
864 /* Update Hcost of the new tile. The pathfinder does not
865 work reliably if the heuristic cost is higher than the
866 real cost. In particular, using Manhattan distance is
867 forbidden here. */
868 int dx = std::abs(x - destX);
869 int dy = std::abs(y - destY);
870 newTile->Hcost = std::abs(dx - dy) * basicCost +
871 std::min(dx, dy) * (basicCost * 362 / 256);
872
873 // Set the current tile as the parent of the new tile
874 newTile->parentX = curr.x;
875 newTile->parentY = curr.y;
876
877 // Update Gcost and Fcost of new tile
878 newTile->Gcost = Gcost;
879 newTile->Fcost = Gcost + newTile->Hcost;
880
881 if (x != destX || y != destY)
882 {
883 // Add this tile to the open list
884 newTile->whichList = mOnOpenList;
885 openList.emplace(x, y, newTile);
886 }
887 else
888 {
889 // Target location was found
890 foundPath = true;
891 }
892 }
893 else if (Gcost < newTile->Gcost)
894 {
895 // Found a shorter route.
896 // Update Gcost and Fcost of the new tile
897 newTile->Gcost = Gcost;
898 newTile->Fcost = Gcost + newTile->Hcost;
899
900 // Set the current tile as the parent of the new tile
901 newTile->parentX = curr.x;
902 newTile->parentY = curr.y;
903
904 // Add this tile to the open list (it's already
905 // there, but this instance has a lower F score)
906 openList.emplace(x, y, newTile);
907 }
908 }
909 }
910 }
911
912 // Two new values to indicate whether a tile is on the open or closed list,
913 // this way we don't have to clear all the values between each pathfinding.
914 if (mOnOpenList > UINT_MAX - 2)
915 {
916 // We reset the list memebers value.
917 mOnClosedList = 1;
918 mOnOpenList = 2;
919
920 // Clean up the metaTiles
921 const int size = mWidth * mHeight;
922 for (int i = 0; i < size; ++i)
923 mMetaTiles[i].whichList = 0;
924 }
925 else
926 {
927 mOnClosedList += 2;
928 mOnOpenList += 2;
929 }
930
931 // If a path has been found, iterate backwards using the parent locations
932 // to extract it.
933 if (foundPath)
934 {
935 int pathX = destX;
936 int pathY = destY;
937
938 while (pathX != startX || pathY != startY)
939 {
940 // Add the new path node to the start of the path list
941 path.emplace_front(pathX, pathY);
942
943 // Find out the next parent
944 MetaTile *tile = getMetaTile(pathX, pathY);
945 pathX = tile->parentX;
946 pathY = tile->parentY;
947 }
948 }
949
950 return path;
951}
952
953void Map::addParticleEffect(const std::string &effectFile, int x, int y, int w,
954 int h)
955{
956 ParticleEffectData &newEffect = particleEffects.emplace_back();
957 newEffect.file = effectFile;
958 newEffect.x = x;
959 newEffect.y = y;
960 newEffect.w = w;
961 newEffect.h = h;
962}
963
965{
967 {
968 for (auto &particleEffect : particleEffects)
969 {
970 Particle *p = particleEngine->addEffect(particleEffect.file,
971 particleEffect.x,
972 particleEffect.y);
973
974 if (p && particleEffect.w > 0 && particleEffect.h > 0)
975 {
976 p->adjustEmitterSize(particleEffect.w, particleEffect.h);
977 }
978 }
979 }
980}
981
982void Map::addAnimation(int gid, TileAnimation animation)
983{
984 auto const [_, inserted] = mTileAnimations.try_emplace(gid, std::move(animation));
985 if (!inserted)
986 {
987 Log::warn("Duplicate tile animation for gid %d", gid);
988 }
989}
990
992{
993 auto i = mTileAnimations.find(gid);
994 return i == mTileAnimations.end() ? nullptr : &i->second;
995}
996
997void Map::setMask(int mask)
998{
999 mMask = mask;
1000}
std::list< Actor * > Actors
Definition actor.h:32
ActorSpriteManager * actorSpriteManager
Definition game.cpp:110
const ActorSprites & getAll() const
Returns the whole list of beings.
Definition actor.h:35
virtual int getDrawOrder() const
Returns the pixel Y coordinate that the actor should be drawn at.
Definition actor.h:77
An animation consists of several frames, each with their own delay and offset.
Definition animation.h:47
A central point of control for graphics.
Definition graphics.h:78
virtual void drawImagePattern(const Image *image, int x, int y, int w, int h)
Definition graphics.cpp:99
int getHeight() const
Returns the logical height of the screen.
Definition graphics.h:226
void setColor(const gcn::Color &color) override
Definition graphics.h:255
bool drawImage(const Image *image, int x, int y)
Blits an image onto the screen.
Definition graphics.cpp:36
int getWidth() const
Returns the logical width of the screen.
Definition graphics.h:221
int getWidth() const
Returns the width of the images in the image set.
Definition imageset.h:49
int getHeight() const
Returns the height of the images in the image set.
Definition imageset.h:54
Defines a class for loading and storing images.
Definition image.h:45
int getWidth() const
Returns the width of the image.
Definition image.h:83
static bool SDLisTransparencyDisabled()
Definition image.h:125
static bool useOpenGL()
Tells if the system is using OpenGL or SDL.
Definition image.cpp:167
A map layer.
Definition map.h:81
Image * getTile(int x, int y) const
Get tile image, with x and y in layer coordinates.
Definition map.h:108
int mHeight
Definition map.h:136
bool mIsFringeLayer
Whether the actors are drawn.
Definition map.h:138
void setTile(int x, int y, Image *img)
Set tile image, with x and y in layer coordinates.
Definition map.cpp:106
int mX
Definition map.h:135
MapLayer(int x, int y, int width, int height, bool isFringeLayer, Map *map)
Constructor, taking layer origin, size and whether this layer is the fringe layer.
Definition map.cpp:89
Image ** mTiles
Definition map.h:139
int mWidth
Definition map.h:136
Map * mMap
Definition map.h:140
~MapLayer()
Definition map.cpp:101
int getTileDrawWidth(int x1, int y1, int endX, int &width) const
Definition map.cpp:189
int mY
Definition map.h:135
void draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, const Actors &actors, int debugFlags) const
Draws this layer to the given graphics context.
Definition map.cpp:111
A tile map.
Definition map.h:147
std::string getMusicFile() const
Definition map.cpp:593
Map(int width, int height, int tileWidth, int tileHeight)
Constructor, taking map and tile size as parameters.
Definition map.cpp:210
int mHeight
Definition map.h:388
Path findPixelPath(int startPixelX, int startPixelY, int destPixelX, int destPixelY, int radius, unsigned char walkmask, int maxCost=20)
Find a pixel path from one location to the next using free offsets.
Definition map.cpp:700
TileAnimation * getAnimationForGid(int gid)
Gets the tile animation for a specific gid.
Definition map.cpp:991
int mMaxTileHeight
Definition map.h:390
void update(int dt)
Updates animations.
Definition map.cpp:299
MetaTile * mMetaTiles
Definition map.h:391
std::vector< AmbientLayer > mBackgrounds
Definition map.h:403
@ BLOCKMASK_CHARACTER
Definition map.h:161
@ BLOCKMASK_MONSTER
Definition map.h:162
@ BLOCKMASK_WALL
Definition map.h:160
void setMask(int mask)
Definition map.cpp:997
int mDebugFlags
Definition map.h:397
std::vector< MapLayer * > mLayers
Definition map.h:392
void draw(Graphics *graphics, int scrollX, int scrollY)
Draws the map to the given graphics output.
Definition map.cpp:308
float mLastScrollY
Definition map.h:406
void initializeParticleEffects(Particle *particleEngine)
Initializes all added particle effects.
Definition map.cpp:964
unsigned mOnOpenList
Definition map.h:400
int getTileHeight() const
Returns the tile height used by this map.
Definition map.h:271
void addLayer(MapLayer *layer)
Adds a layer to this map.
Definition map.cpp:284
void removeActor(Actors::iterator iterator)
Removes an actor from the map.
Definition map.cpp:588
Actors mActors
Definition map.h:394
std::string getName() const
Definition map.cpp:598
Path findPath(int startX, int startY, int destX, int destY, unsigned char walkmask, int maxCost=20)
Find a path from one location to the next in tile coordinates.
Definition map.cpp:750
void addAnimation(int gid, TileAnimation animation)
Adds a tile animation to the map.
Definition map.cpp:982
float mLastScrollX
Definition map.h:405
void updateAmbientLayers(float scrollX, float scrollY)
Updates scrolling of ambient layers.
Definition map.cpp:432
std::string getFilename() const
Gives the map id based on filepath (ex: 009-1)
Definition map.cpp:606
LayerType
Definition map.h:362
@ FOREGROUND_LAYERS
Definition map.h:363
@ BACKGROUND_LAYERS
Definition map.h:364
int mMask
Definition map.h:421
MetaTile * getMetaTile(int x, int y) const
Get tile reference.
Definition map.cpp:577
std::vector< Tileset * > mTilesets
Definition map.h:393
BlockType
Definition map.h:150
@ BLOCKTYPE_WALL
Definition map.h:152
@ BLOCKTYPE_CHARACTER
Definition map.h:153
@ BLOCKTYPE_MONSTER
Definition map.h:154
@ BLOCKTYPE_NONE
Definition map.h:151
int mTileHeight
Definition map.h:389
void initializeAmbientLayers()
Initialize ambient layers.
Definition map.cpp:241
void drawAmbientLayers(Graphics *graphics, LayerType type, float scrollX, float scrollY, int detail)
Draws the foreground or background layers to the given graphics output.
Definition map.cpp:463
int getTileWidth() const
Returns the tile width of this map.
Definition map.h:265
void drawCollision(Graphics *graphics, int scrollX, int scrollY, int debugFlags) const
Visualizes collision layer for debugging.
Definition map.cpp:371
bool occupied(int x, int y) const
Tells whether a tile is occupied by a being.
Definition map.cpp:551
void blockTile(int x, int y, BlockType type)
Marks a tile as occupied.
Definition map.cpp:513
std::vector< AmbientLayer > mForegrounds
Definition map.h:404
unsigned mOnClosedList
Definition map.h:400
Tileset * getTilesetWithGid(unsigned gid) const
Finds the tile set that a tile with the given global id is part of.
Definition map.cpp:500
std::list< ParticleEffectData > particleEffects
Definition map.h:417
Position checkNodeOffsets(int radius, unsigned char walkMask, const Position &position) const
Check the current position against surrounding blocking tiles, and correct the position offset within...
Definition map.cpp:615
bool contains(int x, int y) const
Tells whether the given coordinates fall within the map boundaries.
Definition map.cpp:572
~Map() override
Definition map.cpp:229
Vector getTileCenter(int x, int y) const
Returns the tile center position in pixel coordinates.
Definition map.cpp:563
std::map< int, TileAnimation > mTileAnimations
Definition map.h:419
int mWidth
Definition map.h:388
unsigned * mOccupation[NB_BLOCKTYPES]
Blockmasks for different entities.
Definition map.h:386
void addTileset(Tileset *tileset)
Adds a tileset to this map.
Definition map.cpp:289
int mMaxTileWidth
Definition map.h:390
bool getWalk(int x, int y, unsigned char walkmask=BLOCKMASK_WALL) const
Gets walkability for a tile with a blocking bitmask.
Definition map.cpp:541
int mTileWidth
Definition map.h:389
Path findTilePath(int startPixelX, int startPixelY, int endPixelX, int endPixelY, unsigned char walkMask, int maxCost=20)
Find a tile-centered path in pixel coordinates from one location to the next.
Definition map.cpp:676
void addParticleEffect(const std::string &effectFile, int x, int y, int w=0, int h=0)
Adds a particle effect.
Definition map.cpp:953
@ DEBUG_SPECIAL3
Definition map.h:178
@ DEBUG_GRID
Definition map.h:169
@ DEBUG_SPECIAL2
Definition map.h:177
@ DEBUG_COLLISION_TILES
Definition map.h:170
@ DEBUG_SPECIAL1
Definition map.h:176
Actors::iterator addActor(Actor *actor)
Adds an actor to the map.
Definition map.cpp:582
A particle spawned by a ParticleEmitter.
Definition particle.h:42
Particle * addEffect(const std::string &particleEffectFile, int pixelX, int pixelY, int rotation=0)
Creates a child particle that hosts some emitters described in the particleEffectFile.
Definition particle.cpp:249
void adjustEmitterSize(int w, int h)
Changes the size of the emitters so that the effect fills a rectangle of this size.
Definition particle.cpp:401
std::string getProperty(const std::string &name, const std::string &def=std::string()) const
Get a map property.
Definition properties.h:44
bool hasProperty(const std::string &name) const
Returns whether a certain property is available.
Definition properties.h:122
float getIntProperty(const std::string &name, int def=0) const
Gets a map property as an int.
Definition properties.h:80
float getFloatProperty(const std::string &name, float def=0.0f) const
Gets a map property as a float.
Definition properties.h:59
bool getBoolProperty(const std::string &name, bool def=false) const
Gets a map property as a boolean.
Definition properties.h:101
A class for loading and managing resources.
static ResourceManager * getInstance()
Returns an instance of the class, creating one if it does not already exist.
ResourceRef< Image > getImage(const std::string &idPath)
Loads the Image resource found at the given identifier path.
Image * getCurrentImage() const
void update(int dt)
Animation cycle of a tile image which changes the map accordingly.
Definition map.h:61
std::vector< std::pair< MapLayer *, int > > mAffected
Definition map.h:71
void update(int dt)
Definition map.cpp:73
Image * mLastImage
Definition map.h:73
TileAnimation(Animation animation)
Definition map.cpp:68
SimpleAnimation mAnimation
Definition map.h:72
A tileset, which is basically just an image set but it stores a firstgid.
Definition tileset.h:30
Vector class.
Definition vector.h:33
float y
Definition vector.h:172
float x
Definition vector.h:171
Config config
Global settings (config.xml)
Definition client.cpp:97
Graphics * graphics
Definition client.cpp:104
void delete_all(Container &c)
Definition dtor.h:46
Particle * particleEngine
Definition game.cpp:113
#define _(s)
Definition gettext.h:38
void warn(const char *log_text,...) LOG_PRINTF_ATTR
ServerType getNetworkType()
Definition net.cpp:200
unsigned deltaTimeMs()
The time in milliseconds since the last frame, but never more than 1000.
Definition time.cpp:39
std::list< Position > Path
Definition position.h:40
std::string toString(const T &arg)
Converts the given value to a string using std::stringstream.
Definition stringutils.h:68
int overlayDetail
bool particleEffects
A location on a tile map.
Definition map.cpp:51
int y
Definition map.cpp:64
Location(int px, int py, MetaTile *ptile)
Definition map.cpp:52
MetaTile * tile
Definition map.cpp:65
bool operator<(const Location &loc) const
Comparison operator.
Definition map.cpp:59
int x
Definition map.cpp:64
std::string file
Definition map.h:411
A meta tile stores additional information about a location on a tile map.
Definition map.h:46
int Fcost
Estimation of total path cost.
Definition map.h:48
int parentY
Y coordinate of parent tile.
Definition map.h:53
unsigned char blockmask
Blocking properties of this tile.
Definition map.h:54
int Hcost
Estimated cost to goal.
Definition map.h:50
int Gcost
Cost from start to this location.
Definition map.h:49
int parentX
X coordinate of parent tile.
Definition map.h:52
unsigned whichList
No list, open list or closed list.
Definition map.h:51
A position along a being's path.
Definition position.h:31
int y
Definition position.h:37
int x
Definition position.h:36