1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Redone RMG template serialization using JSON serializer, added tests

This commit is contained in:
AlexVinS 2018-03-09 21:11:20 +03:00
parent 7d27d144a5
commit 9d108d59db
25 changed files with 991 additions and 718 deletions

View 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;
}
}

View File

@ -165,6 +165,9 @@ set_source_files_properties(${CMAKE_BINARY_DIR}/Version.cpp
) )
set(lib_HEADERS 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 StdInc.h
../Global.h ../Global.h

View File

@ -130,6 +130,7 @@
<Unit filename="../Global.h" /> <Unit filename="../Global.h" />
<Unit filename="../Version.h" /> <Unit filename="../Version.h" />
<Unit filename="../include/vstd/CLoggerBase.h" /> <Unit filename="../include/vstd/CLoggerBase.h" />
<Unit filename="../include/vstd/ContainerUtils.h" />
<Unit filename="../include/vstd/RNG.h" /> <Unit filename="../include/vstd/RNG.h" />
<Unit filename="AI_Base.h" /> <Unit filename="AI_Base.h" />
<Unit filename="CArtHandler.cpp" /> <Unit filename="CArtHandler.cpp" />

View File

@ -406,8 +406,8 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand
for(const auto & tplPair : tpls) for(const auto & tplPair : tpls)
{ {
const auto & tpl = tplPair.second; const auto & tpl = tplPair.second;
CRmgTemplate::CSize tplSize(width, height, hasTwoLevels); int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));
if(tplSize >= tpl->getMinSize() && tplSize <= tpl->getMaxSize()) if(tpl->matchesSize(tplSize))
{ {
bool isPlayerCountValid = false; bool isPlayerCountValid = false;
if (getPlayerCount() != RANDOM_SIZE) if (getPlayerCount() != RANDOM_SIZE)

View File

@ -149,6 +149,7 @@ std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, in
map->calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded map->calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded
fillZones(); fillZones();
//updated guarded tiles will be calculated in CGameState::initMapObjects() //updated guarded tiles will be calculated in CGameState::initMapObjects()
zones.clear();
} }
catch (rmgException &e) catch (rmgException &e)
{ {
@ -277,8 +278,8 @@ void CMapGenerator::genZones()
zones.clear(); zones.clear();
for(const auto & option : tmpl->getZones()) for(const auto & option : tmpl->getZones())
{ {
auto zone = new CRmgTemplateZone(); auto zone = std::make_shared<CRmgTemplateZone>();
zone->setOptions(option.second); zone->setOptions(option.second.get());
zones[zone->getId()] = zone; zones[zone->getId()] = zone;
//todo: move to CRmgTemplateZone constructor //todo: move to CRmgTemplateZone constructor
zone->setGenPtr(this);//immediately set gen pointer before taking any actions on zones 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 createConnections2(); //subterranean gates and monoliths
std::vector<CRmgTemplateZone*> treasureZones; std::vector<std::shared_ptr<CRmgTemplateZone>> treasureZones;
for (auto it : zones) for (auto it : zones)
{ {
it.second->fill(); 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; return zones;
} }

View File

@ -50,6 +50,8 @@ public:
class DLL_LINKAGE CMapGenerator class DLL_LINKAGE CMapGenerator
{ {
public: public:
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>;
explicit CMapGenerator(); explicit CMapGenerator();
~CMapGenerator(); // required due to std::unique_ptr ~CMapGenerator(); // required due to std::unique_ptr
@ -61,7 +63,7 @@ public:
int randomSeed; int randomSeed;
CMapEditManager * editManager; CMapEditManager * editManager;
std::map<TRmgTemplateZoneId, CRmgTemplateZone*> getZones() const; Zones & getZones();
void createDirectConnections(); void createDirectConnections();
void createConnections2(); void createConnections2();
void findZonesForQuestArts(); void findZonesForQuestArts();
@ -100,7 +102,7 @@ public:
private: private:
std::list<rmg::ZoneConnection> connectionsLeft; std::list<rmg::ZoneConnection> connectionsLeft;
std::map<TRmgTemplateZoneId, CRmgTemplateZone*> zones; Zones zones;
std::map<TFaction, ui32> zonesPerFaction; std::map<TFaction, ui32> zonesPerFaction;
ui32 zonesTotal; //zones that have their main town only ui32 zonesTotal; //zones that have their main town only

View File

@ -9,15 +9,93 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include <vstd/ContainerUtils.h>
#include "CRmgTemplate.h" #include "CRmgTemplate.h"
#include "../mapping/CMap.h" #include "../mapping/CMap.h"
#include "../VCMI_Lib.h" #include "../VCMI_Lib.h"
#include "../CTownHandler.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 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 = const std::set<ETerrainType> ZoneOptions::DEFAULT_TERRAIN_TYPES =
{ {
ETerrainType::DIRT, ETerrainType::DIRT,
@ -30,6 +108,8 @@ const std::set<ETerrainType> ZoneOptions::DEFAULT_TERRAIN_TYPES =
ETerrainType::LAVA ETerrainType::LAVA
}; };
const TRmgTemplateZoneId ZoneOptions::NO_ZONE = -1;
ZoneOptions::CTownInfo::CTownInfo() ZoneOptions::CTownInfo::CTownInfo()
: townCount(0), : townCount(0),
castleCount(0), castleCount(0),
@ -44,49 +124,30 @@ int ZoneOptions::CTownInfo::getTownCount() const
return townCount; 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 int ZoneOptions::CTownInfo::getCastleCount() const
{ {
return castleCount; 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 int ZoneOptions::CTownInfo::getTownDensity() const
{ {
return townDensity; 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 int ZoneOptions::CTownInfo::getCastleDensity() const
{ {
return castleDensity; return castleDensity;
} }
void ZoneOptions::CTownInfo::setCastleDensity(int value) void ZoneOptions::CTownInfo::serializeJson(JsonSerializeFormat & handler)
{ {
if(value < 0) handler.serializeInt("towns", townCount, 0);
throw std::runtime_error("Negative value for castle density not allowed."); handler.serializeInt("castles", castleCount, 0);
castleDensity = value; handler.serializeInt("townDensity", townDensity, 0);
handler.serializeInt("castleDensity", castleDensity, 0);
} }
ZoneOptions::ZoneOptions() ZoneOptions::ZoneOptions()
: id(0), : id(0),
type(ETemplateZoneType::PLAYER_START), type(ETemplateZoneType::PLAYER_START),
@ -102,7 +163,10 @@ ZoneOptions::ZoneOptions()
zoneMonsterStrength(EMonsterStrength::ZONE_NORMAL), zoneMonsterStrength(EMonsterStrength::ZONE_NORMAL),
mines(), mines(),
treasureInfo(), 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; mines = other.mines;
treasureInfo = other.treasureInfo; treasureInfo = other.treasureInfo;
connections = other.connections; connections = other.connections;
minesLikeZone = other.minesLikeZone;
terrainTypeLikeZone = other.terrainTypeLikeZone;
treasureLikeZone = other.treasureLikeZone;
return *this; return *this;
} }
@ -143,10 +210,6 @@ ETemplateZoneType::ETemplateZoneType ZoneOptions::getType() const
{ {
return type; return type;
} }
void ZoneOptions::setType(ETemplateZoneType::ETemplateZoneType value)
{
type = value;
}
int ZoneOptions::getSize() const int ZoneOptions::getSize() const
{ {
@ -155,8 +218,6 @@ int ZoneOptions::getSize() const
void ZoneOptions::setSize(int value) 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; size = value;
} }
@ -165,43 +226,6 @@ boost::optional<int> ZoneOptions::getOwner() const
return owner; 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 const std::set<ETerrainType> & ZoneOptions::getTerrainTypes() const
{ {
return terrainTypes; return terrainTypes;
@ -214,16 +238,6 @@ void ZoneOptions::setTerrainTypes(const std::set<ETerrainType> & value)
terrainTypes = 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> ZoneOptions::getDefaultTownTypes() const
{ {
std::set<TFaction> defaultTowns; std::set<TFaction> defaultTowns;
@ -250,15 +264,9 @@ void ZoneOptions::setMonsterTypes(const std::set<TFaction> & value)
monsterTypes = 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)); mines = value;
zoneMonsterStrength = val;
}
void ZoneOptions::setMinesAmount(TResource res, ui16 amount)
{
mines[res] = amount;
} }
std::map<TResource, ui16> ZoneOptions::getMinesInfo() const std::map<TResource, ui16> ZoneOptions::getMinesInfo() const
@ -266,9 +274,9 @@ std::map<TResource, ui16> ZoneOptions::getMinesInfo() const
return mines; 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 const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const
@ -276,11 +284,102 @@ const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const
return treasureInfo; 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) void ZoneOptions::addConnection(TRmgTemplateZoneId otherZone)
{ {
connections.push_back (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() ZoneConnection::ZoneConnection()
: zoneA(-1), : zoneA(-1),
@ -290,157 +389,60 @@ ZoneConnection::ZoneConnection()
} }
TRmgTemplateZoneId ZoneConnection::getZoneA() const TRmgTemplateZoneId ZoneConnection::getZoneA() const
{ {
return zoneA; return zoneA;
} }
void ZoneConnection::setZoneA(TRmgTemplateZoneId value)
{
zoneA = value;
}
TRmgTemplateZoneId ZoneConnection::getZoneB() const TRmgTemplateZoneId ZoneConnection::getZoneB() const
{ {
return zoneB; return zoneB;
} }
void ZoneConnection::setZoneB(TRmgTemplateZoneId value)
{
zoneB = value;
}
int ZoneConnection::getGuardStrength() const int ZoneConnection::getGuardStrength() const
{ {
return guardStrength; 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."); handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("a", zoneA, -1);
guardStrength = value; handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("b", zoneB, -1);
handler.serializeInt("guard", guardStrength, 0);
} }
} }
using namespace rmg;//todo: remove 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() CRmgTemplate::CRmgTemplate()
: minSize(72, 72, 2),
maxSize(72, 72, 2)
{ {
} }
CRmgTemplate::~CRmgTemplate() 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 const std::string & CRmgTemplate::getName() const
{ {
return name; return name.empty() ? id : 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 const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const
@ -448,41 +450,21 @@ const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getPlayers() const
return players; return players;
} }
void CRmgTemplate::setPlayers(const CPlayerCountRange & value)
{
players = value;
}
const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getCpuPlayers() const const CRmgTemplate::CPlayerCountRange & CRmgTemplate::getCpuPlayers() const
{ {
return cpuPlayers; return cpuPlayers;
} }
void CRmgTemplate::setCpuPlayers(const CPlayerCountRange & value) const CRmgTemplate::Zones & CRmgTemplate::getZones() const
{
cpuPlayers = value;
}
const std::map<TRmgTemplateZoneId, ZoneOptions *> & CRmgTemplate::getZones() const
{ {
return zones; return zones;
} }
void CRmgTemplate::setZones(const std::map<TRmgTemplateZoneId, ZoneOptions *> & value) const std::vector<ZoneConnection> & CRmgTemplate::getConnections() const
{
zones = value;
}
const std::list<ZoneConnection> & CRmgTemplate::getConnections() const
{ {
return connections; return connections;
} }
void CRmgTemplate::setConnections(const std::list<ZoneConnection> & value)
{
connections = value;
}
void CRmgTemplate::validate() const void CRmgTemplate::validate() const
{ {
//TODO add some validation checks, throw on failure //TODO add some validation checks, throw on failure
@ -516,3 +498,212 @@ std::set<int> CRmgTemplate::CPlayerCountRange::getNumbers() const
} }
return numbers; 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);
}

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "../int3.h"
#include "../GameConstants.h" #include "../GameConstants.h"
#include "../ResourceSet.h" #include "../ResourceSet.h"
#include "CMapGenOptions.h" #include "CMapGenOptions.h"
@ -33,6 +34,11 @@ public:
ui32 min; ui32 min;
ui32 max; ui32 max;
ui16 density; ui16 density;
CTreasureInfo();
bool operator ==(const CTreasureInfo & other) const;
void serializeJson(JsonSerializeFormat & handler);
}; };
namespace rmg namespace rmg
@ -44,12 +50,10 @@ public:
ZoneConnection(); ZoneConnection();
TRmgTemplateZoneId getZoneA() const; TRmgTemplateZoneId getZoneA() const;
void setZoneA(TRmgTemplateZoneId value);
TRmgTemplateZoneId getZoneB() const; TRmgTemplateZoneId getZoneB() const;
void setZoneB(TRmgTemplateZoneId value); int getGuardStrength() const;
int getGuardStrength() const; /// Default: 0
void setGuardStrength(int value);
void serializeJson(JsonSerializeFormat & handler);
private: private:
TRmgTemplateZoneId zoneA; TRmgTemplateZoneId zoneA;
TRmgTemplateZoneId zoneB; TRmgTemplateZoneId zoneB;
@ -60,23 +64,25 @@ class DLL_LINKAGE ZoneOptions
{ {
public: public:
static const std::set<ETerrainType> DEFAULT_TERRAIN_TYPES; static const std::set<ETerrainType> DEFAULT_TERRAIN_TYPES;
static const TRmgTemplateZoneId NO_ZONE;
class DLL_LINKAGE CTownInfo class DLL_LINKAGE CTownInfo
{ {
public: public:
CTownInfo(); CTownInfo();
int getTownCount() const; /// Default: 0 int getTownCount() const;
void setTownCount(int value); int getCastleCount() const;
int getCastleCount() const; /// Default: 0 int getTownDensity() const;
void setCastleCount(int value); int getCastleDensity() const;
int getTownDensity() const; /// Default: 0
void setTownDensity(int value); void serializeJson(JsonSerializeFormat & handler);
int getCastleDensity() const; /// Default: 0
void setCastleDensity(int value);
private: private:
int townCount, castleCount, townDensity, castleDensity; int townCount;
int castleCount;
int townDensity;
int castleDensity;
}; };
ZoneOptions(); ZoneOptions();
@ -86,44 +92,34 @@ public:
TRmgTemplateZoneId getId() const; TRmgTemplateZoneId getId() const;
void setId(TRmgTemplateZoneId value); void setId(TRmgTemplateZoneId value);
ETemplateZoneType::ETemplateZoneType getType() const; /// Default: ETemplateZoneType::PLAYER_START ETemplateZoneType::ETemplateZoneType getType() const;
void setType(ETemplateZoneType::ETemplateZoneType value); int getSize() const;
int getSize() const; /// Default: 1
void setSize(int value); void setSize(int value);
boost::optional<int> getOwner() const; boost::optional<int> getOwner() const;
void setOwner(boost::optional<int> value);
const CTownInfo & getPlayerTowns() const; const std::set<ETerrainType> & getTerrainTypes() 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
void setTerrainTypes(const std::set<ETerrainType> & value); void setTerrainTypes(const std::set<ETerrainType> & value);
bool getTownsAreSameType() const; /// Default: false
void setTownsAreSameType(bool value);
std::set<TFaction> getDefaultTownTypes() const; 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 setTownTypes(const std::set<TFaction> & value);
void setMonsterTypes(const std::set<TFaction> & value); void setMonsterTypes(const std::set<TFaction> & value);
void setMonsterStrength(EMonsterStrength::EMonsterStrength val); void setMinesInfo(const std::map<TResource, ui16> & value);
void setMinesAmount (TResource res, ui16 amount);
std::map<TResource, ui16> getMinesInfo() const; std::map<TResource, ui16> getMinesInfo() const;
void addTreasureInfo(const CTreasureInfo & info); void setTreasureInfo(const std::vector<CTreasureInfo> & value);
const std::vector<CTreasureInfo> & getTreasureInfo() const; const std::vector<CTreasureInfo> & getTreasureInfo() const;
TRmgTemplateZoneId getMinesLikeZone() const;
TRmgTemplateZoneId getTerrainTypeLikeZone() const;
TRmgTemplateZoneId getTreasureLikeZone() const;
void addConnection(TRmgTemplateZoneId otherZone); void addConnection(TRmgTemplateZoneId otherZone);
std::vector<TRmgTemplateZoneId> getConnections() const;
void serializeJson(JsonSerializeFormat & handler);
protected: protected:
TRmgTemplateZoneId id; TRmgTemplateZoneId id;
@ -146,6 +142,10 @@ protected:
std::vector<CTreasureInfo> treasureInfo; std::vector<CTreasureInfo> treasureInfo;
std::vector<TRmgTemplateZoneId> connections; //list of adjacent zones std::vector<TRmgTemplateZoneId> connections; //list of adjacent zones
TRmgTemplateZoneId minesLikeZone;
TRmgTemplateZoneId terrainTypeLikeZone;
TRmgTemplateZoneId treasureLikeZone;
}; };
} }
@ -154,27 +154,9 @@ protected:
class DLL_LINKAGE CRmgTemplate class DLL_LINKAGE CRmgTemplate
{ {
public: public:
class CSize using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<rmg::ZoneOptions>>;
{
public:
CSize();
CSize(int width, int height, bool under);
int getWidth() const; /// Default: CMapHeader::MAP_SIZE_MIDDLE class DLL_LINKAGE CPlayerCountRange
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: public:
void addRange(int lower, int upper); void addRange(int lower, int upper);
@ -182,34 +164,39 @@ public:
bool isInRange(int count) const; bool isInRange(int count) const;
std::set<int> getNumbers() const; std::set<int> getNumbers() const;
std::string toString() const;
void fromString(const std::string & value);
private: private:
std::list<std::pair<int, int> > range; std::vector<std::pair<int, int> > range;
}; };
CRmgTemplate(); CRmgTemplate();
~CRmgTemplate(); ~CRmgTemplate();
bool matchesSize(const int3 & value) const;
void setId(const std::string & value);
const std::string & getName() const; 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; const CPlayerCountRange & getPlayers() const;
void setPlayers(const CPlayerCountRange & value);
const CPlayerCountRange & getCpuPlayers() const; const CPlayerCountRange & getCpuPlayers() const;
void setCpuPlayers(const CPlayerCountRange & value); const Zones & getZones() const;
const std::map<TRmgTemplateZoneId, rmg::ZoneOptions *> & getZones() const; const std::vector<rmg::ZoneConnection> & getConnections() 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);
void validate() const; /// Tests template on validity and throws exception on failure void validate() const; /// Tests template on validity and throws exception on failure
void serializeJson(JsonSerializeFormat & handler);
private: private:
std::string id;
std::string name; std::string name;
CSize minSize, maxSize; int3 minSize, maxSize;
CPlayerCountRange players, cpuPlayers; CPlayerCountRange players, cpuPlayers;
std::map<TRmgTemplateZoneId, rmg::ZoneOptions *> zones; Zones zones;
std::list<rmg::ZoneConnection> connections; 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);
}; };

