New lightweight minimap format, finish #15

This commit is contained in:
Eduardo Bart 2013-01-27 14:06:47 -02:00
parent cf0ecf418d
commit e81dd31ae3
14 changed files with 261 additions and 124 deletions

View File

@ -530,7 +530,10 @@ function processChannelTabMenu(tab, mousePos, mouseButton)
--menu:addOption(tr('Show Server Messages'), function() --[[TODO]] end)
menu:addSeparator()
end
menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end)
if consoleTabBar:getCurrentTab() == tab then
menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end)
end
--menu:addOption(tr('Save Messages'), function() --[[TODO]] end)
menu:display(mousePos)

View File

@ -551,9 +551,8 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
player:stopAutoWalk()
if autoWalkPos and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseLeftButton then
if not player:autoWalk(autoWalkPos) then
modules.game_textmessage.displayStatusMessage(tr('There is no way.'))
end
player.onAutoWalkFail = function() modules.game_textmessage.displayStatusMessage(tr('There is no way.')) end
player:autoWalk(autoWalkPos)
return true
end

View File

@ -6,7 +6,7 @@ navigating = false
minimapWidget = nil
minimapButton = nil
minimapWindow = nil
otmm = false
flagsPanel = nil
flagWindow = nil
nextFlagId = 0
@ -49,6 +49,7 @@ function init()
reset()
minimapWindow:setup()
loadMapFlags()
useOTMM()
if g_game.isOnline() then
addEvent(function() updateMapFlags() end)
@ -263,17 +264,32 @@ end
function loadMap()
local protocolVersion = g_game.getProtocolVersion()
local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
if g_resources.fileExists(minimapFile) then
g_map.clean()
g_map.loadOtcm(minimapFile)
g_map.clean()
g_minimap.clean()
if otmm then
local minimapFile = '/minimap.otmm'
if g_resources.fileExists(minimapFile) then
g_minimap.loadOtmm(minimapFile)
end
else
local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
if g_resources.fileExists(minimapFile) then
g_map.loadOtcm(minimapFile)
end
end
end
function saveMap()
local protocolVersion = g_game.getProtocolVersion()
local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
g_map.saveOtcm(minimapFile)
if otmm then
local minimapFile = '/minimap.otmm'
g_minimap.saveOtmm(minimapFile)
else
local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
g_map.saveOtcm(minimapFile)
end
end
function toggle()
@ -286,6 +302,10 @@ function toggle()
end
end
function useOTMM()
otmm = true
end
function isClickInRange(position, fromPosition, toPosition)
return (position.x >= fromPosition.x and position.y >= fromPosition.y and position.x <= toPosition.x and position.y <= toPosition.y)
end

View File

@ -363,11 +363,10 @@ namespace Otc
};
enum PathFindFlags {
PathFindAllowNullTiles = 1,
PathFindAllowNotSeenTiles = 1,
PathFindAllowCreatures = 2,
PathFindAllowNonPathable = 4,
PathFindAllowNonWalkable = 8,
PathFindAllowChangeFloor = 16
PathFindAllowNonWalkable = 8
};
enum AutomapFlags

View File

