mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Redone RMG template serialization using JSON serializer, added tests
This commit is contained in:
		
							
								
								
									
										28
									
								
								include/vstd/ContainerUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/vstd/ContainerUtils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * ContainerUtils.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 | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| 	template<typename K, typename V> | ||||
| 	std::map<V, K> invertMap(const std::map<K, V> & m) | ||||
| 	{ | ||||
| 		std::map<V,K> other; | ||||
| 		std::transform(m.cbegin(), m.cend(), std::inserter(other, other.begin()), [](const std::pair<K, V> & p) | ||||
| 		{ | ||||
| 			return std::make_pair(p.second, p.first); | ||||
| 		}); | ||||
| 		return other; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -165,6 +165,9 @@ set_source_files_properties(${CMAKE_BINARY_DIR}/Version.cpp | ||||
| ) | ||||
|  | ||||
| set(lib_HEADERS | ||||
| 		${CMAKE_HOME_DIRECTORY}/include/vstd/CLoggerBase.h | ||||
| 		${CMAKE_HOME_DIRECTORY}/include/vstd/ContainerUtils.h | ||||
| 		${CMAKE_HOME_DIRECTORY}/include/vstd/RNG.h | ||||
| 		StdInc.h | ||||
| 		../Global.h | ||||
|  | ||||
|   | ||||
| @@ -130,6 +130,7 @@ | ||||
| 		<Unit filename="../Global.h" /> | ||||
| 		<Unit filename="../Version.h" /> | ||||
| 		<Unit filename="../include/vstd/CLoggerBase.h" /> | ||||
| 		<Unit filename="../include/vstd/ContainerUtils.h" /> | ||||
| 		<Unit filename="../include/vstd/RNG.h" /> | ||||
| 		<Unit filename="AI_Base.h" /> | ||||
| 		<Unit filename="CArtHandler.cpp" /> | ||||
|   | ||||
| @@ -406,8 +406,8 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand | ||||
| 	for(const auto & tplPair : tpls) | ||||
| 	{ | ||||
| 		const auto & tpl = tplPair.second; | ||||
| 		CRmgTemplate::CSize tplSize(width, height, hasTwoLevels); | ||||
| 		if(tplSize >= tpl->getMinSize() && tplSize <= tpl->getMaxSize()) | ||||
| 		int3 tplSize(width, height, (hasTwoLevels ? 2 : 1)); | ||||
| 		if(tpl->matchesSize(tplSize)) | ||||
| 		{ | ||||
| 			bool isPlayerCountValid = false; | ||||
| 			if (getPlayerCount() != RANDOM_SIZE) | ||||
|   | ||||
| @@ -149,6 +149,7 @@ std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, in | ||||
| 		map->calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded | ||||
| 		fillZones(); | ||||
| 		//updated guarded tiles will be calculated in CGameState::initMapObjects() | ||||
| 		zones.clear(); | ||||
| 	} | ||||
| 	catch (rmgException &e) | ||||
| 	{ | ||||
| @@ -277,8 +278,8 @@ void CMapGenerator::genZones() | ||||
| 	zones.clear(); | ||||
| 	for(const auto & option : tmpl->getZones()) | ||||
| 	{ | ||||
| 		auto zone = new CRmgTemplateZone(); | ||||
| 		zone->setOptions(option.second); | ||||
| 		auto zone = std::make_shared<CRmgTemplateZone>(); | ||||
| 		zone->setOptions(option.second.get()); | ||||
| 		zones[zone->getId()] = zone; | ||||
| 		//todo: move to CRmgTemplateZone constructor | ||||
| 		zone->setGenPtr(this);//immediately set gen pointer before taking any actions on zones | ||||
| @@ -317,7 +318,7 @@ void CMapGenerator::fillZones() | ||||
|  | ||||
| 	createConnections2(); //subterranean gates and monoliths | ||||
|  | ||||
| 	std::vector<CRmgTemplateZone*> treasureZones; | ||||
| 	std::vector<std::shared_ptr<CRmgTemplateZone>> treasureZones; | ||||
| 	for (auto it : zones) | ||||
| 	{ | ||||
| 		it.second->fill(); | ||||
| @@ -716,7 +717,7 @@ void CMapGenerator::checkIsOnMap(const int3& tile) const | ||||
| } | ||||
|  | ||||
|  | ||||
| std::map<TRmgTemplateZoneId, CRmgTemplateZone*> CMapGenerator::getZones() const | ||||
| CMapGenerator::Zones & CMapGenerator::getZones() | ||||
| { | ||||
| 	return zones; | ||||
| } | ||||
|   | ||||
| @@ -50,6 +50,8 @@ public: | ||||
| class DLL_LINKAGE CMapGenerator | ||||
| { | ||||
| public: | ||||
| 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>; | ||||
|  | ||||
| 	explicit CMapGenerator(); | ||||
| 	~CMapGenerator(); // required due to std::unique_ptr | ||||
|  | ||||
| @@ -61,7 +63,7 @@ public: | ||||
| 	int randomSeed; | ||||
| 	CMapEditManager * editManager; | ||||
|  | ||||
| 	std::map<TRmgTemplateZoneId, CRmgTemplateZone*> getZones() const; | ||||
| 	Zones & getZones(); | ||||
| 	void createDirectConnections(); | ||||
| 	void createConnections2(); | ||||
| 	void findZonesForQuestArts(); | ||||
| @@ -100,7 +102,7 @@ public: | ||||
|  | ||||
| private: | ||||
| 	std::list<rmg::ZoneConnection> connectionsLeft; | ||||
| 	std::map<TRmgTemplateZoneId, CRmgTemplateZone*> zones; | ||||
| 	Zones zones; | ||||
| 	std::map<TFaction, ui32> zonesPerFaction; | ||||
| 	ui32 zonesTotal; //zones that have their main town only | ||||
|  | ||||
|   | ||||
| @@ -9,15 +9,93 @@ | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include <vstd/ContainerUtils.h> | ||||
| #include "CRmgTemplate.h" | ||||
|  | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../VCMI_Lib.h" | ||||
| #include "../CTownHandler.h" | ||||
| #include "../serializer/JsonSerializeFormat.h" | ||||
| #include "../StringConstants.h" | ||||
|  | ||||
| namespace | ||||
| { | ||||
| 	si32 decodeZoneId(const std::string & json) | ||||
| 	{ | ||||
| 		return boost::lexical_cast<si32>(json); | ||||
| 	} | ||||
|  | ||||
| 	std::string encodeZoneId(si32 id) | ||||
| 	{ | ||||
| 		return boost::lexical_cast<std::string>(id); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CTreasureInfo::CTreasureInfo() | ||||
| 	: min(0), | ||||
| 	max(0), | ||||
| 	density(0) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| bool CTreasureInfo::operator==(const CTreasureInfo & other) const | ||||
| { | ||||
| 	return (min == other.min) && (max == other.max) && (density == other.density); | ||||
| } | ||||
|  | ||||
| void CTreasureInfo::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	handler.serializeInt("min", min, 0); | ||||
| 	handler.serializeInt("max", max, 0); | ||||
| 	handler.serializeInt("density", density, 0); | ||||
| } | ||||
|  | ||||
| namespace rmg | ||||
| { | ||||
|  | ||||
| class TerrainEncoder | ||||
| { | ||||
| public: | ||||
| 	static si32 decode(const std::string & identifier) | ||||
| 	{ | ||||
| 		return vstd::find_pos(GameConstants::TERRAIN_NAMES, identifier); | ||||
| 	} | ||||
|  | ||||
| 	static std::string encode(const si32 index) | ||||
| 	{ | ||||
| 		return (index >=0 && index < GameConstants::TERRAIN_TYPES) ? GameConstants::TERRAIN_NAMES[index] : "<INVALID TERRAIN>"; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class ZoneEncoder | ||||
| { | ||||
| public: | ||||
| 	static si32 decode(const std::string & json) | ||||
| 	{ | ||||
| 		return boost::lexical_cast<si32>(json); | ||||
| 	} | ||||
|  | ||||
| 	static std::string encode(si32 id) | ||||
| 	{ | ||||
| 		return boost::lexical_cast<std::string>(id); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class FactionEncoder | ||||
| { | ||||
| public: | ||||
| 	static si32 decode(const std::string & json) | ||||
| 	{ | ||||
| 		return VLC->townh->decodeFaction(json); | ||||
| 	} | ||||
|  | ||||
| 	static std::string encode(si32 id) | ||||
| 	{ | ||||
| 		return VLC->townh->encodeFaction(id); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| const std::set<ETerrainType> ZoneOptions::DEFAULT_TERRAIN_TYPES = | ||||
| { | ||||
| 	ETerrainType::DIRT, | ||||
| @@ -30,6 +108,8 @@ const std::set<ETerrainType> ZoneOptions::DEFAULT_TERRAIN_TYPES = | ||||
| 	ETerrainType::LAVA | ||||
| }; | ||||
|  | ||||
| const TRmgTemplateZoneId ZoneOptions::NO_ZONE = -1; | ||||
|  | ||||
| ZoneOptions::CTownInfo::CTownInfo() | ||||
| 	: townCount(0), | ||||
| 	castleCount(0), | ||||
| @@ -44,49 +124,30 @@ int ZoneOptions::CTownInfo::getTownCount() const | ||||
| 	return townCount; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::CTownInfo::setTownCount(int value) | ||||
| { | ||||
| 	if(value < 0) | ||||
| 		throw std::runtime_error("Negative value for town count not allowed."); | ||||
| 	townCount = value; | ||||
| } | ||||
|  | ||||
| int ZoneOptions::CTownInfo::getCastleCount() const | ||||
| { | ||||
| 	return castleCount; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::CTownInfo::setCastleCount(int value) | ||||
| { | ||||
| 	if(value < 0) | ||||
| 		throw std::runtime_error("Negative value for castle count not allowed."); | ||||
| 	castleCount = value; | ||||
| } | ||||
|  | ||||
| int ZoneOptions::CTownInfo::getTownDensity() const | ||||
| { | ||||
| 	return townDensity; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::CTownInfo::setTownDensity(int value) | ||||
| { | ||||
| 	if(value < 0) | ||||
| 		throw std::runtime_error("Negative value for town density not allowed."); | ||||
| 	townDensity = value; | ||||
| } | ||||
|  | ||||
| int ZoneOptions::CTownInfo::getCastleDensity() const | ||||
| { | ||||
| 	return castleDensity; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::CTownInfo::setCastleDensity(int value) | ||||
| void ZoneOptions::CTownInfo::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	if(value < 0) | ||||
| 		throw std::runtime_error("Negative value for castle density not allowed."); | ||||
| 	castleDensity = value; | ||||
| 	handler.serializeInt("towns", townCount, 0); | ||||
| 	handler.serializeInt("castles", castleCount, 0); | ||||
| 	handler.serializeInt("townDensity", townDensity, 0); | ||||
| 	handler.serializeInt("castleDensity", castleDensity, 0); | ||||
| } | ||||
|  | ||||
|  | ||||
| ZoneOptions::ZoneOptions() | ||||
| 	: id(0), | ||||
| 	type(ETemplateZoneType::PLAYER_START), | ||||
| @@ -102,7 +163,10 @@ ZoneOptions::ZoneOptions() | ||||
| 	zoneMonsterStrength(EMonsterStrength::ZONE_NORMAL), | ||||
| 	mines(), | ||||
| 	treasureInfo(), | ||||
| 	connections() | ||||
| 	connections(), | ||||
| 	minesLikeZone(NO_ZONE), | ||||
| 	terrainTypeLikeZone(NO_ZONE), | ||||
| 	treasureLikeZone(NO_ZONE) | ||||
| { | ||||
|  | ||||
| } | ||||
| @@ -124,6 +188,9 @@ ZoneOptions & ZoneOptions::operator=(const ZoneOptions & other) | ||||
| 	mines = other.mines; | ||||
| 	treasureInfo = other.treasureInfo; | ||||
| 	connections = other.connections; | ||||
| 	minesLikeZone = other.minesLikeZone; | ||||
| 	terrainTypeLikeZone = other.terrainTypeLikeZone; | ||||
| 	treasureLikeZone = other.treasureLikeZone; | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| @@ -143,10 +210,6 @@ ETemplateZoneType::ETemplateZoneType ZoneOptions::getType() const | ||||
| { | ||||
| 	return type; | ||||
| } | ||||
| void ZoneOptions::setType(ETemplateZoneType::ETemplateZoneType value) | ||||
| { | ||||
| 	type = value; | ||||
| } | ||||
|  | ||||
| int ZoneOptions::getSize() const | ||||
| { | ||||
| @@ -155,8 +218,6 @@ int ZoneOptions::getSize() const | ||||
|  | ||||
| void ZoneOptions::setSize(int value) | ||||
| { | ||||
| 	if(value <= 0) | ||||
| 		throw std::runtime_error(boost::to_string(boost::format("Zone %d size needs to be greater than 0.") % id)); | ||||
| 	size = value; | ||||
| } | ||||
|  | ||||
| @@ -165,43 +226,6 @@ boost::optional<int> ZoneOptions::getOwner() const | ||||
| 	return owner; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setOwner(boost::optional<int> value) | ||||
| { | ||||
| 	if(value && !(*value >= 0 && *value <= PlayerColor::PLAYER_LIMIT_I)) | ||||
| 		throw std::runtime_error(boost::to_string(boost::format ("Owner of zone %d has to be in range 0 to max player count.") % id)); | ||||
| 	owner = value; | ||||
| } | ||||
|  | ||||
| const ZoneOptions::CTownInfo & ZoneOptions::getPlayerTowns() const | ||||
| { | ||||
| 	return playerTowns; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setPlayerTowns(const CTownInfo & value) | ||||
| { | ||||
| 	playerTowns = value; | ||||
| } | ||||
|  | ||||
| const ZoneOptions::CTownInfo & ZoneOptions::getNeutralTowns() const | ||||
| { | ||||
| 	return neutralTowns; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setNeutralTowns(const CTownInfo & value) | ||||
| { | ||||
| 	neutralTowns = value; | ||||
| } | ||||
|  | ||||
| bool ZoneOptions::getMatchTerrainToTown() const | ||||
| { | ||||
| 	return matchTerrainToTown; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setMatchTerrainToTown(bool value) | ||||
| { | ||||
| 	matchTerrainToTown = value; | ||||
| } | ||||
|  | ||||
| const std::set<ETerrainType> & ZoneOptions::getTerrainTypes() const | ||||
| { | ||||
| 	return terrainTypes; | ||||
| @@ -214,16 +238,6 @@ void ZoneOptions::setTerrainTypes(const std::set<ETerrainType> & value) | ||||
| 	terrainTypes = value; | ||||
| } | ||||
|  | ||||
| bool ZoneOptions::getTownsAreSameType() const | ||||
| { | ||||
| 	return townsAreSameType; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setTownsAreSameType(bool value) | ||||
| { | ||||
| 	townsAreSameType = value; | ||||
| } | ||||
|  | ||||
| std::set<TFaction> ZoneOptions::getDefaultTownTypes() const | ||||
| { | ||||
| 	std::set<TFaction> defaultTowns; | ||||
| @@ -250,15 +264,9 @@ void ZoneOptions::setMonsterTypes(const std::set<TFaction> & value) | ||||
| 	monsterTypes = value; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength val) | ||||
| void ZoneOptions::setMinesInfo(const std::map<TResource, ui16> & value) | ||||
| { | ||||
| 	assert (vstd::iswithin(val, EMonsterStrength::ZONE_WEAK, EMonsterStrength::ZONE_STRONG)); | ||||
| 	zoneMonsterStrength = val; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setMinesAmount(TResource res, ui16 amount) | ||||
| { | ||||
| 	mines[res] = amount; | ||||
| 	mines = value; | ||||
| } | ||||
|  | ||||
| std::map<TResource, ui16> ZoneOptions::getMinesInfo() const | ||||
| @@ -266,9 +274,9 @@ std::map<TResource, ui16> ZoneOptions::getMinesInfo() const | ||||
| 	return mines; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::addTreasureInfo(const CTreasureInfo & info) | ||||
| void ZoneOptions::setTreasureInfo(const std::vector<CTreasureInfo> & value) | ||||
| { | ||||
| 	treasureInfo.push_back(info); | ||||
| 	treasureInfo = value; | ||||
| } | ||||
|  | ||||
| const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const | ||||
| @@ -276,11 +284,102 @@ const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const | ||||
| 	return treasureInfo; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId ZoneOptions::getMinesLikeZone() const | ||||
| { | ||||
| 	return minesLikeZone; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId ZoneOptions::getTerrainTypeLikeZone() const | ||||
| { | ||||
| 	return terrainTypeLikeZone; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId ZoneOptions::getTreasureLikeZone() const | ||||
| { | ||||
|     return treasureLikeZone; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::addConnection(TRmgTemplateZoneId otherZone) | ||||
| { | ||||
| 	connections.push_back (otherZone); | ||||
| } | ||||
|  | ||||
| std::vector<TRmgTemplateZoneId> ZoneOptions::getConnections() const | ||||
| { | ||||
| 	return connections; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	static const std::vector<std::string> zoneTypes = | ||||
| 	{ | ||||
| 		"playerStart", | ||||
| 		"cpuStart", | ||||
| 		"treasure", | ||||
| 		"junction" | ||||
| 	}; | ||||
|  | ||||
| 	handler.serializeEnum("type", type, zoneTypes); | ||||
| 	handler.serializeInt("size", size, 1); | ||||
| 	handler.serializeInt("owner", owner); | ||||
| 	handler.serializeStruct("playerTowns", playerTowns); | ||||
| 	handler.serializeStruct("neutralTowns", neutralTowns); | ||||
| 	handler.serializeBool("matchTerrainToTown", matchTerrainToTown, true); | ||||
|  | ||||
| 	#define SERIALIZE_ZONE_LINK(fieldName) handler.serializeInt(#fieldName, fieldName, NO_ZONE); | ||||
|  | ||||
| 	SERIALIZE_ZONE_LINK(minesLikeZone); | ||||
| 	SERIALIZE_ZONE_LINK(terrainTypeLikeZone); | ||||
| 	SERIALIZE_ZONE_LINK(treasureLikeZone); | ||||
|  | ||||
| 	#undef SERIALIZE_ZONE_LINK | ||||
|  | ||||
| 	if(terrainTypeLikeZone == NO_ZONE) | ||||
| 		handler.serializeIdArray<ETerrainType, TerrainEncoder>("terrainTypes", terrainTypes, DEFAULT_TERRAIN_TYPES); | ||||
|  | ||||
| 	handler.serializeBool("townsAreSameType", townsAreSameType, false); | ||||
| 	handler.serializeIdArray<TFaction, FactionEncoder>("allowedMonsters", monsterTypes, VLC->townh->getAllowedFactions(false)); | ||||
| 	handler.serializeIdArray<TFaction, FactionEncoder>("allowedTowns", townTypes, VLC->townh->getAllowedFactions(true)); | ||||
|  | ||||
| 	{ | ||||
| 		//TODO: add support for std::map to serializeEnum | ||||
| 		static const std::vector<std::string> STRENGTH = | ||||
| 		{ | ||||
| 			"weak", | ||||
| 			"normal", | ||||
| 			"strong" | ||||
| 		}; | ||||
|  | ||||
| 		si32 rawStrength = 0; | ||||
| 		if(handler.saving) | ||||
| 		{ | ||||
| 			rawStrength = static_cast<decltype(rawStrength)>(zoneMonsterStrength); | ||||
| 			rawStrength++; | ||||
| 		} | ||||
| 		handler.serializeEnum("monsters", rawStrength, STRENGTH); | ||||
| 		if(!handler.saving) | ||||
| 		{ | ||||
| 			rawStrength--; | ||||
| 			zoneMonsterStrength = static_cast<decltype(zoneMonsterStrength)>(rawStrength); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(treasureLikeZone == NO_ZONE) | ||||
| 	{ | ||||
| 		auto treasureData = handler.enterArray("treasure"); | ||||
| 		treasureData.serializeStruct(treasureInfo); | ||||
| 	} | ||||
|  | ||||
| 	if((minesLikeZone == NO_ZONE) && (!handler.saving || !mines.empty())) | ||||
| 	{ | ||||
| 		auto minesData = handler.enterStruct("mines"); | ||||
|  | ||||
| 		for(TResource idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++) | ||||
| 		{ | ||||
| 			handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], mines[idx], 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ZoneConnection::ZoneConnection() | ||||
| 	: zoneA(-1), | ||||
| @@ -290,157 +389,60 @@ ZoneConnection::ZoneConnection() | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| TRmgTemplateZoneId ZoneConnection::getZoneA() const | ||||
| { | ||||
| 	return zoneA; | ||||
| } | ||||
|  | ||||
| void ZoneConnection::setZoneA(TRmgTemplateZoneId value) | ||||
| { | ||||
| 	zoneA = value; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId ZoneConnection::getZoneB() const | ||||
| { | ||||
| 	return zoneB; | ||||
| } | ||||
|  | ||||
| void ZoneConnection::setZoneB(TRmgTemplateZoneId value) | ||||
| { | ||||
| 	zoneB = value; | ||||
| } | ||||
|  | ||||
| int ZoneConnection::getGuardStrength() const | ||||
| { | ||||
| 	return guardStrength; | ||||
| } | ||||
|  | ||||
| void ZoneConnection::setGuardStrength(int value) | ||||
| void ZoneConnection::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	if(value < 0) throw std::runtime_error("Negative value for guard strength not allowed."); | ||||
| 	guardStrength = value; | ||||
| 	handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("a", zoneA, -1); | ||||
| 	handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("b", zoneB, -1); | ||||
| 	handler.serializeInt("guard", guardStrength, 0); | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| using namespace rmg;//todo: remove | ||||
|  | ||||
|  | ||||
| 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() | ||||
| 	: minSize(72, 72, 2), | ||||
| 	maxSize(72, 72, 2) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| CRmgTemplate::~CRmgTemplate() | ||||
| { | ||||
| 	for (auto & pair : zones) delete pair.second; | ||||
| } | ||||
|  | ||||
| bool CRmgTemplate::matchesSize(const int3 & value) const | ||||
| { | ||||
| 	const int64_t square = value.x * value.y * value.z; | ||||
| 	const int64_t minSquare = minSize.x * minSize.y * minSize.z; | ||||
| 	const int64_t maxSquare = maxSize.x * maxSize.y * maxSize.z; | ||||
|  | ||||
| 	return minSquare <= square && square <= maxSquare; | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::setId(const std::string & value) | ||||
| { | ||||
| 	id = value; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	return name.empty() ? id : name; | ||||
| } | ||||
|  | ||||
| const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const | ||||
| @@ -448,41 +450,21 @@ 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<TRmgTemplateZoneId, ZoneOptions *> & CRmgTemplate::getZones() const | ||||
| const CRmgTemplate::Zones & CRmgTemplate::getZones() const | ||||
| { | ||||
| 	return zones; | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::setZones(const std::map<TRmgTemplateZoneId, ZoneOptions *> & value) | ||||
| { | ||||
| 	zones = value; | ||||
| } | ||||
|  | ||||
| const std::list<ZoneConnection> & CRmgTemplate::getConnections() const | ||||
| const std::vector<ZoneConnection> & CRmgTemplate::getConnections() const | ||||
| { | ||||
| 	return connections; | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::setConnections(const std::list<ZoneConnection> & value) | ||||
| { | ||||
| 	connections = value; | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::validate() const | ||||
| { | ||||
| 	//TODO add some validation checks, throw on failure | ||||
| @@ -516,3 +498,212 @@ std::set<int> CRmgTemplate::CPlayerCountRange::getNumbers() const | ||||
| 	} | ||||
| 	return numbers; | ||||
| } | ||||
|  | ||||
| std::string CRmgTemplate::CPlayerCountRange::toString() const | ||||
| { | ||||
| 	if(range.size() == 1) | ||||
| 	{ | ||||
| 		const auto & p = range.front(); | ||||
| 		if((p.first == p.second) && (p.first == 0)) | ||||
| 			return ""; | ||||
| 	} | ||||
|  | ||||
| 	std::string ret; | ||||
|  | ||||
| 	bool first = true; | ||||
|  | ||||
| 	for(auto & p : range) | ||||
| 	{ | ||||
| 		if(!first) | ||||
| 			ret +=","; | ||||
| 		else | ||||
| 			first = false; | ||||
|  | ||||
| 		if(p.first == p.second) | ||||
| 		{ | ||||
| 			ret += boost::lexical_cast<std::string>(p.first); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			ret += boost::to_string(boost::format("%d-%d") % p.first % p.second); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::CPlayerCountRange::fromString(const std::string & value) | ||||
| { | ||||
| 	range.clear(); | ||||
|  | ||||
| 	if(value.empty()) | ||||
| 	{ | ||||
| 		addNumber(0); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		std::vector<std::string> commaParts; | ||||
| 		boost::split(commaParts, value, boost::is_any_of(",")); | ||||
| 		for(const auto & commaPart : commaParts) | ||||
| 		{ | ||||
| 			std::vector<std::string> rangeParts; | ||||
| 			boost::split(rangeParts, commaPart, boost::is_any_of("-")); | ||||
| 			if(rangeParts.size() == 2) | ||||
| 			{ | ||||
| 				auto lower = boost::lexical_cast<int>(rangeParts[0]); | ||||
| 				auto upper = boost::lexical_cast<int>(rangeParts[1]); | ||||
| 				addRange(lower, upper); | ||||
| 			} | ||||
| 			else if(rangeParts.size() == 1) | ||||
| 			{ | ||||
| 				auto val = boost::lexical_cast<int>(rangeParts.front()); | ||||
| 				addNumber(val); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	handler.serializeString("name", name); | ||||
| 	serializeSize(handler, minSize, "minSize"); | ||||
| 	serializeSize(handler, maxSize, "maxSize"); | ||||
| 	serializePlayers(handler, players, "players"); | ||||
| 	serializePlayers(handler, cpuPlayers, "cpu"); | ||||
|  | ||||
| 	{ | ||||
| 		auto connectionsData = handler.enterArray("connections"); | ||||
| 		connectionsData.serializeStruct(connections); | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		auto zonesData = handler.enterStruct("zones"); | ||||
|         if(handler.saving) | ||||
| 		{ | ||||
| 			for(auto & idAndZone : zones) | ||||
| 			{ | ||||
| 				auto guard = handler.enterStruct(encodeZoneId(idAndZone.first)); | ||||
| 				idAndZone.second->serializeJson(handler); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			for(auto & idAndZone : zonesData->getCurrent().Struct()) | ||||
| 			{ | ||||
| 				auto guard = handler.enterStruct(idAndZone.first); | ||||
| 				auto zone = std::make_shared<ZoneOptions>(); | ||||
| 				zone->setId(decodeZoneId(idAndZone.first)); | ||||
| 				zone->serializeJson(handler); | ||||
| 				zones[zone->getId()] = zone; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if(!handler.saving) | ||||
| 		afterLoad(); | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::afterLoad() | ||||
| { | ||||
| 	for(auto & idAndZone : zones) | ||||
| 	{ | ||||
| 		auto zone = idAndZone.second; | ||||
| 		if(zone->getMinesLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 		{ | ||||
| 			const auto otherZone = zones.at(zone->getMinesLikeZone()); | ||||
| 			zone->setMinesInfo(otherZone->getMinesInfo()); | ||||
| 		} | ||||
|  | ||||
| 		if(zone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 		{ | ||||
| 			const auto otherZone = zones.at(zone->getTerrainTypeLikeZone()); | ||||
| 			zone->setTerrainTypes(otherZone->getTerrainTypes()); | ||||
| 		} | ||||
|  | ||||
| 		if(zone->getTreasureLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 		{ | ||||
| 			const auto otherZone = zones.at(zone->getTreasureLikeZone()); | ||||
| 			zone->setTreasureInfo(otherZone->getTreasureInfo()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(const auto & connection : connections) | ||||
| 	{ | ||||
| 		auto id1 = connection.getZoneA(); | ||||
| 		auto id2 = connection.getZoneB(); | ||||
|  | ||||
| 		auto zone1 = zones.at(id1); | ||||
| 		auto zone2 = zones.at(id2); | ||||
|  | ||||
| 		zone1->addConnection(id2); | ||||
| 		zone2->addConnection(id1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName) | ||||
| { | ||||
| 	static const std::map<std::string, int3> sizeMapping = | ||||
| 	{ | ||||
| 		{"s",    { 36,  36, 1}}, | ||||
| 		{"s+u",  { 36,  36, 2}}, | ||||
| 		{"m",    { 72,  72, 1}}, | ||||
| 		{"m+u",  { 72,  72, 2}}, | ||||
| 		{"l",    {108, 108, 1}}, | ||||
| 		{"l+u",  {108, 108, 2}}, | ||||
| 		{"xl",   {144, 144, 1}}, | ||||
| 		{"xl+u", {144, 144, 2}}, | ||||
| 		{"h",    {180, 180, 1}}, | ||||
| 		{"h+u",  {180, 180, 2}}, | ||||
| 		{"xh",   {216, 216, 1}}, | ||||
| 		{"xh+u", {216, 216, 2}}, | ||||
| 		{"g",    {252, 252, 1}}, | ||||
| 		{"g+u",  {252, 252, 2}} | ||||
| 	}; | ||||
|  | ||||
| 	static const std::map<int3, std::string> sizeReverseMapping = vstd::invertMap(sizeMapping); | ||||
|  | ||||
| 	std::string encodedValue; | ||||
|  | ||||
| 	if(handler.saving) | ||||
| 	{ | ||||
| 		auto iter = sizeReverseMapping.find(value); | ||||
| 		if(iter == sizeReverseMapping.end()) | ||||
| 			encodedValue = boost::str(boost::format("%dx%dx%d") % value.x % value.y % value.z); | ||||
| 		else | ||||
| 			encodedValue = iter->second; | ||||
| 	} | ||||
|  | ||||
| 	handler.serializeString(fieldName, encodedValue); | ||||
|  | ||||
| 	if(!handler.saving) | ||||
| 	{ | ||||
| 		auto iter = sizeMapping.find(encodedValue); | ||||
|  | ||||
| 		if(iter == sizeMapping.end()) | ||||
| 		{ | ||||
| 			std::vector<std::string> parts; | ||||
| 			boost::split(parts, encodedValue, boost::is_any_of("x")); | ||||
|  | ||||
| 			value.x = (boost::lexical_cast<int>(parts.at(0))); | ||||
| 			value.y = (boost::lexical_cast<int>(parts.at(1))); | ||||
| 			value.z = (boost::lexical_cast<int>(parts.at(2))); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			value = iter->second; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CRmgTemplate::serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & value, const std::string & fieldName) | ||||
| { | ||||
| 	std::string encodedValue; | ||||
|  | ||||
| 	if(handler.saving) | ||||
| 		encodedValue = value.toString(); | ||||
|  | ||||
| 	handler.serializeString(fieldName, encodedValue); | ||||
|  | ||||
| 	if(!handler.saving) | ||||
| 		value.fromString(encodedValue); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../ResourceSet.h" | ||||
| #include "CMapGenOptions.h" | ||||
| @@ -33,6 +34,11 @@ public: | ||||
| 	ui32 min; | ||||
| 	ui32 max; | ||||
| 	ui16 density; | ||||
| 	CTreasureInfo(); | ||||
|  | ||||
| 	bool operator ==(const CTreasureInfo & other) const; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| }; | ||||
|  | ||||
| namespace rmg | ||||
| @@ -44,12 +50,10 @@ public: | ||||
| 	ZoneConnection(); | ||||
|  | ||||
| 	TRmgTemplateZoneId getZoneA() const; | ||||
| 	void setZoneA(TRmgTemplateZoneId value); | ||||
| 	TRmgTemplateZoneId getZoneB() const; | ||||
| 	void setZoneB(TRmgTemplateZoneId value); | ||||
| 	int getGuardStrength() const; /// Default: 0 | ||||
| 	void setGuardStrength(int value); | ||||
| 	int getGuardStrength() const; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| private: | ||||
| 	TRmgTemplateZoneId zoneA; | ||||
| 	TRmgTemplateZoneId zoneB; | ||||
| @@ -60,23 +64,25 @@ class DLL_LINKAGE ZoneOptions | ||||
| { | ||||
| public: | ||||
| 	static const std::set<ETerrainType> DEFAULT_TERRAIN_TYPES; | ||||
| 	static const TRmgTemplateZoneId NO_ZONE; | ||||
|  | ||||
| 	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); | ||||
| 		int getTownCount() const; | ||||
| 		int getCastleCount() const; | ||||
| 		int getTownDensity() const; | ||||
| 		int getCastleDensity() const; | ||||
|  | ||||
| 		void serializeJson(JsonSerializeFormat & handler); | ||||
|  | ||||
| 	private: | ||||
| 		int townCount, castleCount, townDensity, castleDensity; | ||||
| 		int townCount; | ||||
| 		int castleCount; | ||||
| 		int townDensity; | ||||
| 		int castleDensity; | ||||
| 	}; | ||||
|  | ||||
| 	ZoneOptions(); | ||||
| @@ -86,44 +92,34 @@ public: | ||||
| 	TRmgTemplateZoneId getId() const; | ||||
| 	void setId(TRmgTemplateZoneId value); | ||||
|  | ||||
| 	ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::PLAYER_START | ||||
| 	void setType(ETemplateZoneType::ETemplateZoneType value); | ||||
|  | ||||
| 	int getSize() const; /// Default: 1 | ||||
| 	ETemplateZoneType::ETemplateZoneType getType() const; | ||||
| 	int getSize() const; | ||||
| 	void setSize(int value); | ||||
|  | ||||
| 	boost::optional<int> getOwner() const; | ||||
| 	void setOwner(boost::optional<int> value); | ||||
|  | ||||
| 	const CTownInfo & getPlayerTowns() const; | ||||
| 	void setPlayerTowns(const CTownInfo & value); | ||||
| 	const CTownInfo & getNeutralTowns() const; | ||||
| 	void setNeutralTowns(const CTownInfo & value); | ||||
|  | ||||
| 	bool getMatchTerrainToTown() const; /// Default: true | ||||
| 	void setMatchTerrainToTown(bool value); | ||||
|  | ||||
| 	const std::set<ETerrainType> & getTerrainTypes() const; /// Default: all | ||||
| 	const std::set<ETerrainType> & getTerrainTypes() const; | ||||
| 	void setTerrainTypes(const std::set<ETerrainType> & value); | ||||
|  | ||||
| 	bool getTownsAreSameType() const; /// Default: false | ||||
| 	void setTownsAreSameType(bool value); | ||||
|  | ||||
| 	std::set<TFaction> getDefaultTownTypes() const; | ||||
| 	const std::set<TFaction> & getTownTypes() const; | ||||
|  | ||||
| 	const std::set<TFaction> & getTownTypes() const; /// Default: all | ||||
| 	void setTownTypes(const std::set<TFaction> & value); | ||||
| 	void setMonsterTypes(const std::set<TFaction> & value); | ||||
|  | ||||
| 	void setMonsterStrength(EMonsterStrength::EMonsterStrength val); | ||||
|  | ||||
| 	void setMinesAmount (TResource res, ui16 amount); | ||||
| 	void setMinesInfo(const std::map<TResource, ui16> & value); | ||||
| 	std::map<TResource, ui16> getMinesInfo() const; | ||||
|  | ||||
| 	void addTreasureInfo(const CTreasureInfo & info); | ||||
| 	void setTreasureInfo(const std::vector<CTreasureInfo> & value); | ||||
| 	const std::vector<CTreasureInfo> & getTreasureInfo() const; | ||||
|  | ||||
| 	TRmgTemplateZoneId getMinesLikeZone() const; | ||||
| 	TRmgTemplateZoneId getTerrainTypeLikeZone() const; | ||||
| 	TRmgTemplateZoneId getTreasureLikeZone() const; | ||||
|  | ||||
| 	void addConnection(TRmgTemplateZoneId otherZone); | ||||
| 	std::vector<TRmgTemplateZoneId> getConnections() const; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
|  | ||||
| protected: | ||||
| 	TRmgTemplateZoneId id; | ||||
| @@ -146,6 +142,10 @@ protected: | ||||
| 	std::vector<CTreasureInfo> treasureInfo; | ||||
|  | ||||
| 	std::vector<TRmgTemplateZoneId> connections; //list of adjacent zones | ||||
|  | ||||
| 	TRmgTemplateZoneId minesLikeZone; | ||||
| 	TRmgTemplateZoneId terrainTypeLikeZone; | ||||
| 	TRmgTemplateZoneId treasureLikeZone; | ||||
| }; | ||||
|  | ||||
| } | ||||
| @@ -154,27 +154,9 @@ protected: | ||||
| class DLL_LINKAGE CRmgTemplate | ||||
| { | ||||
| public: | ||||
| 	class CSize | ||||
| 	{ | ||||
| 	public: | ||||
| 		CSize(); | ||||
| 		CSize(int width, int height, bool under); | ||||
| 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<rmg::ZoneOptions>>; | ||||
|  | ||||
| 		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 | ||||
| 	class DLL_LINKAGE CPlayerCountRange | ||||
| 	{ | ||||
| 	public: | ||||
| 		void addRange(int lower, int upper); | ||||
| @@ -182,34 +164,39 @@ public: | ||||
| 		bool isInRange(int count) const; | ||||
| 		std::set<int> getNumbers() const; | ||||
|  | ||||
| 		std::string toString() const; | ||||
| 		void fromString(const std::string & value); | ||||
|  | ||||
| 	private: | ||||
| 		std::list<std::pair<int, int> > range; | ||||
| 		std::vector<std::pair<int, int> > range; | ||||
| 	}; | ||||
|  | ||||
| 	CRmgTemplate(); | ||||
| 	~CRmgTemplate(); | ||||
|  | ||||
| 	bool matchesSize(const int3 & value) const; | ||||
|  | ||||
| 	void setId(const std::string & value); | ||||
| 	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<TRmgTemplateZoneId, rmg::ZoneOptions *> & getZones() const; | ||||
| 	void setZones(const std::map<TRmgTemplateZoneId, rmg::ZoneOptions *> & value); | ||||
| 	const std::list<rmg::ZoneConnection> & getConnections() const; | ||||
| 	void setConnections(const std::list<rmg::ZoneConnection> & value); | ||||
| 	const Zones & getZones() const; | ||||
| 	const std::vector<rmg::ZoneConnection> & getConnections() const; | ||||
|  | ||||
| 	void validate() const; /// Tests template on validity and throws exception on failure | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
|  | ||||
| private: | ||||
| 	std::string id; | ||||
| 	std::string name; | ||||
| 	CSize minSize, maxSize; | ||||
| 	int3 minSize, maxSize; | ||||
| 	CPlayerCountRange players, cpuPlayers; | ||||
| 	std::map<TRmgTemplateZoneId, rmg::ZoneOptions *> zones; | ||||
| 	std::list<rmg::ZoneConnection> connections; | ||||
| 	Zones zones; | ||||
| 	std::vector<rmg::ZoneConnection> connections; | ||||
|  | ||||
| 	void afterLoad(); | ||||
| 	void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName); | ||||
| 	void serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & value, const std::string & fieldName); | ||||
| }; | ||||
|   | ||||
| @@ -9,16 +9,11 @@ | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "CRmgTemplateStorage.h" | ||||
|  | ||||
| #include "../filesystem/Filesystem.h" | ||||
| #include "../JsonNode.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../VCMI_Lib.h" | ||||
| #include "../CModHandler.h" | ||||
| #include "../CTownHandler.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../StringConstants.h" | ||||
| #include "CRmgTemplateStorage.h" | ||||
| #include "CRmgTemplate.h" | ||||
|  | ||||
| #include "../serializer/JsonDeserializer.h" | ||||
|  | ||||
| using namespace rmg; | ||||
|  | ||||
| @@ -38,198 +33,12 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const | ||||
| 	auto tpl = new CRmgTemplate(); | ||||
| 	try | ||||
| 	{ | ||||
| 		const auto & templateNode = data; | ||||
| 		if (!templateNode["name"].isNull()) | ||||
| 			tpl->setName(templateNode["name"].String()); //name can be customised. Allow duplicated names for different template versions. | ||||
| 		else | ||||
| 			tpl->setName(name); //identifier becomes default name | ||||
|  | ||||
| 		// 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<TRmgTemplateZoneId, ZoneOptions *> zones; | ||||
| 		for (const auto & zonePair : templateNode["zones"].Struct()) | ||||
| 		{ | ||||
| 			auto zone = new ZoneOptions(); | ||||
| 			auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(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"])); | ||||
| 			if (!zoneNode["matchTerrainToTown"].isNull()) //default : true | ||||
| 				zone->setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool()); | ||||
| 			zone->setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), ZoneOptions::DEFAULT_TERRAIN_TYPES)); | ||||
|  | ||||
| 			if (!zoneNode["townsAreSameType"].isNull()) //default : false | ||||
| 				zone->setTownsAreSameType((zoneNode["townsAreSameType"].Bool())); | ||||
|  | ||||
| 			for (int i = 0; i < 2; ++i) | ||||
| 			{ | ||||
| 				std::set<TFaction> allowedTownTypes; | ||||
| 				if (i) | ||||
| 				{ | ||||
| 					if (zoneNode["allowedTowns"].isNull()) | ||||
| 						allowedTownTypes = zone->getDefaultTownTypes(); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if (zoneNode["allowedMonsters"].isNull()) | ||||
| 						allowedTownTypes = VLC->townh->getAllowedFactions(false); | ||||
| 				} | ||||
|  | ||||
| 				if (allowedTownTypes.empty()) | ||||
| 				{ | ||||
| 					for (const JsonNode & allowedTown : zoneNode[i ? "allowedTowns" : "allowedMonsters"].Vector()) | ||||
| 					{ | ||||
| 						//complain if the town type is not present in our game | ||||
| 						if (auto id = VLC->modh->identifiers.getIdentifier("faction", allowedTown, false)) | ||||
| 							allowedTownTypes.insert(id.get()); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if (!zoneNode[i ? "bannedTowns" : "bannedMonsters"].isNull()) | ||||
| 				{ | ||||
| 					for (const JsonNode & bannedTown : zoneNode[i ? "bannedTowns" : "bannedMonsters"].Vector()) | ||||
| 					{ | ||||
| 						//erase unindentified towns silently | ||||
| 						if (auto id = VLC->modh->identifiers.getIdentifier("faction", bannedTown, true)) | ||||
| 							vstd::erase_if_present(allowedTownTypes, id.get()); | ||||
| 					} | ||||
| 				} | ||||
| 				if (i) | ||||
| 					zone->setTownTypes(allowedTownTypes); | ||||
| 				else | ||||
| 					zone->setMonsterTypes(allowedTownTypes); | ||||
| 			} | ||||
|  | ||||
| 			const std::string monsterStrength = zoneNode["monsters"].String(); | ||||
| 			if (monsterStrength == "weak") | ||||
| 				zone->setMonsterStrength(EMonsterStrength::ZONE_WEAK); | ||||
| 			else if (monsterStrength == "normal") | ||||
| 				zone->setMonsterStrength(EMonsterStrength::ZONE_NORMAL); | ||||
| 			else if (monsterStrength == "strong") | ||||
| 				zone->setMonsterStrength(EMonsterStrength::ZONE_STRONG); | ||||
| 			else | ||||
| 			{ | ||||
| 				delete zone; | ||||
| 				throw (std::runtime_error("incorrect monster power")); | ||||
| 			} | ||||
|  | ||||
| 			if (!zoneNode["mines"].isNull()) | ||||
| 			{ | ||||
| 				auto mines = zoneNode["mines"].Struct(); | ||||
| 				//FIXME: maybe there is a smarter way to parse it already? | ||||
| 				zone->setMinesAmount(Res::WOOD, mines["wood"].Float()); | ||||
| 				zone->setMinesAmount(Res::ORE, mines["ore"].Float()); | ||||
| 				zone->setMinesAmount(Res::GEMS, mines["gems"].Float()); | ||||
| 				zone->setMinesAmount(Res::CRYSTAL, mines["crystal"].Float()); | ||||
| 				zone->setMinesAmount(Res::SULFUR, mines["sulfur"].Float()); | ||||
| 				zone->setMinesAmount(Res::MERCURY, mines["mercury"].Float()); | ||||
| 				zone->setMinesAmount(Res::GOLD, mines["gold"].Float()); | ||||
| 				//TODO: Mithril | ||||
| 			} | ||||
|  | ||||
| 			//treasures | ||||
| 			if (!zoneNode["treasure"].isNull()) | ||||
| 			{ | ||||
| 				//TODO: parse vector of different treasure settings | ||||
| 				if (zoneNode["treasure"].getType() == JsonNode::JsonType::DATA_STRUCT) | ||||
| 				{ | ||||
| 					auto treasureInfo = zoneNode["treasure"].Struct(); | ||||
| 					{ | ||||
| 						CTreasureInfo ti; | ||||
| 						ti.min = treasureInfo["min"].Float(); | ||||
| 						ti.max = treasureInfo["max"].Float(); | ||||
| 						ti.density = treasureInfo["density"].Float(); //TODO: use me | ||||
| 						zone->addTreasureInfo(ti); | ||||
| 					} | ||||
| 				} | ||||
| 				else if (zoneNode["treasure"].getType() == JsonNode::JsonType::DATA_VECTOR) | ||||
| 				{ | ||||
| 					for (auto treasureInfo : zoneNode["treasure"].Vector()) | ||||
| 					{ | ||||
| 						CTreasureInfo ti; | ||||
| 						ti.min = treasureInfo["min"].Float(); | ||||
| 						ti.max = treasureInfo["max"].Float(); | ||||
| 						ti.density = treasureInfo["density"].Float(); | ||||
| 						zone->addTreasureInfo(ti); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			zones[zone->getId()] = zone; | ||||
| 		} | ||||
|  | ||||
| 		//copy settings from already parsed zones | ||||
| 		for (const auto & zonePair : templateNode["zones"].Struct()) | ||||
| 		{ | ||||
| 			auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(zonePair.first); | ||||
| 			auto zone = zones[zoneId]; | ||||
|  | ||||
| 			const auto & zoneNode = zonePair.second; | ||||
|  | ||||
| 			if (!zoneNode["terrainTypeLikeZone"].isNull()) | ||||
| 			{ | ||||
| 				int id = zoneNode["terrainTypeLikeZone"].Float(); | ||||
| 				zone->setTerrainTypes(zones[id]->getTerrainTypes()); | ||||
| 				zone->setMatchTerrainToTown(zones[id]->getMatchTerrainToTown()); | ||||
| 			} | ||||
|  | ||||
| 			if (!zoneNode["townTypeLikeZone"].isNull()) | ||||
| 				zone->setTownTypes (zones[zoneNode["townTypeLikeZone"].Float()]->getTownTypes()); | ||||
|  | ||||
| 			if (!zoneNode["treasureLikeZone"].isNull()) | ||||
| 			{ | ||||
| 				for (auto treasureInfo : zones[zoneNode["treasureLikeZone"].Float()]->getTreasureInfo()) | ||||
| 				{ | ||||
| 					zone->addTreasureInfo(treasureInfo); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (!zoneNode["minesLikeZone"].isNull()) | ||||
| 			{ | ||||
| 				for (auto mineInfo : zones[zoneNode["minesLikeZone"].Float()]->getMinesInfo()) | ||||
| 				{ | ||||
| 					zone->setMinesAmount (mineInfo.first, mineInfo.second); | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		tpl->setZones(zones); | ||||
|  | ||||
| 		// Parse connections | ||||
| 		std::list<ZoneConnection> connections; | ||||
| 		for(const auto & connPair : templateNode["connections"].Vector()) | ||||
| 		{ | ||||
| 			ZoneConnection conn; | ||||
| 			conn.setZoneA(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["a"].String()))->second->getId()); | ||||
| 			conn.setZoneB(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["b"].String()))->second->getId()); | ||||
| 			conn.setGuardStrength(connPair["guard"].Float()); | ||||
| 			connections.push_back(conn); | ||||
| 		} | ||||
| 		tpl->setConnections(connections); | ||||
| 		{ | ||||
| 			auto zones = tpl->getZones(); | ||||
| 			for (auto con : tpl->getConnections()) | ||||
| 			{ | ||||
| 				auto idA = con.getZoneA(); | ||||
| 				auto idB = con.getZoneB(); | ||||
| 				zones[idA]->addConnection(idB); | ||||
| 				zones[idB]->addConnection(idA); | ||||
| 			} | ||||
| 		} | ||||
| 		JsonDeserializer handler(nullptr, data); | ||||
| 		auto fullKey = normalizeIdentifier(scope, "core", name); | ||||
| 		tpl->setId(name); | ||||
| 		tpl->serializeJson(handler); | ||||
| 		tpl->validate(); | ||||
| 		templates[tpl->getName()] = tpl; | ||||
| 		templates[fullKey] = tpl; | ||||
| 	} | ||||
| 	catch(const std::exception & e) | ||||
| 	{ | ||||
| @@ -237,142 +46,8 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CRmgTemplate::CSize CRmgTemplateStorage::parseMapTemplateSize(const std::string & text) const | ||||
| { | ||||
| 	CRmgTemplate::CSize size; | ||||
| 	if(text.empty()) return size; | ||||
|  | ||||
| 	std::vector<std::string> parts; | ||||
| 	boost::split(parts, text, boost::is_any_of("+")); | ||||
| 	static const std::map<std::string, int> mapSizeMapping = | ||||
| 	{ | ||||
| 		{"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 std::string numericalRep = parts[0]; | ||||
| 		parts.clear(); | ||||
| 		boost::split(parts, numericalRep, boost::is_any_of("x")); | ||||
| 		assert(parts.size() == 3); | ||||
| 		size.setWidth(boost::lexical_cast<int>(parts[0])); | ||||
| 		size.setHeight(boost::lexical_cast<int>(parts[1])); | ||||
| 		size.setUnder(boost::lexical_cast<int>(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 CRmgTemplateStorage::parseZoneType(const std::string & type) const | ||||
| { | ||||
| 	static const std::map<std::string, ETemplateZoneType::ETemplateZoneType> zoneTypeMapping = | ||||
| 	{ | ||||
| 		{"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; | ||||
| } | ||||
|  | ||||
| ZoneOptions::CTownInfo CRmgTemplateStorage::parseTemplateZoneTowns(const JsonNode & node) const | ||||
| { | ||||
| 	ZoneOptions::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<TFaction> CRmgTemplateStorage::parseTownTypes(const JsonVector & townTypesVector, const std::set<TFaction> & defaultTownTypes) const | ||||
| { | ||||
| 	std::set<TFaction> 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<ETerrainType> CRmgTemplateStorage::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set<ETerrainType> & defaultTerrainTypes) const | ||||
| { | ||||
| 	std::set<ETerrainType> terTypes; | ||||
| 	if (terTypeStrings.empty()) //nothing was specified | ||||
| 		return defaultTerrainTypes; | ||||
|  | ||||
| 	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 CRmgTemplateStorage::parsePlayers(const std::string & players) const | ||||
| { | ||||
| 	CRmgTemplate::CPlayerCountRange playerRange; | ||||
| 	if(players.empty()) | ||||
| 	{ | ||||
| 		playerRange.addNumber(0); | ||||
| 		return playerRange; | ||||
| 	} | ||||
| 	std::vector<std::string> commaParts; | ||||
| 	boost::split(commaParts, players, boost::is_any_of(",")); | ||||
| 	for(const auto & commaPart : commaParts) | ||||
| 	{ | ||||
| 		std::vector<std::string> rangeParts; | ||||
| 		boost::split(rangeParts, commaPart, boost::is_any_of("-")); | ||||
| 		if(rangeParts.size() == 2) | ||||
| 		{ | ||||
| 			auto lower = boost::lexical_cast<int>(rangeParts[0]); | ||||
| 			auto upper = boost::lexical_cast<int>(rangeParts[1]); | ||||
| 			playerRange.addRange(lower, upper); | ||||
| 		} | ||||
| 		else if(rangeParts.size() == 1) | ||||
| 		{ | ||||
| 			auto val = boost::lexical_cast<int>(rangeParts.front()); | ||||
| 			playerRange.addNumber(val); | ||||
| 		} | ||||
| 	} | ||||
| 	return playerRange; | ||||
| } | ||||
|  | ||||
| CRmgTemplateStorage::CRmgTemplateStorage() | ||||
| { | ||||
| 	//TODO: load all | ||||
| } | ||||
|  | ||||
| CRmgTemplateStorage::~CRmgTemplateStorage() | ||||
|   | ||||
| @@ -10,12 +10,10 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "CRmgTemplate.h" | ||||
| #include "../IHandlerBase.h" | ||||
|  | ||||
| class JsonNode; | ||||
|  | ||||
| typedef std::vector<JsonNode> JsonVector; | ||||
| class CRmgTemplate; | ||||
|  | ||||
| /// The CJsonRmgTemplateLoader loads templates from a JSON file. | ||||
| class DLL_LINKAGE CRmgTemplateStorage : public IHandlerBase | ||||
| @@ -34,14 +32,6 @@ public: | ||||
| 	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; | ||||
|  | ||||
| private: | ||||
| 	CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const; | ||||
| 	rmg::ZoneOptions::CTownInfo parseTemplateZoneTowns(const JsonNode & node) const; | ||||
| 	ETemplateZoneType::ETemplateZoneType parseZoneType(const std::string & type) const; | ||||
| 	std::set<TFaction> parseTownTypes(const JsonVector & townTypesVector, const std::set<TFaction> & defaultTownTypes) const; | ||||
| 	std::set<ETerrainType> parseTerrainTypes(const JsonVector & terTypeStrings, const std::set<ETerrainType> & defaultTerrainTypes) const; | ||||
| 	CRmgTemplate::CPlayerCountRange parsePlayers(const std::string & players) const; | ||||
|  | ||||
| protected: | ||||
| 	std::map<std::string, CRmgTemplate *> templates; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -108,7 +108,7 @@ CRmgTemplateZone::CRmgTemplateZone() | ||||
| 	townType(ETownType::NEUTRAL), | ||||
| 	terrainType (ETerrainType::GRASS), | ||||
| 	minGuardedValue(0), | ||||
| 	questArtZone(nullptr), | ||||
| 	questArtZone(), | ||||
| 	gen(nullptr) | ||||
| { | ||||
|  | ||||
| @@ -124,16 +124,11 @@ void CRmgTemplateZone::setGenPtr(CMapGenerator * Gen) | ||||
| 	gen = Gen; | ||||
| } | ||||
|  | ||||
| void CRmgTemplateZone::setQuestArtZone(CRmgTemplateZone * otherZone) | ||||
| void CRmgTemplateZone::setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone) | ||||
| { | ||||
| 	questArtZone = otherZone; | ||||
| } | ||||
|  | ||||
| std::vector<TRmgTemplateZoneId> CRmgTemplateZone::getConnections() const | ||||
| { | ||||
| 	return connections; | ||||
| } | ||||
|  | ||||
| std::set<int3>* CRmgTemplateZone::getFreePaths() | ||||
| { | ||||
| 	return &freePaths; | ||||
| @@ -2488,7 +2483,7 @@ void CRmgTemplateZone::addAllPossibleObjects() | ||||
|  | ||||
| 	//seer huts with creatures or generic rewards | ||||
|  | ||||
| 	if (questArtZone) //we won't be placing seer huts if there is no zone left to place arties | ||||
| 	if(questArtZone.lock()) //we won't be placing seer huts if there is no zone left to place arties | ||||
| 	{ | ||||
| 		static const int genericSeerHuts = 8; | ||||
| 		int seerHutsPerType = 0; | ||||
| @@ -2549,7 +2544,7 @@ void CRmgTemplateZone::addAllPossibleObjects() | ||||
|  | ||||
| 				gen->banQuestArt(artid); | ||||
|  | ||||
| 				this->questArtZone->possibleObjects.push_back (generateArtInfo(artid)); | ||||
| 				this->questArtZone.lock()->possibleObjects.push_back (generateArtInfo(artid)); | ||||
|  | ||||
| 				return obj; | ||||
| 			}; | ||||
| @@ -2587,7 +2582,7 @@ void CRmgTemplateZone::addAllPossibleObjects() | ||||
|  | ||||
| 				gen->banQuestArt(artid); | ||||
|  | ||||
| 				this->questArtZone->possibleObjects.push_back(generateArtInfo(artid)); | ||||
| 				this->questArtZone.lock()->possibleObjects.push_back(generateArtInfo(artid)); | ||||
|  | ||||
| 				return obj; | ||||
| 			}; | ||||
| @@ -2610,7 +2605,7 @@ void CRmgTemplateZone::addAllPossibleObjects() | ||||
|  | ||||
| 				gen->banQuestArt(artid); | ||||
|  | ||||
| 				this->questArtZone->possibleObjects.push_back(generateArtInfo(artid)); | ||||
| 				this->questArtZone.lock()->possibleObjects.push_back(generateArtInfo(artid)); | ||||
|  | ||||
| 				return obj; | ||||
| 			}; | ||||
|   | ||||
| @@ -136,8 +136,7 @@ public: | ||||
| 	std::vector<int3> getAccessibleOffsets (const CGObjectInstance* object); | ||||
| 	bool areAllTilesAvailable(CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const; | ||||
|  | ||||
| 	void setQuestArtZone(CRmgTemplateZone * otherZone); | ||||
| 	std::vector<TRmgTemplateZoneId> getConnections() const; | ||||
| 	void setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone); | ||||
| 	std::set<int3>* getFreePaths(); | ||||
|  | ||||
| 	ObjectInfo getRandomObject (CTreasurePileInfo &info, ui32 desiredValue, ui32 maxValue, ui32 currentValue); | ||||
| @@ -166,7 +165,7 @@ private: | ||||
|  | ||||
| 	si32 townType; | ||||
| 	ETerrainType terrainType; | ||||
| 	CRmgTemplateZone * questArtZone; //artifacts required for Seer Huts will be placed here - or not if null | ||||
| 	std::weak_ptr<CRmgTemplateZone> questArtZone; //artifacts required for Seer Huts will be placed here - or not if null | ||||
|  | ||||
| 	std::vector<ObjectInfo> possibleObjects; | ||||
| 	int minGuardedValue; | ||||
|   | ||||
| @@ -18,11 +18,6 @@ | ||||
|  | ||||
| class CRandomGenerator; | ||||
|  | ||||
| CPlacedZone::CPlacedZone(const CRmgTemplateZone * zone) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| CZonePlacer::CZonePlacer(CMapGenerator * Gen) | ||||
| 	: width(0), height(0), scaleX(0), scaleY(0), mapSize(0), gravityConstant(0), stiffnessConstant(0), | ||||
| 	gen(Gen) | ||||
| @@ -78,7 +73,7 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera | ||||
| 	float bestTotalDistance = 1e10; | ||||
| 	float bestTotalOverlap = 1e10; | ||||
|  | ||||
| 	std::map<CRmgTemplateZone *, float3> bestSolution; | ||||
| 	std::map<std::shared_ptr<CRmgTemplateZone>, float3> bestSolution; | ||||
|  | ||||
| 	TForceVector forces; | ||||
| 	TForceVector totalForces; //  both attraction and pushback, overcomplicated? | ||||
| @@ -359,7 +354,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista | ||||
| { | ||||
| 	float maxRatio = 0; | ||||
| 	const int maxDistanceMovementRatio = zones.size() * zones.size(); //experimental - the more zones, the greater total distance expected | ||||
| 	CRmgTemplateZone * misplacedZone = nullptr; | ||||
| 	std::shared_ptr<CRmgTemplateZone> misplacedZone; | ||||
|  | ||||
| 	float totalDistance = 0; | ||||
| 	float totalOverlap = 0; | ||||
| @@ -379,7 +374,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista | ||||
|  | ||||
| 	if (maxRatio > maxDistanceMovementRatio && misplacedZone) | ||||
| 	{ | ||||
| 		CRmgTemplateZone * targetZone = nullptr; | ||||
| 		std::shared_ptr<CRmgTemplateZone> targetZone; | ||||
| 		float3 ourCenter = misplacedZone->getCenter(); | ||||
|  | ||||
| 		if (totalDistance > totalOverlap) | ||||
| @@ -471,7 +466,7 @@ void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions) | ||||
|  | ||||
| 	auto zones = gen->getZones(); | ||||
|  | ||||
| 	typedef std::pair<CRmgTemplateZone *, float> Dpair; | ||||
| 	typedef std::pair<std::shared_ptr<CRmgTemplateZone>, float> Dpair; | ||||
| 	std::vector <Dpair> distances; | ||||
| 	distances.reserve(zones.size()); | ||||
|  | ||||
| @@ -483,7 +478,7 @@ void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions) | ||||
| 		return lhs.second / lhs.first->getSize() < rhs.second / rhs.first->getSize(); | ||||
| 	}; | ||||
|  | ||||
| 	auto moveZoneToCenterOfMass = [](CRmgTemplateZone * zone) -> void | ||||
| 	auto moveZoneToCenterOfMass = [](std::shared_ptr<CRmgTemplateZone> zone) -> void | ||||
| 	{ | ||||
| 		int3 total(0, 0, 0); | ||||
| 		auto tiles = zone->getTileInfo(); | ||||
|   | ||||
| @@ -20,22 +20,10 @@ class CRandomGenerator; | ||||
| class CRmgTemplateZone; | ||||
| class CMapGenerator; | ||||
|  | ||||
| typedef std::vector<std::pair<TRmgTemplateZoneId, CRmgTemplateZone*>> TZoneVector; | ||||
| typedef std::map <TRmgTemplateZoneId, CRmgTemplateZone*> TZoneMap; | ||||
| typedef std::map <CRmgTemplateZone *, float3> TForceVector; | ||||
| typedef std::map <CRmgTemplateZone *, float> TDistanceVector; | ||||
|  | ||||
| class CPlacedZone | ||||
| { | ||||
| public: | ||||
| 	explicit CPlacedZone(const CRmgTemplateZone * Zone); | ||||
|  | ||||
| private: | ||||
|     //const CRmgTemplateZone * zone; | ||||
|  | ||||
| 	//TODO exact outline data of zone | ||||
| 	//TODO perhaps further zone data, guards, obstacles, etc... | ||||
| }; | ||||
| typedef std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>> TZoneVector; | ||||
| typedef std::map <TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>> TZoneMap; | ||||
| typedef std::map <std::shared_ptr<CRmgTemplateZone>, float3> TForceVector; | ||||
| typedef std::map <std::shared_ptr<CRmgTemplateZone>, float> TDistanceVector; | ||||
|  | ||||
| class CZonePlacer | ||||
| { | ||||
|   | ||||
| @@ -232,6 +232,7 @@ public: | ||||
| 	}; | ||||
|  | ||||
| 	///Anything int64-convertible <-> Json integer | ||||
| 	///no default value | ||||
| 	template <typename T> | ||||
| 	void serializeInt(const std::string & fieldName, T & value) | ||||
| 	{ | ||||
| @@ -239,12 +240,21 @@ public: | ||||
| 	}; | ||||
|  | ||||
| 	///Anything int64-convertible <-> Json integer | ||||
| 	///custom default value | ||||
| 	template <typename T, typename U> | ||||
| 	void serializeInt(const std::string & fieldName, T & value, const U & defaultValue) | ||||
| 	{ | ||||
| 		doSerializeInternal<T, U, si64>(fieldName, value, defaultValue); | ||||
| 	}; | ||||
|  | ||||
| 	///Anything int64-convertible <-> Json integer | ||||
| 	///default value is boost::none | ||||
| 	template <typename T> | ||||
| 	void serializeInt(const std::string & fieldName, boost::optional<T> & value) | ||||
| 	{ | ||||
| 		dispatchOptional<T, si64>(fieldName, value); | ||||
| 	}; | ||||
|  | ||||
| 	///si32-convertible identifier <-> Json string | ||||
| 	template <typename T, typename U> | ||||
| 	void serializeId(const std::string & fieldName, T & value, const U & defaultValue, const TDecoder & decoder, const TEncoder & encoder) | ||||
| @@ -253,14 +263,14 @@ public: | ||||
| 	} | ||||
|  | ||||
| 	///si32-convertible identifier <-> Json string | ||||
| 	template <typename T, typename U> | ||||
| 	template <typename T, typename U, typename E = T> | ||||
| 	void serializeId(const std::string & fieldName, T & value, const U & defaultValue) | ||||
| 	{ | ||||
| 		doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, &T::decode, &T::encode); | ||||
| 		doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, &E::decode, &E::encode); | ||||
| 	} | ||||
|  | ||||
| 	///si32-convertible identifier vector <-> Json array of string | ||||
| 	template <typename T, typename U = T> | ||||
| 	template <typename T, typename E = T> | ||||
| 	void serializeIdArray(const std::string & fieldName, std::vector<T> & value) | ||||
| 	{ | ||||
| 		std::vector<si32> temp; | ||||
| @@ -276,7 +286,7 @@ public: | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		serializeInternal(fieldName, temp, &U::decode, &U::encode); | ||||
| 		serializeInternal(fieldName, temp, &E::decode, &E::encode); | ||||
| 		if(!saving) | ||||
| 		{ | ||||
| 			value.clear(); | ||||
| @@ -320,6 +330,43 @@ public: | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	///si32-convertible identifier set <-> Json array of string | ||||
| 	template <typename T, typename U = T> | ||||
| 	void serializeIdArray(const std::string & fieldName, std::set<T> & value, const std::set<T> & defaultValue) | ||||
| 	{ | ||||
| 		std::vector<si32> temp; | ||||
|  | ||||
| 		if(saving && value != defaultValue) | ||||
| 		{ | ||||
| 			temp.reserve(value.size()); | ||||
|  | ||||
| 			for(const T & vitem : value) | ||||
| 			{ | ||||
| 				si32 item = static_cast<si32>(vitem); | ||||
| 				temp.push_back(item); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		serializeInternal(fieldName, temp, &U::decode, &U::encode); | ||||
| 		if(!saving) | ||||
| 		{ | ||||
| 			if(temp.empty()) | ||||
| 			{ | ||||
| 				value = defaultValue; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				value.clear(); | ||||
|  | ||||
| 				for(const si32 item : temp) | ||||
| 				{ | ||||
| 					T vitem = static_cast<T>(item); | ||||
| 					value.insert(vitem); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	///bitmask <-> Json array of string | ||||
| 	template <typename T, int Size> | ||||
| 	void serializeIdArray(const std::string & fieldName, T & value, const T & defaultValue, const TDecoder & decoder, const TEncoder & encoder) | ||||
| @@ -397,6 +444,7 @@ protected: | ||||
| 	virtual void pushStruct(const std::string & fieldName) = 0; | ||||
| 	virtual void pushArray(const std::string & fieldName) = 0; | ||||
| 	virtual void pushArrayElement(const size_t index) = 0; | ||||
| 	virtual void pushField(const std::string & fieldName) = 0; | ||||
|  | ||||
| 	virtual void resizeCurrent(const size_t newSize, JsonNode::JsonType type){}; | ||||
|  | ||||
| @@ -406,9 +454,9 @@ protected: | ||||
| private: | ||||
| 	const IInstanceResolver * instanceResolver; | ||||
|  | ||||
|     template <typename VType, typename DVType, typename IType, typename... Args> | ||||
|     void doSerializeInternal(const std::string & fieldName, VType & value, const boost::optional<DVType> & defaultValue, Args ... args) | ||||
|     { | ||||
| 	template <typename VType, typename DVType, typename IType, typename... Args> | ||||
| 	void doSerializeInternal(const std::string & fieldName, VType & value, const boost::optional<DVType> & defaultValue, Args ... args) | ||||
| 	{ | ||||
| 		const boost::optional<IType> tempDefault = defaultValue ? boost::optional<IType>(static_cast<IType>(defaultValue.get())) : boost::none; | ||||
| 		IType temp = static_cast<IType>(value); | ||||
|  | ||||
| @@ -416,7 +464,39 @@ private: | ||||
|  | ||||
| 		if(!saving) | ||||
| 			value = static_cast<VType>(temp); | ||||
|     } | ||||
| 	} | ||||
|  | ||||
| 	template <typename VType, typename IType, typename... Args> | ||||
| 	void dispatchOptional(const std::string & fieldName, boost::optional<VType> & value, Args ... args) | ||||
| 	{ | ||||
| 		if(saving) | ||||
| 		{ | ||||
| 			if(value) | ||||
| 			{ | ||||
| 				IType temp = static_cast<IType>(value.get()); | ||||
| 				pushField(fieldName); | ||||
| 				serializeInternal(temp, args...); | ||||
| 				pop(); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			pushField(fieldName); | ||||
|  | ||||
| 			if(getCurrent().getType() == JsonNode::JsonType::DATA_NULL) | ||||
| 			{ | ||||
| 				value = boost::none; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				IType temp = IType(); | ||||
| 				serializeInternal(temp, args...); | ||||
| 				value = boost::make_optional(temp); | ||||
| 			} | ||||
|  | ||||
| 			pop(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	friend class JsonSerializeHelper; | ||||
| 	friend class JsonStructSerializer; | ||||
|   | ||||
| @@ -69,14 +69,12 @@ void JsonSerializer::serializeInternal(const std::string & fieldName, std::vecto | ||||
|  | ||||
| void JsonSerializer::serializeInternal(std::string & value) | ||||
| { | ||||
| 	if(value != "") | ||||
| 		currentObject->String() = value; | ||||
| 	currentObject->String() = value; | ||||
| } | ||||
|  | ||||
| void JsonSerializer::serializeInternal(int64_t & value) | ||||
| { | ||||
| 	if(value != 0) | ||||
| 		currentObject->Integer() = value; | ||||
| 	currentObject->Integer() = value; | ||||
| } | ||||
|  | ||||
| void JsonSerializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) | ||||
|   | ||||
| @@ -54,6 +54,11 @@ protected: | ||||
| 		pushObject(&(currentObject->Vector().at(index))); | ||||
| 	} | ||||
|  | ||||
| 	void pushField(const std::string & fieldName) override | ||||
| 	{ | ||||
| 		pushObject(fieldName); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	void pushObject(const std::string & fieldName) | ||||
| 	{ | ||||
|   | ||||
| @@ -46,6 +46,8 @@ bool JsonComparer::isEmpty(const JsonNode & value) | ||||
| 		return !value.Bool(); | ||||
| 	case JsonNode::JsonType::DATA_FLOAT: | ||||
| 		return value.Float() == 0; | ||||
| 	case JsonNode::JsonType::DATA_INTEGER: | ||||
| 		return value.Integer() == 0; | ||||
| 	case JsonNode::JsonType::DATA_STRING: | ||||
| 		return value.String() == ""; | ||||
| 	case JsonNode::JsonType::DATA_VECTOR: | ||||
|   | ||||
| @@ -111,6 +111,7 @@ | ||||
| 		<Unit filename="mock/mock_spells_Problem.h" /> | ||||
| 		<Unit filename="mock/mock_spells_Spell.h" /> | ||||
| 		<Unit filename="mock/mock_vstd_RNG.h" /> | ||||
| 		<Unit filename="rmg/CRmgTemplateTest.cpp" /> | ||||
| 		<Unit filename="spells/AbilityCasterTest.cpp" /> | ||||
| 		<Unit filename="spells/TargetConditionTest.cpp" /> | ||||
| 		<Unit filename="spells/effects/CatapultTest.cpp" /> | ||||
| @@ -137,6 +138,7 @@ | ||||
| 		<Unit filename="spells/targetConditions/SpellEffectConditionTest.cpp" /> | ||||
| 		<Unit filename="spells/targetConditions/TargetConditionItemFixture.cpp" /> | ||||
| 		<Unit filename="spells/targetConditions/TargetConditionItemFixture.h" /> | ||||
| 		<Unit filename="testdata/rmg/1.json" /> | ||||
| 		<Extensions> | ||||
| 			<code_completion /> | ||||
| 			<envvars /> | ||||
|   | ||||
| @@ -9,15 +9,15 @@ | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "../lib/JsonDetail.h" | ||||
| #include "../../lib/JsonDetail.h" | ||||
|  | ||||
| #include "../lib/filesystem/CMemoryBuffer.h" | ||||
| #include "../lib/filesystem/Filesystem.h" | ||||
| #include "../../lib/filesystem/CMemoryBuffer.h" | ||||
| #include "../../lib/filesystem/Filesystem.h" | ||||
|  | ||||
| #include "../lib/mapping/CMap.h" | ||||
| #include "../lib/rmg/CMapGenOptions.h" | ||||
| #include "../lib/rmg/CMapGenerator.h" | ||||
| #include "../lib/mapping/MapFormatJson.h" | ||||
| #include "../../lib/mapping/CMap.h" | ||||
| #include "../../lib/rmg/CMapGenOptions.h" | ||||
| #include "../../lib/rmg/CMapGenerator.h" | ||||
| #include "../../lib/mapping/MapFormatJson.h" | ||||
|  | ||||
| #include "../lib/VCMIDirs.h" | ||||
|  | ||||
|   | ||||
| @@ -144,7 +144,6 @@ void checkEqual(const TerrainTile & actual, const TerrainTile & expected) | ||||
|  | ||||
| 	VCMI_REQUIRE_FIELD_EQUAL(visitable); | ||||
| 	VCMI_REQUIRE_FIELD_EQUAL(blocked); | ||||
|  | ||||
| } | ||||
|  | ||||
| //MapComparer | ||||
|   | ||||
							
								
								
									
										116
									
								
								test/rmg/CRmgTemplateTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								test/rmg/CRmgTemplateTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* | ||||
|  * CRmgTemplateTest.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 "../../lib/JsonNode.h" | ||||
| #include "../../lib/filesystem/ResourceID.h" | ||||
|  | ||||
| #include "../../lib/rmg/CRmgTemplate.h" | ||||
|  | ||||
| #include "../../lib/serializer/JsonSerializer.h" | ||||
| #include "../../lib/serializer/JsonDeserializer.h" | ||||
|  | ||||
| #include "../JsonComparer.h" | ||||
|  | ||||
|  | ||||
| namespace test | ||||
| { | ||||
| using namespace ::rmg; | ||||
| using namespace ::testing; | ||||
|  | ||||
| class CRmgTemplateTest : public Test | ||||
| { | ||||
| public: | ||||
| 	const std::string TEST_DATA_PATH = "test/rmg/"; | ||||
|  | ||||
| protected: | ||||
|  | ||||
| 	void testLoadSave(const std::string & id, const JsonNode & config) | ||||
| 	{ | ||||
| 		std::shared_ptr<CRmgTemplate> subject = std::make_shared<CRmgTemplate>(); | ||||
| 		subject->setId(id); | ||||
|  | ||||
| 		{ | ||||
| 			JsonDeserializer handler(nullptr, config); | ||||
| 			subject->serializeJson(handler); | ||||
| 		} | ||||
|  | ||||
| 		EXPECT_FALSE(subject->getName().empty()); | ||||
|  | ||||
| 		for(const auto & idAndZone : subject->getZones()) | ||||
| 		{ | ||||
| 			auto thisZone = idAndZone.second; | ||||
| 			if(thisZone->getMinesLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 			{ | ||||
| 				auto otherZoneId = thisZone->getMinesLikeZone(); | ||||
|  | ||||
| 				const auto otherZone = subject->getZones().at(otherZoneId); | ||||
| 				GTEST_ASSERT_NE(otherZone, nullptr); | ||||
| 				EXPECT_THAT(thisZone->getMinesInfo(), ContainerEq(otherZone->getMinesInfo())); | ||||
| 			} | ||||
|  | ||||
| 			if(thisZone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 			{ | ||||
| 				auto otherZoneId = thisZone->getTerrainTypeLikeZone(); | ||||
|  | ||||
| 				const auto otherZone = subject->getZones().at(otherZoneId); | ||||
| 				GTEST_ASSERT_NE(otherZone, nullptr); | ||||
| 				EXPECT_THAT(thisZone->getTerrainTypes(), ContainerEq(otherZone->getTerrainTypes())); | ||||
| 			} | ||||
|  | ||||
| 			if(thisZone->getTreasureLikeZone() != ZoneOptions::NO_ZONE) | ||||
| 			{ | ||||
| 				auto otherZoneId = thisZone->getTreasureLikeZone(); | ||||
|  | ||||
| 				const auto otherZone = subject->getZones().at(otherZoneId); | ||||
| 				GTEST_ASSERT_NE(otherZone, nullptr); | ||||
| 				EXPECT_THAT(thisZone->getTreasureInfo(), ContainerEq(otherZone->getTreasureInfo()));; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for(const auto & connection : subject->getConnections()) | ||||
| 		{ | ||||
| 			auto id1 = connection.getZoneA(); | ||||
| 			auto id2 = connection.getZoneB(); | ||||
|  | ||||
| 			auto zone1 = subject->getZones().at(id1); | ||||
| 			auto zone2 = subject->getZones().at(id2); | ||||
|  | ||||
| 			EXPECT_THAT(zone1->getConnections(), Contains(id2)); | ||||
| 			EXPECT_THAT(zone2->getConnections(), Contains(id1)); | ||||
| 		} | ||||
|  | ||||
| 		JsonNode actual(JsonNode::JsonType::DATA_STRUCT); | ||||
|  | ||||
| 		{ | ||||
| 			JsonSerializer handler(nullptr, actual); | ||||
| 			subject->serializeJson(handler); | ||||
| 		} | ||||
|  | ||||
| 		JsonComparer cmp(false); | ||||
| 		cmp.compare(id, actual, config); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| }; | ||||
|  | ||||
| TEST_F(CRmgTemplateTest, SerializeCycle) | ||||
| { | ||||
| 	const std::string testFilePath = TEST_DATA_PATH + "1.json"; | ||||
| 	ResourceID testFileRes(testFilePath); | ||||
| 	JsonNode testData(testFileRes); | ||||
|  | ||||
| 	ASSERT_FALSE((testData.Struct().empty())); | ||||
|  | ||||
| 	for(const auto & idAndConfig : testData.Struct()) | ||||
| 		testLoadSave(idAndConfig.first, idAndConfig.second); | ||||
| } | ||||
|  | ||||
| } | ||||
| @@ -13,8 +13,8 @@ | ||||
| #include "mock/mock_BonusBearer.h" | ||||
| #include "mock/mock_spells_Spell.h" | ||||
|  | ||||
| #include "../../../lib/NetPacksBase.h" | ||||
| #include "../../../lib/spells/AbilityCaster.h" | ||||
| #include "../../lib/NetPacksBase.h" | ||||
| #include "../../lib/spells/AbilityCaster.h" | ||||
|  | ||||
| namespace test | ||||
| { | ||||
|   | ||||
| @@ -11,9 +11,9 @@ | ||||
|  | ||||
| #include <vstd/RNG.h> | ||||
|  | ||||
| #include "../../../lib/NetPacksBase.h" | ||||
| #include "../../../lib/spells/TargetCondition.h" | ||||
| #include "../../../lib/serializer/JsonDeserializer.h" | ||||
| #include "../../lib/NetPacksBase.h" | ||||
| #include "../../lib/spells/TargetCondition.h" | ||||
| #include "../../lib/serializer/JsonDeserializer.h" | ||||
|  | ||||
| #include "mock/mock_spells_Mechanics.h" | ||||
| #include "mock/mock_BonusBearer.h" | ||||
|   | ||||
							
								
								
									
										216
									
								
								test/testdata/rmg/1.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								test/testdata/rmg/1.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| { | ||||
| 	"2SM2a" : | ||||
| 	{ | ||||
| 		"minSize" : "s", "maxSize" : "s+u", | ||||
| 		"players" : "2", | ||||
| 		"zones" : | ||||
| 		{ | ||||
| 			"1" : | ||||
| 			{ | ||||
| 				"type" : "playerStart", | ||||
| 				"size" : 11, | ||||
| 				"owner" : 1, | ||||
| 				"monsters" : "normal", | ||||
| 				"playerTowns" : { "castles" : 1 }, | ||||
| 				"mines" : { "wood" : 1, "ore" : 1 }, | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 10000, "max" : 15000, "density" : 1 }, | ||||
| 					{ "min" : 3000, "max" : 6000, "density" : 6 } | ||||
| 				] | ||||
| 			}, | ||||
| 			"2" : | ||||
| 			{ | ||||
| 				"type" : "playerStart", | ||||
| 				"size" : 59, | ||||
| 				"owner" : 2, | ||||
| 				"monsters" : "normal", | ||||
| 				"playerTowns" : { "castles" : 7 }, | ||||
| 				"minesLikeZone" : 1, | ||||
| 				"treasureLikeZone" : 1 | ||||
| 			}, | ||||
| 			"3" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 11, | ||||
| 				"monsters" : "normal", | ||||
| 				"neutralTowns" : { "towns" : 1 }, | ||||
| 				"matchTerrainToTown" : false, | ||||
| 				"mines" : { "mercury" : 1, "sulfur" : 1, "gold" : 1 }, | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 15000, "max" : 20000, "density" : 1 }, | ||||
| 					{ "min" : 10000, "max" : 15000, "density" : 6 }, | ||||
| 					{ "min" : 3000, "max" : 6000, "density" : 9 } | ||||
| 				] | ||||
| 			}, | ||||
| 			"4" : | ||||
| 			{ | ||||
| 				"type" : "junction", | ||||
| 				"size" : 11, | ||||
| 				"monsters" : "normal", | ||||
| 				"neutralTowns" : { "towns" : 1 }, | ||||
| 				"matchTerrainToTown" : false, | ||||
| 				"mines" : { "crystal" : 1, "gems" : 1, "gold" : 1 }, | ||||
| 				"treasureLikeZone" : 3 | ||||
| 			} | ||||
| 		}, | ||||
| 		"connections" : | ||||
| 		[ | ||||
| 			{ "a" : "1", "b" : "3", "guard" : 3000 }, | ||||
| 			{ "a" : "2", "b" : "3", "guard" : 12500 }, | ||||
| 			{ "a" : "2", "b" : "4", "guard" : 123456 } | ||||
| 		] | ||||
| 	}, | ||||
|  | ||||
| 	"Midnight Mix" : | ||||
| 	{ | ||||
| 		"name" : "test name", | ||||
| 		"minSize" : "l", "maxSize" : "l", | ||||
| 		"players" : "1,2-4", | ||||
| 		"cpu" : "1,2", | ||||
| 		"zones" : | ||||
| 		{ | ||||
| 			"1" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 12, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "castles" : 1 }, | ||||
| 				"allowedMonsters" : [ "castle", "rampart", "tower", "inferno", "necropolis", "dungeon", "stronghold", "fortress", "neutral" ], | ||||
| 				"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ], | ||||
| 				"mines" : { "gold" : 1 }, | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 8000, "max" : 9300, "density" : 4 }, | ||||
| 					{ "min" : 6000, "max" : 8000, "density" : 8 }, | ||||
| 					{ "min" : 800, "max" : 800, "density" : 4 } | ||||
| 				] | ||||
| 			}, | ||||
| 			"2" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 12, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "castles" : 1 }, | ||||
| 				"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ], | ||||
| 				"minesLikeZone" : 1, | ||||
| 				"treasureLikeZone" : 1 | ||||
| 			}, | ||||
| 			"4" : | ||||
| 			{ | ||||
| 				"type" : "playerStart", | ||||
| 				"size" : 12, | ||||
| 				"owner" : 1, | ||||
| 				"monsters" : "normal", | ||||
| 				"playerTowns" : { "castles" : 1 }, | ||||
| 				"allowedTowns" : [ "castle", "rampart", "tower", "inferno", "dungeon", "stronghold", "fortress" ], | ||||
| 				"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ], | ||||
| 				"mines" : { "wood" : 1, "mercury" : 3, "ore" : 5, "sulfur" : 7, "crystal" : 10, "gems" : 34 }, | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 4500, "max" : 6000, "density" : 1 }, | ||||
| 					{ "min" : 3500, "max" : 4500, "density" : 4 }, | ||||
| 					{ "min" : 300, "max" : 2000, "density" : 12 } | ||||
| 				] | ||||
| 			}, | ||||
| 			"5" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 10, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "towns" : 1 }, | ||||
| 				"matchTerrainToTown" : false, | ||||
| 				"terrainTypes" : [ "dirt", "grass", "subterra", "lava" ], | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 6000, "max" : 7999, "density" : 6 }, | ||||
| 					{ "min" : 4000, "max" : 6000, "density" : 8 }, | ||||
| 					{ "min" : 1200, "max" : 2000, "density" : 5 } | ||||
| 				] | ||||
| 			}, | ||||
| 			"6" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 10, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "towns" : 5 }, | ||||
| 				"townsAreSameType" : true, | ||||
| 				"terrainTypeLikeZone" : 5, | ||||
| 				"treasureLikeZone" : 5 | ||||
| 			}, | ||||
| 			"7" : | ||||
| 			{ | ||||
| 				"type" : "playerStart", | ||||
| 				"size" : 12, | ||||
| 				"owner" : 2, | ||||
| 				"monsters" : "normal", | ||||
| 				"playerTowns" : { "castles" : 1 }, | ||||
| 				"allowedTowns" : [ "castle", "rampart", "tower", "inferno", "dungeon", "stronghold", "fortress" ], | ||||
| 				"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ], | ||||
| 				"minesLikeZone" : 4, | ||||
| 				"treasureLikeZone" : 4 | ||||
| 			}, | ||||
| 			"8" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 10, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "towns" : 1 }, | ||||
| 				"terrainTypeLikeZone" : 5, | ||||
| 				"treasureLikeZone" : 5 | ||||
| 			}, | ||||
| 			"10" : | ||||
| 			{ | ||||
| 				"type" : "playerStart", | ||||
| 				"size" : 12, | ||||
| 				"owner" : 3, | ||||
| 				"monsters" : "normal", | ||||
| 				"playerTowns" : { "castles" : 1 }, | ||||
| 				"allowedTowns" : [ "castle", "rampart", "tower", "inferno", "dungeon", "stronghold", "fortress" ], | ||||
| 				"allowedMonsters" : [ "castle", "rampart", "tower", "inferno", "necropolis", "dungeon", "stronghold", "fortress", "neutral" ], | ||||
| 				"terrainTypes" : [ "dirt", "grass", "snow", "swamp", "rough", "subterra", "lava" ], | ||||
| 				"minesLikeZone" : 4, | ||||
| 				"treasureLikeZone" : 4 | ||||
| 			}, | ||||
| 			"11" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 10, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "towns" : 1 }, | ||||
| 				"terrainTypeLikeZone" : 5, | ||||
| 				"treasureLikeZone" : 5 | ||||
| 			}, | ||||
| 			"13" : | ||||
| 			{ | ||||
| 				"type" : "treasure", | ||||
| 				"size" : 25, | ||||
| 				"monsters" : "strong", | ||||
| 				"neutralTowns" : { "castles" : 1 }, | ||||
| 				"allowedTowns" : [ "tower" ], | ||||
| 				"terrainTypes" : [ "snow" ], | ||||
| 				"mines" : { "mercury" : 1, "sulfur" : 1, "crystal" : 1, "gems" : 1, "gold" : 4 }, | ||||
| 				"treasure" : | ||||
| 				[ | ||||
| 					{ "min" : 6000, "max" : 40000, "density" : 8 }, | ||||
| 					{ "min" : 9000, "max" : 9500, "density" : 8 }, | ||||
| 					{ "min" : 2500, "max" : 3000, "density" : 4 } | ||||
| 				] | ||||
| 			} | ||||
| 		}, | ||||
| 		"connections" : | ||||
| 		[ | ||||
| 			{ "a" : "1", "b" : "5", "guard" : 9000 }, | ||||
| 			{ "a" : "4", "b" : "5", "guard" : 5000 }, | ||||
| 			{ "a" : "4", "b" : "6", "guard" : 5000 }, | ||||
| 			{ "a" : "5", "b" : "6", "guard" : 5000 }, | ||||
| 			{ "a" : "1", "b" : "8", "guard" : 9000 }, | ||||
| 			{ "a" : "7", "b" : "8", "guard" : 5000 }, | ||||
| 			{ "a" : "10", "b" : "11", "guard" : 5000 }, | ||||
| 			{ "a" : "1", "b" : "2", "guard" : 10000 }, | ||||
| 			{ "a" : "1", "b" : "13", "guard" : 20000 }, | ||||
| 			{ "a" : "2", "b" : "13", "guard" : 20000 } | ||||
| 		] | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user