View File

@ -9,16 +9,11 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "CRmgTemplateStorage.h"
#include "../filesystem/Filesystem.h" #include "CRmgTemplateStorage.h"
#include "../JsonNode.h" #include "CRmgTemplate.h"
#include "../mapping/CMap.h"
#include "../VCMI_Lib.h" #include "../serializer/JsonDeserializer.h"
#include "../CModHandler.h"
#include "../CTownHandler.h"
#include "../GameConstants.h"
#include "../StringConstants.h"
using namespace rmg; using namespace rmg;
@ -38,198 +33,12 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const
auto tpl = new CRmgTemplate(); auto tpl = new CRmgTemplate();
try try
{ {
const auto & templateNode = data; JsonDeserializer handler(nullptr, data);
if (!templateNode["name"].isNull()) auto fullKey = normalizeIdentifier(scope, "core", name);
tpl->setName(templateNode["name"].String()); //name can be customised. Allow duplicated names for different template versions. tpl->setId(name);
else tpl->serializeJson(handler);
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);
}
}
tpl->validate(); tpl->validate();
templates[tpl->getName()] = tpl; templates[fullKey] = tpl;
} }
catch(const std::exception & e) 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() CRmgTemplateStorage::CRmgTemplateStorage()
{ {
//TODO: load all
} }
CRmgTemplateStorage::~CRmgTemplateStorage() CRmgTemplateStorage::~CRmgTemplateStorage()