@ -193,9 +193,11 @@ bool LocalPlayer::autoWalk(const Position& destination)
limitedPath.resize(127);
} else {
// no known path found, try to discover one
result = g_map.findPath(m_position, destination, 1000, Otc::PathFindAllowNullTiles);
if(std::get<1>(result) != Otc::PathFindResultOk)
result = g_map.findPath(m_position, destination, 1000, Otc::PathFindAllowNotSeenTiles);
if(std::get<1>(result) != Otc::PathFindResultOk) {
callLuaField("onAutoWalkFail");
return false;
}
Position currentPos = m_position;
for(auto dir : std::get<0>(result)) {

View File

@ -36,6 +36,7 @@
#include "player.h"
#include "localplayer.h"
#include "map.h"
#include "minimap.h"
#include "thingtypemanager.h"
#include "spritemanager.h"
#include "shadermanager.h"
@ -116,7 +117,12 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_map", "getSpawnFile", &Map::getSpawnFile, &g_map);
g_lua.bindSingletonFunction("g_map", "setSpawnFile", &Map::setSpawnFile, &g_map);
g_lua.bindSingletonFunction("g_map", "createTile", &Map::createTile, &g_map);
g_lua.bindSingletonFunction("g_map", "getSize", &Map::getSize, &g_map);;
g_lua.bindSingletonFunction("g_map", "getSize", &Map::getSize, &g_map);
g_lua.registerSingletonClass("g_minimap");
g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "loadOtmm", &Minimap::loadOtmm, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "saveOtmm", &Minimap::saveOtmm, &g_minimap);
g_lua.registerSingletonClass("g_creatures");
g_lua.bindSingletonFunction("g_creatures", "getCreatures", &CreatureManager::getCreatures, &g_creatures);

View File

@ -600,27 +600,39 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
if(i == 0 && j == 0)
continue;
bool wasSeen = false;
bool hasCreature = false;
bool isNotWalkable = true;
bool isNotPathable = true;
int speed = 100;
Position neighborPos = currentNode->pos.translated(i, j);
const TilePtr& tile = getTile(neighborPos);
if(g_map.isAwareOfPosition(neighborPos)) {
wasSeen = true;
if(const TilePtr& tile = getTile(neighborPos)) {
hasCreature = tile->hasCreature();
isNotWalkable = !tile->isWalkable();
isNotPathable = !tile->isPathable();
speed = tile->getGroundSpeed();
}
} else {
const MinimapTile& mtile = g_minimap.getTile(neighborPos);
wasSeen = mtile.hasFlag(MinimapTileWasSeen);
isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable);
isNotPathable = mtile.hasFlag(MinimapTileNotPathable);
speed = mtile.getSpeed();
}
float walkFactor = 0;
if(neighborPos != goalPos) {
/*
Known Issue with Otc::PathFindAllowNullTiles flag:
If you are above ground floor this will attempt to path over null
tiles, need to rework this for "fly servers" and blank map click,
but it is breaking normal path finding.
*/
if(!(flags & Otc::PathFindAllowNullTiles) && (!tile || tile->isEmpty()))
if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen)
continue;
if(tile) {
if(!(flags & Otc::PathFindAllowCreatures) && tile->hasCreature())
if(wasSeen) {
if(!(flags & Otc::PathFindAllowCreatures) && hasCreature)
continue;
if(!(flags & Otc::PathFindAllowNonPathable) && !tile->isPathable())
if(!(flags & Otc::PathFindAllowNonPathable) && isNotPathable)
continue;
if(!(flags & Otc::PathFindAllowNonWalkable) && !tile->isWalkable())
continue;
if(!(flags & Otc::PathFindAllowChangeFloor) && tile->changesFloor())
if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable)
continue;
}
}
@ -631,8 +643,7 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
else
walkFactor += 1.0f;
int groundSpeed = tile ? tile->getGroundSpeed() : 100;
float cost = currentNode->cost + (groundSpeed * walkFactor) / 100.0f;
float cost = currentNode->cost + (speed * walkFactor) / 100.0f;
Node *neighborNode;
if(nodes.find(neighborPos) == nodes.end()) {

View File

@ -382,7 +382,6 @@ bool Map::loadOtcm(const std::string& fileName)
if(!fin)
stdext::throw_exception("unable to open file");
stdext::timer loadTimer;
fin->cache();
uint32 signature = fin->getU32();
@ -446,8 +445,6 @@ bool Map::loadOtcm(const std::string& fileName)
fin->close();
// well, this is really slow
g_logger.debug(stdext::format("Otcm load time: %.2f seconds", loadTimer.elapsed_seconds()));
return true;
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to load OTCM map: %s", e.what()));
@ -519,7 +516,6 @@ void Map::saveOtcm(const std::string& fileName)
fin->flush();
fin->close();
//g_logger.debug(stdext::format("Otcm save time: %.2f seconds", saveTimer.elapsed_seconds()));
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to save OTCM map: %s", e.what()));
}

View File

