1
0
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:
Nordsoft91 2022-05-28 16:03:50 +03:00 committed by GitHub
parent d92356f085
commit 9d06e51631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 214 additions and 196 deletions

View File

@ -4,6 +4,7 @@
"index" : 5, "index" : 5,
"nativeTerrain": "subterra", "nativeTerrain": "subterra",
"alignment" : "evil", "alignment" : "evil",
"preferUndergroundPlacement": true,
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASDUN", "120px" : "TPCASDUN",

View File

@ -4,6 +4,7 @@
"index" : 3, "index" : 3,
"nativeTerrain": "lava", "nativeTerrain": "lava",
"alignment" : "evil", "alignment" : "evil",
"preferUndergroundPlacement": true,
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASINF", "120px" : "TPCASINF",

View File

@ -4,6 +4,7 @@
"index" : 4, "index" : 4,
"nativeTerrain": "dirt", "nativeTerrain": "dirt",
"alignment" : "evil", "alignment" : "evil",
"preferUndergroundPlacement": true,
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASNEC", "120px" : "TPCASNEC",

View File

@ -73,6 +73,10 @@
"type":"string", "type":"string",
"description": "Native terrain for creatures. Creatures fighting on native terrain receive several bonuses" "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": { "puzzleMap": {
"type":"object", "type":"object",
"additionalProperties" : false, "additionalProperties" : false,

View File

@ -853,9 +853,9 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
CStopWatch sw; CStopWatch sw;
// Gen map // 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) if(allowSavingRandomMap)
{ {

View File

@ -211,6 +211,7 @@ CFaction::CFaction()
town = nullptr; town = nullptr;
index = 0; index = 0;
alignment = EAlignment::NEUTRAL; alignment = EAlignment::NEUTRAL;
preferUndergroundPlacement = false;
} }
CFaction::~CFaction() CFaction::~CFaction()
@ -1099,6 +1100,9 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
int terrainNum = nativeTerrain.isNull() int terrainNum = nativeTerrain.isNull()
? -1 ? -1
: vstd::find_pos(GameConstants::TERRAIN_NAMES, nativeTerrain.String()); : 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= //Contructor is not called here, but operator=
faction->nativeTerrain = terrainNum < 0 faction->nativeTerrain = terrainNum < 0

View File

@ -207,6 +207,7 @@ public:
ETerrainType nativeTerrain; ETerrainType nativeTerrain;
EAlignment::EAlignment alignment; EAlignment::EAlignment alignment;
bool preferUndergroundPlacement;
CTown * town; //NOTE: can be null CTown * town; //NOTE: can be null
@ -399,8 +400,8 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
void loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source); void loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source);
void loadBuildings(CTown * town, const JsonNode & source); void loadBuildings(CTown * town, const JsonNode & source);
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype = -1); std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype = -1);
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1); std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1);
std::shared_ptr<Bonus> createBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype = -1); std::shared_ptr<Bonus> createBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype = -1);
/// loads CStructure's into town /// loads CStructure's into town

View File

@ -205,11 +205,6 @@ void CMapGenOptions::setMapTemplate(const CRmgTemplate * value)
assert(0); assert(0);
} }
const std::map<std::string, CRmgTemplate *> & CMapGenOptions::getAvailableTemplates() const
{
return VLC->tplh->getTemplates();
}
void CMapGenOptions::finalize(CRandomGenerator & rand) void CMapGenOptions::finalize(CRandomGenerator & rand)
{ {
logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d", 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 const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const
{ {
// Find potential templates int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));
const auto & tpls = getAvailableTemplates();
std::list<const CRmgTemplate *> potentialTpls; auto templates = VLC->tplh->getTemplates(tplSize, getPlayerCount(), countHumanPlayers(), compOnlyPlayerCount);
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;
}
}
if (isPlayerCountValid)
{
bool isCpuPlayerCountValid = false;
if(compOnlyPlayerCount != RANDOM_SIZE)
{
if (tpl->getCpuPlayers().isInRange(compOnlyPlayerCount))
isCpuPlayerCountValid = true;
}
else
{
isCpuPlayerCountValid = true;
}
if(isCpuPlayerCountValid)
potentialTpls.push_back(tpl);
}
}
}
// Select tpl // Select tpl
if(potentialTpls.empty()) if(templates.empty())
{
return nullptr; return nullptr;
}
else return *RandomGeneratorUtil::nextItem(templates, rand);
{
return *RandomGeneratorUtil::nextItem(potentialTpls, rand);
}
} }
CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI) CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI)