View File

@ -10,12 +10,10 @@
#pragma once #pragma once
#include "CRmgTemplate.h"
#include "../IHandlerBase.h" #include "../IHandlerBase.h"
class JsonNode; class JsonNode;
class CRmgTemplate;
typedef std::vector<JsonNode> JsonVector;
/// The CJsonRmgTemplateLoader loads templates from a JSON file. /// The CJsonRmgTemplateLoader loads templates from a JSON file.
class DLL_LINKAGE CRmgTemplateStorage : public IHandlerBase 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; virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
private: 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; std::map<std::string, CRmgTemplate *> templates;
}; };

View File

@ -108,7 +108,7 @@ CRmgTemplateZone::CRmgTemplateZone()
townType(ETownType::NEUTRAL), townType(ETownType::NEUTRAL),
terrainType (ETerrainType::GRASS), terrainType (ETerrainType::GRASS),
minGuardedValue(0), minGuardedValue(0),
questArtZone(nullptr), questArtZone(),
gen(nullptr) gen(nullptr)
{ {
@ -124,16 +124,11 @@ void CRmgTemplateZone::setGenPtr(CMapGenerator * Gen)
gen = Gen; gen = Gen;
} }
void CRmgTemplateZone::setQuestArtZone(CRmgTemplateZone * otherZone) void CRmgTemplateZone::setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone)
{ {
questArtZone = otherZone; questArtZone = otherZone;
} }
std::vector<TRmgTemplateZoneId> CRmgTemplateZone::getConnections() const
{
return connections;
}
std::set<int3>* CRmgTemplateZone::getFreePaths() std::set<int3>* CRmgTemplateZone::getFreePaths()
{ {
return &freePaths; return &freePaths;
@ -2488,7 +2483,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
//seer huts with creatures or generic rewards //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; static const int genericSeerHuts = 8;
int seerHutsPerType = 0; int seerHutsPerType = 0;
@ -2549,7 +2544,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
gen->banQuestArt(artid); gen->banQuestArt(artid);
this->questArtZone->possibleObjects.push_back (generateArtInfo(artid)); this->questArtZone.lock()->possibleObjects.push_back (generateArtInfo(artid));
return obj; return obj;
}; };
@ -2587,7 +2582,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
gen->banQuestArt(artid); gen->banQuestArt(artid);
this->questArtZone->possibleObjects.push_back(generateArtInfo(artid)); this->questArtZone.lock()->possibleObjects.push_back(generateArtInfo(artid));
return obj; return obj;
}; };
@ -2610,7 +2605,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
gen->banQuestArt(artid); gen->banQuestArt(artid);
this->questArtZone->possibleObjects.push_back(generateArtInfo(artid)); this->questArtZone.lock()->possibleObjects.push_back(generateArtInfo(artid));
return obj; return obj;
}; };

