mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Place proper towns in underground (#743)
Implement feature of proper town selection in underground and surface * Some minor refactoring of rmg
This commit is contained in:
parent
d92356f085
commit
9d06e51631
@ -4,6 +4,7 @@
|
||||
"index" : 5,
|
||||
"nativeTerrain": "subterra",
|
||||
"alignment" : "evil",
|
||||
"preferUndergroundPlacement": true,
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASDUN",
|
||||
|
@ -4,6 +4,7 @@
|
||||
"index" : 3,
|
||||
"nativeTerrain": "lava",
|
||||
"alignment" : "evil",
|
||||
"preferUndergroundPlacement": true,
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASINF",
|
||||
|
@ -4,6 +4,7 @@
|
||||
"index" : 4,
|
||||
"nativeTerrain": "dirt",
|
||||
"alignment" : "evil",
|
||||
"preferUndergroundPlacement": true,
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASNEC",
|
||||
|
@ -73,6 +73,10 @@
|
||||
"type":"string",
|
||||
"description": "Native terrain for creatures. Creatures fighting on native terrain receive several bonuses"
|
||||
},
|
||||
"preferUndergroundPlacement": {
|
||||
"type":"bool",
|
||||
"description": "Random map generator places player/cpu-owned towns underground if true is specified and on the ground otherwise. Parameter is unused for maps without underground. False by default."
|
||||
},
|
||||
"puzzleMap": {
|
||||
"type":"object",
|
||||
"additionalProperties" : false,
|
||||
|
@ -853,9 +853,9 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
||||
CStopWatch sw;
|
||||
|
||||
// Gen map
|
||||
CMapGenerator mapGenerator;
|
||||
CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
|
||||
|
||||
std::unique_ptr<CMap> randomMap = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed);
|
||||
std::unique_ptr<CMap> randomMap = mapGenerator.generate();
|
||||
|
||||
if(allowSavingRandomMap)
|
||||
{
|
||||
|
@ -211,6 +211,7 @@ CFaction::CFaction()
|
||||
town = nullptr;
|
||||
index = 0;
|
||||
alignment = EAlignment::NEUTRAL;
|
||||
preferUndergroundPlacement = false;
|
||||
}
|
||||
|
||||
CFaction::~CFaction()
|
||||
@ -1100,6 +1101,9 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
|
||||
? -1
|
||||
: vstd::find_pos(GameConstants::TERRAIN_NAMES, nativeTerrain.String());
|
||||
|
||||
auto preferUndergound = source["preferUndergroundPlacement"];
|
||||
faction->preferUndergroundPlacement = preferUndergound.isNull() ? false : preferUndergound.Bool();
|
||||
|
||||
//Contructor is not called here, but operator=
|
||||
faction->nativeTerrain = terrainNum < 0
|
||||
? getDefaultTerrainForAlignment(faction->alignment)
|
||||
|
@ -207,6 +207,7 @@ public:
|
||||
|
||||
ETerrainType nativeTerrain;
|
||||
EAlignment::EAlignment alignment;
|
||||
bool preferUndergroundPlacement;
|
||||
|
||||
CTown * town; //NOTE: can be null
|
||||
|
||||
|
@ -205,11 +205,6 @@ void CMapGenOptions::setMapTemplate(const CRmgTemplate * value)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
const std::map<std::string, CRmgTemplate *> & CMapGenOptions::getAvailableTemplates() const
|
||||
{
|
||||
return VLC->tplh->getTemplates();
|
||||
}
|
||||
|
||||
void CMapGenOptions::finalize(CRandomGenerator & rand)
|
||||
{
|
||||
logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d",
|
||||
@ -394,59 +389,15 @@ bool CMapGenOptions::checkOptions() const
|
||||
|
||||
const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const
|
||||
{
|
||||
// Find potential templates
|
||||
const auto & tpls = getAvailableTemplates();
|
||||
std::list<const CRmgTemplate *> potentialTpls;
|
||||
for(const auto & tplPair : tpls)
|
||||
{
|
||||
const auto & tpl = tplPair.second;
|
||||
int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));
|
||||
if(tpl->matchesSize(tplSize))
|
||||
{
|
||||
bool isPlayerCountValid = false;
|
||||
if (getPlayerCount() != RANDOM_SIZE)
|
||||
{
|
||||
if (tpl->getPlayers().isInRange(getPlayerCount()))
|
||||
isPlayerCountValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Human players shouldn't be banned when playing with random player count
|
||||
auto playerNumbers = tpl->getPlayers().getNumbers();
|
||||
if(countHumanPlayers() <= *boost::min_element(playerNumbers))
|
||||
{
|
||||
isPlayerCountValid = true;
|
||||
}
|
||||
}
|
||||
int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));
|
||||
|
||||
if (isPlayerCountValid)
|
||||
{
|
||||
bool isCpuPlayerCountValid = false;
|
||||
if(compOnlyPlayerCount != RANDOM_SIZE)
|
||||
{
|
||||
if (tpl->getCpuPlayers().isInRange(compOnlyPlayerCount))
|
||||
isCpuPlayerCountValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCpuPlayerCountValid = true;
|
||||
}
|
||||
|
||||
if(isCpuPlayerCountValid)
|
||||
potentialTpls.push_back(tpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto templates = VLC->tplh->getTemplates(tplSize, getPlayerCount(), countHumanPlayers(), compOnlyPlayerCount);
|
||||
|
||||
// Select tpl
|
||||
if(potentialTpls.empty())
|
||||
{
|
||||
if(templates.empty())
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *RandomGeneratorUtil::nextItem(potentialTpls, rand);
|
||||
}
|
||||
|
||||
return *RandomGeneratorUtil::nextItem(templates, rand);
|
||||
}
|
||||
|
||||
CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI)
|
||||
|
@ -94,6 +94,7 @@ public:
|
||||
};
|
||||
|
||||
CMapGenOptions();
|
||||
CMapGenOptions(const CMapGenOptions&) = delete;
|
||||
|
||||
si32 getWidth() const;
|
||||
void setWidth(si32 value);
|
||||
@ -141,8 +142,6 @@ public:
|
||||
const CRmgTemplate * getMapTemplate() const;
|
||||
void setMapTemplate(const CRmgTemplate * value);
|
||||
|
||||
const std::map<std::string, CRmgTemplate *> & getAvailableTemplates() const;
|
||||
|
||||
/// Finalizes the options. All random sizes for various properties will be overwritten by numbers from
|
||||
/// a random number generator by keeping the options in a valid state. Check options should return true, otherwise
|
||||
/// this function fails.
|
||||
|
@ -46,7 +46,7 @@ void CMapGenerator::foreachDirectNeighbour(const int3& pos, std::function<void(i
|
||||
}
|
||||
}
|
||||
|
||||
void CMapGenerator::foreachDiagonaltNeighbour(const int3& pos, std::function<void(int3& pos)> foo)
|
||||
void CMapGenerator::foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo)
|
||||
{
|
||||
for (const int3 &dir : dirsDiagonal)
|
||||
{
|
||||
@ -57,11 +57,13 @@ void CMapGenerator::foreachDiagonaltNeighbour(const int3& pos, std::function<voi
|
||||
}
|
||||
|
||||
|
||||
CMapGenerator::CMapGenerator() :
|
||||
mapGenOptions(nullptr), randomSeed(0), editManager(nullptr),
|
||||
CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
|
||||
mapGenOptions(mapGenOptions), randomSeed(RandomSeed),
|
||||
zonesTotal(0), tiles(nullptr), prisonsRemaining(0),
|
||||
monolithIndex(0)
|
||||
{
|
||||
rand.setSeed(this->randomSeed);
|
||||
mapGenOptions.finalize(rand);
|
||||
}
|
||||
|
||||
void CMapGenerator::initTiles()
|
||||
@ -89,8 +91,8 @@ CMapGenerator::~CMapGenerator()
|
||||
{
|
||||
if (tiles)
|
||||
{
|
||||
int width = mapGenOptions->getWidth();
|
||||
int height = mapGenOptions->getHeight();
|
||||
int width = mapGenOptions.getWidth();
|
||||
int height = mapGenOptions.getHeight();
|
||||
for (int i=0; i < width; i++)
|
||||
{
|
||||
for(int j=0; j < height; j++)
|
||||
@ -111,7 +113,7 @@ void CMapGenerator::initPrisonsRemaining()
|
||||
if (isAllowed)
|
||||
prisonsRemaining++;
|
||||
}
|
||||
prisonsRemaining = std::max<int> (0, prisonsRemaining - 16 * mapGenOptions->getPlayerCount()); //so at least 16 heroes will be available for every player
|
||||
prisonsRemaining = std::max<int> (0, prisonsRemaining - 16 * mapGenOptions.getPlayerCount()); //so at least 16 heroes will be available for every player
|
||||
}
|
||||
|
||||
void CMapGenerator::initQuestArtsRemaining()
|
||||
@ -123,26 +125,27 @@ void CMapGenerator::initQuestArtsRemaining()
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, int randomSeed)
|
||||
const CMapGenOptions& CMapGenerator::getMapGenOptions() const
|
||||
{
|
||||
this->mapGenOptions = mapGenOptions;
|
||||
this->randomSeed = randomSeed;
|
||||
return mapGenOptions;
|
||||
}
|
||||
|
||||
assert(mapGenOptions);
|
||||
|
||||
rand.setSeed(this->randomSeed);
|
||||
mapGenOptions->finalize(rand);
|
||||
CMapEditManager* CMapGenerator::getEditManager() const
|
||||
{
|
||||
if(!map)
|
||||
return nullptr;
|
||||
return map->getEditManager();
|
||||
}
|
||||
|
||||
std::unique_ptr<CMap> CMapGenerator::generate()
|
||||
{
|
||||
map = make_unique<CMap>();
|
||||
editManager = map->getEditManager();
|
||||
|
||||
try
|
||||
{
|
||||
editManager->getUndoManager().setUndoRedoLimit(0);
|
||||
map->getEditManager()->getUndoManager().setUndoRedoLimit(0);
|
||||
//FIXME: somehow mapGenOption is nullptr at this point :?
|
||||
addHeaderInfo();
|
||||
initTiles();
|
||||
|
||||
initPrisonsRemaining();
|
||||
initQuestArtsRemaining();
|
||||
genZones();
|
||||
@ -160,14 +163,13 @@ std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, in
|
||||
|
||||
std::string CMapGenerator::getMapDescription() const
|
||||
{
|
||||
assert(mapGenOptions);
|
||||
assert(map);
|
||||
|
||||
const std::string waterContentStr[3] = { "none", "normal", "islands" };
|
||||
const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
|
||||
|
||||
int monsterStrengthIndex = mapGenOptions->getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0
|
||||
const auto * mapTemplate = mapGenOptions->getMapTemplate();
|
||||
int monsterStrengthIndex = mapGenOptions.getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0
|
||||
const auto * mapTemplate = mapGenOptions.getMapTemplate();
|
||||
|
||||
if(!mapTemplate)
|
||||
throw rmgException("Map template for Random Map Generator is not found. Could not start the game.");
|
||||
@ -175,11 +177,11 @@ std::string CMapGenerator::getMapDescription() const
|
||||
std::stringstream ss;
|
||||
ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
|
||||
", levels %s, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
|
||||
randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions->getPlayerCount()) %
|
||||
static_cast<int>(mapGenOptions->getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions->getWaterContent()] %
|
||||
randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions.getPlayerCount()) %
|
||||
static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
|
||||
monsterStrengthStr[monsterStrengthIndex]);
|
||||
|
||||
for(const auto & pair : mapGenOptions->getPlayersSettings())
|
||||
for(const auto & pair : mapGenOptions.getPlayersSettings())
|
||||
{
|
||||
const auto & pSettings = pair.second;
|
||||
if(pSettings.getPlayerType() == EPlayerType::HUMAN)
|
||||
@ -211,13 +213,13 @@ void CMapGenerator::addPlayerInfo()
|
||||
{
|
||||
if (i == CPHUMAN)
|
||||
{
|
||||
playerCount = mapGenOptions->getPlayerCount();
|
||||
teamCount = mapGenOptions->getTeamCount();
|
||||
playerCount = mapGenOptions.getPlayerCount();
|
||||
teamCount = mapGenOptions.getTeamCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
playerCount = mapGenOptions->getCompOnlyPlayerCount();
|
||||
teamCount = mapGenOptions->getCompOnlyTeamCount();
|
||||
playerCount = mapGenOptions.getCompOnlyPlayerCount();
|
||||
teamCount = mapGenOptions.getCompOnlyTeamCount();
|
||||
}
|
||||
|
||||
if(playerCount == 0)
|
||||
@ -246,7 +248,7 @@ void CMapGenerator::addPlayerInfo()
|
||||
|
||||
// Team numbers are assigned randomly to every player
|
||||
//TODO: allow customize teams in rmg template
|
||||
for(const auto & pair : mapGenOptions->getPlayersSettings())
|
||||
for(const auto & pair : mapGenOptions.getPlayersSettings())
|
||||
{
|
||||
const auto & pSettings = pair.second;
|
||||
PlayerInfo player;
|
||||
@ -262,36 +264,34 @@ void CMapGenerator::addPlayerInfo()
|
||||
logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human"));
|
||||
assert (teamNumbers[j].size());
|
||||
}
|
||||
auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
|
||||
auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
|
||||
player.team = TeamID(*itTeam);
|
||||
teamNumbers[j].erase(itTeam);
|
||||
map->players[pSettings.getColor().getNum()] = player;
|
||||
}
|
||||
|
||||
map->howManyTeams = (mapGenOptions->getTeamCount() == 0 ? mapGenOptions->getPlayerCount() : mapGenOptions->getTeamCount())
|
||||
+ (mapGenOptions->getCompOnlyTeamCount() == 0 ? mapGenOptions->getCompOnlyPlayerCount() : mapGenOptions->getCompOnlyTeamCount());
|
||||
map->howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount())
|
||||
+ (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount());
|
||||
}
|
||||
|
||||
void CMapGenerator::genZones()
|
||||
{
|
||||
editManager->clearTerrain(&rand);
|
||||
editManager->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions->getWidth(), mapGenOptions->getHeight()));
|
||||
editManager->drawTerrain(ETerrainType::GRASS, &rand);
|
||||
getEditManager()->clearTerrain(&rand);
|
||||
getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight()));
|
||||
getEditManager()->drawTerrain(ETerrainType::GRASS, &rand);
|
||||
|
||||
auto tmpl = mapGenOptions->getMapTemplate();
|
||||
auto tmpl = mapGenOptions.getMapTemplate();
|
||||
zones.clear();
|
||||
for(const auto & option : tmpl->getZones())
|
||||
{
|
||||
auto zone = std::make_shared<CRmgTemplateZone>();
|
||||
auto zone = std::make_shared<CRmgTemplateZone>(this);
|
||||
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
|
||||
}
|
||||
|
||||
CZonePlacer placer(this);
|
||||
placer.placeZones(mapGenOptions, &rand);
|
||||
placer.assignZones(mapGenOptions);
|
||||
placer.placeZones(&rand);
|
||||
placer.assignZones();
|
||||
|
||||
logGlobal->info("Zones generated successfully");
|
||||
}
|
||||
@ -316,6 +316,7 @@ void CMapGenerator::fillZones()
|
||||
it.second->initFreeTiles();
|
||||
|
||||
createDirectConnections(); //direct
|
||||
|
||||
//make sure all connections are passable before creating borders
|
||||
for (auto it : zones)
|
||||
it.second->createBorder(); //once direct connections are done
|
||||
@ -335,6 +336,7 @@ void CMapGenerator::fillZones()
|
||||
//set back original terrain for underground zones
|
||||
for (auto it : zones)
|
||||
it.second->createObstacles1();
|
||||
|
||||
createObstaclesCommon2();
|
||||
//place actual obstacles matching zone terrain
|
||||
for (auto it : zones)
|
||||
@ -345,7 +347,7 @@ void CMapGenerator::fillZones()
|
||||
#define PRINT_MAP_BEFORE_ROADS false
|
||||
if (PRINT_MAP_BEFORE_ROADS) //enable to debug
|
||||
{
|
||||
std::ofstream out("road debug");
|
||||
std::ofstream out("road_debug.txt");
|
||||
int levels = map->twoLevel ? 2 : 1;
|
||||
int width = map->width;
|
||||
int height = map->height;
|
||||
@ -413,8 +415,8 @@ void CMapGenerator::createObstaclesCommon1()
|
||||
}
|
||||
}
|
||||
}
|
||||
editManager->getTerrainSelection().setSelection(rockTiles);
|
||||
editManager->drawTerrain(ETerrainType::ROCK, &rand);
|
||||
getEditManager()->getTerrainSelection().setSelection(rockTiles);
|
||||
getEditManager()->drawTerrain(ETerrainType::ROCK, &rand);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +485,7 @@ void CMapGenerator::findZonesForQuestArts()
|
||||
{
|
||||
//we want to place arties in zones that were not yet filled (higher index)
|
||||
|
||||
for (auto connection : mapGenOptions->getMapTemplate()->getConnections())
|
||||
for (auto connection : mapGenOptions.getMapTemplate()->getConnections())
|
||||
{
|
||||
auto zoneA = zones[connection.getZoneA()];
|
||||
auto zoneB = zones[connection.getZoneB()];
|
||||
@ -501,7 +503,7 @@ void CMapGenerator::findZonesForQuestArts()
|
||||
|
||||
void CMapGenerator::createDirectConnections()
|
||||
{
|
||||
for (auto connection : mapGenOptions->getMapTemplate()->getConnections())
|
||||
for (auto connection : mapGenOptions.getMapTemplate()->getConnections())
|
||||
{
|
||||
auto zoneA = zones[connection.getZoneA()];
|
||||
auto zoneB = zones[connection.getZoneB()];
|
||||
@ -703,9 +705,9 @@ void CMapGenerator::createConnections2()
|
||||
void CMapGenerator::addHeaderInfo()
|
||||
{
|
||||
map->version = EMapFormat::VCMI;
|
||||
map->width = mapGenOptions->getWidth();
|
||||
map->height = mapGenOptions->getHeight();
|
||||
map->twoLevel = mapGenOptions->getHasTwoLevels();
|
||||
map->width = mapGenOptions.getWidth();
|
||||
map->height = mapGenOptions.getHeight();
|
||||
map->twoLevel = mapGenOptions.getHasTwoLevels();
|
||||
map->name = VLC->generaltexth->allTexts[740];
|
||||
map->description = getMapDescription();
|
||||
map->difficulty = 1;
|
||||
|
@ -52,16 +52,16 @@ class DLL_LINKAGE CMapGenerator
|
||||
public:
|
||||
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>;
|
||||
|
||||
explicit CMapGenerator();
|
||||
explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
|
||||
~CMapGenerator(); // required due to std::unique_ptr
|
||||
|
||||
std::unique_ptr<CMap> generate(CMapGenOptions * mapGenOptions, int RandomSeed = std::time(nullptr));
|
||||
|
||||
CMapGenOptions * mapGenOptions;
|
||||
std::unique_ptr<CMap> map;
|
||||
mutable std::unique_ptr<CMap> map;
|
||||
CRandomGenerator rand;
|
||||
int randomSeed;
|
||||
CMapEditManager * editManager;
|
||||
|
||||
CMapEditManager* getEditManager() const;
|
||||
const CMapGenOptions& getMapGenOptions() const;
|
||||
|
||||
std::unique_ptr<CMap> generate();
|
||||
|
||||
Zones & getZones();
|
||||
void createDirectConnections();
|
||||
@ -69,7 +69,7 @@ public:
|
||||
void findZonesForQuestArts();
|
||||
void foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo);
|
||||
void foreachDirectNeighbour(const int3 &pos, std::function<void(int3& pos)> foo);
|
||||
void foreachDiagonaltNeighbour(const int3& pos, std::function<void(int3& pos)> foo);
|
||||
void foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo);
|
||||
|
||||
bool isBlocked(const int3 &tile) const;
|
||||
bool shouldBeBlocked(const int3 &tile) const;
|
||||
@ -101,6 +101,9 @@ public:
|
||||
void setZoneID(const int3& tile, TRmgTemplateZoneId zid);
|
||||
|
||||
private:
|
||||
int randomSeed;
|
||||
CMapGenOptions& mapGenOptions;
|
||||
|
||||
std::list<rmg::ZoneConnection> connectionsLeft;
|
||||
Zones zones;
|
||||
std::map<TFaction, ui32> zonesPerFaction;
|
||||
|
@ -17,11 +17,6 @@
|
||||
|
||||
using namespace rmg;
|
||||
|
||||
const std::map<std::string, CRmgTemplate *> & CRmgTemplateStorage::getTemplates() const
|
||||
{
|
||||
return templates;
|
||||
}
|
||||
|
||||
void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
|
||||
{
|
||||
//unused
|
||||
@ -30,31 +25,20 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const
|
||||
|
||||
void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data)
|
||||
{
|
||||
auto tpl = new CRmgTemplate();
|
||||
try
|
||||
{
|
||||
JsonDeserializer handler(nullptr, data);
|
||||
auto fullKey = normalizeIdentifier(scope, "core", name);
|
||||
tpl->setId(name);
|
||||
tpl->serializeJson(handler);
|
||||
tpl->validate();
|
||||
templates[fullKey] = tpl;
|
||||
auto fullKey = normalizeIdentifier(scope, "core", name); //actually it's not used
|
||||
templates[fullKey].setId(name);
|
||||
templates[fullKey].serializeJson(handler);
|
||||
templates[fullKey].validate();
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
logGlobal->error("Template %s has errors. Message: %s.", tpl->getName(), std::string(e.what()));
|
||||
logGlobal->error("Template %s has errors. Message: %s.", name, std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
CRmgTemplateStorage::CRmgTemplateStorage()
|
||||
{
|
||||
}
|
||||
|
||||
CRmgTemplateStorage::~CRmgTemplateStorage()
|
||||
{
|
||||
for (auto & pair : templates) delete pair.second;
|
||||
}
|
||||
|
||||
std::vector<bool> CRmgTemplateStorage::getDefaultAllowed() const
|
||||
{
|
||||
//all templates are allowed
|
||||
@ -66,3 +50,54 @@ std::vector<JsonNode> CRmgTemplateStorage::loadLegacyData(size_t dataSize)
|
||||
return std::vector<JsonNode>();
|
||||
//it would be cool to load old rmg.txt files
|
||||
}
|
||||
|
||||
const CRmgTemplate * CRmgTemplateStorage::getTemplate(const std::string & templateName) const
|
||||
{
|
||||
auto iter = templates.find(templateName);
|
||||
if(iter==templates.end())
|
||||
return nullptr;
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
std::vector<const CRmgTemplate *> CRmgTemplateStorage::getTemplates() const
|
||||
{
|
||||
std::vector<const CRmgTemplate *> result;
|
||||
for(auto i=templates.cbegin(); i!=templates.cend(); ++i)
|
||||
{
|
||||
result.push_back(&i->second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const CRmgTemplate *> CRmgTemplateStorage::getTemplates(const int3& filterSize, si8 filterPlayers, si8 filterHumanPlayers, si8 filterCpuPlayers) const
|
||||
{
|
||||
std::vector<const CRmgTemplate *> result;
|
||||
for(auto i=templates.cbegin(); i!=templates.cend(); ++i)
|
||||
{
|
||||
auto& tmpl = i->second;
|
||||
|
||||
if (!tmpl.matchesSize(filterSize))
|
||||
continue;
|
||||
|
||||
if (filterPlayers != -1)
|
||||
{
|
||||
if (!tmpl.getPlayers().isInRange(filterPlayers))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Human players shouldn't be banned when playing with random player count
|
||||
if (filterHumanPlayers > *boost::min_element(tmpl.getPlayers().getNumbers()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(filterCpuPlayers != -1)
|
||||
{
|
||||
if (!tmpl.getCpuPlayers().isInRange(filterCpuPlayers))
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(&i->second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -11,18 +11,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../IHandlerBase.h"
|
||||
#include "../int3.h"
|
||||
#include "CRmgTemplate.h"
|
||||
|
||||
class JsonNode;
|
||||
class CRmgTemplate;
|
||||
|
||||
/// The CJsonRmgTemplateLoader loads templates from a JSON file.
|
||||
class DLL_LINKAGE CRmgTemplateStorage : public IHandlerBase
|
||||
{
|
||||
public:
|
||||
CRmgTemplateStorage();
|
||||
~CRmgTemplateStorage();
|
||||
|
||||
const std::map<std::string, CRmgTemplate *> & getTemplates() const;
|
||||
CRmgTemplateStorage() = default;
|
||||
|
||||
std::vector<bool> getDefaultAllowed() const override;
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
@ -31,7 +29,11 @@ public:
|
||||
virtual void loadObject(std::string scope, std::string name, const JsonNode & data) override;
|
||||
virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
|
||||
|
||||
const CRmgTemplate * getTemplate(const std::string & templateName) const;
|
||||
std::vector<const CRmgTemplate *> getTemplates() const;
|
||||
std::vector<const CRmgTemplate *> getTemplates(const int3& filterSize, si8 filterPlayers, si8 filterHumanPlayers, si8 filterCpuPlayers) const;
|
||||
|
||||
private:
|
||||
std::map<std::string, CRmgTemplate *> templates;
|
||||
std::map<std::string, CRmgTemplate> templates;
|
||||
};
|
||||
|
||||
|
@ -103,27 +103,27 @@ void CTileInfo::setRoadType(ERoadType::ERoadType value)
|
||||
}
|
||||
|
||||
|
||||
CRmgTemplateZone::CRmgTemplateZone()
|
||||
CRmgTemplateZone::CRmgTemplateZone(CMapGenerator * Gen)
|
||||
: ZoneOptions(),
|
||||
townType(ETownType::NEUTRAL),
|
||||
terrainType (ETerrainType::GRASS),
|
||||
minGuardedValue(0),
|
||||
questArtZone(),
|
||||
gen(nullptr)
|
||||
gen(Gen)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CRmgTemplateZone::isUnderground() const
|
||||
{
|
||||
return getPos().z;
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::setOptions(const ZoneOptions * options)
|
||||
{
|
||||
ZoneOptions::operator=(*options);
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::setGenPtr(CMapGenerator * Gen)
|
||||
{
|
||||
gen = Gen;
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone)
|
||||
{
|
||||
questArtZone = otherZone;
|
||||
@ -384,7 +384,7 @@ void CRmgTemplateZone::fractalize()
|
||||
#define PRINT_FRACTALIZED_MAP false
|
||||
if (PRINT_FRACTALIZED_MAP) //enable to debug
|
||||
{
|
||||
std::ofstream out(boost::to_string(boost::format("zone %d") % id));
|
||||
std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id));
|
||||
int levels = gen->map->twoLevel ? 2 : 1;
|
||||
int width = gen->map->width;
|
||||
int height = gen->map->height;
|
||||
@ -618,7 +618,7 @@ bool CRmgTemplateZone::createRoad(const int3& src, const int3& dst)
|
||||
if (!directNeighbourFound)
|
||||
{
|
||||
movementCost = 2.1f; //moving diagonally is penalized over moving two tiles straight
|
||||
gen->foreachDiagonaltNeighbour(currentNode, foo);
|
||||
gen->foreachDiagonalNeighbour(currentNode, foo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,7 +805,7 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin
|
||||
//precalculate actual (randomized) monster strength based on this post
|
||||
//http://forum.vcmi.eu/viewtopic.php?p=12426#12426
|
||||
|
||||
int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength();
|
||||
int mapMonsterStrength = gen->getMapGenOptions().getMonsterStrength();
|
||||
int monsterStrength = (zoneGuard ? 0 : zoneMonsterStrength) + mapMonsterStrength - 1; //array index from 0 to 4
|
||||
static const int value1[] = {2500, 1500, 1000, 500, 0};
|
||||
static const int value2[] = {7500, 7500, 7500, 5000, 5000};
|
||||
@ -1181,10 +1181,10 @@ void CRmgTemplateZone::initTownType ()
|
||||
if (playerInfo.canAnyonePlay())
|
||||
{
|
||||
player = PlayerColor(player_id);
|
||||
townType = gen->mapGenOptions->getPlayersSettings().find(player)->second.getStartingTown();
|
||||
townType = gen->getMapGenOptions().getPlayersSettings().find(player)->second.getStartingTown();
|
||||
|
||||
if (townType == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
|
||||
randomizeTownType();
|
||||
randomizeTownType(true);
|
||||
}
|
||||
else //no player - randomize town
|
||||
{
|
||||
@ -1261,12 +1261,25 @@ void CRmgTemplateZone::initTownType ()
|
||||
}
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::randomizeTownType ()
|
||||
void CRmgTemplateZone::randomizeTownType(bool matchUndergroundType)
|
||||
{
|
||||
if (townTypes.size())
|
||||
townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
|
||||
else
|
||||
townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed, we still need some
|
||||
auto townTypesAllowed = (townTypes.size() ? townTypes : getDefaultTownTypes());
|
||||
if(matchUndergroundType && gen->getMapGenOptions().getHasTwoLevels())
|
||||
{
|
||||
std::set<TFaction> townTypesVerify;
|
||||
for(TFaction factionIdx : townTypesAllowed)
|
||||
{
|
||||
bool preferUnderground = (*VLC->townh)[factionIdx]->preferUndergroundPlacement;
|
||||
if(isUnderground() ? preferUnderground : !preferUnderground)
|
||||
{
|
||||
townTypesVerify.insert(factionIdx);
|
||||
}
|
||||
}
|
||||
if(!townTypesVerify.empty())
|
||||
townTypesAllowed = townTypesVerify;
|
||||
}
|
||||
|
||||
townType = *RandomGeneratorUtil::nextItem(townTypesAllowed, gen->rand);
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::initTerrainType ()
|
||||
@ -1278,7 +1291,7 @@ void CRmgTemplateZone::initTerrainType ()
|
||||
terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand);
|
||||
|
||||
//TODO: allow new types of terrain?
|
||||
if (pos.z)
|
||||
if (isUnderground())
|
||||
{
|
||||
if (terrainType != ETerrainType::LAVA)
|
||||
terrainType = ETerrainType::SUBTERRANEAN;
|
||||
@ -1295,8 +1308,8 @@ void CRmgTemplateZone::initTerrainType ()
|
||||
void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType)
|
||||
{
|
||||
std::vector<int3> tiles(tileinfo.begin(), tileinfo.end());
|
||||
gen->editManager->getTerrainSelection().setSelection(tiles);
|
||||
gen->editManager->drawTerrain(terrainType, &gen->rand);
|
||||
gen->getEditManager()->getTerrainSelection().setSelection(tiles);
|
||||
gen->getEditManager()->drawTerrain(terrainType, &gen->rand);
|
||||
}
|
||||
|
||||
bool CRmgTemplateZone::placeMines ()
|
||||
@ -1493,7 +1506,7 @@ bool CRmgTemplateZone::createRequiredObjects()
|
||||
|
||||
void CRmgTemplateZone::createTreasures()
|
||||
{
|
||||
int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength();
|
||||
int mapMonsterStrength = gen->getMapGenOptions().getMonsterStrength();
|
||||
int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
|
||||
|
||||
static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 };
|
||||
@ -1570,8 +1583,8 @@ void CRmgTemplateZone::createObstacles1()
|
||||
accessibleTiles.push_back(tile);
|
||||
}
|
||||
}
|
||||
gen->editManager->getTerrainSelection().setSelection(accessibleTiles);
|
||||
gen->editManager->drawTerrain(terrainType, &gen->rand);
|
||||
gen->getEditManager()->getTerrainSelection().setSelection(accessibleTiles);
|
||||
gen->getEditManager()->drawTerrain(terrainType, &gen->rand);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1610,7 +1623,7 @@ void CRmgTemplateZone::createObstacles2()
|
||||
return p1.first > p2.first; //bigger obstacles first
|
||||
});
|
||||
|
||||
auto sel = gen->editManager->getTerrainSelection();
|
||||
auto sel = gen->getEditManager()->getTerrainSelection();
|
||||
sel.clearSelection();
|
||||
|
||||
auto tryToPlaceObstacleHere = [this, &possibleObstacles](int3& tile, int index)-> bool
|
||||
@ -1705,8 +1718,8 @@ void CRmgTemplateZone::drawRoads()
|
||||
tiles.push_back(tile);
|
||||
}
|
||||
|
||||
gen->editManager->getTerrainSelection().setSelection(tiles);
|
||||
gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
|
||||
gen->getEditManager()->getTerrainSelection().setSelection(tiles);
|
||||
gen->getEditManager()->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
|
||||
}
|
||||
|
||||
|
||||
@ -1903,7 +1916,7 @@ void CRmgTemplateZone::checkAndPlaceObject(CGObjectInstance* object, const int3
|
||||
object->appearance = templates.front();
|
||||
}
|
||||
|
||||
gen->editManager->insertObject(object);
|
||||
gen->getEditManager()->insertObject(object);
|
||||
}
|
||||
|
||||
void CRmgTemplateZone::placeObject(CGObjectInstance* object, const int3 &pos, bool updateDistance)
|
||||
|
@ -89,11 +89,10 @@ struct DLL_LINKAGE CTreasurePileInfo
|
||||
class DLL_LINKAGE CRmgTemplateZone : public rmg::ZoneOptions
|
||||
{
|
||||
public:
|
||||
CRmgTemplateZone();
|
||||
CRmgTemplateZone(CMapGenerator * Gen);
|
||||
|
||||
void setOptions(const rmg::ZoneOptions * options);
|
||||
|
||||
void setGenPtr(CMapGenerator * Gen);
|
||||
bool isUnderground() const;
|
||||
|
||||
float3 getCenter() const;
|
||||
void setCenter(const float3 &f);
|
||||
@ -119,7 +118,7 @@ public:
|
||||
bool placeMines ();
|
||||
void initTownType ();
|
||||
void paintZoneTerrain (ETerrainType terrainType);
|
||||
void randomizeTownType(); //helper function
|
||||
void randomizeTownType(bool matchUndergroundType = false); //helper function
|
||||
void initTerrainType ();
|
||||
void createBorder();
|
||||
void fractalize();
|
||||
|
@ -40,15 +40,15 @@ float CZonePlacer::getDistance (float distance) const
|
||||
return (distance ? distance * distance : 1e-6f);
|
||||
}
|
||||
|
||||
void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenerator * rand)
|
||||
void CZonePlacer::placeZones(CRandomGenerator * rand)
|
||||
{
|
||||
logGlobal->info("Starting zone placement");
|
||||
|
||||
width = mapGenOptions->getWidth();
|
||||
height = mapGenOptions->getHeight();
|
||||
width = gen->getMapGenOptions().getWidth();
|
||||
height = gen->getMapGenOptions().getHeight();
|
||||
|
||||
auto zones = gen->getZones();
|
||||
bool underground = mapGenOptions->getHasTwoLevels();
|
||||
bool underground = gen->getMapGenOptions().getHasTwoLevels();
|
||||
|
||||
/*
|
||||
gravity-based algorithm
|
||||
@ -174,7 +174,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
|
||||
if (boost::optional<int> owner = zone.second->getOwner())
|
||||
{
|
||||
auto player = PlayerColor(*owner - 1);
|
||||
auto playerSettings = gen->mapGenOptions->getPlayersSettings();
|
||||
auto playerSettings = gen->getMapGenOptions().getPlayersSettings();
|
||||
si32 faction = CMapGenOptions::CPlayerSettings::RANDOM_TOWN;
|
||||
if (vstd::contains(playerSettings, player))
|
||||
faction = playerSettings[player].getStartingTown();
|
||||
@ -453,12 +453,12 @@ d = 0.01 * dx^3 - 0.1618 * dx^2 + 1 * dx + ...
|
||||
return dx * (1.0f + dx * (0.1f + dx * 0.01f)) + dy * (1.618f + dy * (-0.1618f + dy * 0.01618f));
|
||||
}
|
||||
|
||||
void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions)
|
||||
void CZonePlacer::assignZones()
|
||||
{
|
||||
logGlobal->info("Starting zone colouring");
|
||||
|
||||
auto width = mapGenOptions->getWidth();
|
||||
auto height = mapGenOptions->getHeight();
|
||||
auto width = gen->getMapGenOptions().getWidth();
|
||||
auto height = gen->getMapGenOptions().getHeight();
|
||||
|
||||
//scale to Medium map to ensure smooth results
|
||||
scaleX = 72.f / width;
|
||||
@ -554,7 +554,7 @@ void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions)
|
||||
|
||||
//TODO: similiar for islands
|
||||
#define CREATE_FULL_UNDERGROUND true //consider linking this with water amount
|
||||
if (zone.second->getPos().z)
|
||||
if (zone.second->isUnderground())
|
||||
{
|
||||
if (!CREATE_FULL_UNDERGROUND)
|
||||
zone.second->discardDistantTiles((float)(zone.second->getSize() + 1));
|
||||
|
@ -34,12 +34,14 @@ public:
|
||||
float getDistance(float distance) const; //additional scaling without 0 divison
|
||||
~CZonePlacer();
|
||||
|
||||
void placeZones(CRandomGenerator * rand);
|
||||
void assignZones();
|
||||
|
||||
private:
|
||||
void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand);
|
||||
void attractConnectedZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &distances);
|
||||
void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps);
|
||||
void moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDistanceVector &distances, TDistanceVector &overlaps);
|
||||
void placeZones(const CMapGenOptions * mapGenOptions, CRandomGenerator * rand);
|
||||
void assignZones(const CMapGenOptions * mapGenOptions);
|
||||
|
||||
private:
|
||||
int width;
|
||||
|
@ -59,9 +59,9 @@ TEST(MapFormat, Random)
|
||||
opt.setPlayerTypeForStandardPlayer(PlayerColor(2), EPlayerType::AI);
|
||||
opt.setPlayerTypeForStandardPlayer(PlayerColor(3), EPlayerType::AI);
|
||||
|
||||
CMapGenerator gen;
|
||||
CMapGenerator gen(opt, TEST_RANDOM_SEED);
|
||||
|
||||
std::unique_ptr<CMap> initialMap = gen.generate(&opt, TEST_RANDOM_SEED);
|
||||
std::unique_ptr<CMap> initialMap = gen.generate();
|
||||
initialMap->name = "Test";
|
||||
SCOPED_TRACE("MapFormat_Random generated");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user