@ -27,40 +27,17 @@
#include <framework/graphics/texture.h>
#include <framework/graphics/painter.h>
#include <framework/graphics/framebuffermanager.h>
#include <framework/core/resourcemanager.h>
#include <framework/core/filestream.h>
#include <boost/concept_check.hpp>
#include <zlib.h>
Minimap g_minimap;
void MinimapBlock::updateImage()
{
if(!m_image)
m_image = ImagePtr(new Image(Size(MMBLOCK_SIZE, MMBLOCK_SIZE)));
else
m_image->resize(Size(MMBLOCK_SIZE, MMBLOCK_SIZE));
for(int x=0;x<MMBLOCK_SIZE;++x)
for(int y=0;y<MMBLOCK_SIZE;++y)
m_image->setPixel(x, y, Color::from8bit(getTile(x, y).color).rgba());
}
void MinimapBlock::updateTexture()
{
if(!m_image)
return;
if(!m_texture) {
m_texture = TexturePtr(new Texture(m_image, true));
} else {
m_texture->uploadPixels(m_image, true);
}
}
void MinimapBlock::clean()
{
m_tiles.fill(MinimapTile());
m_image.reset();
m_texture.reset();
m_shouldDraw = false;
m_mustUpdate = false;
}
@ -69,19 +46,32 @@ void MinimapBlock::update()
if(!m_mustUpdate)
return;
if(m_shouldDraw) {
updateImage();
updateTexture();
ImagePtr image(new Image(Size(MMBLOCK_SIZE, MMBLOCK_SIZE)));
bool shouldDraw = false;
for(int x=0;x<MMBLOCK_SIZE;++x) {
for(int y=0;y<MMBLOCK_SIZE;++y) {
uint32 col = Color::from8bit(getTile(x, y).color).rgba();
image->setPixel(x, y, col);
if(col != 0)
shouldDraw = true;
}
}
if(shouldDraw) {
if(!m_texture) {
m_texture = TexturePtr(new Texture(image, true));
} else {
m_texture->uploadPixels(image, true);
}
} else
m_texture.reset();
m_mustUpdate = false;
}
void MinimapBlock::updateTile(int x, int y, const MinimapTile& tile)
{
if(tile.color != 0)
m_shouldDraw = true;
if(m_tiles[getTileIndex(x,y)].color != tile.color)
m_mustUpdate = true;
@ -90,7 +80,6 @@ void MinimapBlock::updateTile(int x, int y, const MinimapTile& tile)
void Minimap::init()
{
}
void Minimap::terminate()
@ -141,13 +130,17 @@ void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scal
if(x < 0 || x >= 65536 - MMBLOCK_SIZE)
continue;
Position blockPos(x, y, mapCenter.z);
if(!hasBlock(blockPos))
continue;
MinimapBlock& block = getBlock(Position(x, y, mapCenter.z));
block.update();
if(block.shouldDraw()) {
const TexturePtr& tex = block.getTexture();
if(tex) {
Rect src(0, 0, MMBLOCK_SIZE, MMBLOCK_SIZE);
Rect dest(Point(xs,ys), src.size() * scale);
const TexturePtr& tex = block.getTexture();
tex->setSmooth(scale < 1.0f);
g_painter->drawTexturedRect(dest, tex, src);
@ -186,36 +179,151 @@ Position Minimap::getPosition(const Point& point, const Rect& screenRect, const
void Minimap::updateTile(const Position& pos, const TilePtr& tile)
{
MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
MinimapTile minimapTile;
if(tile) {
minimapTile.color = tile->getMinimapColorByte();
if(tile->isWalkable())
minimapTile.flags |= MinimapTileWalkable;
if(tile->isPathable())
minimapTile.flags |= MinimapTilePathable;
if(tile->changesFloor())
minimapTile.flags |= MinimapTileChangesFloor;
minimapTile.flags |= MinimapTileWasSeen;
if(!tile->isWalkable(true))
minimapTile.flags |= MinimapTileNotWalkable;
if(!tile->isPathable())
minimapTile.flags |= MinimapTileNotPathable;
minimapTile.speed = std::min((int)std::ceil(tile->getGroundSpeed() / 10.0f), 255);
}
block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile);
if(minimapTile != MinimapTile()) {
MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile);
}
}
bool Minimap::checkTileProperty(const Position& pos, int flags)
const MinimapTile& Minimap::getTile(const Position& pos)
{
MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
return block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y).flags & flags;
static MinimapTile nulltile;
if(pos.z <= Otc::MAX_Z && hasBlock(pos)) {
MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
return block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
}
return nulltile;
}
void Minimap::loadOtmm(const std::string& fileName)
bool Minimap::loadOtmm(const std::string& fileName)
{
try {
FileStreamPtr fin = g_resources.openFile(fileName);
if(!fin)
stdext::throw_exception("unable to open file");
fin->cache();
uint32 signature = fin->getU32();
if(signature != OTMM_SIGNATURE)
stdext::throw_exception("invalid OTMM file");
uint16 start = fin->getU16();
uint16 version = fin->getU16();
fin->getU32(); // flags
switch(version) {
case 1: {
fin->getString(); // description
break;
}
default:
stdext::throw_exception("OTMM version not supported");
}
fin->seek(start);
uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
std::vector<uchar> compressBuffer(compressBound(blockSize));
while(true) {
Position pos;
pos.x = fin->getU16();
pos.y = fin->getU16();
pos.z = fin->getU8();
// end of file
if(!pos.isValid())
break;
MinimapBlock& block = getBlock(pos);
ulong len = fin->getU16();
ulong destLen = blockSize;
fin->read(compressBuffer.data(), len);
int ret = uncompress((uchar*)&block.getTiles(), &destLen, compressBuffer.data(), len);
assert(ret == Z_OK);
assert(destLen == blockSize);
block.mustUpdate();
}
fin->close();
return true;
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
return false;
}
}
void Minimap::saveOtmm(const std::string& fileName)
{
try {
stdext::timer saveTimer;
FileStreamPtr fin = g_resources.createFile(fileName);
fin->cache();
//TODO: compression flag with zlib
uint32 flags = 0;
// header
fin->addU32(OTMM_SIGNATURE);
fin->addU16(0); // data start, will be overwritten later
fin->addU16(OTMM_VERSION);
fin->addU32(flags);
// version 1 header
fin->addString("OTMM 1.0"); // description
// go back and rewrite where the map data starts
uint32 start = fin->tell();
fin->seek(4);
fin->addU16(start);
fin->seek(start);
uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
std::vector<uchar> compressBuffer(compressBound(blockSize));
const int COMPRESS_LEVEL = 3;
for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
for(auto& it : m_tileBlocks[z]) {
int index = it.first;
MinimapBlock& block = it.second;
Position pos = getIndexPosition(index, z);
fin->addU16(pos.x);
fin->addU16(pos.y);
fin->addU8(pos.z);
ulong len = blockSize;
int ret = compress2(compressBuffer.data(), &len, (uchar*)&block.getTiles(), blockSize, COMPRESS_LEVEL);
assert(ret == Z_OK);
fin->addU16(len);
fin->write(compressBuffer.data(), len);
}
}
// end of file
Position invalidPos;
fin->addU16(invalidPos.x);
fin->addU16(invalidPos.y);
fin->addU8(invalidPos.z);
fin->flush();
fin->close();
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to save OTMM minimap: %s", e.what()));
}
}

