From dafaf86eef6daffb2b50d8332a614216bd0ce471 Mon Sep 17 00:00:00 2001 From: beegee1 Date: Sun, 22 Mar 2015 20:32:22 +0100 Subject: [PATCH] Add some debug logging, Fix one special case when updating terrain type, Improve visual look of updated terrain types --- .gitignore | 1 + lib/GameConstants.cpp | 35 ++++++++++- lib/GameConstants.h | 6 +- lib/mapping/CMapEditManager.cpp | 102 +++++++++++++++++++++++++++----- lib/mapping/CMapEditManager.h | 9 +++ test/CMapEditManagerTest.cpp | 45 +++++++++++--- test/TerrainViewTest.h3m | Bin 4221 -> 4241 bytes test/terrainViewMappings.json | 4 ++ 8 files changed, 176 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 0c0b53772..8f43f9877 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ ui_*.h /CPackConfig.cmake /CPackSourceConfig.cmake build-* +CMakeLists.txt.user.* diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 61113b6ce..9a48b476f 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -88,10 +88,10 @@ CCreature * CreatureID::toCreature() const CSpell * SpellID::toSpell() const { if(num < 0 || num >= VLC->spellh->objects.size()) - { + { logGlobal->errorStream() << "Unable to get spell of invalid ID " << int(num); return nullptr; - } + } return VLC->spellh->objects[*this]; } @@ -129,3 +129,34 @@ std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType if (it == actionTypeToString.end()) return os << ""; else return os << it->second; } + +std::ostream & operator<<(std::ostream & os, const ETerrainType actionType) +{ + static const std::map terrainTypeToString = + { + #define DEFINE_ELEMENT(element) {ETerrainType::element, #element} + DEFINE_ELEMENT(WRONG), + DEFINE_ELEMENT(BORDER), + DEFINE_ELEMENT(DIRT), + DEFINE_ELEMENT(SAND), + DEFINE_ELEMENT(GRASS), + DEFINE_ELEMENT(SNOW), + DEFINE_ELEMENT(SWAMP), + DEFINE_ELEMENT(ROUGH), + DEFINE_ELEMENT(SUBTERRANEAN), + DEFINE_ELEMENT(LAVA), + DEFINE_ELEMENT(WATER), + DEFINE_ELEMENT(ROCK) + }; + + auto it = terrainTypeToString.find(actionType.num); + if (it == terrainTypeToString.end()) return os << ""; + else return os << it->second; +} + +std::string ETerrainType::toString() const +{ + std::stringstream ss; + ss << *this; + return ss.str(); +} diff --git a/lib/GameConstants.h b/lib/GameConstants.h index c653ffd78..0b9c6e32b 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -702,7 +702,7 @@ namespace Battle std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType); -class ETerrainType +class DLL_LINKAGE ETerrainType { public: enum EETerrainType @@ -717,8 +717,12 @@ public: ID_LIKE_CLASS_COMMON(ETerrainType, EETerrainType) EETerrainType num; + + std::string toString() const; }; +DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType actionType); + ID_LIKE_OPERATORS_DECLS(ETerrainType, ETerrainType::EETerrainType) diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index 990a93a41..422adea7a 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -482,13 +482,15 @@ void CDrawTerrainOperation::updateTerrainTypes() { const auto & centerPos = *(positions.begin()); auto centerTile = map->getTile(centerPos); + logGlobal->debugStream() << boost::format("Set terrain tile at pos '%s' to type '%s'") % centerPos % centerTile.terType; auto tiles = getInvalidTiles(centerPos); auto updateTerrainType = [&](const int3 & pos, bool tileRequiresValidation) { map->getTile(pos).terType = centerTile.terType; if(tileRequiresValidation) positions.insert(pos); invalidateTerrainViews(pos); - logGlobal->debugStream() << boost::format("Update terrain tile at '%s' to type '%i'.") % pos % centerTile.terType; + logGlobal->debugStream() << boost::format("Set additional terrain tile at pos '%s' to type '%s'; tileRequiresValidation '%s'") % pos + % centerTile.terType % tileRequiresValidation; }; // Fill foreign invalid tiles @@ -497,12 +499,14 @@ void CDrawTerrainOperation::updateTerrainTypes() updateTerrainType(tile, true); } + tiles = getInvalidTiles(centerPos); if(tiles.nativeTiles.find(centerPos) != tiles.nativeTiles.end()) { // Blow up auto rect = extendTileAroundSafely(centerPos); std::set suitableTiles; int invalidForeignTilesCnt = std::numeric_limits::max(), invalidNativeTilesCnt = 0; + bool centerPosValid = false; rect.forEach([&](const int3 & posToTest) { auto & terrainTile = map->getTile(posToTest); @@ -511,28 +515,64 @@ void CDrawTerrainOperation::updateTerrainTypes() auto formerTerType = terrainTile.terType; terrainTile.terType = centerTile.terType; auto testTile = getInvalidTiles(posToTest); - auto addToSuitableTiles = [&](const int3 & pos) - { - suitableTiles.insert(pos); - logGlobal->debugStream() << boost::format(std::string("Found suitable tile '%s' for main tile '%s': ") + - "Invalid native tiles '%i', invalid foreign tiles '%i'.") % pos % centerPos % testTile.nativeTiles.size() % - testTile.foreignTiles.size(); - }; int nativeTilesCntNorm = testTile.nativeTiles.empty() ? std::numeric_limits::max() : testTile.nativeTiles.size(); - if(nativeTilesCntNorm > invalidNativeTilesCnt || - (nativeTilesCntNorm == invalidNativeTilesCnt && testTile.foreignTiles.size() < invalidForeignTilesCnt)) + + bool putSuitableTile = false; + bool addToSuitableTiles = false; + if(testTile.centerPosValid) { + if (!centerPosValid) + { + centerPosValid = true; + putSuitableTile = true; + } + else + { + if(testTile.foreignTiles.size() < invalidForeignTilesCnt) + { + putSuitableTile = true; + } + else + { + addToSuitableTiles = true; + } + } + } + else if (!centerPosValid) + { + if((nativeTilesCntNorm > invalidNativeTilesCnt) || + (nativeTilesCntNorm == invalidNativeTilesCnt && testTile.foreignTiles.size() < invalidForeignTilesCnt)) + { + putSuitableTile = true; + } + else if(nativeTilesCntNorm == invalidNativeTilesCnt && testTile.foreignTiles.size() == invalidForeignTilesCnt) + { + addToSuitableTiles = true; + } + } + + if (putSuitableTile) + { + if(!suitableTiles.empty()) + { + logGlobal->debugStream() << "Clear suitables tiles."; + } + invalidNativeTilesCnt = nativeTilesCntNorm; invalidForeignTilesCnt = testTile.foreignTiles.size(); suitableTiles.clear(); - addToSuitableTiles(posToTest); + addToSuitableTiles = true; } - else if(nativeTilesCntNorm == invalidNativeTilesCnt && - testTile.foreignTiles.size() == invalidForeignTilesCnt) + + if (addToSuitableTiles) { - addToSuitableTiles(posToTest); + suitableTiles.insert(posToTest); + logGlobal->debugStream() << boost::format(std::string("Found suitable tile '%s' for main tile '%s': ") + + "Invalid native tiles '%i', invalid foreign tiles '%i', centerPosValid '%i'") % posToTest % centerPos % testTile.nativeTiles.size() % + testTile.foreignTiles.size() % testTile.centerPosValid; } + terrainTile.terType = formerTerType; } }); @@ -591,6 +631,7 @@ void CDrawTerrainOperation::updateTerrainViews() { // This shouldn't be the case logGlobal->warnStream() << boost::format("No pattern detected at pos '%s'.") % pos; + CTerrainViewPatternUtils::printDebuggingInfoAboutTile(map, pos); continue; } @@ -878,6 +919,10 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const if(terType == centerTerType) tiles.nativeTiles.insert(pos); else tiles.foreignTiles.insert(pos); } + else if(centerPos == pos) + { + tiles.centerPosValid = true; + } } }); return tiles; @@ -899,6 +944,35 @@ CDrawTerrainOperation::ValidationResult::ValidationResult(bool result, const std } +void CTerrainViewPatternUtils::printDebuggingInfoAboutTile(const CMap * map, int3 pos) +{ + logGlobal->debugStream() << "Printing detailed info about nearby map tiles of pos '" << pos << "'"; + for(int y = pos.y - 2; y <= pos.y + 2; ++y) + { + std::string line; + const int PADDED_LENGTH = 10; + for(int x = pos.x - 2; x <= pos.x + 2; ++x) + { + auto debugPos = int3(x, y, pos.z); + if(map->isInTheMap(debugPos)) + { + auto debugTile = map->getTile(debugPos); + + std::string terType = debugTile.terType.toString().substr(0, 6); + line += terType; + line.insert(line.end(), PADDED_LENGTH - terType.size(), ' '); + } + else + { + line += "X"; + line.insert(line.end(), PADDED_LENGTH - 1, ' '); + } + } + + logGlobal->debugStream() << line; + } +} + CClearTerrainOperation::CClearTerrainOperation(CMap * map, CRandomGenerator * gen) : CComposedOperation(map) { CTerrainSelection terrainSel(map); diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index b09deec9e..2941a8df2 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -318,6 +318,9 @@ private: struct InvalidTiles { std::set foreignTiles, nativeTiles; + bool centerPosValid; + + InvalidTiles() : centerPosValid(false) { } }; void updateTerrainTypes(); @@ -346,6 +349,12 @@ private: std::set invalidatedTerViews; }; +class DLL_LINKAGE CTerrainViewPatternUtils +{ +public: + static void printDebuggingInfoAboutTile(const CMap * map, int3 pos); +}; + /// The CClearTerrainOperation clears+initializes the terrain. class CClearTerrainOperation : public CComposedOperation { diff --git a/test/CMapEditManagerTest.cpp b/test/CMapEditManagerTest.cpp index 6baa5cf73..394bdaded 100644 --- a/test/CMapEditManagerTest.cpp +++ b/test/CMapEditManagerTest.cpp @@ -71,6 +71,32 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type) } editManager->drawTerrain(ETerrainType::GRASS); BOOST_CHECK(map->getTile(int3(35, 44, 0)).terType == ETerrainType::WATER); + + // Rock case + editManager->getTerrainSelection().selectRange(MapRect(int3(1, 1, 1), 15, 15)); + editManager->drawTerrain(ETerrainType::SUBTERRANEAN); + std::vector vec({ int3(6, 6, 1), int3(7, 6, 1), int3(8, 6, 1), int3(5, 7, 1), int3(6, 7, 1), int3(7, 7, 1), + int3(8, 7, 1), int3(4, 8, 1), int3(5, 8, 1), int3(6, 8, 1)}); + editManager->getTerrainSelection().setSelection(vec); + editManager->drawTerrain(ETerrainType::ROCK); + BOOST_CHECK(map->getTile(int3(5, 6, 1)).terType == ETerrainType::ROCK || map->getTile(int3(7, 8, 1)).terType == ETerrainType::ROCK); + + // next check + auto map2 = make_unique(); + map2->width = 128; + map2->height = 128; + map2->initTerrain(); + auto editManager2 = map2->getEditManager(); + + editManager2->getTerrainSelection().selectRange(MapRect(int3(0, 0, 1), 128, 128)); + editManager2->drawTerrain(ETerrainType::SUBTERRANEAN); + + std::vector selection({ int3(95, 43, 1), int3(95, 44, 1), int3(94, 45, 1), int3(95, 45, 1), int3(96, 45, 1), + int3(93, 46, 1), int3(94, 46, 1), int3(95, 46, 1), int3(96, 46, 1), int3(97, 46, 1), + int3(98, 46, 1), int3(99, 46, 1)}); + editManager2->getTerrainSelection().setSelection(selection); + editManager2->drawTerrain(ETerrainType::ROCK); + } catch(const std::exception & e) { @@ -82,15 +108,15 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View) { try { - // Load maps and json config - - #if defined(__MINGW32__) - const std::string TEST_DATA_DIR = "test/"; - #else - const std::string TEST_DATA_DIR = "."; - #endif // defined - - + // Load maps and json config + + #if defined(__MINGW32__) + const std::string TEST_DATA_DIR = "test/"; + #else + const std::string TEST_DATA_DIR = "."; + #endif // defined + + auto loader = new CFilesystemLoader("test/", TEST_DATA_DIR); dynamic_cast(CResourceHandler::get())->addLoader(loader, false); const auto originalMap = CMapService::loadMap("test/TerrainViewTest"); @@ -124,6 +150,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View) if(posVector.size() != 3) throw std::runtime_error("A position should consist of three values x,y,z. Continue with next position."); int3 pos(posVector[0].Float(), posVector[1].Float(), posVector[2].Float()); logGlobal->infoStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z; + CTerrainViewPatternUtils::printDebuggingInfoAboutTile(map.get(), pos); const auto & originalTile = originalMap->getTile(pos); editManager->getTerrainSelection().selectRange(MapRect(pos, 1, 1)); editManager->drawTerrain(originalTile.terType, &gen); diff --git a/test/TerrainViewTest.h3m b/test/TerrainViewTest.h3m index 5ae7c35fec03d177de76ec23fd84c6669d9d7675..16200ffabd0bdd654f11a8d85a4eafa094945b0e 100644 GIT binary patch literal 4241 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 BR|Eh6 diff --git a/test/terrainViewMappings.json b/test/terrainViewMappings.json index 7a6cdb2d0..0c492ce7c 100644 --- a/test/terrainViewMappings.json +++ b/test/terrainViewMappings.json @@ -165,6 +165,10 @@ "pos" : [ [ 12,24,1 ] ], "pattern" : "rock.s5" }, + { + "pos" : [ [ 16,4,1 ] ], + "pattern" : "rock.s5" + }, { "pos" : [ [ 8,23,1 ] ], "pattern" : "rock.s6"