diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua index f1bb23f9..613f4858 100644 --- a/modules/game_console/console.lua +++ b/modules/game_console/console.lua @@ -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) diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index bf88209c..cec157a3 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -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 diff --git a/modules/game_minimap/minimap.lua b/modules/game_minimap/minimap.lua index 4b81e54d..e7a309f7 100644 --- a/modules/game_minimap/minimap.lua +++ b/modules/game_minimap/minimap.lua @@ -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 diff --git a/src/client/const.h b/src/client/const.h index 817b8db2..d9ae1b94 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -363,11 +363,10 @@ namespace Otc }; enum PathFindFlags { - PathFindAllowNullTiles = 1, + PathFindAllowNotSeenTiles = 1, PathFindAllowCreatures = 2, PathFindAllowNonPathable = 4, - PathFindAllowNonWalkable = 8, - PathFindAllowChangeFloor = 16 + PathFindAllowNonWalkable = 8 }; enum AutomapFlags diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 3040ebb3..f7876e5f 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -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)) { diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index d98bb3fe..04185093 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -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); diff --git a/src/client/map.cpp b/src/client/map.cpp index abf16761..b908a5d5 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -600,27 +600,39 @@ std::tuple, 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()) - continue; - if(!(flags & Otc::PathFindAllowNonPathable) && !tile->isPathable()) + if(wasSeen) { + if(!(flags & Otc::PathFindAllowCreatures) && hasCreature) continue; - if(!(flags & Otc::PathFindAllowNonWalkable) && !tile->isWalkable()) + if(!(flags & Otc::PathFindAllowNonPathable) && isNotPathable) continue; - if(!(flags & Otc::PathFindAllowChangeFloor) && tile->changesFloor()) + if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable) continue; } } @@ -631,8 +643,7 @@ std::tuple, 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()) { diff --git a/src/client/mapio.cpp b/src/client/mapio.cpp index 7ea720ba..460ea921 100644 --- a/src/client/mapio.cpp +++ b/src/client/mapio.cpp @@ -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())); } diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index bfa73e0a..0980cb8f 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -27,40 +27,17 @@ #include #include #include +#include +#include #include +#include 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;xsetPixel(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,28 +46,40 @@ 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;xsetPixel(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; m_tiles[getTileIndex(x,y)] = 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 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 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())); + } } diff --git a/src/client/minimap.h b/src/client/minimap.h index ef4eea62..5f8aeb2e 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -28,31 +28,33 @@ #include 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 getTiles() { return m_tiles; } - bool shouldDraw() { return m_shouldDraw; } - + std::array& getTiles() { return m_tiles; } + void mustUpdate() { m_mustUpdate = true; } private: - ImagePtr m_image; TexturePtr m_texture; - stdext::boolean m_shouldDraw; std::array m_tiles; stdext::boolean 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 m_tileBlocks[Otc::MAX_Z+1]; }; diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 391f6745..5e10fb7e 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -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); } diff --git a/src/client/thingtypemanager.cpp b/src/client/thingtypemanager.cpp index 999235da..6105b2e8 100644 --- a/src/client/thingtypemanager.cpp +++ b/src/client/thingtypemanager.cpp @@ -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(); diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 03c9208c..05f296a7 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -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(); - if(!creature->isPassable() && creature->canBeSeen()) - return false; + if(!ignoreCreatures) { + if(thing->isCreature()) { + CreaturePtr creature = thing->static_self_cast(); + 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) { diff --git a/src/client/tile.h b/src/client/tile.h index 0f3b9ebe..e193144c 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -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();