View File

@ -28,31 +28,33 @@
#include <framework/graphics/declarations.h>
enum {
MMBLOCK_SIZE = 64
MMBLOCK_SIZE = 64,
OTMM_SIGNATURE = 0x4D4d544F,
OTMM_VERSION = 1
};
enum MinimapTileFlags {
MinimapTilePathable = 1,
MinimapTileWalkable = 2,
MinimapTileChangesFloor = 4
MinimapTileWasSeen = 1,
MinimapTileNotPathable = 2,
MinimapTileNotWalkable = 4
};
#pragma pack(push,1) // disable memory alignment
struct MinimapTile
{
MinimapTile() : flags(0), color(0) { }
MinimapTile() : flags(0), color(0), speed(100) { }
uint8 flags;
uint8 color;
bool operator==(const MinimapTile& other) { return color == other.color && flags == other.flags; }
uint8 speed;
bool hasFlag(MinimapTileFlags flag) const { return flags & flag; }
int getSpeed() const { return speed * 10; }
bool operator==(const MinimapTile& other) const { return color == other.color && flags == other.flags && speed == other.speed; }
bool operator!=(const MinimapTile& other) const { return !(*this == other); }
};
#pragma pack(pop)
class MinimapBlock
{
public:
void updateImage();
void updateTexture();
void clean();
void update();
void updateTile(int x, int y, const MinimapTile& tile);
@ -60,17 +62,16 @@ public:
void resetTile(int x, int y) { m_tiles[getTileIndex(x,y)] = MinimapTile(); }
uint getTileIndex(int x, int y) { return ((y % MMBLOCK_SIZE) * MMBLOCK_SIZE) + (x % MMBLOCK_SIZE); }
const TexturePtr& getTexture() { return m_texture; }
std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE> getTiles() { return m_tiles; }
bool shouldDraw() { return m_shouldDraw; }
std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE>& getTiles() { return m_tiles; }
void mustUpdate() { m_mustUpdate = true; }
private:
ImagePtr m_image;
TexturePtr m_texture;
stdext::boolean<false> m_shouldDraw;
std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE> m_tiles;
stdext::boolean<true> m_mustUpdate;
};
#pragma pack(pop)
class Minimap
{
@ -84,15 +85,18 @@ public:
Position getPosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale);
void updateTile(const Position& pos, const TilePtr& tile);
bool checkTileProperty(const Position& pos, int flags);
const MinimapTile& getTile(const Position& pos);
void loadOtmm(const std::string& fileName);
bool loadOtmm(const std::string& fileName);
void saveOtmm(const std::string& fileName);
private:
bool hasBlock(const Position& pos) { return m_tileBlocks[pos.z].find(getBlockIndex(pos)) != m_tileBlocks[pos.z].end(); }
MinimapBlock& getBlock(const Position& pos) { return m_tileBlocks[pos.z][getBlockIndex(pos)]; }
Point getBlockOffset(const Point& pos) { return Point(pos.x - pos.x % MMBLOCK_SIZE,
pos.y - pos.y % MMBLOCK_SIZE); }
Position getIndexPosition(int index, int z) { return Position((index % (65536 / MMBLOCK_SIZE))*MMBLOCK_SIZE,
(index / (65536 / MMBLOCK_SIZE))*MMBLOCK_SIZE, z); }
uint getBlockIndex(const Position& pos) { return ((pos.y / MMBLOCK_SIZE) * (65536 / MMBLOCK_SIZE)) + (pos.x / MMBLOCK_SIZE); }
std::unordered_map<uint, MinimapBlock> m_tileBlocks[Otc::MAX_Z+1];
};

