From 1ac328635a029d934d8872c5f63d73c0832c560a Mon Sep 17 00:00:00 2001 From: beegee1 Date: Sat, 17 Aug 2013 12:46:48 +0000 Subject: [PATCH] - Added handler classes CRmgTemplateStorage and CTerrainViewPatternConfig to LibClasses - Re-organized CMapGenerator - Created CZone and CTemplate objects in the heap and used pointers - Added stub classes CZoneGraphGenerator and CZonePlacer (include warnings of unused variables, please ignore them) - Fixed CRandomGenerator bug that always the same number was produced - Better structure of Visual Studio project files with using filters - Updated project files (VS, CMake) - Excluded compiler warning mismatched-tags (false positive) - Fixed a bug when compiling with unit tests enabled --- CMakeLists.txt | 6 +- lib/CMakeLists.txt | 6 + lib/CRandomGenerator.h | 6 +- lib/GameConstants.h | 1 + lib/VCMI_Lib.cpp | 10 + lib/VCMI_Lib.h | 4 + lib/VCMI_lib.vcxproj | 12 + lib/VCMI_lib.vcxproj.filters | 252 ++++++++ lib/mapping/CMapEditManager.cpp | 22 +- lib/mapping/CMapEditManager.h | 7 +- lib/rmg/CMapGenOptions.cpp | 410 ++++++++++++ lib/rmg/CMapGenOptions.h | 177 ++++++ lib/rmg/CMapGenerator.cpp | 1037 +------------------------------ lib/rmg/CMapGenerator.h | 371 +---------- lib/rmg/CRmgTemplate.cpp | 241 +++++++ lib/rmg/CRmgTemplate.h | 98 +++ lib/rmg/CRmgTemplateStorage.cpp | 232 +++++++ lib/rmg/CRmgTemplateStorage.h | 59 ++ lib/rmg/CRmgTemplateZone.cpp | 216 +++++++ lib/rmg/CRmgTemplateZone.h | 92 +++ lib/rmg/CZoneGraphGenerator.cpp | 34 + lib/rmg/CZoneGraphGenerator.h | 48 ++ lib/rmg/CZonePlacer.cpp | 35 ++ lib/rmg/CZonePlacer.h | 45 ++ test/CMapEditManagerTest.cpp | 5 +- 25 files changed, 2004 insertions(+), 1422 deletions(-) create mode 100644 lib/VCMI_lib.vcxproj.filters create mode 100644 lib/rmg/CMapGenOptions.cpp create mode 100644 lib/rmg/CMapGenOptions.h create mode 100644 lib/rmg/CRmgTemplate.cpp create mode 100644 lib/rmg/CRmgTemplate.h create mode 100644 lib/rmg/CRmgTemplateStorage.cpp create mode 100644 lib/rmg/CRmgTemplateStorage.h create mode 100644 lib/rmg/CRmgTemplateZone.cpp create mode 100644 lib/rmg/CRmgTemplateZone.h create mode 100644 lib/rmg/CZoneGraphGenerator.cpp create mode 100644 lib/rmg/CZoneGraphGenerator.h create mode 100644 lib/rmg/CZonePlacer.cpp create mode 100644 lib/rmg/CZonePlacer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bdf9fb5b9..54d0cc975 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,9 @@ if (ENABLE_EDITOR) endif() if(ENABLE_TEST) - find_package(Boost 1.46.0 COMPONENTS unit_test_framework REQUIRED) + # find_package overwrites BOOST_* variables which are already set, so all components have to be + # included again + find_package(Boost 1.46.0 COMPONENTS program_options filesystem system thread unit_test_framework REQUIRED) endif() if(NOT WIN32) @@ -80,7 +82,7 @@ if(NOT WIN32) endif() if(CMAKE_COMPILER_IS_GNUCXX OR NOT WIN32) #so far all *nix compilers support such parameters - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wextra -Wpointer-arith -Wno-switch -Wno-sign-compare -Wno-unused-parameter -Wno-overloaded-virtual") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wextra -Wpointer-arith -Wno-switch -Wno-sign-compare -Wno-unused-parameter -Wno-overloaded-virtual -Wno-mismatched-tags") endif() if(WIN32) # on Win everything goes into H3 root directory diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 502537767..589579a37 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -27,6 +27,12 @@ set(lib_SRCS mapping/MapFormatH3M.cpp rmg/CMapGenerator.cpp + rmg/CMapGenOptions.cpp + rmg/CRmgTemplate.cpp + rmg/CRmgTemplateZone.cpp + rmg/CRmgTemplateStorage.cpp + rmg/CZoneGraphGenerator.cpp + rmg/CZonePlacer.cpp BattleAction.cpp BattleHex.cpp diff --git a/lib/CRandomGenerator.h b/lib/CRandomGenerator.h index 35c4473cd..fb4618bdb 100644 --- a/lib/CRandomGenerator.h +++ b/lib/CRandomGenerator.h @@ -19,7 +19,7 @@ typedef std::function TRand; /// The random generator randomly generates integers and real numbers("doubles") between /// a given range. This is a header only class and mainly a wrapper for -/// convenient usage of the boost random API. +/// convenient usage of the standard random API. class CRandomGenerator { public: @@ -38,7 +38,7 @@ public: /// e.g.: auto a = gen.getRangeI(0,10); a(); a(); a(); TRandI getRangeI(int lower, int upper) { - return boost::bind(TIntDist(lower, upper), gen); + return boost::bind(TIntDist(lower, upper), boost::ref(gen)); } int getInteger(int lower, int upper) @@ -50,7 +50,7 @@ public: /// e.g.: auto a = gen.getRangeI(0,10); a(); a(); a(); TRand getRange(double lower, double upper) { - return boost::bind(TRealDist(lower, upper), gen); + return boost::bind(TRealDist(lower, upper), boost::ref(gen)); } double getDouble(double lower, double upper) diff --git a/lib/GameConstants.h b/lib/GameConstants.h index f4c9d1cd6..306928e87 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -892,6 +892,7 @@ typedef std::pair TDmgRange; typedef si32 TBonusSubtype; typedef si32 TQuantity; +typedef int TRmgTemplateZoneId; #undef ID_LIKE_CLASS_COMMON #undef ID_LIKE_OPERATORS_DECLS diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 7fb334199..96e11b212 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -27,6 +27,8 @@ #include "VCMIDirs.h" #include "filesystem/Filesystem.h" #include "CConsoleHandler.h" +#include "rmg/CRmgTemplateStorage.h" +#include "mapping/CMapEditManager.h" LibClasses * VLC = nullptr; @@ -111,6 +113,10 @@ void LibClasses::init() createHandler(spellh, "Spell", pomtime); + createHandler(terviewh, "Terrain view pattern", pomtime); + + createHandler(tplh, "Template", pomtime); + logGlobal->infoStream()<<"\tInitializing handlers: "<< totalTime.getDiff(); modh->loadGameContent(); @@ -133,6 +139,8 @@ void LibClasses::clear() delete spellh; delete modh; delete bth; + delete tplh; + delete terviewh; makeNull(); } @@ -148,6 +156,8 @@ void LibClasses::makeNull() spellh = nullptr; modh = nullptr; bth = nullptr; + tplh = nullptr; + terviewh = nullptr; } LibClasses::LibClasses() diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index ce4779288..b1b52b9bf 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -22,6 +22,8 @@ class CGeneralTextHandler; class CModHandler; class IBonusTypeHandler; class CBonusTypeHandler; +class CTerrainViewPatternConfig; +class CRmgTemplateStorage; /// Loads and constructs several handlers class DLL_LINKAGE LibClasses @@ -44,6 +46,8 @@ public: CTownHandler * townh; CGeneralTextHandler * generaltexth; CModHandler * modh; + CTerrainViewPatternConfig * terviewh; + CRmgTemplateStorage * tplh; LibClasses(); //c-tor, loads .lods and NULLs handlers ~LibClasses(); diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj index 2b4bfc8c2..3167be30a 100644 --- a/lib/VCMI_lib.vcxproj +++ b/lib/VCMI_lib.vcxproj @@ -212,6 +212,12 @@ + + + + + + VCMI_DLL;%(PreprocessorDefinitions) Create @@ -287,6 +293,12 @@ + + + + + + diff --git a/lib/VCMI_lib.vcxproj.filters b/lib/VCMI_lib.vcxproj.filters new file mode 100644 index 000000000..16dc4a7e8 --- /dev/null +++ b/lib/VCMI_lib.vcxproj.filters @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + filesystem + + + filesystem + + + logging + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + mapping + + + mapping + + + mapping + + + mapping + + + mapping + + + logging + + + filesystem + + + filesystem + + + filesystem + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + rmg + + + filesystem + + + filesystem + + + logging + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + filesystem + + + mapping + + + mapping + + + mapping + + + mapping + + + mapping + + + logging + + + filesystem + + + filesystem + + + filesystem + + + + + {3d02cdc5-d660-4937-8753-9633a07ad25a} + + + {00f77034-143f-4867-b422-c16e9f2bd3cb} + + + {e967e59c-781a-4794-9382-4a7a6d166cbf} + + + {96f9ac5c-05d9-43ae-b715-a1e6234075cd} + + + \ No newline at end of file diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index a113bba06..fa248fbf7 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -4,6 +4,7 @@ #include "../JsonNode.h" #include "../filesystem/Filesystem.h" #include "../CDefObjInfoHandler.h" +#include "../VCMI_Lib.h" MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0) { @@ -304,15 +305,6 @@ bool TerrainViewPattern::WeightedRule::isStandardRule() const || TerrainViewPattern::RULE_TRANSITION == name || TerrainViewPattern::RULE_NATIVE_STRONG == name; } -boost::mutex CTerrainViewPatternConfig::smx; - -CTerrainViewPatternConfig & CTerrainViewPatternConfig::get() -{ - TLockGuard _(smx); - static CTerrainViewPatternConfig instance; - return instance; -} - CTerrainViewPatternConfig::CTerrainViewPatternConfig() { const JsonNode config(ResourceID("config/terrainViewPatterns.json")); @@ -565,7 +557,7 @@ void CDrawTerrainOperation::updateTerrainViews() for(const auto & pos : invalidatedTerViews) { const auto & patterns = - CTerrainViewPatternConfig::get().getTerrainViewPatternsForGroup(getTerrainGroup(map->getTile(pos).terType)); + VLC->terviewh->getTerrainViewPatternsForGroup(getTerrainGroup(map->getTile(pos).terType)); // Detect a pattern which fits best int bestPattern = -1; @@ -696,7 +688,7 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi { if(terType == centerTerType) { - const auto & patternForRule = CTerrainViewPatternConfig::get().getTerrainViewPatternById(getTerrainGroup(centerTerType), rule.name); + const auto & patternForRule = VLC->terviewh->getTerrainViewPatternById(getTerrainGroup(centerTerType), rule.name); if(patternForRule) { auto rslt = validateTerrainView(currentPos, *patternForRule, 1); @@ -841,9 +833,9 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const { if(map->isInTheMap(pos)) { - auto & ptrConfig = CTerrainViewPatternConfig::get(); + auto ptrConfig = VLC->terviewh; auto terType = map->getTile(pos).terType; - auto valid = validateTerrainView(pos, ptrConfig.getTerrainTypePatternById("n1")).result; + auto valid = validateTerrainView(pos, ptrConfig->getTerrainTypePatternById("n1")).result; // Special validity check for rock & water if(valid && centerTerType != terType && (terType == ETerrainType::WATER || terType == ETerrainType::ROCK)) @@ -851,7 +843,7 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const static const std::string patternIds[] = { "s1", "s2" }; for(auto & patternId : patternIds) { - valid = !validateTerrainView(pos, ptrConfig.getTerrainTypePatternById(patternId)).result; + valid = !validateTerrainView(pos, ptrConfig->getTerrainTypePatternById(patternId)).result; if(!valid) break; } } @@ -861,7 +853,7 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const static const std::string patternIds[] = { "n2", "n3" }; for(auto & patternId : patternIds) { - valid = validateTerrainView(pos, ptrConfig.getTerrainTypePatternById(patternId)).result; + valid = validateTerrainView(pos, ptrConfig->getTerrainTypePatternById(patternId)).result; if(valid) break; } } diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 686a469b6..30abca9f6 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -279,7 +279,8 @@ struct DLL_LINKAGE TerrainViewPattern class DLL_LINKAGE CTerrainViewPatternConfig : public boost::noncopyable { public: - static CTerrainViewPatternConfig & get(); + CTerrainViewPatternConfig(); + ~CTerrainViewPatternConfig(); const std::vector & getTerrainViewPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const; boost::optional getTerrainViewPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const; @@ -287,12 +288,8 @@ public: ETerrainGroup::ETerrainGroup getTerrainGroup(const std::string & terGroup) const; private: - CTerrainViewPatternConfig(); - ~CTerrainViewPatternConfig(); - std::map > terrainViewPatterns; std::map terrainTypePatterns; - static boost::mutex smx; }; /// The CDrawTerrainOperation class draws a terrain area on the map. diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp new file mode 100644 index 000000000..2382918bf --- /dev/null +++ b/lib/rmg/CMapGenOptions.cpp @@ -0,0 +1,410 @@ + +/* + * CMapGenOptions.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CMapGenOptions.h" + +#include "../GameConstants.h" +#include "../mapping/CMap.h" +#include "CRmgTemplateStorage.h" +#include "CRmgTemplate.h" +#include "../VCMI_Lib.h" +#include "../CTownHandler.h" + +CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false), + playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(0), compOnlyTeamCount(RANDOM_SIZE), + waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr) +{ + resetPlayersMap(); +} + +si32 CMapGenOptions::getWidth() const +{ + return width; +} + +void CMapGenOptions::setWidth(si32 value) +{ + assert(value >= 1); + width = value; +} + +si32 CMapGenOptions::getHeight() const +{ + return height; +} + +void CMapGenOptions::setHeight(si32 value) +{ + assert(value >= 1); + height = value; +} + +bool CMapGenOptions::getHasTwoLevels() const +{ + return hasTwoLevels; +} + +void CMapGenOptions::setHasTwoLevels(bool value) +{ + hasTwoLevels = value; +} + +si8 CMapGenOptions::getPlayerCount() const +{ + return playerCount; +} + +void CMapGenOptions::setPlayerCount(si8 value) +{ + assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE); + playerCount = value; + resetPlayersMap(); +} + +si8 CMapGenOptions::getTeamCount() const +{ + return teamCount; +} + +void CMapGenOptions::setTeamCount(si8 value) +{ + assert(playerCount == RANDOM_SIZE || (value >= 0 && value < playerCount) || value == RANDOM_SIZE); + teamCount = value; +} + +si8 CMapGenOptions::getCompOnlyPlayerCount() const +{ + return compOnlyPlayerCount; +} + +void CMapGenOptions::setCompOnlyPlayerCount(si8 value) +{ + assert(value == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - playerCount)); + compOnlyPlayerCount = value; + resetPlayersMap(); +} + +si8 CMapGenOptions::getCompOnlyTeamCount() const +{ + return compOnlyTeamCount; +} + +void CMapGenOptions::setCompOnlyTeamCount(si8 value) +{ + assert(value == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayerCount - 1, 0))); + compOnlyTeamCount = value; +} + +EWaterContent::EWaterContent CMapGenOptions::getWaterContent() const +{ + return waterContent; +} + +void CMapGenOptions::setWaterContent(EWaterContent::EWaterContent value) +{ + waterContent = value; +} + +EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const +{ + return monsterStrength; +} + +void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value) +{ + monsterStrength = value; +} + +void CMapGenOptions::resetPlayersMap() +{ + players.clear(); + int realPlayersCnt = playerCount == RANDOM_SIZE ? static_cast(PlayerColor::PLAYER_LIMIT_I) : playerCount; + int realCompOnlyPlayersCnt = compOnlyPlayerCount == RANDOM_SIZE ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount; + for(int color = 0; color < (realPlayersCnt + realCompOnlyPlayersCnt); ++color) + { + CPlayerSettings player; + player.setColor(PlayerColor(color)); + player.setPlayerType((color >= realPlayersCnt) ? EPlayerType::COMP_ONLY : EPlayerType::AI); + players[PlayerColor(color)] = player; + } +} + +const std::map & CMapGenOptions::getPlayersSettings() const +{ + return players; +} + +void CMapGenOptions::setStartingTownForPlayer(PlayerColor color, si32 town) +{ + auto it = players.find(color); + if(it == players.end()) assert(0); + it->second.setStartingTown(town); +} + +void CMapGenOptions::setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType) +{ + assert(playerType != EPlayerType::COMP_ONLY); + auto it = players.find(color); + if(it == players.end()) assert(0); + it->second.setPlayerType(playerType); +} + +const CRmgTemplate * CMapGenOptions::getMapTemplate() const +{ + return mapTemplate; +} + +void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) +{ + mapTemplate = value; + //TODO validate & adapt options according to template + assert(0); +} + +const std::map & CMapGenOptions::getAvailableTemplates() const +{ + return VLC->tplh->getTemplates(); +} + +void CMapGenOptions::finalize() +{ + CRandomGenerator gen; + finalize(gen); +} + +void CMapGenOptions::finalize(CRandomGenerator & gen) +{ + if(!mapTemplate) + { + mapTemplate = getPossibleTemplate(gen); + assert(mapTemplate); + } + + if(playerCount == RANDOM_SIZE) + { + auto possiblePlayers = mapTemplate->getPlayers().getNumbers(); + possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers())); + assert(!possiblePlayers.empty()); + playerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); + updatePlayers(); + } + if(teamCount == RANDOM_SIZE) + { + teamCount = gen.getInteger(0, playerCount - 1); + } + if(compOnlyPlayerCount == RANDOM_SIZE) + { + auto possiblePlayers = mapTemplate->getCpuPlayers().getNumbers(); + compOnlyPlayerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); + updateCompOnlyPlayers(); + } + if(compOnlyTeamCount == RANDOM_SIZE) + { + compOnlyTeamCount = gen.getInteger(0, std::max(compOnlyPlayerCount - 1, 0)); + } + + // 1 team isn't allowed + if(teamCount == 1 && compOnlyPlayerCount == 0) + { + teamCount = 0; + } + + if(waterContent == EWaterContent::RANDOM) + { + waterContent = static_cast(gen.getInteger(0, 2)); + } + if(monsterStrength == EMonsterStrength::RANDOM) + { + monsterStrength = static_cast(gen.getInteger(0, 2)); + } +} + +void CMapGenOptions::updatePlayers() +{ + // Remove AI players only from the end of the players map if necessary + for(auto itrev = players.end(); itrev != players.begin();) + { + auto it = itrev; + --it; + if(players.size() == playerCount) break; + if(it->second.getPlayerType() == EPlayerType::AI) + { + players.erase(it); + } + else + { + --itrev; + } + } +} + +void CMapGenOptions::updateCompOnlyPlayers() +{ + auto totalPlayersCnt = playerCount + compOnlyPlayerCount; + + // Remove comp only players only from the end of the players map if necessary + for(auto itrev = players.end(); itrev != players.begin();) + { + auto it = itrev; + --it; + if(players.size() <= totalPlayersCnt) break; + if(it->second.getPlayerType() == EPlayerType::COMP_ONLY) + { + players.erase(it); + } + else + { + --itrev; + } + } + + // Add some comp only players if necessary + auto compOnlyPlayersToAdd = totalPlayersCnt - players.size(); + for(int i = 0; i < compOnlyPlayersToAdd; ++i) + { + CPlayerSettings pSettings; + pSettings.setPlayerType(EPlayerType::COMP_ONLY); + pSettings.setColor(getNextPlayerColor()); + players[pSettings.getColor()] = pSettings; + } +} + +int CMapGenOptions::countHumanPlayers() const +{ + return static_cast(boost::count_if(players, [](const std::pair & pair) + { + return pair.second.getPlayerType() == EPlayerType::HUMAN; + })); +} + +PlayerColor CMapGenOptions::getNextPlayerColor() const +{ + for(PlayerColor i = PlayerColor(0); i < PlayerColor::PLAYER_LIMIT; i.advance(1)) + { + if(!players.count(i)) + { + return i; + } + } + assert(0); + return PlayerColor(0); +} + +bool CMapGenOptions::checkOptions() const +{ + assert(countHumanPlayers() > 0); + if(mapTemplate) + { + return true; + } + else + { + CRandomGenerator gen; + return getPossibleTemplate(gen) != nullptr; + } +} + +const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & gen) const +{ + // Find potential templates + const auto & tpls = getAvailableTemplates(); + std::list potentialTpls; + for(const auto & tplPair : tpls) + { + const auto & tpl = tplPair.second; + CRmgTemplate::CSize tplSize(width, height, hasTwoLevels); + if(tplSize >= tpl->getMinSize() && tplSize <= tpl->getMaxSize()) + { + bool isPlayerCountValid = false; + if(playerCount != RANDOM_SIZE) + { + if(tpl->getPlayers().isInRange(playerCount)) isPlayerCountValid = true; + } + else + { + // Human players shouldn't be banned when playing with random player count + auto playerNumbers = tpl->getPlayers().getNumbers(); + if(playerNumbers.lower_bound(countHumanPlayers()) != playerNumbers.end()) + { + isPlayerCountValid = true; + } + } + + if(isPlayerCountValid) + { + bool isCpuPlayerCountValid = false; + if(compOnlyPlayerCount != RANDOM_SIZE) + { + if(tpl->getCpuPlayers().isInRange(compOnlyPlayerCount)) isCpuPlayerCountValid = true; + } + else + { + isCpuPlayerCountValid = true; + } + + if(isCpuPlayerCountValid) potentialTpls.push_back(tpl); + } + } + } + + // Select tpl + if(potentialTpls.empty()) + { + return nullptr; + } + else + { + return *std::next(potentialTpls.begin(), gen.getInteger(0, potentialTpls.size() - 1)); + } +} + +CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI) +{ + +} + +PlayerColor CMapGenOptions::CPlayerSettings::getColor() const +{ + return color; +} + +void CMapGenOptions::CPlayerSettings::setColor(PlayerColor value) +{ + assert(value >= PlayerColor(0) && value < PlayerColor::PLAYER_LIMIT); + color = value; +} + +si32 CMapGenOptions::CPlayerSettings::getStartingTown() const +{ + return startingTown; +} + +void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value) +{ + assert(value >= -1); + if(value >= 0) + { + assert(value < static_cast(VLC->townh->factions.size())); + assert(VLC->townh->factions[value]->town != nullptr); + } + startingTown = value; +} + +EPlayerType::EPlayerType CMapGenOptions::CPlayerSettings::getPlayerType() const +{ + return playerType; +} + +void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType::EPlayerType value) +{ + playerType = value; +} diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h new file mode 100644 index 000000000..c564606f4 --- /dev/null +++ b/lib/rmg/CMapGenOptions.h @@ -0,0 +1,177 @@ + +/* + * CMapGenOptions.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "../GameConstants.h" +#include "../CRandomGenerator.h" + +class CRmgTemplate; + +namespace EWaterContent +{ +enum EWaterContent +{ + RANDOM = -1, + NONE, + NORMAL, + ISLANDS +}; +} + +namespace EMonsterStrength +{ +enum EMonsterStrength +{ + RANDOM = -1, + WEAK, + NORMAL, + STRONG +}; +} + +namespace EPlayerType +{ +enum EPlayerType +{ + HUMAN, + AI, + COMP_ONLY +}; +} + +/// The map gen options class holds values about general map generation settings +/// e.g. the size of the map, the count of players,... +class DLL_LINKAGE CMapGenOptions +{ +public: + /// The player settings class maps the player color, starting town and human player flag. + class DLL_LINKAGE CPlayerSettings + { + public: + CPlayerSettings(); + + /// The color of the player ranging from 0 to PlayerColor::PLAYER_LIMIT - 1. + /// The default value is 0. + PlayerColor getColor() const; + void setColor(PlayerColor value); + + /// The starting town of the player ranging from 0 to town max count or RANDOM_TOWN. + /// The default value is RANDOM_TOWN. + si32 getStartingTown() const; + void setStartingTown(si32 value); + + /// The default value is EPlayerType::AI. + EPlayerType::EPlayerType getPlayerType() const; + void setPlayerType(EPlayerType::EPlayerType value); + + /// Constant for a random town selection. + static const si32 RANDOM_TOWN = -1; + + private: + PlayerColor color; + si32 startingTown; + EPlayerType::EPlayerType playerType; + + public: + template + void serialize(Handler & h, const int version) + { + h & color & startingTown & playerType; + } + }; + + CMapGenOptions(); + + si32 getWidth() const; + void setWidth(si32 value); + + si32 getHeight() const; + void setHeight(si32 value); + + bool getHasTwoLevels() const; + void setHasTwoLevels(bool value); + + /// The count of the players ranging from 1 to PlayerColor::PLAYER_LIMIT or RANDOM_SIZE for random. If you call + /// this method, all player settings are reset to default settings. + si8 getPlayerCount() const; + void setPlayerCount(si8 value); + + /// The count of the teams ranging from 0 to or RANDOM_SIZE for random. + si8 getTeamCount() const; + void setTeamCount(si8 value); + + /// The count of the computer only players ranging from 0 to or RANDOM_SIZE for random. + /// If you call this method, all player settings are reset to default settings. + si8 getCompOnlyPlayerCount() const; + void setCompOnlyPlayerCount(si8 value); + + /// The count of the computer only teams ranging from 0 to or RANDOM_SIZE for random. + si8 getCompOnlyTeamCount() const; + void setCompOnlyTeamCount(si8 value); + + EWaterContent::EWaterContent getWaterContent() const; + void setWaterContent(EWaterContent::EWaterContent value); + + EMonsterStrength::EMonsterStrength getMonsterStrength() const; + void setMonsterStrength(EMonsterStrength::EMonsterStrength value); + + /// The first player colors belong to standard players and the last player colors belong to comp only players. + /// All standard players are by default of type EPlayerType::AI. + const std::map & getPlayersSettings() const; + void setStartingTownForPlayer(PlayerColor color, si32 town); + /// Sets a player type for a standard player. A standard player is the opposite of a computer only player. The + /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. + void setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType); + + /// The random map template to generate the map with or empty/not set if the template should be chosen randomly. + /// Default: Not set/random. + const CRmgTemplate * getMapTemplate() const; + void setMapTemplate(const CRmgTemplate * value); + + const std::map & getAvailableTemplates() const; + + /// Finalizes the options. All random sizes for various properties will be overwritten by numbers from + /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise + /// this function fails. + void finalize(); + void finalize(CRandomGenerator & gen); + + /// Returns false if there is no template available which fits to the currently selected options. + bool checkOptions() const; + + static const si8 RANDOM_SIZE = -1; + +private: + void resetPlayersMap(); + int countHumanPlayers() const; + PlayerColor getNextPlayerColor() const; + void updateCompOnlyPlayers(); + void updatePlayers(); + const CRmgTemplate * getPossibleTemplate(CRandomGenerator & gen) const; + + si32 width, height; + bool hasTwoLevels; + si8 playerCount, teamCount, compOnlyPlayerCount, compOnlyTeamCount; + EWaterContent::EWaterContent waterContent; + EMonsterStrength::EMonsterStrength monsterStrength; + std::map players; + const CRmgTemplate * mapTemplate; + +public: + template + void serialize(Handler & h, const int version) + { + h & width & height & hasTwoLevels & playerCount & teamCount & compOnlyPlayerCount; + h & compOnlyTeamCount & waterContent & monsterStrength & players; + //TODO add name of template to class, enables selection of a template by a user + } +}; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index b12c3b58b..d557e76af 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -1,3 +1,14 @@ + +/* + * CMapGenerator.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + #include "StdInc.h" #include "CMapGenerator.h" @@ -9,397 +20,7 @@ #include "../CDefObjInfoHandler.h" #include "../CTownHandler.h" #include "../StringConstants.h" -#include "../filesystem/Filesystem.h" - -CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false), - playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(0), compOnlyTeamCount(RANDOM_SIZE), - waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr) -{ - resetPlayersMap(); -} - -si32 CMapGenOptions::getWidth() const -{ - return width; -} - -void CMapGenOptions::setWidth(si32 value) -{ - assert(value >= 1); - width = value; -} - -si32 CMapGenOptions::getHeight() const -{ - return height; -} - -void CMapGenOptions::setHeight(si32 value) -{ - assert(value >= 1); - height = value; -} - -bool CMapGenOptions::getHasTwoLevels() const -{ - return hasTwoLevels; -} - -void CMapGenOptions::setHasTwoLevels(bool value) -{ - hasTwoLevels = value; -} - -si8 CMapGenOptions::getPlayerCount() const -{ - return playerCount; -} - -void CMapGenOptions::setPlayerCount(si8 value) -{ - assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE); - playerCount = value; - resetPlayersMap(); -} - -si8 CMapGenOptions::getTeamCount() const -{ - return teamCount; -} - -void CMapGenOptions::setTeamCount(si8 value) -{ - assert(playerCount == RANDOM_SIZE || (value >= 0 && value < playerCount) || value == RANDOM_SIZE); - teamCount = value; -} - -si8 CMapGenOptions::getCompOnlyPlayerCount() const -{ - return compOnlyPlayerCount; -} - -void CMapGenOptions::setCompOnlyPlayerCount(si8 value) -{ - assert(value == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - playerCount)); - compOnlyPlayerCount = value; - resetPlayersMap(); -} - -si8 CMapGenOptions::getCompOnlyTeamCount() const -{ - return compOnlyTeamCount; -} - -void CMapGenOptions::setCompOnlyTeamCount(si8 value) -{ - assert(value == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayerCount - 1, 0))); - compOnlyTeamCount = value; -} - -EWaterContent::EWaterContent CMapGenOptions::getWaterContent() const -{ - return waterContent; -} - -void CMapGenOptions::setWaterContent(EWaterContent::EWaterContent value) -{ - waterContent = value; -} - -EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const -{ - return monsterStrength; -} - -void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value) -{ - monsterStrength = value; -} - -void CMapGenOptions::resetPlayersMap() -{ - players.clear(); - int realPlayersCnt = playerCount == RANDOM_SIZE ? static_cast(PlayerColor::PLAYER_LIMIT_I) : playerCount; - int realCompOnlyPlayersCnt = compOnlyPlayerCount == RANDOM_SIZE ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount; - for(int color = 0; color < (realPlayersCnt + realCompOnlyPlayersCnt); ++color) - { - CPlayerSettings player; - player.setColor(PlayerColor(color)); - player.setPlayerType((color >= realPlayersCnt) ? EPlayerType::COMP_ONLY : EPlayerType::AI); - players[PlayerColor(color)] = player; - } -} - -const std::map & CMapGenOptions::getPlayersSettings() const -{ - return players; -} - -void CMapGenOptions::setStartingTownForPlayer(PlayerColor color, si32 town) -{ - auto it = players.find(color); - if(it == players.end()) assert(0); - it->second.setStartingTown(town); -} - -void CMapGenOptions::setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType) -{ - assert(playerType != EPlayerType::COMP_ONLY); - auto it = players.find(color); - if(it == players.end()) assert(0); - it->second.setPlayerType(playerType); -} - -const CRmgTemplate * CMapGenOptions::getMapTemplate() const -{ - return mapTemplate; -} - -void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) -{ - mapTemplate = value; - //TODO validate & adapt options according to template - assert(0); -} - -const std::map & CMapGenOptions::getAvailableTemplates() const -{ - return CRmgTemplateStorage::get().getTemplates(); -} - -void CMapGenOptions::finalize() -{ - CRandomGenerator gen; - finalize(gen); -} - -void CMapGenOptions::finalize(CRandomGenerator & gen) -{ - if(!mapTemplate) - { - mapTemplate = getPossibleTemplate(gen); - assert(mapTemplate); - } - - if(playerCount == RANDOM_SIZE) - { - auto possiblePlayers = mapTemplate->getPlayers().getNumbers(); - possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers())); - assert(!possiblePlayers.empty()); - playerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); - updatePlayers(); - } - if(teamCount == RANDOM_SIZE) - { - teamCount = gen.getInteger(0, playerCount - 1); - } - if(compOnlyPlayerCount == RANDOM_SIZE) - { - auto possiblePlayers = mapTemplate->getCpuPlayers().getNumbers(); - compOnlyPlayerCount = *std::next(possiblePlayers.begin(), gen.getInteger(0, possiblePlayers.size() - 1)); - updateCompOnlyPlayers(); - } - if(compOnlyTeamCount == RANDOM_SIZE) - { - compOnlyTeamCount = gen.getInteger(0, std::max(compOnlyPlayerCount - 1, 0)); - } - - // 1 team isn't allowed - if(teamCount == 1 && compOnlyPlayerCount == 0) - { - teamCount = 0; - } - - if(waterContent == EWaterContent::RANDOM) - { - waterContent = static_cast(gen.getInteger(0, 2)); - } - if(monsterStrength == EMonsterStrength::RANDOM) - { - monsterStrength = static_cast(gen.getInteger(0, 2)); - } -} - -void CMapGenOptions::updatePlayers() -{ - // Remove AI players only from the end of the players map if necessary - for(auto itrev = players.end(); itrev != players.begin();) - { - auto it = itrev; - --it; - if(players.size() == playerCount) break; - if(it->second.getPlayerType() == EPlayerType::AI) - { - players.erase(it); - } - else - { - --itrev; - } - } -} - -void CMapGenOptions::updateCompOnlyPlayers() -{ - auto totalPlayersCnt = playerCount + compOnlyPlayerCount; - - // Remove comp only players only from the end of the players map if necessary - for(auto itrev = players.end(); itrev != players.begin();) - { - auto it = itrev; - --it; - if(players.size() <= totalPlayersCnt) break; - if(it->second.getPlayerType() == EPlayerType::COMP_ONLY) - { - players.erase(it); - } - else - { - --itrev; - } - } - - // Add some comp only players if necessary - auto compOnlyPlayersToAdd = totalPlayersCnt - players.size(); - for(int i = 0; i < compOnlyPlayersToAdd; ++i) - { - CPlayerSettings pSettings; - pSettings.setPlayerType(EPlayerType::COMP_ONLY); - pSettings.setColor(getNextPlayerColor()); - players[pSettings.getColor()] = pSettings; - } -} - -int CMapGenOptions::countHumanPlayers() const -{ - return static_cast(boost::count_if(players, [](const std::pair & pair) - { - return pair.second.getPlayerType() == EPlayerType::HUMAN; - })); -} - -PlayerColor CMapGenOptions::getNextPlayerColor() const -{ - for(PlayerColor i = PlayerColor(0); i < PlayerColor::PLAYER_LIMIT; i.advance(1)) - { - if(!players.count(i)) - { - return i; - } - } - assert(0); - return PlayerColor(0); -} - -bool CMapGenOptions::checkOptions() const -{ - assert(countHumanPlayers() > 0); - if(mapTemplate) - { - return true; - } - else - { - CRandomGenerator gen; - return getPossibleTemplate(gen) != nullptr; - } -} - -const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & gen) const -{ - // Find potential templates - const auto & tpls = getAvailableTemplates(); - std::list potentialTpls; - for(const auto & tplPair : tpls) - { - const auto & tpl = tplPair.second; - CRmgTemplate::CSize tplSize(width, height, hasTwoLevels); - if(tplSize >= tpl.getMinSize() && tplSize <= tpl.getMaxSize()) - { - bool isPlayerCountValid = false; - if(playerCount != RANDOM_SIZE) - { - if(tpl.getPlayers().isInRange(playerCount)) isPlayerCountValid = true; - } - else - { - // Human players shouldn't be banned when playing with random player count - auto playerNumbers = tpl.getPlayers().getNumbers(); - if(playerNumbers.lower_bound(countHumanPlayers()) != playerNumbers.end()) - { - isPlayerCountValid = true; - } - } - - if(isPlayerCountValid) - { - bool isCpuPlayerCountValid = false; - if(compOnlyPlayerCount != RANDOM_SIZE) - { - if(tpl.getCpuPlayers().isInRange(compOnlyPlayerCount)) isCpuPlayerCountValid = true; - } - else - { - isCpuPlayerCountValid = true; - } - - if(isCpuPlayerCountValid) potentialTpls.push_back(&tpl); - } - } - } - - // Select tpl - if(potentialTpls.empty()) - { - return nullptr; - } - else - { - return *std::next(potentialTpls.begin(), gen.getInteger(0, potentialTpls.size() - 1)); - } -} - -CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI) -{ - -} - -PlayerColor CMapGenOptions::CPlayerSettings::getColor() const -{ - return color; -} - -void CMapGenOptions::CPlayerSettings::setColor(PlayerColor value) -{ - assert(value >= PlayerColor(0) && value < PlayerColor::PLAYER_LIMIT); - color = value; -} - -si32 CMapGenOptions::CPlayerSettings::getStartingTown() const -{ - return startingTown; -} - -void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value) -{ - assert(value >= -1); - if(value >= 0) - { - assert(value < static_cast(VLC->townh->factions.size())); - assert(VLC->townh->factions[value]->town != nullptr); - } - startingTown = value; -} - -EPlayerType::EPlayerType CMapGenOptions::CPlayerSettings::getPlayerType() const -{ - return playerType; -} - -void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType::EPlayerType value) -{ - playerType = value; -} +#include "CRmgTemplate.h" CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, int randomSeed /*= std::time(nullptr)*/) : mapGenOptions(mapGenOptions), randomSeed(randomSeed) @@ -563,637 +184,3 @@ void CMapGenerator::addHeaderInfo() map->difficulty = 1; addPlayerInfo(); } - -CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0) -{ - -} - -int CRmgTemplateZone::CTownInfo::getTownCount() const -{ - return townCount; -} - -void CRmgTemplateZone::CTownInfo::setTownCount(int value) -{ - if(value < 0) throw std::runtime_error("Negative value for town count not allowed."); - townCount = value; -} - -int CRmgTemplateZone::CTownInfo::getCastleCount() const -{ - return castleCount; -} - -void CRmgTemplateZone::CTownInfo::setCastleCount(int value) -{ - if(value < 0) throw std::runtime_error("Negative value for castle count not allowed."); - castleCount = value; -} - -int CRmgTemplateZone::CTownInfo::getTownDensity() const -{ - return townDensity; -} - -void CRmgTemplateZone::CTownInfo::setTownDensity(int value) -{ - if(value < 0) throw std::runtime_error("Negative value for town density not allowed."); - townDensity = value; -} - -int CRmgTemplateZone::CTownInfo::getCastleDensity() const -{ - return castleDensity; -} - -void CRmgTemplateZone::CTownInfo::setCastleDensity(int value) -{ - if(value < 0) throw std::runtime_error("Negative value for castle density not allowed."); - castleDensity = value; -} - -CRmgTemplateZone::CRmgTemplateZone() : id(0), type(ETemplateZoneType::PLAYER_START), size(1), - townsAreSameType(false), matchTerrainToTown(true) -{ - townTypes = getDefaultTownTypes(); - terrainTypes = getDefaultTerrainTypes(); -} - -TRmgTemplateZoneId CRmgTemplateZone::getId() const -{ - return id; -} - -void CRmgTemplateZone::setId(TRmgTemplateZoneId value) -{ - if(value <= 0) throw std::runtime_error("Zone id should be greater than 0."); - id = value; -} - -ETemplateZoneType::ETemplateZoneType CRmgTemplateZone::getType() const -{ - return type; -} -void CRmgTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value) -{ - type = value; -} - -int CRmgTemplateZone::getSize() const -{ - return size; -} - -void CRmgTemplateZone::setSize(int value) -{ - if(value <= 0) throw std::runtime_error("Zone size needs to be greater than 0."); - size = value; -} - -boost::optional CRmgTemplateZone::getOwner() const -{ - return owner; -} - -void CRmgTemplateZone::setOwner(boost::optional value) -{ - if(!(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I)) throw std::runtime_error("Owner has to be in range 0 to max player count."); - owner = value; -} - -const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getPlayerTowns() const -{ - return playerTowns; -} - -void CRmgTemplateZone::setPlayerTowns(const CTownInfo & value) -{ - playerTowns = value; -} - -const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getNeutralTowns() const -{ - return neutralTowns; -} - -void CRmgTemplateZone::setNeutralTowns(const CTownInfo & value) -{ - neutralTowns = value; -} - -bool CRmgTemplateZone::getTownsAreSameType() const -{ - return townsAreSameType; -} - -void CRmgTemplateZone::setTownsAreSameType(bool value) -{ - townsAreSameType = value; -} - -const std::set & CRmgTemplateZone::getTownTypes() const -{ - return townTypes; -} - -void CRmgTemplateZone::setTownTypes(const std::set & value) -{ - townTypes = value; -} - -std::set CRmgTemplateZone::getDefaultTownTypes() const -{ - std::set defaultTowns; - auto towns = VLC->townh->getDefaultAllowed(); - for(int i = 0; i < towns.size(); ++i) - { - if(towns[i]) defaultTowns.insert(i); - } - return defaultTowns; -} - -bool CRmgTemplateZone::getMatchTerrainToTown() const -{ - return matchTerrainToTown; -} - -void CRmgTemplateZone::setMatchTerrainToTown(bool value) -{ - matchTerrainToTown = value; -} - -const std::set & CRmgTemplateZone::getTerrainTypes() const -{ - return terrainTypes; -} - -void CRmgTemplateZone::setTerrainTypes(const std::set & value) -{ - assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && - value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); - terrainTypes = value; -} - -std::set CRmgTemplateZone::getDefaultTerrainTypes() const -{ - std::set terTypes; - static const ETerrainType::EETerrainType allowedTerTypes[] = { ETerrainType::DIRT, ETerrainType::SAND, ETerrainType::GRASS, ETerrainType::SNOW, - ETerrainType::SWAMP, ETerrainType::ROUGH, ETerrainType::SUBTERRANEAN, ETerrainType::LAVA }; - for(auto & allowedTerType : allowedTerTypes) terTypes.insert(allowedTerType); - return terTypes; -} - -boost::optional CRmgTemplateZone::getTerrainTypeLikeZone() const -{ - return terrainTypeLikeZone; -} - -void CRmgTemplateZone::setTerrainTypeLikeZone(boost::optional value) -{ - terrainTypeLikeZone = value; -} - -boost::optional CRmgTemplateZone::getTownTypeLikeZone() const -{ - return townTypeLikeZone; -} - -void CRmgTemplateZone::setTownTypeLikeZone(boost::optional value) -{ - townTypeLikeZone = value; -} - -CRmgTemplateZoneConnection::CRmgTemplateZoneConnection() : zoneA(0), zoneB(0), guardStrength(0) -{ - -} - -TRmgTemplateZoneId CRmgTemplateZoneConnection::getZoneA() const -{ - return zoneA; -} - -void CRmgTemplateZoneConnection::setZoneA(TRmgTemplateZoneId value) -{ - zoneA = value; -} - -TRmgTemplateZoneId CRmgTemplateZoneConnection::getZoneB() const -{ - return zoneB; -} - -void CRmgTemplateZoneConnection::setZoneB(TRmgTemplateZoneId value) -{ - zoneB = value; -} - -int CRmgTemplateZoneConnection::getGuardStrength() const -{ - return guardStrength; -} - -void CRmgTemplateZoneConnection::setGuardStrength(int value) -{ - if(value < 0) throw std::runtime_error("Negative value for guard strenth not allowed."); - guardStrength = value; -} - -CRmgTemplate::CSize::CSize() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), under(true) -{ - -} - -CRmgTemplate::CSize::CSize(int width, int height, bool under) : under(under) -{ - setWidth(width); - setHeight(height); -} - -int CRmgTemplate::CSize::getWidth() const -{ - return width; -} - -void CRmgTemplate::CSize::setWidth(int value) -{ - if(value <= 0) throw std::runtime_error("Width > 0 failed."); - width = value; -} - -int CRmgTemplate::CSize::getHeight() const -{ - return height; -} - -void CRmgTemplate::CSize::setHeight(int value) -{ - if(value <= 0) throw std::runtime_error("Height > 0 failed."); - height = value; -} - -bool CRmgTemplate::CSize::getUnder() const -{ - return under; -} - -void CRmgTemplate::CSize::setUnder(bool value) -{ - under = value; -} - -bool CRmgTemplate::CSize::operator<=(const CSize & value) const -{ - if(width < value.width && height < value.height) - { - return true; - } - else if(width == value.width && height == value.height) - { - return under ? value.under : true; - } - else - { - return false; - } -} - -bool CRmgTemplate::CSize::operator>=(const CSize & value) const -{ - if(width > value.width && height > value.height) - { - return true; - } - else if(width == value.width && height == value.height) - { - return under ? true : !value.under; - } - else - { - return false; - } -} - -CRmgTemplate::CRmgTemplate() -{ - -} - -const std::string & CRmgTemplate::getName() const -{ - return name; -} - -void CRmgTemplate::setName(const std::string & value) -{ - name = value; -} - -const CRmgTemplate::CSize & CRmgTemplate::getMinSize() const -{ - return minSize; -} - -void CRmgTemplate::setMinSize(const CSize & value) -{ - minSize = value; -} - -const CRmgTemplate::CSize & CRmgTemplate::getMaxSize() const -{ - return maxSize; -} - -void CRmgTemplate::setMaxSize(const CSize & value) -{ - maxSize = value; -} - -const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const -{ - return players; -} - -void CRmgTemplate::setPlayers(const CPlayerCountRange & value) -{ - players = value; -} - -const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getCpuPlayers() const -{ - return cpuPlayers; -} - -void CRmgTemplate::setCpuPlayers(const CPlayerCountRange & value) -{ - cpuPlayers = value; -} - -const std::map & CRmgTemplate::getZones() const -{ - return zones; -} - -void CRmgTemplate::setZones(const std::map & value) -{ - zones = value; -} - -const std::list & CRmgTemplate::getConnections() const -{ - return connections; -} - -void CRmgTemplate::setConnections(const std::list & value) -{ - connections = value; -} - -void CRmgTemplate::validate() const -{ - //TODO add some validation checks, throw on failure -} - -void CRmgTemplate::CPlayerCountRange::addRange(int lower, int upper) -{ - range.push_back(std::make_pair(lower, upper)); -} - -void CRmgTemplate::CPlayerCountRange::addNumber(int value) -{ - range.push_back(std::make_pair(value, value)); -} - -bool CRmgTemplate::CPlayerCountRange::isInRange(int count) const -{ - for(const auto & pair : range) - { - if(count >= pair.first && count <= pair.second) return true; - } - return false; -} - -std::set CRmgTemplate::CPlayerCountRange::getNumbers() const -{ - std::set numbers; - for(const auto & pair : range) - { - for(int i = pair.first; i <= pair.second; ++i) numbers.insert(i); - } - return numbers; -} - -const std::map & CRmgTemplateLoader::getTemplates() const -{ - return templates; -} - -void CJsonRmgTemplateLoader::loadTemplates() -{ - const JsonNode rootNode(ResourceID("config/rmg.json")); - for(const auto & templatePair : rootNode.Struct()) - { - CRmgTemplate tpl; - try - { - tpl.setName(templatePair.first); - const auto & templateNode = templatePair.second; - - // Parse main template data - tpl.setMinSize(parseMapTemplateSize(templateNode["minSize"].String())); - tpl.setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String())); - tpl.setPlayers(parsePlayers(templateNode["players"].String())); - tpl.setCpuPlayers(parsePlayers(templateNode["cpu"].String())); - - // Parse zones - std::map zones; - for(const auto & zonePair : templateNode["zones"].Struct()) - { - CRmgTemplateZone zone; - auto zoneId = boost::lexical_cast(zonePair.first); - zone.setId(zoneId); - const auto & zoneNode = zonePair.second; - zone.setType(parseZoneType(zoneNode["type"].String())); - zone.setSize(zoneNode["size"].Float()); - if(!zoneNode["owner"].isNull()) zone.setOwner(zoneNode["owner"].Float()); - zone.setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"])); - zone.setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"])); - zone.setTownTypes(parseTownTypes(zoneNode["townTypes"].Vector(), zone.getDefaultTownTypes())); - zone.setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool()); - zone.setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), zone.getDefaultTerrainTypes())); - zone.setTownsAreSameType((zoneNode["townsAreSameType"].Bool())); - if(!zoneNode["terrainTypeLikeZone"].isNull()) zone.setTerrainTypeLikeZone(boost::lexical_cast(zoneNode["terrainTypeLikeZone"].String())); - if(!zoneNode["townTypeLikeZone"].isNull()) zone.setTownTypeLikeZone(boost::lexical_cast(zoneNode["townTypeLikeZone"].String())); - zones[zone.getId()] = zone; - } - tpl.setZones(zones); - - // Parse connections - std::list connections; - for(const auto & connPair : templateNode["connections"].Vector()) - { - CRmgTemplateZoneConnection conn; - conn.setZoneA(boost::lexical_cast(connPair["a"].String())); - conn.setZoneB(boost::lexical_cast(connPair["b"].String())); - conn.setGuardStrength(connPair["guard"].Float()); - connections.push_back(conn); - } - tpl.setConnections(connections); - tpl.validate(); - templates[tpl.getName()] = tpl; - } - catch(const std::exception & e) - { - logGlobal->errorStream() << boost::format("Template %s has errors. Message: %s.") % tpl.getName() % std::string(e.what()); - } - } -} - -CRmgTemplate::CSize CJsonRmgTemplateLoader::parseMapTemplateSize(const std::string & text) const -{ - CRmgTemplate::CSize size; - if(text.empty()) return size; - - std::vector parts; - boost::split(parts, text, boost::is_any_of("+")); - static const std::map mapSizeMapping = boost::assign::map_list_of("s", CMapHeader::MAP_SIZE_SMALL) - ("m", CMapHeader::MAP_SIZE_MIDDLE)("l", CMapHeader::MAP_SIZE_LARGE)("xl", CMapHeader::MAP_SIZE_XLARGE); - auto it = mapSizeMapping.find(parts[0]); - if(it == mapSizeMapping.end()) - { - // Map size is given as a number representation - const auto & numericalRep = parts[0]; - parts.clear(); - boost::split(parts, numericalRep, boost::is_any_of("x")); - assert(parts.size() == 3); - size.setWidth(boost::lexical_cast(parts[0])); - size.setHeight(boost::lexical_cast(parts[1])); - size.setUnder(boost::lexical_cast(parts[2]) == 1); - } - else - { - size.setWidth(it->second); - size.setHeight(it->second); - size.setUnder(parts.size() > 1 ? parts[1] == std::string("u") : false); - } - return size; -} - -ETemplateZoneType::ETemplateZoneType CJsonRmgTemplateLoader::parseZoneType(const std::string & type) const -{ - static const std::map zoneTypeMapping = boost::assign::map_list_of - ("playerStart", ETemplateZoneType::PLAYER_START)("cpuStart", ETemplateZoneType::CPU_START) - ("treasure", ETemplateZoneType::TREASURE)("junction", ETemplateZoneType::JUNCTION); - auto it = zoneTypeMapping.find(type); - if(it == zoneTypeMapping.end()) throw std::runtime_error("Zone type unknown."); - return it->second; -} - -CRmgTemplateZone::CTownInfo CJsonRmgTemplateLoader::parseTemplateZoneTowns(const JsonNode & node) const -{ - CRmgTemplateZone::CTownInfo towns; - towns.setTownCount(node["towns"].Float()); - towns.setCastleCount(node["castles"].Float()); - towns.setTownDensity(node["townDensity"].Float()); - towns.setCastleDensity(node["castleDensity"].Float()); - return towns; -} - -std::set CJsonRmgTemplateLoader::parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const -{ - std::set townTypes; - for(const auto & townTypeNode : townTypesVector) - { - auto townTypeStr = townTypeNode.String(); - if(townTypeStr == "all") return defaultTownTypes; - - bool foundFaction = false; - for(auto factionPtr : VLC->townh->factions) - { - if(factionPtr->town != nullptr && townTypeStr == factionPtr->name) - { - townTypes.insert(factionPtr->index); - foundFaction = true; - } - } - if(!foundFaction) throw std::runtime_error("Given faction is invalid."); - } - return townTypes; -} - -std::set CJsonRmgTemplateLoader::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const -{ - std::set terTypes; - for(const auto & node : terTypeStrings) - { - const auto & terTypeStr = node.String(); - if(terTypeStr == "all") return defaultTerrainTypes; - auto pos = vstd::find_pos(GameConstants::TERRAIN_NAMES, terTypeStr); - if (pos != -1) - { - terTypes.insert(ETerrainType(pos)); - } - else - { - throw std::runtime_error("Terrain type is invalid."); - } - } - return terTypes; -} - -CRmgTemplate::CPlayerCountRange CJsonRmgTemplateLoader::parsePlayers(const std::string & players) const -{ - CRmgTemplate::CPlayerCountRange playerRange; - if(players.empty()) - { - playerRange.addNumber(0); - return playerRange; - } - std::vector commaParts; - boost::split(commaParts, players, boost::is_any_of(",")); - for(const auto & commaPart : commaParts) - { - std::vector rangeParts; - boost::split(rangeParts, commaPart, boost::is_any_of("-")); - if(rangeParts.size() == 2) - { - auto lower = boost::lexical_cast(rangeParts[0]); - auto upper = boost::lexical_cast(rangeParts[1]); - playerRange.addRange(lower, upper); - } - else if(rangeParts.size() == 1) - { - auto val = boost::lexical_cast(rangeParts.front()); - playerRange.addNumber(val); - } - } - return playerRange; -} - -boost::mutex CRmgTemplateStorage::smx; - -CRmgTemplateStorage & CRmgTemplateStorage::get() -{ - TLockGuard _(smx); - static CRmgTemplateStorage storage; - return storage; -} - -const std::map & CRmgTemplateStorage::getTemplates() const -{ - return templates; -} - -CRmgTemplateStorage::CRmgTemplateStorage() -{ - auto jsonLoader = make_unique(); - jsonLoader->loadTemplates(); - const auto & tpls = jsonLoader->getTemplates(); - templates.insert(tpls.begin(), tpls.end()); -} - -CRmgTemplateStorage::~CRmgTemplateStorage() -{ - -} diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 68a307b4b..6d80d3459 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -11,333 +11,11 @@ #pragma once -#include "../GameConstants.h" #include "../CRandomGenerator.h" +#include "CMapGenOptions.h" class CMap; -class CTerrainViewPatternConfig; class CMapEditManager; -class JsonNode; - -typedef std::vector JsonVector; - -namespace ETemplateZoneType -{ -enum ETemplateZoneType -{ - PLAYER_START, - CPU_START, - TREASURE, - JUNCTION -}; -} - -typedef int TRmgTemplateZoneId; - -/// The CRmgTemplateZone describes a zone in a template. -class DLL_LINKAGE CRmgTemplateZone -{ -public: - class DLL_LINKAGE CTownInfo - { - public: - CTownInfo(); - - int getTownCount() const; /// Default: 0 - void setTownCount(int value); - int getCastleCount() const; /// Default: 0 - void setCastleCount(int value); - int getTownDensity() const; /// Default: 0 - void setTownDensity(int value); - int getCastleDensity() const; /// Default: 0 - void setCastleDensity(int value); - - private: - int townCount, castleCount, townDensity, castleDensity; - }; - - CRmgTemplateZone(); - - TRmgTemplateZoneId getId() const; /// Default: 0 - void setId(TRmgTemplateZoneId value); - ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::PLAYER_START - void setType(ETemplateZoneType::ETemplateZoneType value); - int getSize() const; /// Default: 1 - void setSize(int value); - boost::optional getOwner() const; - void setOwner(boost::optional value); - const CTownInfo & getPlayerTowns() const; - void setPlayerTowns(const CTownInfo & value); - const CTownInfo & getNeutralTowns() const; - void setNeutralTowns(const CTownInfo & value); - bool getTownsAreSameType() const; /// Default: false - void setTownsAreSameType(bool value); - const std::set & getTownTypes() const; /// Default: all - void setTownTypes(const std::set & value); - std::set getDefaultTownTypes() const; - bool getMatchTerrainToTown() const; /// Default: true - void setMatchTerrainToTown(bool value); - const std::set & getTerrainTypes() const; /// Default: all - void setTerrainTypes(const std::set & value); - std::set getDefaultTerrainTypes() const; - boost::optional getTerrainTypeLikeZone() const; - void setTerrainTypeLikeZone(boost::optional value); - boost::optional getTownTypeLikeZone() const; - void setTownTypeLikeZone(boost::optional value); - -private: - TRmgTemplateZoneId id; - ETemplateZoneType::ETemplateZoneType type; - int size; - boost::optional owner; - CTownInfo playerTowns, neutralTowns; - bool townsAreSameType; - std::set townTypes; - bool matchTerrainToTown; - std::set terrainTypes; - boost::optional terrainTypeLikeZone, townTypeLikeZone; -}; - -/// The CRmgTemplateZoneConnection describes the connection between two zones. -class DLL_LINKAGE CRmgTemplateZoneConnection -{ -public: - CRmgTemplateZoneConnection(); - - TRmgTemplateZoneId getZoneA() const; /// Default: 0 - void setZoneA(TRmgTemplateZoneId value); - TRmgTemplateZoneId getZoneB() const; /// Default: 0 - void setZoneB(TRmgTemplateZoneId value); - int getGuardStrength() const; /// Default: 0 - void setGuardStrength(int value); - -private: - TRmgTemplateZoneId zoneA, zoneB; - int guardStrength; -}; - -/// The CRmgTemplate describes a random map template. -class DLL_LINKAGE CRmgTemplate -{ -public: - class CSize - { - public: - CSize(); - CSize(int width, int height, bool under); - - int getWidth() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE - void setWidth(int value); - int getHeight() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE - void setHeight(int value); - bool getUnder() const; /// Default: true - void setUnder(bool value); - bool operator<=(const CSize & value) const; - bool operator>=(const CSize & value) const; - - private: - int width, height; - bool under; - }; - - class CPlayerCountRange - { - public: - void addRange(int lower, int upper); - void addNumber(int value); - bool isInRange(int count) const; - std::set getNumbers() const; - - private: - std::list > range; - }; - - CRmgTemplate(); - - const std::string & getName() const; - void setName(const std::string & value); - const CSize & getMinSize() const; - void setMinSize(const CSize & value); - const CSize & getMaxSize() const; - void setMaxSize(const CSize & value); - const CPlayerCountRange & getPlayers() const; - void setPlayers(const CPlayerCountRange & value); - const CPlayerCountRange & getCpuPlayers() const; - void setCpuPlayers(const CPlayerCountRange & value); - const std::map & getZones() const; - void setZones(const std::map & value); - const std::list & getConnections() const; - void setConnections(const std::list & value); - - void validate() const; /// Tests template on validity and throws exception on failure - -private: - std::string name; - CSize minSize, maxSize; - CPlayerCountRange players, cpuPlayers; - std::map zones; - std::list connections; -}; - -namespace EWaterContent -{ -enum EWaterContent -{ - RANDOM = -1, - NONE, - NORMAL, - ISLANDS -}; -} - -namespace EMonsterStrength -{ -enum EMonsterStrength -{ - RANDOM = -1, - WEAK, - NORMAL, - STRONG -}; -} - -namespace EPlayerType -{ -enum EPlayerType -{ - HUMAN, - AI, - COMP_ONLY -}; -} - -/// The map gen options class holds values about general map generation settings -/// e.g. the size of the map, the count of players,... -class DLL_LINKAGE CMapGenOptions -{ -public: - /// The player settings class maps the player color, starting town and human player flag. - class DLL_LINKAGE CPlayerSettings - { - public: - CPlayerSettings(); - - /// The color of the player ranging from 0 to PlayerColor::PLAYER_LIMIT - 1. - /// The default value is 0. - PlayerColor getColor() const; - void setColor(PlayerColor value); - - /// The starting town of the player ranging from 0 to town max count or RANDOM_TOWN. - /// The default value is RANDOM_TOWN. - si32 getStartingTown() const; - void setStartingTown(si32 value); - - /// The default value is EPlayerType::AI. - EPlayerType::EPlayerType getPlayerType() const; - void setPlayerType(EPlayerType::EPlayerType value); - - /// Constant for a random town selection. - static const si32 RANDOM_TOWN = -1; - - private: - PlayerColor color; - si32 startingTown; - EPlayerType::EPlayerType playerType; - - public: - template - void serialize(Handler & h, const int version) - { - h & color & startingTown & playerType; - } - }; - - CMapGenOptions(); - - si32 getWidth() const; - void setWidth(si32 value); - - si32 getHeight() const; - void setHeight(si32 value); - - bool getHasTwoLevels() const; - void setHasTwoLevels(bool value); - - /// The count of the players ranging from 1 to PlayerColor::PLAYER_LIMIT or RANDOM_SIZE for random. If you call - /// this method, all player settings are reset to default settings. - si8 getPlayerCount() const; - void setPlayerCount(si8 value); - - /// The count of the teams ranging from 0 to or RANDOM_SIZE for random. - si8 getTeamCount() const; - void setTeamCount(si8 value); - - /// The count of the computer only players ranging from 0 to or RANDOM_SIZE for random. - /// If you call this method, all player settings are reset to default settings. - si8 getCompOnlyPlayerCount() const; - void setCompOnlyPlayerCount(si8 value); - - /// The count of the computer only teams ranging from 0 to or RANDOM_SIZE for random. - si8 getCompOnlyTeamCount() const; - void setCompOnlyTeamCount(si8 value); - - EWaterContent::EWaterContent getWaterContent() const; - void setWaterContent(EWaterContent::EWaterContent value); - - EMonsterStrength::EMonsterStrength getMonsterStrength() const; - void setMonsterStrength(EMonsterStrength::EMonsterStrength value); - - /// The first player colors belong to standard players and the last player colors belong to comp only players. - /// All standard players are by default of type EPlayerType::AI. - const std::map & getPlayersSettings() const; - void setStartingTownForPlayer(PlayerColor color, si32 town); - /// Sets a player type for a standard player. A standard player is the opposite of a computer only player. The - /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. - void setPlayerTypeForStandardPlayer(PlayerColor color, EPlayerType::EPlayerType playerType); - - /// The random map template to generate the map with or empty/not set if the template should be chosen randomly. - /// Default: Not set/random. - const CRmgTemplate * getMapTemplate() const; - void setMapTemplate(const CRmgTemplate * value); - - const std::map & getAvailableTemplates() const; - - /// Finalizes the options. All random sizes for various properties will be overwritten by numbers from - /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise - /// this function fails. - void finalize(); - void finalize(CRandomGenerator & gen); - - /// Returns false if there is no template available which fits to the currently selected options. - bool checkOptions() const; - - static const si8 RANDOM_SIZE = -1; - -private: - void resetPlayersMap(); - int countHumanPlayers() const; - PlayerColor getNextPlayerColor() const; - void updateCompOnlyPlayers(); - void updatePlayers(); - const CRmgTemplate * getPossibleTemplate(CRandomGenerator & gen) const; - - si32 width, height; - bool hasTwoLevels; - si8 playerCount, teamCount, compOnlyPlayerCount, compOnlyTeamCount; - EWaterContent::EWaterContent waterContent; - EMonsterStrength::EMonsterStrength monsterStrength; - std::map players; - const CRmgTemplate * mapTemplate; - -public: - template - void serialize(Handler & h, const int version) - { - h & width & height & hasTwoLevels & playerCount & teamCount & compOnlyPlayerCount; - h & compOnlyTeamCount & waterContent & monsterStrength & players; - //TODO add name of template to class, enables selection of a template by a user - } -}; /// The map generator creates a map randomly. class DLL_LINKAGE CMapGenerator @@ -362,50 +40,3 @@ private: int randomSeed; CMapEditManager * editManager; }; - -/* ---------------------------------------------------------------------------- */ -/* Implementation/Detail classes, Private API */ -/* ---------------------------------------------------------------------------- */ - -/// The CRmgTemplateLoader is a abstract base class for loading templates. -class DLL_LINKAGE CRmgTemplateLoader -{ -public: - virtual ~CRmgTemplateLoader() { }; - virtual void loadTemplates() = 0; - const std::map & getTemplates() const; - -protected: - std::map templates; -}; - -/// The CJsonRmgTemplateLoader loads templates from a JSON file. -class DLL_LINKAGE CJsonRmgTemplateLoader : public CRmgTemplateLoader -{ -public: - void loadTemplates() override; - -private: - CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const; - CRmgTemplateZone::CTownInfo parseTemplateZoneTowns(const JsonNode & node) const; - ETemplateZoneType::ETemplateZoneType parseZoneType(const std::string & type) const; - std::set parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const; - std::set parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const; - CRmgTemplate::CPlayerCountRange parsePlayers(const std::string & players) const; -}; - -/// The CRmgTemplateStorage is a singleton object where templates are stored and which can be accessed from anywhere. -class DLL_LINKAGE CRmgTemplateStorage -{ -public: - static CRmgTemplateStorage & get(); - - const std::map & getTemplates() const; - -private: - CRmgTemplateStorage(); - ~CRmgTemplateStorage(); - - static boost::mutex smx; - std::map templates; /// Key: Template name -}; diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp new file mode 100644 index 000000000..10f62592b --- /dev/null +++ b/lib/rmg/CRmgTemplate.cpp @@ -0,0 +1,241 @@ + +/* + * CRmgTemplate.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CRmgTemplate.h" + +#include "CRmgTemplateZone.h" +#include "../mapping/CMap.h" + +CRmgTemplateZoneConnection::CRmgTemplateZoneConnection() : zoneA(nullptr), zoneB(nullptr), guardStrength(0) +{ + +} + +CRmgTemplateZone * CRmgTemplateZoneConnection::getZoneA() const +{ + return zoneA; +} + +void CRmgTemplateZoneConnection::setZoneA(CRmgTemplateZone * value) +{ + zoneA = value; +} + +CRmgTemplateZone * CRmgTemplateZoneConnection::getZoneB() const +{ + return zoneB; +} + +void CRmgTemplateZoneConnection::setZoneB(CRmgTemplateZone * value) +{ + zoneB = value; +} + +int CRmgTemplateZoneConnection::getGuardStrength() const +{ + return guardStrength; +} + +void CRmgTemplateZoneConnection::setGuardStrength(int value) +{ + if(value < 0) throw std::runtime_error("Negative value for guard strenth not allowed."); + guardStrength = value; +} + +CRmgTemplate::CSize::CSize() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), under(true) +{ + +} + +CRmgTemplate::CSize::CSize(int width, int height, bool under) : under(under) +{ + setWidth(width); + setHeight(height); +} + +int CRmgTemplate::CSize::getWidth() const +{ + return width; +} + +void CRmgTemplate::CSize::setWidth(int value) +{ + if(value <= 0) throw std::runtime_error("Width > 0 failed."); + width = value; +} + +int CRmgTemplate::CSize::getHeight() const +{ + return height; +} + +void CRmgTemplate::CSize::setHeight(int value) +{ + if(value <= 0) throw std::runtime_error("Height > 0 failed."); + height = value; +} + +bool CRmgTemplate::CSize::getUnder() const +{ + return under; +} + +void CRmgTemplate::CSize::setUnder(bool value) +{ + under = value; +} + +bool CRmgTemplate::CSize::operator<=(const CSize & value) const +{ + if(width < value.width && height < value.height) + { + return true; + } + else if(width == value.width && height == value.height) + { + return under ? value.under : true; + } + else + { + return false; + } +} + +bool CRmgTemplate::CSize::operator>=(const CSize & value) const +{ + if(width > value.width && height > value.height) + { + return true; + } + else if(width == value.width && height == value.height) + { + return under ? true : !value.under; + } + else + { + return false; + } +} + +CRmgTemplate::CRmgTemplate() +{ + +} + +CRmgTemplate::~CRmgTemplate() +{ + for (auto & pair : zones) delete pair.second; +} + +const std::string & CRmgTemplate::getName() const +{ + return name; +} + +void CRmgTemplate::setName(const std::string & value) +{ + name = value; +} + +const CRmgTemplate::CSize & CRmgTemplate::getMinSize() const +{ + return minSize; +} + +void CRmgTemplate::setMinSize(const CSize & value) +{ + minSize = value; +} + +const CRmgTemplate::CSize & CRmgTemplate::getMaxSize() const +{ + return maxSize; +} + +void CRmgTemplate::setMaxSize(const CSize & value) +{ + maxSize = value; +} + +const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const +{ + return players; +} + +void CRmgTemplate::setPlayers(const CPlayerCountRange & value) +{ + players = value; +} + +const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getCpuPlayers() const +{ + return cpuPlayers; +} + +void CRmgTemplate::setCpuPlayers(const CPlayerCountRange & value) +{ + cpuPlayers = value; +} + +const std::map & CRmgTemplate::getZones() const +{ + return zones; +} + +void CRmgTemplate::setZones(const std::map & value) +{ + zones = value; +} + +const std::list & CRmgTemplate::getConnections() const +{ + return connections; +} + +void CRmgTemplate::setConnections(const std::list & value) +{ + connections = value; +} + +void CRmgTemplate::validate() const +{ + //TODO add some validation checks, throw on failure +} + +void CRmgTemplate::CPlayerCountRange::addRange(int lower, int upper) +{ + range.push_back(std::make_pair(lower, upper)); +} + +void CRmgTemplate::CPlayerCountRange::addNumber(int value) +{ + range.push_back(std::make_pair(value, value)); +} + +bool CRmgTemplate::CPlayerCountRange::isInRange(int count) const +{ + for(const auto & pair : range) + { + if(count >= pair.first && count <= pair.second) return true; + } + return false; +} + +std::set CRmgTemplate::CPlayerCountRange::getNumbers() const +{ + std::set numbers; + for(const auto & pair : range) + { + for(int i = pair.first; i <= pair.second; ++i) numbers.insert(i); + } + return numbers; +} diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h new file mode 100644 index 000000000..a792b1691 --- /dev/null +++ b/lib/rmg/CRmgTemplate.h @@ -0,0 +1,98 @@ + +/* + * CRmgTemplate.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "../GameConstants.h" + +class CRmgTemplateZone; + +/// The CRmgTemplateZoneConnection describes the connection between two zones. +class DLL_LINKAGE CRmgTemplateZoneConnection +{ +public: + CRmgTemplateZoneConnection(); + + CRmgTemplateZone * getZoneA() const; + void setZoneA(CRmgTemplateZone * value); + CRmgTemplateZone * getZoneB() const; + void setZoneB(CRmgTemplateZone * value); + int getGuardStrength() const; /// Default: 0 + void setGuardStrength(int value); + +private: + CRmgTemplateZone * zoneA, * zoneB; + int guardStrength; +}; + +/// The CRmgTemplate describes a random map template. +class DLL_LINKAGE CRmgTemplate +{ +public: + class CSize + { + public: + CSize(); + CSize(int width, int height, bool under); + + int getWidth() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE + void setWidth(int value); + int getHeight() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE + void setHeight(int value); + bool getUnder() const; /// Default: true + void setUnder(bool value); + bool operator<=(const CSize & value) const; + bool operator>=(const CSize & value) const; + + private: + int width, height; + bool under; + }; + + class CPlayerCountRange + { + public: + void addRange(int lower, int upper); + void addNumber(int value); + bool isInRange(int count) const; + std::set getNumbers() const; + + private: + std::list > range; + }; + + CRmgTemplate(); + ~CRmgTemplate(); + + const std::string & getName() const; + void setName(const std::string & value); + const CSize & getMinSize() const; + void setMinSize(const CSize & value); + const CSize & getMaxSize() const; + void setMaxSize(const CSize & value); + const CPlayerCountRange & getPlayers() const; + void setPlayers(const CPlayerCountRange & value); + const CPlayerCountRange & getCpuPlayers() const; + void setCpuPlayers(const CPlayerCountRange & value); + const std::map & getZones() const; + void setZones(const std::map & value); + const std::list & getConnections() const; + void setConnections(const std::list & value); + + void validate() const; /// Tests template on validity and throws exception on failure + +private: + std::string name; + CSize minSize, maxSize; + CPlayerCountRange players, cpuPlayers; + std::map zones; + std::list connections; +}; diff --git a/lib/rmg/CRmgTemplateStorage.cpp b/lib/rmg/CRmgTemplateStorage.cpp new file mode 100644 index 000000000..2e12dc9bf --- /dev/null +++ b/lib/rmg/CRmgTemplateStorage.cpp @@ -0,0 +1,232 @@ + +/* + * CRmgTemplateStorage.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CRmgTemplateStorage.h" + +#include "CRmgTemplate.h" +#include "CRmgTemplateZone.h" +#include "../filesystem/Filesystem.h" +#include "../JsonNode.h" +#include "../mapping/CMap.h" +#include "../VCMI_Lib.h" +#include "../CTownHandler.h" +#include "../GameConstants.h" +#include "../StringConstants.h" + +const std::map & CRmgTemplateLoader::getTemplates() const +{ + return templates; +} + +void CJsonRmgTemplateLoader::loadTemplates() +{ + const JsonNode rootNode(ResourceID("config/rmg.json")); + for(const auto & templatePair : rootNode.Struct()) + { + auto tpl = new CRmgTemplate(); + try + { + tpl->setName(templatePair.first); + const auto & templateNode = templatePair.second; + + // Parse main template data + tpl->setMinSize(parseMapTemplateSize(templateNode["minSize"].String())); + tpl->setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String())); + tpl->setPlayers(parsePlayers(templateNode["players"].String())); + tpl->setCpuPlayers(parsePlayers(templateNode["cpu"].String())); + + // Parse zones + std::map zones; + for(const auto & zonePair : templateNode["zones"].Struct()) + { + auto zone = new CRmgTemplateZone(); + auto zoneId = boost::lexical_cast(zonePair.first); + zone->setId(zoneId); + + const auto & zoneNode = zonePair.second; + zone->setType(parseZoneType(zoneNode["type"].String())); + zone->setSize(zoneNode["size"].Float()); + if(!zoneNode["owner"].isNull()) zone->setOwner(zoneNode["owner"].Float()); + + zone->setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"])); + zone->setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"])); + zone->setTownTypes(parseTownTypes(zoneNode["townTypes"].Vector(), zone->getDefaultTownTypes())); + zone->setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool()); + zone->setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), zone->getDefaultTerrainTypes())); + zone->setTownsAreSameType((zoneNode["townsAreSameType"].Bool())); + if(!zoneNode["terrainTypeLikeZone"].isNull()) zone->setTerrainTypeLikeZone(boost::lexical_cast(zoneNode["terrainTypeLikeZone"].String())); + if(!zoneNode["townTypeLikeZone"].isNull()) zone->setTownTypeLikeZone(boost::lexical_cast(zoneNode["townTypeLikeZone"].String())); + + zones[zone->getId()] = zone; + } + tpl->setZones(zones); + + // Parse connections + std::list connections; + for(const auto & connPair : templateNode["connections"].Vector()) + { + CRmgTemplateZoneConnection conn; + conn.setZoneA(zones.find(boost::lexical_cast(connPair["a"].String()))->second); + conn.setZoneB(zones.find(boost::lexical_cast(connPair["b"].String()))->second); + conn.setGuardStrength(connPair["guard"].Float()); + connections.push_back(conn); + } + tpl->setConnections(connections); + tpl->validate(); + templates[tpl->getName()] = tpl; + } + catch(const std::exception & e) + { + logGlobal->errorStream() << boost::format("Template %s has errors. Message: %s.") % tpl->getName() % std::string(e.what()); + } + } +} + +CRmgTemplate::CSize CJsonRmgTemplateLoader::parseMapTemplateSize(const std::string & text) const +{ + CRmgTemplate::CSize size; + if(text.empty()) return size; + + std::vector parts; + boost::split(parts, text, boost::is_any_of("+")); + static const std::map mapSizeMapping = boost::assign::map_list_of("s", CMapHeader::MAP_SIZE_SMALL) + ("m", CMapHeader::MAP_SIZE_MIDDLE)("l", CMapHeader::MAP_SIZE_LARGE)("xl", CMapHeader::MAP_SIZE_XLARGE); + auto it = mapSizeMapping.find(parts[0]); + if(it == mapSizeMapping.end()) + { + // Map size is given as a number representation + const auto & numericalRep = parts[0]; + parts.clear(); + boost::split(parts, numericalRep, boost::is_any_of("x")); + assert(parts.size() == 3); + size.setWidth(boost::lexical_cast(parts[0])); + size.setHeight(boost::lexical_cast(parts[1])); + size.setUnder(boost::lexical_cast(parts[2]) == 1); + } + else + { + size.setWidth(it->second); + size.setHeight(it->second); + size.setUnder(parts.size() > 1 ? parts[1] == std::string("u") : false); + } + return size; +} + +ETemplateZoneType::ETemplateZoneType CJsonRmgTemplateLoader::parseZoneType(const std::string & type) const +{ + static const std::map zoneTypeMapping = boost::assign::map_list_of + ("playerStart", ETemplateZoneType::PLAYER_START)("cpuStart", ETemplateZoneType::CPU_START) + ("treasure", ETemplateZoneType::TREASURE)("junction", ETemplateZoneType::JUNCTION); + auto it = zoneTypeMapping.find(type); + if(it == zoneTypeMapping.end()) throw std::runtime_error("Zone type unknown."); + return it->second; +} + +CRmgTemplateZone::CTownInfo CJsonRmgTemplateLoader::parseTemplateZoneTowns(const JsonNode & node) const +{ + CRmgTemplateZone::CTownInfo towns; + towns.setTownCount(node["towns"].Float()); + towns.setCastleCount(node["castles"].Float()); + towns.setTownDensity(node["townDensity"].Float()); + towns.setCastleDensity(node["castleDensity"].Float()); + return towns; +} + +std::set CJsonRmgTemplateLoader::parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const +{ + std::set townTypes; + for(const auto & townTypeNode : townTypesVector) + { + auto townTypeStr = townTypeNode.String(); + if(townTypeStr == "all") return defaultTownTypes; + + bool foundFaction = false; + for(auto factionPtr : VLC->townh->factions) + { + if(factionPtr->town != nullptr && townTypeStr == factionPtr->name) + { + townTypes.insert(factionPtr->index); + foundFaction = true; + } + } + if(!foundFaction) throw std::runtime_error("Given faction is invalid."); + } + return townTypes; +} + +std::set CJsonRmgTemplateLoader::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const +{ + std::set terTypes; + for(const auto & node : terTypeStrings) + { + const auto & terTypeStr = node.String(); + if(terTypeStr == "all") return defaultTerrainTypes; + auto pos = vstd::find_pos(GameConstants::TERRAIN_NAMES, terTypeStr); + if (pos != -1) + { + terTypes.insert(ETerrainType(pos)); + } + else + { + throw std::runtime_error("Terrain type is invalid."); + } + } + return terTypes; +} + +CRmgTemplate::CPlayerCountRange CJsonRmgTemplateLoader::parsePlayers(const std::string & players) const +{ + CRmgTemplate::CPlayerCountRange playerRange; + if(players.empty()) + { + playerRange.addNumber(0); + return playerRange; + } + std::vector commaParts; + boost::split(commaParts, players, boost::is_any_of(",")); + for(const auto & commaPart : commaParts) + { + std::vector rangeParts; + boost::split(rangeParts, commaPart, boost::is_any_of("-")); + if(rangeParts.size() == 2) + { + auto lower = boost::lexical_cast(rangeParts[0]); + auto upper = boost::lexical_cast(rangeParts[1]); + playerRange.addRange(lower, upper); + } + else if(rangeParts.size() == 1) + { + auto val = boost::lexical_cast(rangeParts.front()); + playerRange.addNumber(val); + } + } + return playerRange; +} + +const std::map & CRmgTemplateStorage::getTemplates() const +{ + return templates; +} + +CRmgTemplateStorage::CRmgTemplateStorage() +{ + auto jsonLoader = make_unique(); + jsonLoader->loadTemplates(); + + const auto & tpls = jsonLoader->getTemplates(); + templates.insert(tpls.begin(), tpls.end()); +} + +CRmgTemplateStorage::~CRmgTemplateStorage() +{ + for (auto & pair : templates) delete pair.second; +} diff --git a/lib/rmg/CRmgTemplateStorage.h b/lib/rmg/CRmgTemplateStorage.h new file mode 100644 index 000000000..08cc64a11 --- /dev/null +++ b/lib/rmg/CRmgTemplateStorage.h @@ -0,0 +1,59 @@ + +/* + * CRmgTemplateStorage.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "CRmgTemplate.h" +#include "CRmgTemplateZone.h" + +class JsonNode; + +typedef std::vector JsonVector; + +/// The CRmgTemplateLoader is a abstract base class for loading templates. +class DLL_LINKAGE CRmgTemplateLoader +{ +public: + virtual ~CRmgTemplateLoader() { }; + virtual void loadTemplates() = 0; + const std::map & getTemplates() const; + +protected: + std::map templates; +}; + +/// The CJsonRmgTemplateLoader loads templates from a JSON file. +class DLL_LINKAGE CJsonRmgTemplateLoader : public CRmgTemplateLoader +{ +public: + void loadTemplates() override; + +private: + CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const; + CRmgTemplateZone::CTownInfo parseTemplateZoneTowns(const JsonNode & node) const; + ETemplateZoneType::ETemplateZoneType parseZoneType(const std::string & type) const; + std::set parseTownTypes(const JsonVector & townTypesVector, const std::set & defaultTownTypes) const; + std::set parseTerrainTypes(const JsonVector & terTypeStrings, const std::set & defaultTerrainTypes) const; + CRmgTemplate::CPlayerCountRange parsePlayers(const std::string & players) const; +}; + +/// The class CRmgTemplateStorage stores random map templates. +class DLL_LINKAGE CRmgTemplateStorage +{ +public: + CRmgTemplateStorage(); + ~CRmgTemplateStorage(); + + const std::map & getTemplates() const; + +private: + std::map templates; /// Key: Template name +}; diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp new file mode 100644 index 000000000..658a0b8cb --- /dev/null +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -0,0 +1,216 @@ + +/* + * CRmgTemplateZone.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CRmgTemplateZone.h" + +#include "../VCMI_Lib.h" +#include "../CTownHandler.h" + +CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0) +{ + +} + +int CRmgTemplateZone::CTownInfo::getTownCount() const +{ + return townCount; +} + +void CRmgTemplateZone::CTownInfo::setTownCount(int value) +{ + if(value < 0) throw std::runtime_error("Negative value for town count not allowed."); + townCount = value; +} + +int CRmgTemplateZone::CTownInfo::getCastleCount() const +{ + return castleCount; +} + +void CRmgTemplateZone::CTownInfo::setCastleCount(int value) +{ + if(value < 0) throw std::runtime_error("Negative value for castle count not allowed."); + castleCount = value; +} + +int CRmgTemplateZone::CTownInfo::getTownDensity() const +{ + return townDensity; +} + +void CRmgTemplateZone::CTownInfo::setTownDensity(int value) +{ + if(value < 0) throw std::runtime_error("Negative value for town density not allowed."); + townDensity = value; +} + +int CRmgTemplateZone::CTownInfo::getCastleDensity() const +{ + return castleDensity; +} + +void CRmgTemplateZone::CTownInfo::setCastleDensity(int value) +{ + if(value < 0) throw std::runtime_error("Negative value for castle density not allowed."); + castleDensity = value; +} + +CRmgTemplateZone::CRmgTemplateZone() : id(0), type(ETemplateZoneType::PLAYER_START), size(1), + townsAreSameType(false), matchTerrainToTown(true) +{ + townTypes = getDefaultTownTypes(); + terrainTypes = getDefaultTerrainTypes(); +} + +TRmgTemplateZoneId CRmgTemplateZone::getId() const +{ + return id; +} + +void CRmgTemplateZone::setId(TRmgTemplateZoneId value) +{ + if(value <= 0) throw std::runtime_error("Zone id should be greater than 0."); + id = value; +} + +ETemplateZoneType::ETemplateZoneType CRmgTemplateZone::getType() const +{ + return type; +} +void CRmgTemplateZone::setType(ETemplateZoneType::ETemplateZoneType value) +{ + type = value; +} + +int CRmgTemplateZone::getSize() const +{ + return size; +} + +void CRmgTemplateZone::setSize(int value) +{ + if(value <= 0) throw std::runtime_error("Zone size needs to be greater than 0."); + size = value; +} + +boost::optional CRmgTemplateZone::getOwner() const +{ + return owner; +} + +void CRmgTemplateZone::setOwner(boost::optional value) +{ + if(!(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I)) throw std::runtime_error("Owner has to be in range 0 to max player count."); + owner = value; +} + +const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getPlayerTowns() const +{ + return playerTowns; +} + +void CRmgTemplateZone::setPlayerTowns(const CTownInfo & value) +{ + playerTowns = value; +} + +const CRmgTemplateZone::CTownInfo & CRmgTemplateZone::getNeutralTowns() const +{ + return neutralTowns; +} + +void CRmgTemplateZone::setNeutralTowns(const CTownInfo & value) +{ + neutralTowns = value; +} + +bool CRmgTemplateZone::getTownsAreSameType() const +{ + return townsAreSameType; +} + +void CRmgTemplateZone::setTownsAreSameType(bool value) +{ + townsAreSameType = value; +} + +const std::set & CRmgTemplateZone::getTownTypes() const +{ + return townTypes; +} + +void CRmgTemplateZone::setTownTypes(const std::set & value) +{ + townTypes = value; +} + +std::set CRmgTemplateZone::getDefaultTownTypes() const +{ + std::set defaultTowns; + auto towns = VLC->townh->getDefaultAllowed(); + for(int i = 0; i < towns.size(); ++i) + { + if(towns[i]) defaultTowns.insert(i); + } + return defaultTowns; +} + +bool CRmgTemplateZone::getMatchTerrainToTown() const +{ + return matchTerrainToTown; +} + +void CRmgTemplateZone::setMatchTerrainToTown(bool value) +{ + matchTerrainToTown = value; +} + +const std::set & CRmgTemplateZone::getTerrainTypes() const +{ + return terrainTypes; +} + +void CRmgTemplateZone::setTerrainTypes(const std::set & value) +{ + assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && + value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); + terrainTypes = value; +} + +std::set CRmgTemplateZone::getDefaultTerrainTypes() const +{ + std::set terTypes; + static const ETerrainType::EETerrainType allowedTerTypes[] = { ETerrainType::DIRT, ETerrainType::SAND, ETerrainType::GRASS, ETerrainType::SNOW, + ETerrainType::SWAMP, ETerrainType::ROUGH, ETerrainType::SUBTERRANEAN, ETerrainType::LAVA }; + for(auto & allowedTerType : allowedTerTypes) terTypes.insert(allowedTerType); + return terTypes; +} + +boost::optional CRmgTemplateZone::getTerrainTypeLikeZone() const +{ + return terrainTypeLikeZone; +} + +void CRmgTemplateZone::setTerrainTypeLikeZone(boost::optional value) +{ + terrainTypeLikeZone = value; +} + +boost::optional CRmgTemplateZone::getTownTypeLikeZone() const +{ + return townTypeLikeZone; +} + +void CRmgTemplateZone::setTownTypeLikeZone(boost::optional value) +{ + townTypeLikeZone = value; +} diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h new file mode 100644 index 000000000..082f00719 --- /dev/null +++ b/lib/rmg/CRmgTemplateZone.h @@ -0,0 +1,92 @@ + +/* + * CRmgTemplateZone.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "../GameConstants.h" + +namespace ETemplateZoneType +{ +enum ETemplateZoneType +{ + PLAYER_START, + CPU_START, + TREASURE, + JUNCTION +}; +} + +/// The CRmgTemplateZone describes a zone in a template. +class DLL_LINKAGE CRmgTemplateZone +{ +public: + class DLL_LINKAGE CTownInfo + { + public: + CTownInfo(); + + int getTownCount() const; /// Default: 0 + void setTownCount(int value); + int getCastleCount() const; /// Default: 0 + void setCastleCount(int value); + int getTownDensity() const; /// Default: 0 + void setTownDensity(int value); + int getCastleDensity() const; /// Default: 0 + void setCastleDensity(int value); + + private: + int townCount, castleCount, townDensity, castleDensity; + }; + + CRmgTemplateZone(); + + TRmgTemplateZoneId getId() const; /// Default: 0 + void setId(TRmgTemplateZoneId value); + ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::PLAYER_START + void setType(ETemplateZoneType::ETemplateZoneType value); + + int getSize() const; /// Default: 1 + void setSize(int value); + boost::optional getOwner() const; + void setOwner(boost::optional value); + + const CTownInfo & getPlayerTowns() const; + void setPlayerTowns(const CTownInfo & value); + const CTownInfo & getNeutralTowns() const; + void setNeutralTowns(const CTownInfo & value); + bool getTownsAreSameType() const; /// Default: false + void setTownsAreSameType(bool value); + const std::set & getTownTypes() const; /// Default: all + void setTownTypes(const std::set & value); + std::set getDefaultTownTypes() const; + bool getMatchTerrainToTown() const; /// Default: true + void setMatchTerrainToTown(bool value); + + const std::set & getTerrainTypes() const; /// Default: all + void setTerrainTypes(const std::set & value); + std::set getDefaultTerrainTypes() const; + boost::optional getTerrainTypeLikeZone() const; + void setTerrainTypeLikeZone(boost::optional value); + boost::optional getTownTypeLikeZone() const; + void setTownTypeLikeZone(boost::optional value); + +private: + TRmgTemplateZoneId id; + ETemplateZoneType::ETemplateZoneType type; + int size; + boost::optional owner; + CTownInfo playerTowns, neutralTowns; + bool townsAreSameType; + std::set townTypes; + bool matchTerrainToTown; + std::set terrainTypes; + boost::optional terrainTypeLikeZone, townTypeLikeZone; +}; diff --git a/lib/rmg/CZoneGraphGenerator.cpp b/lib/rmg/CZoneGraphGenerator.cpp new file mode 100644 index 000000000..6595a504b --- /dev/null +++ b/lib/rmg/CZoneGraphGenerator.cpp @@ -0,0 +1,34 @@ + +/* + * CZoneGraphGenerator.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CZoneGraphGenerator.h" + +CZoneCell::CZoneCell(CRmgTemplateZone * zone) : zone(zone) +{ + +} + + +CZoneGraph::CZoneGraph() +{ + +} + +CZoneGraphGenerator::CZoneGraphGenerator() : gen(nullptr) +{ + +} + +unique_ptr CZoneGraphGenerator::generate(const CMapGenOptions & options, CRandomGenerator * gen) +{ + return make_unique(); +} diff --git a/lib/rmg/CZoneGraphGenerator.h b/lib/rmg/CZoneGraphGenerator.h new file mode 100644 index 000000000..2737382a9 --- /dev/null +++ b/lib/rmg/CZoneGraphGenerator.h @@ -0,0 +1,48 @@ + +/* + * CZoneGraphGenerator.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +class CRmgTemplateZone; +class CRandomGenerator; +class CMapGenOptions; + +class CZoneCell +{ +public: + CZoneCell(CRmgTemplateZone * zone); + +private: + CRmgTemplateZone * zone; + + //TODO additional data +}; + +class CZoneGraph +{ +public: + CZoneGraph(); + +private: + //TODO zone graph storage +}; + +class CZoneGraphGenerator +{ +public: + CZoneGraphGenerator(); + + unique_ptr generate(const CMapGenOptions & options, CRandomGenerator * gen); + +private: + unique_ptr graph; + CRandomGenerator * gen; +}; diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp new file mode 100644 index 000000000..e81bdb5f4 --- /dev/null +++ b/lib/rmg/CZonePlacer.cpp @@ -0,0 +1,35 @@ + +/* + * CZonePlacer.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "CZonePlacer.h" + +#include "CZoneGraphGenerator.h" + +CPlacedZone::CPlacedZone(CRmgTemplateZone * zone) : zone(zone) +{ + +} + +CZonePlacer::CZonePlacer() : map(nullptr), gen(nullptr) +{ + +} + +CZonePlacer::~CZonePlacer() +{ + +} + +void CZonePlacer::placeZones(CMap * map, unique_ptr graph, CRandomGenerator * gen) +{ + +} diff --git a/lib/rmg/CZonePlacer.h b/lib/rmg/CZonePlacer.h new file mode 100644 index 000000000..6c6df5510 --- /dev/null +++ b/lib/rmg/CZonePlacer.h @@ -0,0 +1,45 @@ + +/* + * CZonePlacer.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +class CZoneGraph; +class CMap; +class CRandomGenerator; +class CRmgTemplateZone; + +class CPlacedZone +{ +public: + CPlacedZone(CRmgTemplateZone * zone); + +private: + CRmgTemplateZone * zone; + + //TODO exact outline data of zone + //TODO perhaps further zone data, guards, obstacles, etc... +}; + +//TODO add voronoi helper classes(?), etc... + +class CZonePlacer +{ +public: + CZonePlacer(); + ~CZonePlacer(); + + void placeZones(CMap * map, unique_ptr graph, CRandomGenerator * gen); + +private: + CMap * map; + unique_ptr graph; + CRandomGenerator * gen; +}; diff --git a/test/CMapEditManagerTest.cpp b/test/CMapEditManagerTest.cpp index 1dd4725dc..ae638da64 100644 --- a/test/CMapEditManagerTest.cpp +++ b/test/CMapEditManagerTest.cpp @@ -21,6 +21,7 @@ #include "../lib/mapping/CMapEditManager.h" #include "../lib/int3.h" #include "../lib/CRandomGenerator.h" +#include "../lib/VCMI_Lib.h" BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type) { @@ -102,10 +103,10 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View) if(patternParts.size() != 2) throw std::runtime_error("A pattern should consist of two parts, the group and the id. Continue with next pattern."); const auto & groupStr = patternParts[0]; const auto & id = patternParts[1]; - auto terGroup = CTerrainViewPatternConfig::get().getTerrainGroup(groupStr); + auto terGroup = VLC->terviewh->getTerrainGroup(groupStr); // Get mapping range - const auto & pattern = CTerrainViewPatternConfig::get().getTerrainViewPatternById(terGroup, id); + const auto & pattern = VLC->terviewh->getTerrainViewPatternById(terGroup, id); const auto & mapping = (*pattern).mapping; const auto & positionsNode = node["pos"].Vector();