View File

@ -94,6 +94,7 @@ public:
}; };
CMapGenOptions(); CMapGenOptions();
CMapGenOptions(const CMapGenOptions&) = delete;
si32 getWidth() const; si32 getWidth() const;
void setWidth(si32 value); void setWidth(si32 value);
@ -141,8 +142,6 @@ public:
const CRmgTemplate * getMapTemplate() const; const CRmgTemplate * getMapTemplate() const;
void setMapTemplate(const CRmgTemplate * value); 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 /// 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 /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise
/// this function fails. /// this function fails.

View File

@ -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) for (const int3 &dir : dirsDiagonal)
{ {
@ -57,11 +57,13 @@ void CMapGenerator::foreachDiagonaltNeighbour(const int3& pos, std::function<voi
} }
CMapGenerator::CMapGenerator() : CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
mapGenOptions(nullptr), randomSeed(0), editManager(nullptr), mapGenOptions(mapGenOptions), randomSeed(RandomSeed),
zonesTotal(0), tiles(nullptr), prisonsRemaining(0), zonesTotal(0), tiles(nullptr), prisonsRemaining(0),
monolithIndex(0) monolithIndex(0)
{ {
rand.setSeed(this->randomSeed);
mapGenOptions.finalize(rand);
} }
void CMapGenerator::initTiles() void CMapGenerator::initTiles()
@ -89,8 +91,8 @@ CMapGenerator::~CMapGenerator()
{ {
if (tiles) if (tiles)
{ {
int width = mapGenOptions->getWidth(); int width = mapGenOptions.getWidth();
int height = mapGenOptions->getHeight(); int height = mapGenOptions.getHeight();
for (int i=0; i < width; i++) for (int i=0; i < width; i++)
{ {
for(int j=0; j < height; j++) for(int j=0; j < height; j++)
@ -111,7 +113,7 @@ void CMapGenerator::initPrisonsRemaining()
if (isAllowed) if (isAllowed)
prisonsRemaining++; 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() 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; return mapGenOptions;
this->randomSeed = randomSeed; }
assert(mapGenOptions); CMapEditManager* CMapGenerator::getEditManager() const
{
rand.setSeed(this->randomSeed); if(!map)
mapGenOptions->finalize(rand); return nullptr;
return map->getEditManager();
}
std::unique_ptr<CMap> CMapGenerator::generate()
{
map = make_unique<CMap>(); map = make_unique<CMap>();
editManager = map->getEditManager();
try try
{ {
editManager->getUndoManager().setUndoRedoLimit(0); map->getEditManager()->getUndoManager().setUndoRedoLimit(0);
//FIXME: somehow mapGenOption is nullptr at this point :? //FIXME: somehow mapGenOption is nullptr at this point :?
addHeaderInfo(); addHeaderInfo();
initTiles(); initTiles();
initPrisonsRemaining(); initPrisonsRemaining();
initQuestArtsRemaining(); initQuestArtsRemaining();
genZones(); genZones();
@ -160,14 +163,13 @@ std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, in
std::string CMapGenerator::getMapDescription() const std::string CMapGenerator::getMapDescription() const
{ {
assert(mapGenOptions);
assert(map); assert(map);
const std::string waterContentStr[3] = { "none", "normal", "islands" }; const std::string waterContentStr[3] = { "none", "normal", "islands" };
const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" }; const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
int monsterStrengthIndex = mapGenOptions->getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0 int monsterStrengthIndex = mapGenOptions.getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0
const auto * mapTemplate = mapGenOptions->getMapTemplate(); const auto * mapTemplate = mapGenOptions.getMapTemplate();
if(!mapTemplate) if(!mapTemplate)
throw rmgException("Map template for Random Map Generator is not found. Could not start the game."); 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; 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") + 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() % ", 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()) % randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions.getPlayerCount()) %
static_cast<int>(mapGenOptions->getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions->getWaterContent()] % static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
monsterStrengthStr[monsterStrengthIndex]); monsterStrengthStr[monsterStrengthIndex]);
for(const auto & pair : mapGenOptions->getPlayersSettings()) for(const auto & pair : mapGenOptions.getPlayersSettings())
{ {
const auto & pSettings = pair.second; const auto & pSettings = pair.second;
if(pSettings.getPlayerType() == EPlayerType::HUMAN) if(pSettings.getPlayerType() == EPlayerType::HUMAN)
@ -211,13 +213,13 @@ void CMapGenerator::addPlayerInfo()
{ {
if (i == CPHUMAN) if (i == CPHUMAN)
{ {
playerCount = mapGenOptions->getPlayerCount(); playerCount = mapGenOptions.getPlayerCount();
teamCount = mapGenOptions->getTeamCount(); teamCount = mapGenOptions.getTeamCount();
} }
else else
{ {
playerCount = mapGenOptions->getCompOnlyPlayerCount(); playerCount = mapGenOptions.getCompOnlyPlayerCount();
teamCount = mapGenOptions->getCompOnlyTeamCount(); teamCount = mapGenOptions.getCompOnlyTeamCount();
} }
if(playerCount == 0) if(playerCount == 0)
@ -246,7 +248,7 @@ void CMapGenerator::addPlayerInfo()
// Team numbers are assigned randomly to every player // Team numbers are assigned randomly to every player
//TODO: allow customize teams in rmg template //TODO: allow customize teams in rmg template
for(const auto & pair : mapGenOptions->getPlayersSettings()) for(const auto & pair : mapGenOptions.getPlayersSettings())
{ {
const auto & pSettings = pair.second; const auto & pSettings = pair.second;
PlayerInfo player; 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")); logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human"));
assert (teamNumbers[j].size()); assert (teamNumbers[j].size());
} }
auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand); auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
player.team = TeamID(*itTeam); player.team = TeamID(*itTeam);
teamNumbers[j].erase(itTeam); teamNumbers[j].erase(itTeam);
map->players[pSettings.getColor().getNum()] = player; map->players[pSettings.getColor().getNum()] = player;
} }
map->howManyTeams = (mapGenOptions->getTeamCount() == 0 ? mapGenOptions->getPlayerCount() : mapGenOptions->getTeamCount()) map->howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount())
+ (mapGenOptions->getCompOnlyTeamCount() == 0 ? mapGenOptions->getCompOnlyPlayerCount() : mapGenOptions->getCompOnlyTeamCount()); + (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount());
} }
void CMapGenerator::genZones() void CMapGenerator::genZones()
{ {
editManager->clearTerrain(&rand); getEditManager()->clearTerrain(&rand);
editManager->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions->getWidth(), mapGenOptions->getHeight())); getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight()));
editManager->drawTerrain(ETerrainType::GRASS, &rand); getEditManager()->drawTerrain(ETerrainType::GRASS, &rand);
auto tmpl = mapGenOptions->getMapTemplate(); auto tmpl = mapGenOptions.getMapTemplate();
zones.clear(); zones.clear();
for(const auto & option : tmpl->getZones()) for(const auto & option : tmpl->getZones())
{ {
auto zone = std::make_shared<CRmgTemplateZone>(); auto zone = std::make_shared<CRmgTemplateZone>(this);
zone->setOptions(option.second.get()); zone->setOptions(option.second.get());
zones[zone->getId()] = zone; 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); CZonePlacer placer(this);
placer.placeZones(mapGenOptions, &rand); placer.placeZones(&rand);
placer.assignZones(mapGenOptions); placer.assignZones();
logGlobal->info("Zones generated successfully"); logGlobal->info("Zones generated successfully");
} }
@ -310,18 +310,19 @@ void CMapGenerator::fillZones()
//place main town in the middle //place main town in the middle
for (auto it : zones) for (auto it : zones)
it.second->initTownType(); it.second->initTownType();
//make sure there are some free tiles in the zone //make sure there are some free tiles in the zone
for (auto it : zones) for (auto it : zones)
it.second->initFreeTiles(); it.second->initFreeTiles();
createDirectConnections(); //direct createDirectConnections(); //direct
//make sure all connections are passable before creating borders //make sure all connections are passable before creating borders
for (auto it : zones) for (auto it : zones)
it.second->createBorder(); //once direct connections are done it.second->createBorder(); //once direct connections are done
createConnections2(); //subterranean gates and monoliths createConnections2(); //subterranean gates and monoliths
std::vector<std::shared_ptr<CRmgTemplateZone>> treasureZones; std::vector<std::shared_ptr<CRmgTemplateZone>> treasureZones;
for (auto it : zones) for (auto it : zones)
{ {
@ -329,12 +330,13 @@ void CMapGenerator::fillZones()
if (it.second->getType() == ETemplateZoneType::TREASURE) if (it.second->getType() == ETemplateZoneType::TREASURE)
treasureZones.push_back(it.second); treasureZones.push_back(it.second);
} }
//set apriopriate free/occupied tiles, including blocked underground rock //set apriopriate free/occupied tiles, including blocked underground rock
createObstaclesCommon1(); createObstaclesCommon1();
//set back original terrain for underground zones //set back original terrain for underground zones
for (auto it : zones) for (auto it : zones)
it.second->createObstacles1(); it.second->createObstacles1();
createObstaclesCommon2(); createObstaclesCommon2();
//place actual obstacles matching zone terrain //place actual obstacles matching zone terrain
for (auto it : zones) for (auto it : zones)
@ -345,7 +347,7 @@ void CMapGenerator::fillZones()
#define PRINT_MAP_BEFORE_ROADS false #define PRINT_MAP_BEFORE_ROADS false
if (PRINT_MAP_BEFORE_ROADS) //enable to debug 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 levels = map->twoLevel ? 2 : 1;
int width = map->width; int width = map->width;
int height = map->height; int height = map->height;
@ -413,8 +415,8 @@ void CMapGenerator::createObstaclesCommon1()
} }
} }
} }
editManager->getTerrainSelection().setSelection(rockTiles); getEditManager()->getTerrainSelection().setSelection(rockTiles);
editManager->drawTerrain(ETerrainType::ROCK, &rand); 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) //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 zoneA = zones[connection.getZoneA()];
auto zoneB = zones[connection.getZoneB()]; auto zoneB = zones[connection.getZoneB()];
@ -501,7 +503,7 @@ void CMapGenerator::findZonesForQuestArts()
void CMapGenerator::createDirectConnections() void CMapGenerator::createDirectConnections()
{ {
for (auto connection : mapGenOptions->getMapTemplate()->getConnections()) for (auto connection : mapGenOptions.getMapTemplate()->getConnections())
{ {
auto zoneA = zones[connection.getZoneA()]; auto zoneA = zones[connection.getZoneA()];
auto zoneB = zones[connection.getZoneB()]; auto zoneB = zones[connection.getZoneB()];
@ -703,9 +705,9 @@ void CMapGenerator::createConnections2()
void CMapGenerator::addHeaderInfo() void CMapGenerator::addHeaderInfo()
{ {
map->version = EMapFormat::VCMI; map->version = EMapFormat::VCMI;
map->width = mapGenOptions->getWidth(); map->width = mapGenOptions.getWidth();
map->height = mapGenOptions->getHeight(); map->height = mapGenOptions.getHeight();
map->twoLevel = mapGenOptions->getHasTwoLevels(); map->twoLevel = mapGenOptions.getHasTwoLevels();
map->name = VLC->generaltexth->allTexts[740]; map->name = VLC->generaltexth->allTexts[740];
map->description = getMapDescription(); map->description = getMapDescription();
map->difficulty = 1; map->difficulty = 1;

View File

@ -52,16 +52,16 @@ class DLL_LINKAGE CMapGenerator
public: public:
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>; 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 ~CMapGenerator(); // required due to std::unique_ptr
std::unique_ptr<CMap> generate(CMapGenOptions * mapGenOptions, int RandomSeed = std::time(nullptr)); mutable std::unique_ptr<CMap> map;
CMapGenOptions * mapGenOptions;
std::unique_ptr<CMap> map;
CRandomGenerator rand; CRandomGenerator rand;
int randomSeed;
CMapEditManager * editManager; CMapEditManager* getEditManager() const;
const CMapGenOptions& getMapGenOptions() const;
std::unique_ptr<CMap> generate();
Zones & getZones(); Zones & getZones();
void createDirectConnections(); void createDirectConnections();
@ -69,7 +69,7 @@ public:
void findZonesForQuestArts(); void findZonesForQuestArts();
void foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo); void foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo);
void foreachDirectNeighbour(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 isBlocked(const int3 &tile) const;
bool shouldBeBlocked(const int3 &tile) const; bool shouldBeBlocked(const int3 &tile) const;
@ -101,6 +101,9 @@ public:
void setZoneID(const int3& tile, TRmgTemplateZoneId zid); void setZoneID(const int3& tile, TRmgTemplateZoneId zid);
private: private:
int randomSeed;
CMapGenOptions& mapGenOptions;
std::list<rmg::ZoneConnection> connectionsLeft; std::list<rmg::ZoneConnection> connectionsLeft;
Zones zones; Zones zones;
std::map<TFaction, ui32> zonesPerFaction; std::map<TFaction, ui32> zonesPerFaction;

View File

@ -17,11 +17,6 @@
using namespace rmg; 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) void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{ {
//unused //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) void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data)
{ {
auto tpl = new CRmgTemplate();
try try
{ {
JsonDeserializer handler(nullptr, data); JsonDeserializer handler(nullptr, data);
auto fullKey = normalizeIdentifier(scope, "core", name); auto fullKey = normalizeIdentifier(scope, "core", name); //actually it's not used
tpl->setId(name); templates[fullKey].setId(name);
tpl->serializeJson(handler); templates[fullKey].serializeJson(handler);
tpl->validate(); templates[fullKey].validate();
templates[fullKey] = tpl;
} }
catch(const std::exception & e) 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 std::vector<bool> CRmgTemplateStorage::getDefaultAllowed() const
{ {
//all templates are allowed //all templates are allowed
@ -66,3 +50,54 @@ std::vector<JsonNode> CRmgTemplateStorage::loadLegacyData(size_t dataSize)
return std::vector<JsonNode>(); return std::vector<JsonNode>();
//it would be cool to load old rmg.txt files //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;
}

View File

@ -11,27 +11,29 @@
#pragma once #pragma once
#include "../IHandlerBase.h" #include "../IHandlerBase.h"
#include "../int3.h"
#include "CRmgTemplate.h"
class JsonNode; class JsonNode;
class CRmgTemplate;
/// 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
{ {
public: public:
CRmgTemplateStorage(); CRmgTemplateStorage() = default;
~CRmgTemplateStorage();
const std::map<std::string, CRmgTemplate *> & getTemplates() const;
std::vector<bool> getDefaultAllowed() const override; std::vector<bool> getDefaultAllowed() const override;
std::vector<JsonNode> loadLegacyData(size_t dataSize) override; std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
/// loads single object into game. Scope is namespace of this object, same as name of source mod /// loads single object into game. Scope is namespace of this object, same as name of source mod
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) override;
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;
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: private:
std::map<std::string, CRmgTemplate *> templates; std::map<std::string, CRmgTemplate> templates;
}; };

View File

@ -103,27 +103,27 @@ void CTileInfo::setRoadType(ERoadType::ERoadType value)
} }
CRmgTemplateZone::CRmgTemplateZone() CRmgTemplateZone::CRmgTemplateZone(CMapGenerator * Gen)
: ZoneOptions(), : ZoneOptions(),
townType(ETownType::NEUTRAL), townType(ETownType::NEUTRAL),
terrainType (ETerrainType::GRASS), terrainType (ETerrainType::GRASS),
minGuardedValue(0), minGuardedValue(0),
questArtZone(), questArtZone(),
gen(nullptr) gen(Gen)
{ {
} }
bool CRmgTemplateZone::isUnderground() const
{
return getPos().z;
}
void CRmgTemplateZone::setOptions(const ZoneOptions * options) void CRmgTemplateZone::setOptions(const ZoneOptions * options)
{ {
ZoneOptions::operator=(*options); ZoneOptions::operator=(*options);
} }
void CRmgTemplateZone::setGenPtr(CMapGenerator * Gen)
{
gen = Gen;
}
void CRmgTemplateZone::setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone) void CRmgTemplateZone::setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone)
{ {
questArtZone = otherZone; questArtZone = otherZone;
@ -384,7 +384,7 @@ void CRmgTemplateZone::fractalize()
#define PRINT_FRACTALIZED_MAP false #define PRINT_FRACTALIZED_MAP false
if (PRINT_FRACTALIZED_MAP) //enable to debug 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 levels = gen->map->twoLevel ? 2 : 1;
int width = gen->map->width; int width = gen->map->width;
int height = gen->map->height; int height = gen->map->height;
@ -618,7 +618,7 @@ bool CRmgTemplateZone::createRoad(const int3& src, const int3& dst)
if (!directNeighbourFound) if (!directNeighbourFound)
{ {
movementCost = 2.1f; //moving diagonally is penalized over moving two tiles straight 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 //precalculate actual (randomized) monster strength based on this post
//http://forum.vcmi.eu/viewtopic.php?p=12426#12426 //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 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 value1[] = {2500, 1500, 1000, 500, 0};
static const int value2[] = {7500, 7500, 7500, 5000, 5000}; static const int value2[] = {7500, 7500, 7500, 5000, 5000};
@ -1181,10 +1181,10 @@ void CRmgTemplateZone::initTownType ()
if (playerInfo.canAnyonePlay()) if (playerInfo.canAnyonePlay())
{ {
player = PlayerColor(player_id); 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) if (townType == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
randomizeTownType(); randomizeTownType(true);
} }
else //no player - randomize town else //no player - randomize town
{ {
@ -1261,12 +1261,25 @@ void CRmgTemplateZone::initTownType ()
} }
} }
void CRmgTemplateZone::randomizeTownType () void CRmgTemplateZone::randomizeTownType(bool matchUndergroundType)
{ {
if (townTypes.size()) auto townTypesAllowed = (townTypes.size() ? townTypes : getDefaultTownTypes());
townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand); if(matchUndergroundType && gen->getMapGenOptions().getHasTwoLevels())
else {
townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed, we still need some 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 () void CRmgTemplateZone::initTerrainType ()
@ -1278,7 +1291,7 @@ void CRmgTemplateZone::initTerrainType ()
terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand); terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand);
//TODO: allow new types of terrain? //TODO: allow new types of terrain?
if (pos.z) if (isUnderground())
{ {
if (terrainType != ETerrainType::LAVA) if (terrainType != ETerrainType::LAVA)
terrainType = ETerrainType::SUBTERRANEAN; terrainType = ETerrainType::SUBTERRANEAN;
@ -1295,8 +1308,8 @@ void CRmgTemplateZone::initTerrainType ()
void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType) void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType)
{ {
std::vector<int3> tiles(tileinfo.begin(), tileinfo.end()); std::vector<int3> tiles(tileinfo.begin(), tileinfo.end());
gen->editManager->getTerrainSelection().setSelection(tiles); gen->getEditManager()->getTerrainSelection().setSelection(tiles);
gen->editManager->drawTerrain(terrainType, &gen->rand); gen->getEditManager()->drawTerrain(terrainType, &gen->rand);
} }
bool CRmgTemplateZone::placeMines () bool CRmgTemplateZone::placeMines ()
@ -1493,7 +1506,7 @@ bool CRmgTemplateZone::createRequiredObjects()
void CRmgTemplateZone::createTreasures() void CRmgTemplateZone::createTreasures()
{ {
int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength(); int mapMonsterStrength = gen->getMapGenOptions().getMonsterStrength();
int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4 int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 }; static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 };
@ -1570,8 +1583,8 @@ void CRmgTemplateZone::createObstacles1()
accessibleTiles.push_back(tile); accessibleTiles.push_back(tile);
} }
} }
gen->editManager->getTerrainSelection().setSelection(accessibleTiles); gen->getEditManager()->getTerrainSelection().setSelection(accessibleTiles);
gen->editManager->drawTerrain(terrainType, &gen->rand); gen->getEditManager()->drawTerrain(terrainType, &gen->rand);
} }
} }
@ -1610,7 +1623,7 @@ void CRmgTemplateZone::createObstacles2()
return p1.first > p2.first; //bigger obstacles first return p1.first > p2.first; //bigger obstacles first
}); });
auto sel = gen->editManager->getTerrainSelection(); auto sel = gen->getEditManager()->getTerrainSelection();
sel.clearSelection(); sel.clearSelection();
auto tryToPlaceObstacleHere = [this, &possibleObstacles](int3& tile, int index)-> bool auto tryToPlaceObstacleHere = [this, &possibleObstacles](int3& tile, int index)-> bool
@ -1705,8 +1718,8 @@ void CRmgTemplateZone::drawRoads()
tiles.push_back(tile); tiles.push_back(tile);
} }
gen->editManager->getTerrainSelection().setSelection(tiles); gen->getEditManager()->getTerrainSelection().setSelection(tiles);
gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand); gen->getEditManager()->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
} }
@ -1903,7 +1916,7 @@ void CRmgTemplateZone::checkAndPlaceObject(CGObjectInstance* object, const int3
object->appearance = templates.front(); object->appearance = templates.front();
} }
gen->editManager->insertObject(object); gen->getEditManager()->insertObject(object);
} }
void CRmgTemplateZone::placeObject(CGObjectInstance* object, const int3 &pos, bool updateDistance) void CRmgTemplateZone::placeObject(CGObjectInstance* object, const int3 &pos, bool updateDistance)

