Otclient  14/8/2020
minimap.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 
24 #include "minimap.h"
25 #include "tile.h"
26 
34 #include <zlib.h>
35 
37 
39 {
40  m_tiles.fill(MinimapTile());
41  m_texture.reset();
42  m_mustUpdate = false;
43 }
44 
46 {
47  if(!m_mustUpdate)
48  return;
49 
51 
52  bool shouldDraw = false;
53  for(int x=0;x<MMBLOCK_SIZE;++x) {
54  for(int y=0;y<MMBLOCK_SIZE;++y) {
55  uint8 c = getTile(x, y).color;
56  uint32 col;
57  if(c != 255) {
58  col = Color::from8bit(c).rgba();
59  shouldDraw = true;
60  } else
61  col = Color::alpha.rgba();
62  image->setPixel(x, y, col);
63  }
64  }
65 
66  if(shouldDraw) {
67  if(!m_texture) {
68  m_texture = TexturePtr(new Texture(image, true));
69  } else {
70  m_texture->uploadPixels(image, true);
71  }
72  } else
73  m_texture.reset();
74 
75  m_mustUpdate = false;
76 }
77 
78 void MinimapBlock::updateTile(int x, int y, const MinimapTile& tile)
79 {
80  if(m_tiles[getTileIndex(x,y)].color != tile.color)
81  m_mustUpdate = true;
82 
83  m_tiles[getTileIndex(x,y)] = tile;
84 }
85 
87 {
88 }
89 
91 {
92  clean();
93 }
94 
96 {
97  for(int i=0;i<=Otc::MAX_Z;++i)
98  m_tileBlocks[i].clear();
99 }
100 
101 void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scale, const Color& color)
102 {
103  if(screenRect.isEmpty())
104  return;
105 
106  Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
107  g_painter->saveState();
108  g_painter->setColor(color);
109  g_painter->drawFilledRect(screenRect);
111  g_painter->setClipRect(screenRect);
112 
113  if(MMBLOCK_SIZE*scale <= 1 || !mapCenter.isMapPosition()) {
115  return;
116  }
117 
118  Point blockOff = getBlockOffset(mapRect.topLeft());
119  Point off = Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint())/2;
120  Point start = screenRect.topLeft() -(mapRect.topLeft() - blockOff)*scale - off;
121 
122  for(int y = blockOff.y, ys = start.y;ys<screenRect.bottom();y += MMBLOCK_SIZE, ys += MMBLOCK_SIZE*scale) {
123  if(y < 0 || y >= 65536)
124  continue;
125 
126  for(int x = blockOff.x, xs = start.x;xs<screenRect.right();x += MMBLOCK_SIZE, xs += MMBLOCK_SIZE*scale) {
127  if(x < 0 || x >= 65536)
128  continue;
129 
130  Position blockPos(x, y, mapCenter.z);
131  if(!hasBlock(blockPos))
132  continue;
133 
134  MinimapBlock& block = getBlock(Position(x, y, mapCenter.z));
135  block.update();
136 
137  const TexturePtr& tex = block.getTexture();
138  if(tex) {
139  Rect src(0, 0, MMBLOCK_SIZE, MMBLOCK_SIZE);
140  Rect dest(Point(xs,ys), src.size() * scale);
141 
142  tex->setSmooth(scale < 1.0f);
143  g_painter->drawTexturedRect(dest, tex, src);
144  }
145  //g_painter->drawBoundingRect(Rect(xs,ys, MMBLOCK_SIZE * scale, MMBLOCK_SIZE * scale));
146  }
147  }
148 
150 }
151 
152 Point Minimap::getTilePoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale)
153 {
154  if(screenRect.isEmpty() || pos.z != mapCenter.z)
155  return Point(-1,-1);
156 
157  Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
158  Point off = Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint())/2;
159  Point posoff = (Point(pos.x,pos.y) - mapRect.topLeft())*scale;
160  return posoff + screenRect.topLeft() - off + (Point(1,1)*scale)/2;
161 }
162 
163 Position Minimap::getTilePosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale)
164 {
165  if(screenRect.isEmpty())
166  return Position();
167 
168  Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
169  Point off = Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint())/2;
170  Point pos2d = (point - screenRect.topLeft() + off)/scale + mapRect.topLeft();
171  return Position(pos2d.x, pos2d.y, mapCenter.z);
172 }
173 
174 Rect Minimap::getTileRect(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale)
175 {
176  if(screenRect.isEmpty() || pos.z != mapCenter.z)
177  return Rect();
178 
179  int tileSize = 32 * scale;
180  Rect tileRect(0,0,tileSize, tileSize);
181  tileRect.moveCenter(getTilePoint(pos, screenRect, mapCenter, scale));
182  return tileRect;
183 }
184 
185 Rect Minimap::calcMapRect(const Rect& screenRect, const Position& mapCenter, float scale)
186 {
187  int w = screenRect.width() / scale, h = std::ceil(screenRect.height() / scale);
188  Rect mapRect(0,0,w,h);
189  mapRect.moveCenter(Point(mapCenter.x, mapCenter.y));
190  return mapRect;
191 }
192 
193 void Minimap::updateTile(const Position& pos, const TilePtr& tile)
194 {
195  MinimapTile minimapTile;
196  if(tile) {
197  minimapTile.color = tile->getMinimapColorByte();
198  minimapTile.flags |= MinimapTileWasSeen;
199  if(!tile->isWalkable(true))
200  minimapTile.flags |= MinimapTileNotWalkable;
201  if(!tile->isPathable())
202  minimapTile.flags |= MinimapTileNotPathable;
203  minimapTile.speed = std::min<int>((int)std::ceil(tile->getGroundSpeed() / 10.0f), 255);
204  }
205 
206  if(minimapTile != MinimapTile()) {
207  MinimapBlock& block = getBlock(pos);
208  Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
209  block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile);
210  block.justSaw();
211  }
212 }
213 
215 {
216  static MinimapTile nulltile;
217  if(pos.z <= Otc::MAX_Z && hasBlock(pos)) {
218  MinimapBlock& block = getBlock(pos);
219  Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
220  return block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
221  }
222  return nulltile;
223 }
224 
225 bool Minimap::loadImage(const std::string& fileName, const Position& topLeft, float colorFactor)
226 {
227  if(colorFactor <= 0.01f)
228  colorFactor = 1.0f;
229 
230  try {
231  ImagePtr image = Image::load(fileName);
232 
233  uint8 waterc = Color::to8bit(std::string("#3300cc"));
234 
235  // non pathable colors
236  Color nonPathableColors[] = {
237  std::string("#ffff00"), // yellow
238  };
239 
240  // non walkable colors
241  Color nonWalkableColors[] = {
242  std::string("#000000"), // oil, black
243  std::string("#006600"), // trees, dark green
244  std::string("#ff3300"), // walls, red
245  std::string("#666666"), // mountain, grey
246  std::string("#ff6600"), // lava, orange
247  std::string("#00ff00"), // positon
248  std::string("#ccffff"), // ice, very light blue
249  };
250 
251  for(int y=0;y<image->getHeight();++y) {
252  for(int x=0;x<image->getWidth();++x) {
253  Color color = *(uint32*)image->getPixel(x,y);
254  uint8 c = Color::to8bit(color * colorFactor);
255  int flags = 0;
256 
257  if(c == waterc || color.a() == 0) {
258  flags |= MinimapTileNotWalkable;
259  c = 255; // alpha
260  }
261 
262  if(flags != 0) {
263  for(Color &col : nonWalkableColors) {
264  if(col == color) {
265  flags |= MinimapTileNotWalkable;
266  break;
267  }
268  }
269  }
270 
271  if(flags != 0) {
272  for(Color &col : nonPathableColors) {
273  if(col == color) {
274  flags |= MinimapTileNotPathable;
275  break;
276  }
277  }
278  }
279 
280  if(c == 255)
281  continue;
282 
283  Position pos(topLeft.x + x, topLeft.y + y, topLeft.z);
284  MinimapBlock& block = getBlock(pos);
285  Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
286  MinimapTile& tile = block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
287  if(!(tile.flags & MinimapTileWasSeen)) {
288  tile.color = c;
289  tile.flags = flags;
290  block.mustUpdate();
291  }
292  }
293  }
294  return true;
295  } catch(stdext::exception& e) {
296  g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
297  return false;
298  }
299 }
300 
301 void Minimap::saveImage(const std::string& fileName, const Rect& mapRect)
302 {
303  //TODO
304 }
305 
306 bool Minimap::loadOtmm(const std::string& fileName)
307 {
308  try {
309  FileStreamPtr fin = g_resources.openFile(fileName);
310  if(!fin)
311  stdext::throw_exception("unable to open file");
312 
313  fin->cache();
314 
315  uint32 signature = fin->getU32();
316  if(signature != OTMM_SIGNATURE)
317  stdext::throw_exception("invalid OTMM file");
318 
319  uint16 start = fin->getU16();
320  uint16 version = fin->getU16();
321  fin->getU32(); // flags
322 
323  switch(version) {
324  case 1: {
325  fin->getString(); // description
326  break;
327  }
328  default:
329  stdext::throw_exception("OTMM version not supported");
330  }
331 
332  fin->seek(start);
333 
334  uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
335  std::vector<uchar> compressBuffer(compressBound(blockSize));
336  std::vector<uchar> decompressBuffer(blockSize);
337 
338  while(true) {
339  Position pos;
340  pos.x = fin->getU16();
341  pos.y = fin->getU16();
342  pos.z = fin->getU8();
343 
344  // end of file or file is corrupted
345  if(!pos.isValid() || pos.z >= Otc::MAX_Z+1)
346  break;
347 
348  MinimapBlock& block = getBlock(pos);
349  ulong len = fin->getU16();
350  ulong destLen = blockSize;
351  fin->read(compressBuffer.data(), len);
352  int ret = uncompress(decompressBuffer.data(), &destLen, compressBuffer.data(), len);
353  if(ret != Z_OK || destLen != blockSize)
354  break;
355 
356  memcpy((uchar*)&block.getTiles(), decompressBuffer.data(), blockSize);
357  block.mustUpdate();
358  block.justSaw();
359  }
360 
361  fin->close();
362  return true;
363  } catch(stdext::exception& e) {
364  g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
365  return false;
366  }
367 }
368 
369 void Minimap::saveOtmm(const std::string& fileName)
370 {
371  try {
372  stdext::timer saveTimer;
373 
374  FileStreamPtr fin = g_resources.createFile(fileName);
375  fin->cache();
376 
377  //TODO: compression flag with zlib
378  uint32 flags = 0;
379 
380  // header
381  fin->addU32(OTMM_SIGNATURE);
382  fin->addU16(0); // data start, will be overwritten later
383  fin->addU16(OTMM_VERSION);
384  fin->addU32(flags);
385 
386  // version 1 header
387  fin->addString("OTMM 1.0"); // description
388 
389  // go back and rewrite where the map data starts
390  uint32 start = fin->tell();
391  fin->seek(4);
392  fin->addU16(start);
393  fin->seek(start);
394 
395  uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
396  std::vector<uchar> compressBuffer(compressBound(blockSize));
397  const int COMPRESS_LEVEL = 3;
398 
399  for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
400  for(auto& it : m_tileBlocks[z]) {
401  int index = it.first;
402  MinimapBlock& block = it.second;
403  if(!block.wasSeen())
404  continue;
405 
406  Position pos = getIndexPosition(index, z);
407  fin->addU16(pos.x);
408  fin->addU16(pos.y);
409  fin->addU8(pos.z);
410 
411  ulong len = blockSize;
412  int ret = compress2(compressBuffer.data(), &len, (uchar*)&block.getTiles(), blockSize, COMPRESS_LEVEL);
413  assert(ret == Z_OK);
414  fin->addU16(len);
415  fin->write(compressBuffer.data(), len);
416  }
417  }
418 
419  // end of file
420  Position invalidPos;
421  fin->addU16(invalidPos.x);
422  fin->addU16(invalidPos.y);
423  fin->addU8(invalidPos.z);
424 
425  fin->flush();
426 
427  fin->close();
428  } catch(stdext::exception& e) {
429  g_logger.error(stdext::format("failed to save OTMM minimap: %s", e.what()));
430  }
431 }
MinimapBlock::mustUpdate
void mustUpdate()
Definition: minimap.h:66
FileStream::write
void write(const void *buffer, uint count)
Definition: filestream.cpp:130
Painter::setColor
virtual void setColor(const Color &color)
Definition: painter.h:77
FileStream::seek
void seek(uint pos)
Definition: filestream.cpp:142
Painter::restoreSavedState
virtual void restoreSavedState()=0
MinimapBlock::getTile
MinimapTile & getTile(int x, int y)
Definition: minimap.h:61
stdext::timer
Definition: time.h:36
MinimapTileNotPathable
@ MinimapTileNotPathable
Definition: minimap.h:38
Color
Definition: color.h:32
Tile::getGroundSpeed
int getGroundSpeed()
Definition: tile.cpp:334
Minimap::loadOtmm
bool loadOtmm(const std::string &fileName)
Definition: minimap.cpp:306
TPoint::y
T y
Definition: point.h:83
MinimapBlock::update
void update()
Definition: minimap.cpp:45
FileStream::close
void close()
Definition: filestream.cpp:78
z
gc sort z
Definition: CMakeLists.txt:176
TRect< int >
FileStream::addU32
void addU32(uint32 v)
Definition: filestream.cpp:377
Position::x
int x
Definition: position.h:243
Image::load
static ImagePtr load(std::string file)
Definition: image.cpp:39
Tile::isWalkable
bool isWalkable(bool ignoreCreatures=false)
Definition: tile.cpp:475
Position::isValid
bool isValid() const
Definition: position.h:182
Texture
Definition: texture.h:28
uint32
uint32_t uint32
Definition: types.h:35
TRect::isEmpty
bool isEmpty() const
Definition: rect.h:49
resourcemanager.h
Logger::error
void error(const std::string &what)
Definition: logger.h:54
TRect::bottom
T bottom() const
Definition: rect.h:55
Minimap::saveImage
void saveImage(const std::string &fileName, const Rect &mapRect)
Definition: minimap.cpp:301
Point
TPoint< int > Point
Definition: point.h:86
Position::y
int y
Definition: position.h:244
FileStream::getString
std::string getString()
Definition: filestream.cpp:309
stdext::format
std::string format()
Definition: format.h:82
Color::a
uint8 a() const
Definition: color.h:44
MinimapTileNotWalkable
@ MinimapTileNotWalkable
Definition: minimap.h:39
OTMM_VERSION
@ OTMM_VERSION
Definition: minimap.h:33
MMBLOCK_SIZE
@ MMBLOCK_SIZE
Definition: minimap.h:31
Tile::getMinimapColorByte
uint8 getMinimapColorByte()
Definition: tile.cpp:342
MinimapTile::color
uint8 color
Definition: minimap.h:47
minimap.h
MinimapBlock::clean
void clean()
Definition: minimap.cpp:38
framebuffermanager.h
Position::z
short z
Definition: position.h:245
FileStream::addString
void addString(const std::string &v)
Definition: filestream.cpp:448
uint16
uint16_t uint16
Definition: types.h:36
Image
Definition: image.h:29
g_resources
ResourceManager g_resources
Definition: resourcemanager.cpp:32
OTMM_SIGNATURE
@ OTMM_SIGNATURE
Definition: minimap.h:32
TRect::topLeft
TPoint< T > topLeft() const
Definition: rect.h:60
painter.h
Minimap::updateTile
void updateTile(const Position &pos, const TilePtr &tile)
Definition: minimap.cpp:193
Texture::uploadPixels
void uploadPixels(const ImagePtr &image, bool buildMipmaps=false, bool compress=false)
Definition: texture.cpp:71
Minimap::getTile
const MinimapTile & getTile(const Position &pos)
Definition: minimap.cpp:214
uint
unsigned int uint
Definition: types.h:31
FileStream::cache
void cache()
Definition: filestream.cpp:58
MinimapTile::flags
uint8 flags
Definition: minimap.h:46
FileStream::getU16
uint16 getU16()
Definition: filestream.cpp:199
MinimapBlock::getTileIndex
uint getTileIndex(int x, int y)
Definition: minimap.h:63
g_logger
Logger g_logger
Definition: logger.cpp:35
Minimap::clean
void clean()
Definition: minimap.cpp:95
stdext::shared_object_ptr::reset
void reset()
Definition: shared_object.h:79
Position
Definition: position.h:33
image.h
Size
TSize< int > Size
Definition: size.h:107
TPoint::x
T x
Definition: point.h:83
TRect::moveCenter
void moveCenter(const TPoint< T > &p)
Definition: rect.h:122
Position::isMapPosition
bool isMapPosition() const
Definition: position.h:181
FileStream::read
int read(void *buffer, uint size, uint nmemb=1)
Definition: filestream.cpp:109
TexturePtr
stdext::shared_object_ptr< Texture > TexturePtr
Definition: declarations.h:49
ulong
unsigned long ulong
Definition: types.h:32
Otc::MAX_Z
@ MAX_Z
Definition: const.h:33
Minimap::terminate
void terminate()
Definition: minimap.cpp:90
Minimap::init
void init()
Definition: minimap.cpp:86
MinimapTile
Definition: minimap.h:43
Minimap::getTileRect
Rect getTileRect(const Position &pos, const Rect &screenRect, const Position &mapCenter, float scale)
Definition: minimap.cpp:174
stdext::throw_exception
void throw_exception(const std::string &what)
Throws a generic exception.
Definition: exception.h:43
Rect
TRect< int > Rect
Definition: rect.h:319
Minimap::getTilePosition
Position getTilePosition(const Point &point, const Rect &screenRect, const Position &mapCenter, float scale)
Definition: minimap.cpp:163
ResourceManager::openFile
FileStreamPtr openFile(const std::string &fileName)
Definition: resourcemanager.cpp:232
Painter::setClipRect
virtual void setClipRect(const Rect &clipRect)=0
filestream.h
Color::alpha
static const Color alpha
Definition: color.h:100
Minimap::getTilePoint
Point getTilePoint(const Position &pos, const Rect &screenRect, const Position &mapCenter, float scale)
Definition: minimap.cpp:152
stdext::exception::what
virtual const char * what() const
Definition: exception.h:37
FileStream::tell
uint tell()
Definition: filestream.cpp:167
Painter::resetColor
void resetColor()
Definition: painter.h:108
uchar
unsigned char uchar
Definition: types.h:29
TRect::right
T right() const
Definition: rect.h:54
texture.h
stdext::shared_object_ptr
Definition: shared_object.h:39
Texture::setSmooth
virtual void setSmooth(bool smooth)
Definition: texture.cpp:127
g_minimap
Minimap g_minimap
Definition: minimap.cpp:36
g_painter
Painter * g_painter
Definition: painter.cpp:28
MinimapTile::speed
uint8 speed
Definition: minimap.h:48
Tile::isPathable
bool isPathable()
Definition: tile.cpp:495
TRect::height
T height() const
Definition: rect.h:70
FileStream::getU32
uint32 getU32()
Definition: filestream.cpp:215
FileStream::flush
void flush()
Definition: filestream.cpp:90
MinimapBlock::updateTile
void updateTile(int x, int y, const MinimapTile &tile)
Definition: minimap.cpp:78
TSize::toPoint
TPoint< T > toPoint() const
Definition: size.h:37
MinimapBlock::getTexture
const TexturePtr & getTexture()
Definition: minimap.h:64
MinimapBlock
Definition: minimap.h:55
MinimapTileWasSeen
@ MinimapTileWasSeen
Definition: minimap.h:37
Minimap
Definition: minimap.h:78
Minimap::saveOtmm
void saveOtmm(const std::string &fileName)
Definition: minimap.cpp:369
MinimapBlock::justSaw
void justSaw()
Definition: minimap.h:67
TPoint< int >
Painter::saveState
virtual void saveState()=0
TRect::size
TSize< T > size() const
Definition: rect.h:71
FileStream::addU8
void addU8(uint8 v)
Definition: filestream.cpp:354
Color::from8bit
static Color from8bit(int color)
Definition: color.h:90
Color::to8bit
static uint8 to8bit(const Color &color)
Definition: color.h:82
Painter::drawFilledRect
virtual void drawFilledRect(const Rect &dest)=0
ResourceManager::createFile
FileStreamPtr createFile(const std::string &fileName)
Definition: resourcemanager.cpp:250
tile.h
TRect::width
T width() const
Definition: rect.h:69
MinimapBlock::getTiles
std::array< MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE > & getTiles()
Definition: minimap.h:65
Color::rgba
uint32 rgba() const
Definition: color.h:54
FileStream::getU8
uint8 getU8()
Definition: filestream.cpp:183
Minimap::draw
void draw(const Rect &screenRect, const Position &mapCenter, float scale, const Color &color)
Definition: minimap.cpp:101
uint8
uint8_t uint8
Definition: types.h:37
Painter::drawTexturedRect
virtual void drawTexturedRect(const Rect &dest, const TexturePtr &texture, const Rect &src)=0
Minimap::loadImage
bool loadImage(const std::string &fileName, const Position &topLeft, float colorFactor)
Definition: minimap.cpp:225
FileStream::addU16
void addU16(uint16 v)
Definition: filestream.cpp:365
stdext::exception
Definition: exception.h:31
MinimapBlock::wasSeen
bool wasSeen()
Definition: minimap.h:68