View File

@ -563,7 +563,6 @@ void ProtocolGame::sendCloseRuleViolation(const std::string& reporter)
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientCloseRuleViolation);
msg->addString(reporter);
dump << "ProtocolGame::sendCloseRuleViolation" << reporter;
send(msg);
}
@ -571,7 +570,6 @@ void ProtocolGame::sendCancelRuleViolation()
{
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientCancelRuleViolation);
dump << "ProtocolGame::sendCancelRuleViolation";
send(msg);
}

View File

@ -72,7 +72,6 @@ bool ThingTypeManager::loadDat(std::string file)
m_datSignature = fin->getU32();
int numThings[ThingLastCategory];
for(int category = 0; category < ThingLastCategory; ++category) {
int count = fin->getU16() + 1;
m_thingTypes[category].clear();

View File

@ -476,7 +476,7 @@ ThingPtr Tile::getTopMultiUseThing()
return m_things[0];
}
bool Tile::isWalkable()
bool Tile::isWalkable(bool ignoreCreatures)
{
if(!getGround())
return false;
@ -485,24 +485,17 @@ bool Tile::isWalkable()
if(thing->isNotWalkable())
return false;
if(thing->isCreature()) {
CreaturePtr creature = thing->static_self_cast<Creature>();
if(!creature->isPassable() && creature->canBeSeen())
return false;
if(!ignoreCreatures) {
if(thing->isCreature()) {
CreaturePtr creature = thing->static_self_cast<Creature>();
if(!creature->isPassable() && creature->canBeSeen())
return false;
}
}
}
return true;
}
bool Tile::changesFloor()
{
for(const ThingPtr& thing : m_things) {
if(thing->isTranslucent() || (thing->isOnBottom() && thing->hasElevation()))
return true;
}
return false;
}
bool Tile::isPathable()
{
for(const ThingPtr& thing : m_things) {

View File

@ -94,8 +94,7 @@ public:
uint8 getMinimapColorByte();
int getThingCount() { return m_things.size() + m_effects.size(); }
bool isPathable();
bool isWalkable();
bool changesFloor();
bool isWalkable(bool ignoreCreatures = false);
bool isFullGround();
bool isFullyOpaque();
bool isSingleDimension();