View File

@ -89,11 +89,10 @@ struct DLL_LINKAGE CTreasurePileInfo
class DLL_LINKAGE CRmgTemplateZone : public rmg::ZoneOptions class DLL_LINKAGE CRmgTemplateZone : public rmg::ZoneOptions
{ {
public: public:
CRmgTemplateZone(); CRmgTemplateZone(CMapGenerator * Gen);
void setOptions(const rmg::ZoneOptions * options); void setOptions(const rmg::ZoneOptions * options);
bool isUnderground() const;
void setGenPtr(CMapGenerator * Gen);
float3 getCenter() const; float3 getCenter() const;
void setCenter(const float3 &f); void setCenter(const float3 &f);
@ -119,7 +118,7 @@ public:
bool placeMines (); bool placeMines ();
void initTownType (); void initTownType ();
void paintZoneTerrain (ETerrainType terrainType); void paintZoneTerrain (ETerrainType terrainType);
void randomizeTownType(); //helper function void randomizeTownType(bool matchUndergroundType = false); //helper function
void initTerrainType (); void initTerrainType ();
void createBorder(); void createBorder();
void fractalize(); void fractalize();
@ -170,7 +169,7 @@ private:
std::vector<ObjectInfo> possibleObjects; std::vector<ObjectInfo> possibleObjects;
int minGuardedValue; int minGuardedValue;
//content info //content info
std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects; std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects;
std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects; std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects;

View File

@ -40,15 +40,15 @@ float CZonePlacer::getDistance (float distance) const
return (distance ? distance * distance : 1e-6f); return (distance ? distance * distance : 1e-6f);
} }
void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenerator * rand) void CZonePlacer::placeZones(CRandomGenerator * rand)
{ {
logGlobal->info("Starting zone placement"); logGlobal->info("Starting zone placement");
width = mapGenOptions->getWidth(); width = gen->getMapGenOptions().getWidth();
height = mapGenOptions->getHeight(); height = gen->getMapGenOptions().getHeight();
auto zones = gen->getZones(); auto zones = gen->getZones();
bool underground = mapGenOptions->getHasTwoLevels(); bool underground = gen->getMapGenOptions().getHasTwoLevels();
/* /*
gravity-based algorithm gravity-based algorithm
@ -174,7 +174,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
if (boost::optional<int> owner = zone.second->getOwner()) if (boost::optional<int> owner = zone.second->getOwner())
{ {
auto player = PlayerColor(*owner - 1); auto player = PlayerColor(*owner - 1);
auto playerSettings = gen->mapGenOptions->getPlayersSettings(); auto playerSettings = gen->getMapGenOptions().getPlayersSettings();
si32 faction = CMapGenOptions::CPlayerSettings::RANDOM_TOWN; si32 faction = CMapGenOptions::CPlayerSettings::RANDOM_TOWN;
if (vstd::contains(playerSettings, player)) if (vstd::contains(playerSettings, player))
faction = playerSettings[player].getStartingTown(); 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)); 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"); logGlobal->info("Starting zone colouring");
auto width = mapGenOptions->getWidth(); auto width = gen->getMapGenOptions().getWidth();
auto height = mapGenOptions->getHeight(); auto height = gen->getMapGenOptions().getHeight();
//scale to Medium map to ensure smooth results //scale to Medium map to ensure smooth results
scaleX = 72.f / width; scaleX = 72.f / width;
@ -554,7 +554,7 @@ void CZonePlacer::assignZones(const CMapGenOptions * mapGenOptions)
//TODO: similiar for islands //TODO: similiar for islands
#define CREATE_FULL_UNDERGROUND true //consider linking this with water amount #define CREATE_FULL_UNDERGROUND true //consider linking this with water amount
if (zone.second->getPos().z) if (zone.second->isUnderground())
{ {
if (!CREATE_FULL_UNDERGROUND) if (!CREATE_FULL_UNDERGROUND)
zone.second->discardDistantTiles((float)(zone.second->getSize() + 1)); zone.second->discardDistantTiles((float)(zone.second->getSize() + 1));

View File

@ -34,12 +34,14 @@ public:
float getDistance(float distance) const; //additional scaling without 0 divison float getDistance(float distance) const; //additional scaling without 0 divison
~CZonePlacer(); ~CZonePlacer();
void placeZones(CRandomGenerator * rand);
void assignZones();
private:
void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand); void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand);
void attractConnectedZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &distances); void attractConnectedZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &distances);
void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps); void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps);
void moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDistanceVector &distances, 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: private:
int width; int width;

View File

@ -59,9 +59,9 @@ TEST(MapFormat, Random)
opt.setPlayerTypeForStandardPlayer(PlayerColor(2), EPlayerType::AI); opt.setPlayerTypeForStandardPlayer(PlayerColor(2), EPlayerType::AI);
opt.setPlayerTypeForStandardPlayer(PlayerColor(3), 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"; initialMap->name = "Test";
SCOPED_TRACE("MapFormat_Random generated"); SCOPED_TRACE("MapFormat_Random generated");