View File

@ -136,8 +136,7 @@ public:
std::vector<int3> getAccessibleOffsets (const CGObjectInstance* object); std::vector<int3> getAccessibleOffsets (const CGObjectInstance* object);
bool areAllTilesAvailable(CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const; bool areAllTilesAvailable(CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const;
void setQuestArtZone(CRmgTemplateZone * otherZone); void setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone);
std::vector<TRmgTemplateZoneId> getConnections() const;
std::set<int3>* getFreePaths(); std::set<int3>* getFreePaths();
ObjectInfo getRandomObject (CTreasurePileInfo &info, ui32 desiredValue, ui32 maxValue, ui32 currentValue); ObjectInfo getRandomObject (CTreasurePileInfo &info, ui32 desiredValue, ui32 maxValue, ui32 currentValue);
@ -166,7 +165,7 @@ private:
si32 townType; si32 townType;
ETerrainType terrainType; 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; std::vector<ObjectInfo> possibleObjects;
int minGuardedValue; int minGuardedValue;

View File

@ -18,11 +18,6 @@
class CRandomGenerator; class CRandomGenerator;
CPlacedZone::CPlacedZone(const CRmgTemplateZone * zone)
{
}
CZonePlacer::CZonePlacer(CMapGenerator * Gen) CZonePlacer::CZonePlacer(CMapGenerator * Gen)
: width(0), height(0), scaleX(0), scaleY(0), mapSize(0), gravityConstant(0), stiffnessConstant(0), : width(0), height(0), scaleX(0), scaleY(0), mapSize(0), gravityConstant(0), stiffnessConstant(0),
gen(Gen) gen(Gen)
@ -78,7 +73,7 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
float bestTotalDistance = 1e10; float bestTotalDistance = 1e10;
float bestTotalOverlap = 1e10; float bestTotalOverlap = 1e10;
std::map<CRmgTemplateZone *, float3> bestSolution; std::map<std::shared_ptr<CRmgTemplateZone>, float3> bestSolution;
TForceVector forces; TForceVector forces;
TForceVector totalForces; // both attraction and pushback, overcomplicated? TForceVector totalForces; // both attraction and pushback, overcomplicated?
@ -359,7 +354,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista
{ {
float maxRatio = 0; float maxRatio = 0;
const int maxDistanceMovementRatio = zones.size() * zones.size(); //experimental - the more zones, the greater total distance expected 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 totalDistance = 0;
float totalOverlap = 0; float totalOverlap = 0;
@ -379,7 +374,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista
if (maxRatio > maxDistanceMovementRatio && misplacedZone) if (maxRatio > maxDistanceMovementRatio && misplacedZone)
{ {
CRmgTemplateZone * targetZone = nullptr; std::shared_ptr<CRmgTemplateZone> targetZone;
float3 ourCenter = misplacedZone->getCenter(); float3 ourCenter = misplacedZone->getCenter();
if (totalDistance > totalOverlap) if (totalDistance > totalOverlap)
@ -471,7 +466,7 @@ void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions)
auto zones = gen->getZones(); auto zones = gen->getZones();
typedef std::pair<CRmgTemplateZone *, float> Dpair; typedef std::pair<std::shared_ptr<CRmgTemplateZone>, float> Dpair;
std::vector <Dpair> distances; std::vector <Dpair> distances;
distances.reserve(zones.size()); 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(); 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); int3 total(0, 0, 0);
auto tiles = zone->getTileInfo(); auto tiles = zone->getTileInfo();

View File

@ -20,22 +20,10 @@ class CRandomGenerator;
class CRmgTemplateZone; class CRmgTemplateZone;
class CMapGenerator; class CMapGenerator;
typedef std::vector<std::pair<TRmgTemplateZoneId, CRmgTemplateZone*>> TZoneVector; typedef std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>> TZoneVector;
typedef std::map <TRmgTemplateZoneId, CRmgTemplateZone*> TZoneMap; typedef std::map <TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>> TZoneMap;
typedef std::map <CRmgTemplateZone *, float3> TForceVector; typedef std::map <std::shared_ptr<CRmgTemplateZone>, float3> TForceVector;
typedef std::map <CRmgTemplateZone *, float> TDistanceVector; typedef std::map <std::shared_ptr<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...
};
class CZonePlacer class CZonePlacer
{ {

View File

@ -232,6 +232,7 @@ public:
}; };
///Anything int64-convertible <-> Json integer ///Anything int64-convertible <-> Json integer
///no default value
template <typename T> template <typename T>
void serializeInt(const std::string & fieldName, T & value) void serializeInt(const std::string & fieldName, T & value)
{ {
@ -239,12 +240,21 @@ public:
}; };
///Anything int64-convertible <-> Json integer ///Anything int64-convertible <-> Json integer
///custom default value
template <typename T, typename U> template <typename T, typename U>
void serializeInt(const std::string & fieldName, T & value, const U & defaultValue) void serializeInt(const std::string & fieldName, T & value, const U & defaultValue)
{ {
doSerializeInternal<T, U, si64>(fieldName, value, 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 ///si32-convertible identifier <-> Json string
template <typename T, typename U> template <typename T, typename U>
void serializeId(const std::string & fieldName, T & value, const U & defaultValue, const TDecoder & decoder, const TEncoder & encoder) 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 ///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) 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 ///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) void serializeIdArray(const std::string & fieldName, std::vector<T> & value)
{ {
std::vector<si32> temp; 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) if(!saving)
{ {
value.clear(); 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 ///bitmask <-> Json array of string
template <typename T, int Size> template <typename T, int Size>
void serializeIdArray(const std::string & fieldName, T & value, const T & defaultValue, const TDecoder & decoder, const TEncoder & encoder) 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 pushStruct(const std::string & fieldName) = 0;
virtual void pushArray(const std::string & fieldName) = 0; virtual void pushArray(const std::string & fieldName) = 0;
virtual void pushArrayElement(const size_t index) = 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){}; virtual void resizeCurrent(const size_t newSize, JsonNode::JsonType type){};
@ -406,9 +454,9 @@ protected:
private: private:
const IInstanceResolver * instanceResolver; const IInstanceResolver * instanceResolver;
template <typename VType, typename DVType, typename IType, typename... 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) 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; const boost::optional<IType> tempDefault = defaultValue ? boost::optional<IType>(static_cast<IType>(defaultValue.get())) : boost::none;
IType temp = static_cast<IType>(value); IType temp = static_cast<IType>(value);
@ -416,7 +464,39 @@ private:
if(!saving) if(!saving)
value = static_cast<VType>(temp); 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 JsonSerializeHelper;
friend class JsonStructSerializer; friend class JsonStructSerializer;

View File

@ -69,14 +69,12 @@ void JsonSerializer::serializeInternal(const std::string & fieldName, std::vecto
void JsonSerializer::serializeInternal(std::string & value) void JsonSerializer::serializeInternal(std::string & value)
{ {
if(value != "") currentObject->String() = value;
currentObject->String() = value;
} }
void JsonSerializer::serializeInternal(int64_t & 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) void JsonSerializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)

View File

@ -54,6 +54,11 @@ protected:
pushObject(&(currentObject->Vector().at(index))); pushObject(&(currentObject->Vector().at(index)));
} }
void pushField(const std::string & fieldName) override
{
pushObject(fieldName);
}
private: private:
void pushObject(const std::string & fieldName) void pushObject(const std::string & fieldName)
{ {

View File

@ -46,6 +46,8 @@ bool JsonComparer::isEmpty(const JsonNode & value)
return !value.Bool(); return !value.Bool();
case JsonNode::JsonType::DATA_FLOAT: case JsonNode::JsonType::DATA_FLOAT:
return value.Float() == 0; return value.Float() == 0;
case JsonNode::JsonType::DATA_INTEGER:
return value.Integer() == 0;
case JsonNode::JsonType::DATA_STRING: case JsonNode::JsonType::DATA_STRING:
return value.String() == ""; return value.String() == "";
case JsonNode::JsonType::DATA_VECTOR: case JsonNode::JsonType::DATA_VECTOR:

View File

@ -111,6 +111,7 @@
<Unit filename="mock/mock_spells_Problem.h" /> <Unit filename="mock/mock_spells_Problem.h" />
<Unit filename="mock/mock_spells_Spell.h" /> <Unit filename="mock/mock_spells_Spell.h" />
<Unit filename="mock/mock_vstd_RNG.h" /> <Unit filename="mock/mock_vstd_RNG.h" />
<Unit filename="rmg/CRmgTemplateTest.cpp" />
<Unit filename="spells/AbilityCasterTest.cpp" /> <Unit filename="spells/AbilityCasterTest.cpp" />
<Unit filename="spells/TargetConditionTest.cpp" /> <Unit filename="spells/TargetConditionTest.cpp" />
<Unit filename="spells/effects/CatapultTest.cpp" /> <Unit filename="spells/effects/CatapultTest.cpp" />
@ -137,6 +138,7 @@
<Unit filename="spells/targetConditions/SpellEffectConditionTest.cpp" /> <Unit filename="spells/targetConditions/SpellEffectConditionTest.cpp" />
<Unit filename="spells/targetConditions/TargetConditionItemFixture.cpp" /> <Unit filename="spells/targetConditions/TargetConditionItemFixture.cpp" />
<Unit filename="spells/targetConditions/TargetConditionItemFixture.h" /> <Unit filename="spells/targetConditions/TargetConditionItemFixture.h" />
<Unit filename="testdata/rmg/1.json" />
<Extensions> <Extensions>
<code_completion /> <code_completion />
<envvars /> <envvars />

View File

@ -9,15 +9,15 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "../lib/JsonDetail.h" #include "../../lib/JsonDetail.h"
#include "../lib/filesystem/CMemoryBuffer.h" #include "../../lib/filesystem/CMemoryBuffer.h"
#include "../lib/filesystem/Filesystem.h" #include "../../lib/filesystem/Filesystem.h"
#include "../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
#include "../lib/rmg/CMapGenOptions.h" #include "../../lib/rmg/CMapGenOptions.h"
#include "../lib/rmg/CMapGenerator.h" #include "../../lib/rmg/CMapGenerator.h"
#include "../lib/mapping/MapFormatJson.h" #include "../../lib/mapping/MapFormatJson.h"
#include "../lib/VCMIDirs.h" #include "../lib/VCMIDirs.h"

View File

@ -144,7 +144,6 @@ void checkEqual(const TerrainTile & actual, const TerrainTile & expected)
VCMI_REQUIRE_FIELD_EQUAL(visitable); VCMI_REQUIRE_FIELD_EQUAL(visitable);
VCMI_REQUIRE_FIELD_EQUAL(blocked); VCMI_REQUIRE_FIELD_EQUAL(blocked);
} }
//MapComparer //MapComparer

View 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);
}
}

View File

@ -13,8 +13,8 @@
#include "mock/mock_BonusBearer.h" #include "mock/mock_BonusBearer.h"
#include "mock/mock_spells_Spell.h" #include "mock/mock_spells_Spell.h"
#include "../../../lib/NetPacksBase.h" #include "../../lib/NetPacksBase.h"
#include "../../../lib/spells/AbilityCaster.h" #include "../../lib/spells/AbilityCaster.h"
namespace test namespace test
{ {

View File

@ -11,9 +11,9 @@
#include <vstd/RNG.h> #include <vstd/RNG.h>
#include "../../../lib/NetPacksBase.h" #include "../../lib/NetPacksBase.h"
#include "../../../lib/spells/TargetCondition.h" #include "../../lib/spells/TargetCondition.h"
#include "../../../lib/serializer/JsonDeserializer.h" #include "../../lib/serializer/JsonDeserializer.h"
#include "mock/mock_spells_Mechanics.h" #include "mock/mock_spells_Mechanics.h"
#include "mock/mock_BonusBearer.h" #include "mock/mock_BonusBearer.h"

216
test/testdata/rmg/1.json vendored Normal file
View 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 }
]
}
}