1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Merge pull request #2053 from vcmi/parellel_rmg

Okay, let's merge this.
This commit is contained in:
DjWarmonger 2023-05-29 13:50:02 +02:00 committed by GitHub
commit c99aa74434
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 2146 additions and 1029 deletions

View File

@ -94,6 +94,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/mapping/MapFormatH3M.cpp
${MAIN_LIB_DIR}/mapping/MapReaderH3M.cpp
${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp
${MAIN_LIB_DIR}/mapping/ObstacleProxy.cpp
${MAIN_LIB_DIR}/registerTypes/RegisterTypes.cpp
${MAIN_LIB_DIR}/registerTypes/TypesClientPacks1.cpp
@ -121,21 +122,25 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/rmg/TileInfo.cpp
${MAIN_LIB_DIR}/rmg/Zone.cpp
${MAIN_LIB_DIR}/rmg/Functions.cpp
${MAIN_LIB_DIR}/rmg/ObjectManager.cpp
${MAIN_LIB_DIR}/rmg/ObjectDistributor.cpp
${MAIN_LIB_DIR}/rmg/RoadPlacer.cpp
${MAIN_LIB_DIR}/rmg/TreasurePlacer.cpp
${MAIN_LIB_DIR}/rmg/RmgMap.cpp
${MAIN_LIB_DIR}/rmg/ConnectionsPlacer.cpp
${MAIN_LIB_DIR}/rmg/WaterAdopter.cpp
${MAIN_LIB_DIR}/rmg/MinePlacer.cpp
${MAIN_LIB_DIR}/rmg/TownPlacer.cpp
${MAIN_LIB_DIR}/rmg/WaterProxy.cpp
${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp
${MAIN_LIB_DIR}/rmg/RockPlacer.cpp
${MAIN_LIB_DIR}/rmg/ObstaclePlacer.cpp
${MAIN_LIB_DIR}/rmg/RiverPlacer.cpp
${MAIN_LIB_DIR}/rmg/TerrainPainter.cpp
${MAIN_LIB_DIR}/rmg/modificators/Modificator.cpp
${MAIN_LIB_DIR}/rmg/modificators/ObjectManager.cpp
${MAIN_LIB_DIR}/rmg/modificators/ObjectDistributor.cpp
${MAIN_LIB_DIR}/rmg/modificators/RoadPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/TreasurePlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/QuestArtifactPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/ConnectionsPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/WaterAdopter.cpp
${MAIN_LIB_DIR}/rmg/modificators/MinePlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/TownPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/WaterProxy.cpp
${MAIN_LIB_DIR}/rmg/modificators/WaterRoutes.cpp
${MAIN_LIB_DIR}/rmg/modificators/RockPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/RockFiller.cpp
${MAIN_LIB_DIR}/rmg/modificators/ObstaclePlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/RiverPlacer.cpp
${MAIN_LIB_DIR}/rmg/modificators/TerrainPainter.cpp
${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.cpp
${MAIN_LIB_DIR}/serializer/BinaryDeserializer.cpp
${MAIN_LIB_DIR}/serializer/BinarySerializer.cpp
@ -385,6 +390,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/mapping/MapFormatH3M.h
${MAIN_LIB_DIR}/mapping/MapReaderH3M.h
${MAIN_LIB_DIR}/mapping/MapFormatJson.h
${MAIN_LIB_DIR}/mapping/ObstacleProxy.h
${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h
@ -404,23 +410,29 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/rmg/CZonePlacer.h
${MAIN_LIB_DIR}/rmg/TileInfo.h
${MAIN_LIB_DIR}/rmg/Zone.h
${MAIN_LIB_DIR}/rmg/Functions.h
${MAIN_LIB_DIR}/rmg/ObjectManager.h
${MAIN_LIB_DIR}/rmg/ObjectDistributor.h
${MAIN_LIB_DIR}/rmg/RoadPlacer.h
${MAIN_LIB_DIR}/rmg/TreasurePlacer.h
${MAIN_LIB_DIR}/rmg/RmgMap.h
${MAIN_LIB_DIR}/rmg/ConnectionsPlacer.h
${MAIN_LIB_DIR}/rmg/WaterAdopter.h
${MAIN_LIB_DIR}/rmg/MinePlacer.h
${MAIN_LIB_DIR}/rmg/TownPlacer.h
${MAIN_LIB_DIR}/rmg/WaterProxy.h
${MAIN_LIB_DIR}/rmg/WaterRoutes.h
${MAIN_LIB_DIR}/rmg/RockPlacer.h
${MAIN_LIB_DIR}/rmg/ObstaclePlacer.h
${MAIN_LIB_DIR}/rmg/RiverPlacer.h
${MAIN_LIB_DIR}/rmg/TerrainPainter.h
${MAIN_LIB_DIR}/rmg/float3.h
${MAIN_LIB_DIR}/rmg/Functions.h
${MAIN_LIB_DIR}/rmg/modificators/Modificator.h
${MAIN_LIB_DIR}/rmg/modificators/ObjectManager.h
${MAIN_LIB_DIR}/rmg/modificators/ObjectDistributor.h
${MAIN_LIB_DIR}/rmg/modificators/RoadPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/TreasurePlacer.h
${MAIN_LIB_DIR}/rmg/modificators/QuestArtifactPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/ConnectionsPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/WaterAdopter.h
${MAIN_LIB_DIR}/rmg/modificators/MinePlacer.h
${MAIN_LIB_DIR}/rmg/modificators/TownPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/WaterProxy.h
${MAIN_LIB_DIR}/rmg/modificators/WaterRoutes.h
${MAIN_LIB_DIR}/rmg/modificators/RockPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/RockFiller.h
${MAIN_LIB_DIR}/rmg/modificators/ObstaclePlacer.h
${MAIN_LIB_DIR}/rmg/modificators/RiverPlacer.h
${MAIN_LIB_DIR}/rmg/modificators/TerrainPainter.h
${MAIN_LIB_DIR}/rmg/threadpool/BlockingQueue.h
${MAIN_LIB_DIR}/rmg/threadpool/ThreadPool.h
${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.h
${MAIN_LIB_DIR}/serializer/BinaryDeserializer.h
${MAIN_LIB_DIR}/serializer/BinarySerializer.h

View File

@ -41,5 +41,6 @@
{
"value" : [2000, 5333, 8666, 12000],
"rewardValue" : [5000, 10000, 15000, 20000]
}
},
"singleThread" : false
}

View File

@ -1,5 +1,5 @@
/*
* ObstaclePlacer.cpp, part of VCMI engine
* ObstacleProxy.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -9,20 +9,9 @@
*/
#include "StdInc.h"
#include "ObstacleProxy.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "ObstaclePlacer.h"
#include "ObjectManager.h"
#include "TreasurePlacer.h"
#include "RockPlacer.h"
#include "WaterRoutes.h"
#include "WaterProxy.h"
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "RmgMap.h"
#include "CMapGenerator.h"
#include "../CRandomGenerator.h"
#include "Functions.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectHandler.h"
#include "../mapping/CMap.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -55,7 +44,27 @@ void ObstacleProxy::collectPossibleObstacles(TerrainId terrain)
});
}
int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
void ObstacleProxy::addBlockedTile(const int3& tile)
{
blockedArea.add(tile);
}
void ObstacleProxy::setBlockedArea(const rmg::Area& area)
{
blockedArea = area;
}
void ObstacleProxy::clearBlockedArea()
{
blockedArea.clear();
}
bool ObstacleProxy::isProhibited(const rmg::Area& objArea) const
{
return false;
};
int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
{
int maxWeight = std::numeric_limits<int>::min();
for(auto & possibleObstacle : possibleObstacles)
@ -75,12 +84,13 @@ int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRand
for(const auto & offset : obj->getBlockedOffsets())
{
rmgObject->setPosition(tile - offset);
if(!map->isInTheMap(rmgObject->getPosition()))
if(!isInTheMap(rmgObject->getPosition()))
continue;
if(!rmgObject->getArea().getSubarea([map](const int3 & t)
if(!rmgObject->getArea().getSubarea([this](const int3 & t)
{
return !map->isInTheMap(t);
return !isInTheMap(t);
}).empty())
continue;
@ -124,7 +134,7 @@ int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRand
return maxWeight;
}
void ObstacleProxy::placeObstacles(CMap * map, CRandomGenerator & rand)
std::set<CGObjectInstance*> ObstacleProxy::createObstacles(CRandomGenerator & rand)
{
//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
auto blockedTiles = blockedArea.getTilesVector();
@ -137,7 +147,7 @@ void ObstacleProxy::placeObstacles(CMap * map, CRandomGenerator & rand)
std::list<rmg::Object> allObjects;
std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
int maxWeight = getWeightedObjects(tile, map, rand, allObjects, weightedObjects);
int maxWeight = getWeightedObjects(tile, rand, allObjects, weightedObjects);
if(weightedObjects.empty())
{
@ -164,9 +174,11 @@ void ObstacleProxy::placeObstacles(CMap * map, CRandomGenerator & rand)
}
}
finalInsertion(map->getEditManager(), objs);
return objs;
}
//FIXME: Only editor placer obstacles directly
void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
{
manager->insertObjects(instances); //insert as one operation - for undo purposes
@ -185,85 +197,20 @@ void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*
}
}
void ObstacleProxy::postProcess(const rmg::Object & object)
EditorObstaclePlacer::EditorObstaclePlacer(CMap* map):
map(map)
{
}
bool ObstacleProxy::isProhibited(const rmg::Area & objArea) const
bool EditorObstaclePlacer::isInTheMap(const int3& tile)
{
return false;
return map->isInTheMap(tile);
}
void ObstaclePlacer::process()
void EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand)
{
manager = zone.getModificator<ObjectManager>();
if(!manager)
return;
riverManager = zone.getModificator<RiverPlacer>();
collectPossibleObstacles(zone.getTerrainType());
blockedArea = zone.area().getSubarea([this](const int3 & t)
{
return map.shouldBeBlocked(t);
});
blockedArea.subtract(zone.areaUsed());
zone.areaPossible().subtract(blockedArea);
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
placeObstacles(&map.map(), generator.rand);
auto obstacles = createObstacles(rand);
finalInsertion(map->getEditManager(), obstacles);
}
void ObstaclePlacer::init()
{
DEPENDENCY(ObjectManager);
DEPENDENCY(TreasurePlacer);
DEPENDENCY(WaterRoutes);
DEPENDENCY(WaterProxy);
DEPENDENCY(RoadPlacer);
DEPENDENCY_ALL(RockPlacer);
}
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
{
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
}
void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
{
manager->placeObject(object, false, false);
}
void ObstaclePlacer::postProcess(const rmg::Object & object)
{
//river processing
if(riverManager)
{
const auto objTypeName = object.instances().front()->object().typeName;
if(objTypeName == "mountain")
riverManager->riverSource().unite(object.getArea());
else if(objTypeName == "lake")
riverManager->riverSink().unite(object.getArea());
}
}
bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
{
if(prohibitedArea.overlap(objArea))
return true;
if(!zone.area().contains(objArea))
return true;
return false;
}
void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set<CGObjectInstance*> &)
{
}
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,76 @@
/*
* ObstacleProxy.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../rmg/RmgObject.h"
#include "CMapEditManager.h"
VCMI_LIB_NAMESPACE_BEGIN
class CMapEditManager;
class CGObjectInstance;
class ObjectTemplate;
class CRandomGenerator;
class DLL_LINKAGE ObstacleProxy
{
//Base class generating random obstacles for RMG and map editor
public:
ObstacleProxy() = default;
virtual ~ObstacleProxy() = default;
void collectPossibleObstacles(TerrainId terrain);
void addBlockedTile(const int3 & tile);
void setBlockedArea(const rmg::Area & area);
void clearBlockedArea();
virtual bool isProhibited(const rmg::Area& objArea) const;
virtual std::pair<bool, bool> verifyCoverage(const int3 & t) const;
virtual void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances);
virtual std::set<CGObjectInstance*> createObstacles(CRandomGenerator & rand);
virtual bool isInTheMap(const int3& tile) = 0;
void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances);
virtual void postProcess(const rmg::Object& object) {};
protected:
int getWeightedObjects(const int3& tile, CRandomGenerator& rand, std::list<rmg::Object>& allObjects, std::vector<std::pair<rmg::Object*, int3>>& weightedObjects);
rmg::Area blockedArea;
using ObstacleVector = std::vector<std::shared_ptr<const ObjectTemplate>>;
std::map<int, ObstacleVector> obstaclesBySize;
using ObstaclePair = std::pair<int, ObstacleVector>;
std::vector<ObstaclePair> possibleObstacles;
};
class DLL_LINKAGE EditorObstaclePlacer : public ObstacleProxy
{
public:
EditorObstaclePlacer(CMap* map);
bool isInTheMap(const int3& tile) override;
void placeObstacles(CRandomGenerator& rand);
private:
CMap* map;
};
VCMI_LIB_NAMESPACE_END

View File

@ -23,20 +23,22 @@
#include "Zone.h"
#include "Functions.h"
#include "RmgMap.h"
#include "ObjectManager.h"
#include "TreasurePlacer.h"
#include "RoadPlacer.h"
#include "threadpool/ThreadPool.h"
#include "modificators/ObjectManager.h"
#include "modificators/TreasurePlacer.h"
#include "modificators/RoadPlacer.h"
VCMI_LIB_NAMESPACE_BEGIN
CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
mapGenOptions(mapGenOptions), randomSeed(RandomSeed),
prisonsRemaining(0), monolithIndex(0)
allowedPrisons(0), monolithIndex(0)
{
loadConfig();
rand.setSeed(this->randomSeed);
mapGenOptions.finalize(rand);
map = std::make_unique<RmgMap>(mapGenOptions);
placer = std::make_shared<CZonePlacer>(*map);
}
int CMapGenerator::getRandomSeed() const
@ -81,6 +83,7 @@ void CMapGenerator::loadConfig()
config.secondaryRoadType = "";
if(!mapGenOptions.isRoadEnabled(config.defaultRoadType))
config.defaultRoadType = config.secondaryRoadType;
config.singleThread = randomMapJson["singleThread"].Bool();
}
const CMapGenerator::Config & CMapGenerator::getConfig() const
@ -98,20 +101,22 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const
void CMapGenerator::initPrisonsRemaining()
{
prisonsRemaining = 0;
for (auto isAllowed : map->map().allowedHeroes)
allowedPrisons = 0;
for (auto isAllowed : map->getMap(this).allowedHeroes)
{
if (isAllowed)
prisonsRemaining++;
allowedPrisons++;
}
prisonsRemaining = std::max<int> (0, prisonsRemaining - 16 * mapGenOptions.getPlayerCount()); //so at least 16 heroes will be available for every player
allowedPrisons = std::max<int> (0, allowedPrisons - 16 * mapGenOptions.getPlayerCount()); //so at least 16 heroes will be available for every player
}
void CMapGenerator::initQuestArtsRemaining()
{
//TODO: Move to QuestArtifactPlacer?
for (auto art : VLC->arth->objects)
{
if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty()) //don't use parts of combined artifacts
//Don't use parts of combined artifacts
if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty())
questArtifacts.push_back(art->getId());
}
}
@ -123,13 +128,13 @@ std::unique_ptr<CMap> CMapGenerator::generate()
try
{
addHeaderInfo();
map->initTiles(*this);
map->initTiles(*this, rand);
Load::Progress::step();
initPrisonsRemaining();
initQuestArtsRemaining();
genZones();
Load::Progress::step();
map->map().calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded
map->getMap(this).calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded
map->addModificators();
Load::Progress::step(3);
fillZones();
@ -160,7 +165,7 @@ 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 %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
randomSeed % map->map().width % map->map().height % static_cast<int>(map->map().levels()) % static_cast<int>(mapGenOptions.getPlayerCount()) %
randomSeed % map->width() % map->height() % static_cast<int>(map->levels()) % static_cast<int>(mapGenOptions.getPlayerCount()) %
static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
monsterStrengthStr[monsterStrengthIndex]);
@ -259,22 +264,21 @@ void CMapGenerator::addPlayerInfo()
teamNumbers[j].erase(itTeam);
}
teamsTotal.insert(player.team.getNum());
map->map().players[pSettings.getColor().getNum()] = player;
map->getMap(this).players[pSettings.getColor().getNum()] = player;
}
map->map().howManyTeams = teamsTotal.size();
map->getMap(this).howManyTeams = teamsTotal.size();
}
void CMapGenerator::genZones()
{
CZonePlacer placer(*map);
placer.placeZones(&rand);
placer.assignZones(&rand);
placer->placeZones(&rand);
placer->assignZones(&rand);
logGlobal->info("Zones generated successfully");
}
void CMapGenerator::createWaterTreasures()
void CMapGenerator::addWaterTreasuresInfo()
{
if (!getZoneWater())
return;
@ -288,14 +292,16 @@ void CMapGenerator::createWaterTreasures()
void CMapGenerator::fillZones()
{
findZonesForQuestArts();
createWaterTreasures();
addWaterTreasuresInfo();
logGlobal->info("Started filling zones");
size_t numZones = map->getZones().size();
//we need info about all town types to evaluate dwellings and pandoras with creatures properly
//place main town in the middle
Load::Progress::setupStepsTill(map->getZones().size(), 50);
Load::Progress::setupStepsTill(numZones, 50);
for (const auto& it : map->getZones())
{
it.second->initFreeTiles();
@ -303,16 +309,82 @@ void CMapGenerator::fillZones()
Progress::Progress::step();
}
Load::Progress::setupStepsTill(map->getZones().size(), 240);
std::vector<std::shared_ptr<Zone>> treasureZones;
TModificators allJobs;
for (auto& it : map->getZones())
{
allJobs.splice(allJobs.end(), it.second->getModificators());
}
Load::Progress::setupStepsTill(allJobs.size(), 240);
if (config.singleThread) //No thread pool, just queue with deterministic order
{
while (!allJobs.empty())
{
for (auto it = allJobs.begin(); it != allJobs.end();)
{
if ((*it)->isReady())
{
auto jobCopy = *it;
jobCopy->run();
Progress::Progress::step(); //Update progress bar
allJobs.erase(it);
break; //Restart from the first job
}
else
{
++it;
}
}
}
}
else
{
ThreadPool pool;
std::vector<boost::future<void>> futures;
//At most one Modificator can run for every zone
pool.init(std::min<int>(boost::thread::hardware_concurrency(), numZones));
while (!allJobs.empty())
{
for (auto it = allJobs.begin(); it != allJobs.end();)
{
if ((*it)->isFinished())
{
it = allJobs.erase(it);
Progress::Progress::step();
}
else if ((*it)->isReady())
{
auto jobCopy = *it;
futures.emplace_back(pool.async([this, jobCopy]() -> void
{
jobCopy->run();
Progress::Progress::step(); //Update progress bar
}
));
it = allJobs.erase(it);
}
else
{
++it;
}
}
}
//Wait for all the tasks
for (auto& fut : futures)
{
fut.get();
}
}
for (const auto& it : map->getZones())
{
it.second->processModificators();
if (it.second->getType() == ETemplateZoneType::TREASURE)
treasureZones.push_back(it.second);
Progress::Progress::step();
}
//find place for Grail
@ -324,44 +396,23 @@ void CMapGenerator::fillZones()
}
auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
map->map().grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand);
map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand);
logGlobal->info("Zones filled successfully");
Load::Progress::set(250);
}
void CMapGenerator::findZonesForQuestArts()
{
//we want to place arties in zones that were not yet filled (higher index)
for (auto connection : mapGenOptions.getMapTemplate()->getConnections())
{
auto zoneA = map->getZones()[connection.getZoneA()];
auto zoneB = map->getZones()[connection.getZoneB()];
if (zoneA->getId() > zoneB->getId())
{
if(auto * m = zoneB->getModificator<TreasurePlacer>())
m->setQuestArtZone(zoneA.get());
}
else if (zoneA->getId() < zoneB->getId())
{
if(auto * m = zoneA->getModificator<TreasurePlacer>())
m->setQuestArtZone(zoneB.get());
}
}
}
void CMapGenerator::addHeaderInfo()
{
map->map().version = EMapFormat::VCMI;
map->map().width = mapGenOptions.getWidth();
map->map().height = mapGenOptions.getHeight();
map->map().twoLevel = mapGenOptions.getHasTwoLevels();
map->map().name = VLC->generaltexth->allTexts[740];
map->map().description = getMapDescription();
map->map().difficulty = 1;
auto& m = map->getMap(this);
m.version = EMapFormat::VCMI;
m.width = mapGenOptions.getWidth();
m.height = mapGenOptions.getHeight();
m.twoLevel = mapGenOptions.getHasTwoLevels();
m.name = VLC->generaltexth->allTexts[740];
m.description = getMapDescription();
m.difficulty = 1;
addPlayerInfo();
}
@ -389,23 +440,41 @@ int CMapGenerator::getNextMonlithIndex()
int CMapGenerator::getPrisonsRemaning() const
{
return prisonsRemaining;
return allowedPrisons;
}
void CMapGenerator::decreasePrisonsRemaining()
std::shared_ptr<CZonePlacer> CMapGenerator::getZonePlacer() const
{
prisonsRemaining = std::max (0, prisonsRemaining - 1);
return placer;
}
const std::vector<ArtifactID> & CMapGenerator::getQuestArtsRemaning() const
const std::vector<ArtifactID> & CMapGenerator::getAllPossibleQuestArtifacts() const
{
return questArtifacts;
}
const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
{
//Skip heroes that were banned, including the ones placed in prisons
std::vector<HeroTypeID> ret;
for (int j = 0; j < map->getMap(this).allowedHeroes.size(); j++)
{
if (map->getMap(this).allowedHeroes[j])
ret.push_back(HeroTypeID(j));
}
return ret;
}
void CMapGenerator::banQuestArt(const ArtifactID & id)
{
map->map().allowedArtifact[id] = false;
vstd::erase_if_present(questArtifacts, id);
//TODO: Protect with mutex
map->getMap(this).allowedArtifact[id] = false;
}
void CMapGenerator::banHero(const HeroTypeID & id)
{
//TODO: Protect with mutex
map->getMap(this).allowedHeroes[id] = false;
}
Zone * CMapGenerator::getZoneWater() const

View File

@ -25,6 +25,7 @@ class JsonNode;
class RmgMap;
class CMap;
class Zone;
class CZonePlacer;
using JsonVector = std::vector<JsonNode>;
@ -46,6 +47,7 @@ public:
int pandoraMultiplierGold, pandoraMultiplierExperience, pandoraMultiplierSpells, pandoraSpellSchool, pandoraSpell60;
std::vector<int> pandoraCreatureValues;
std::vector<int> questValues, questRewardValues;
bool singleThread;
};
explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
@ -53,37 +55,34 @@ public:
const Config & getConfig() const;
CRandomGenerator rand;
const CMapGenOptions& getMapGenOptions() const;
std::unique_ptr<CMap> generate();
void findZonesForQuestArts();
int getNextMonlithIndex();
int getPrisonsRemaning() const;
void decreasePrisonsRemaining();
const std::vector<ArtifactID> & getQuestArtsRemaning() const;
std::shared_ptr<CZonePlacer> getZonePlacer() const;
const std::vector<ArtifactID> & getAllPossibleQuestArtifacts() const;
const std::vector<HeroTypeID> getAllPossibleHeroes() const;
void banQuestArt(const ArtifactID & id);
void banHero(const HeroTypeID& id);
Zone * getZoneWater() const;
void createWaterTreasures();
void addWaterTreasuresInfo();
int getRandomSeed() const;
private:
CRandomGenerator rand;
int randomSeed;
CMapGenOptions& mapGenOptions;
Config config;
std::unique_ptr<RmgMap> map;
std::shared_ptr<CZonePlacer> placer;
std::vector<rmg::ZoneConnection> connectionsLeft;
//std::pair<Zones::key_type, Zones::mapped_type> zoneWater;
int prisonsRemaining;
//int questArtsRemaining;
int allowedPrisons;
int monolithIndex;
std::vector<ArtifactID> questArtifacts;
@ -98,7 +97,6 @@ private:
void addHeaderInfo();
void genZones();
void fillZones();
};
VCMI_LIB_NAMESPACE_END

View File

@ -38,7 +38,7 @@ CZonePlacer::CZonePlacer(RmgMap & map)
int3 CZonePlacer::cords(const float3 & f) const
{
return int3(static_cast<si32>(std::max(0.f, (f.x * map.map().width) - 1)), static_cast<si32>(std::max(0.f, (f.y * map.map().height - 1))), f.z);
return int3(static_cast<si32>(std::max(0.f, (f.x * map.width()) - 1)), static_cast<si32>(std::max(0.f, (f.y * map.height() - 1))), f.z);
}
float CZonePlacer::getDistance (float distance) const
@ -221,11 +221,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
}
//Spread apart player starting zones
auto zoneType = zone->getType();
auto existingZoneType = existingZone->getType();
if ((zoneType == ETemplateZoneType::PLAYER_START || zoneType == ETemplateZoneType::CPU_START) &&
(existingZoneType == ETemplateZoneType::PLAYER_START || existingZoneType == ETemplateZoneType::CPU_START))
if (zone->getOwner() && existingZone->getOwner()) //Players participate in game
{
int firstPlayer = zone->getOwner().value();
int secondPlayer = existingZone->getOwner().value();
@ -785,7 +781,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
zone->setPos(int3(total.x / size, total.y / size, total.z / size));
};
int levels = map.map().levels();
int levels = map.levels();
/*
1. Create Voronoi diagram
@ -862,11 +858,17 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
}
//make sure that terrain inside zone is not a rock
//FIXME: reorder actions?
paintZoneTerrain(*zone.second, *rand, map, ETerrainId::SUBTERRANEAN);
auto v = zone.second->getArea().getTilesVector();
map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN);
}
}
logGlobal->info("Finished zone colouring");
}
const TDistanceMap& CZonePlacer::getDistanceMap()
{
return distancesBetweenZones;
}
VCMI_LIB_NAMESPACE_END

View File

@ -22,10 +22,11 @@ class CRandomGenerator;
class RmgMap;
class Zone;
using TZoneVector = std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>>>;
using TZoneMap = std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>>;
using TForceVector = std::map<std::shared_ptr<Zone>, float3>;
using TDistanceVector = std::map<std::shared_ptr<Zone>, float>;
typedef std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>>> TZoneVector;
typedef std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>> TZoneMap;
typedef std::map<std::shared_ptr<Zone>, float3> TForceVector;
typedef std::map<std::shared_ptr<Zone>, float> TDistanceVector;
typedef std::map<int, std::map<int, size_t>> TDistanceMap;
class CZonePlacer
{
@ -40,6 +41,8 @@ public:
void findPathsBetweenZones();
void placeOnGrid(CRandomGenerator* rand);
void assignZones(CRandomGenerator * rand);
const TDistanceMap & getDistanceMap();
private:
void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand);
@ -65,7 +68,7 @@ private:
float bestTotalOverlap;
//distance [a][b] = number of zone connections required to travel between the zones
std::map<int, std::map<int, size_t>> distancesBetweenZones;
TDistanceMap distancesBetweenZones;
std::set<TRmgTemplateZoneId> lastSwappedZones;
RmgMap & map;
};

View File

@ -11,19 +11,11 @@
#include "StdInc.h"
#include "Functions.h"
#include "CMapGenerator.h"
#include "ObjectManager.h"
#include "RoadPlacer.h"
#include "TreasurePlacer.h"
#include "ConnectionsPlacer.h"
#include "TownPlacer.h"
#include "WaterProxy.h"
#include "WaterRoutes.h"
#include "RmgMap.h"
#include "TileInfo.h"
#include "RmgPath.h"
#include "../TerrainHandler.h"
#include "../CTownHandler.h"
#include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h"
#include "../mapObjects/CommonConstructors.h"
#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
@ -31,32 +23,6 @@
VCMI_LIB_NAMESPACE_BEGIN
void createModificators(RmgMap & map)
{
for(auto & z : map.getZones())
{
auto & zone = *z.second;
switch(zone.getType())
{
case ETemplateZoneType::WATER:
zone.addModificator<ObjectManager>();
zone.addModificator<TreasurePlacer>();
zone.addModificator<WaterProxy>();
zone.addModificator<WaterRoutes>();
break;
default:
zone.addModificator<TownPlacer>();
zone.addModificator<ObjectManager>();
zone.addModificator<ConnectionsPlacer>();
zone.addModificator<TreasurePlacer>();
zone.addModificator<RoadPlacer>();
break;
}
}
}
rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
{
uint32_t distanceSq = distance * distance;
@ -67,132 +33,16 @@ rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
return subarea.getTiles();
}
void createBorder(RmgMap & gen, Zone & zone)
{
rmg::Area borderArea(zone.getArea().getBorder());
rmg::Area borderOutsideArea(zone.getArea().getBorderOutside());
auto blockBorder = borderArea.getSubarea([&gen, &borderOutsideArea](const int3 & t)
{
auto tile = borderOutsideArea.nearest(t);
return gen.isOnMap(tile) && gen.getZones()[gen.getZoneID(tile)]->getType() != ETemplateZoneType::WATER;
});
for(const auto & tile : blockBorder.getTilesVector())
{
if(gen.isPossible(tile))
{
gen.setOccupied(tile, ETileType::BLOCKED);
zone.areaPossible().erase(tile);
}
gen.foreachDirectNeighbour(tile, [&gen, &zone](int3 &nearbyPos)
{
if(gen.isPossible(nearbyPos) && gen.getZoneID(nearbyPos) == zone.getId())
{
gen.setOccupied(nearbyPos, ETileType::BLOCKED);
zone.areaPossible().erase(nearbyPos);
}
});
}
}
void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrain)
{
auto v = zone.getArea().getTilesVector();
map.getEditManager()->getTerrainSelection().setSelection(v);
map.getEditManager()->drawTerrain(terrain, &generator);
}
int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain)
{
auto factories = VLC->objtypeh->knownSubObjects(ObjID);
vstd::erase_if(factories, [ObjID, &terrain](si32 f)
{
//TODO: Use templates with lowest number of terrains (most specific)
return VLC->objtypeh->getHandlerFor(ObjID, f)->getTemplates(terrain).empty();
});
return *RandomGeneratorUtil::nextItem(factories, generator);
}
void initTerrainType(Zone & zone, CMapGenerator & gen)
{
if(zone.getType()==ETemplateZoneType::WATER)
{
//collect all water terrain types
std::vector<TerrainId> waterTerrains;
for(const auto & terrain : VLC->terrainTypeHandler->objects)
if(terrain->isWater())
waterTerrains.push_back(terrain->getId());
zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, gen.rand));
}
else
{
if(zone.isMatchTerrainToTown() && zone.getTownType() != ETownType::NEUTRAL)
{
auto terrainType = (*VLC->townh)[zone.getTownType()]->nativeTerrain;
if (terrainType <= ETerrainId::NONE)
{
logGlobal->warn("Town %s has invalid terrain type: %d", zone.getTownType(), terrainType);
zone.setTerrainType(ETerrainId::DIRT);
}
else
{
zone.setTerrainType(terrainType);
}
}
else
{
auto terrainTypes = zone.getTerrainTypes();
if (terrainTypes.empty())
{
logGlobal->warn("No terrain types found, falling back to DIRT");
zone.setTerrainType(ETerrainId::DIRT);
}
else
{
zone.setTerrainType(*RandomGeneratorUtil::nextItem(terrainTypes, gen.rand));
}
}
//Now, replace disallowed terrains on surface and in the underground
const auto & terrainType = VLC->terrainTypeHandler->getById(zone.getTerrainType());
if(zone.isUnderground())
{
if(!terrainType->isUnderground())
{
zone.setTerrainType(ETerrainId::SUBTERRANEAN);
}
}
else
{
if (!terrainType->isSurface())
{
zone.setTerrainType(ETerrainId::DIRT);
}
}
}
}
void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator)
{
if(map.map().twoLevel)
{
//finally mark rock tiles as occupied, spawn no obstacles there
for(int x = 0; x < map.map().width; x++)
{
for(int y = 0; y < map.map().height; y++)
{
int3 tile(x, y, 1);
if(!map.map().getTile(tile).terType->isPassable())
{
map.setOccupied(tile, ETileType::USED);
}
}
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -41,12 +41,6 @@ public:
rmg::Tileset collectDistantTiles(const Zone & zone, int distance);
void createBorder(RmgMap & gen, Zone & zone);
void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrainType);
void initTerrainType(Zone & zone, CMapGenerator & gen);
int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain);

View File

@ -1,76 +0,0 @@
/*
* ObstaclePlacer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "Zone.h"
VCMI_LIB_NAMESPACE_BEGIN
class CMap;
class CMapEditManager;
class RiverPlacer;
class ObjectManager;
class DLL_LINKAGE ObstacleProxy
{
public:
ObstacleProxy() = default;
virtual ~ObstacleProxy() = default;
rmg::Area blockedArea;
void collectPossibleObstacles(TerrainId terrain);
void placeObstacles(CMap * map, CRandomGenerator & rand);
virtual std::pair<bool, bool> verifyCoverage(const int3 & t) const;
virtual void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances);
virtual void postProcess(const rmg::Object & object);
virtual bool isProhibited(const rmg::Area & objArea) const;
virtual void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances);
protected:
int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects);
using ObstacleVector = std::vector<std::shared_ptr<const ObjectTemplate>>;
std::map<int, ObstacleVector> obstaclesBySize;
using ObstaclePair = std::pair<int, ObstacleVector>;
std::vector<ObstaclePair> possibleObstacles;
};
class ObstaclePlacer: public Modificator, public ObstacleProxy
{
public:
MODIFICATOR(ObstaclePlacer);
void process() override;
void init() override;
std::pair<bool, bool> verifyCoverage(const int3 & t) const override;
void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances) override;
void postProcess(const rmg::Object & object) override;
bool isProhibited(const rmg::Area & objArea) const override;
void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances) override;
private:
rmg::Area prohibitedArea;
RiverPlacer * riverManager;
ObjectManager * manager;
};
VCMI_LIB_NAMESPACE_END

View File

@ -16,20 +16,22 @@
#include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h"
#include "../CTownHandler.h"
#include "ObjectManager.h"
#include "RoadPlacer.h"
#include "TreasurePlacer.h"
#include "ConnectionsPlacer.h"
#include "TownPlacer.h"
#include "MinePlacer.h"
#include "ObjectDistributor.h"
#include "WaterAdopter.h"
#include "WaterProxy.h"
#include "WaterRoutes.h"
#include "RockPlacer.h"
#include "ObstaclePlacer.h"
#include "RiverPlacer.h"
#include "TerrainPainter.h"
#include "modificators/ObjectManager.h"
#include "modificators/RoadPlacer.h"
#include "modificators/TreasurePlacer.h"
#include "modificators/QuestArtifactPlacer.h"
#include "modificators/ConnectionsPlacer.h"
#include "modificators/TownPlacer.h"
#include "modificators/MinePlacer.h"
#include "modificators/ObjectDistributor.h"
#include "modificators/WaterAdopter.h"
#include "modificators/WaterProxy.h"
#include "modificators/WaterRoutes.h"
#include "modificators/RockPlacer.h"
#include "modificators/RockFiller.h"
#include "modificators/ObstaclePlacer.h"
#include "modificators/RiverPlacer.h"
#include "modificators/TerrainPainter.h"
#include "Functions.h"
#include "CMapGenerator.h"
@ -39,6 +41,7 @@ RmgMap::RmgMap(const CMapGenOptions& mapGenOptions) :
mapGenOptions(mapGenOptions), zonesTotal(0)
{
mapInstance = std::make_unique<CMap>();
mapProxy = std::make_shared<MapProxy>(*this);
getEditManager()->getUndoManager().setUndoRedoLimit(0);
}
@ -74,7 +77,7 @@ void RmgMap::foreachDiagonalNeighbour(const int3 & pos, const std::function<void
}
}
void RmgMap::initTiles(CMapGenerator & generator)
void RmgMap::initTiles(CMapGenerator & generator, CRandomGenerator & rand)
{
mapInstance->initTerrain();
@ -85,15 +88,15 @@ void RmgMap::initTiles(CMapGenerator & generator)
for (auto faction : VLC->townh->getAllowedFactions())
zonesPerFaction[faction] = 0;
getEditManager()->clearTerrain(&generator.rand);
getEditManager()->clearTerrain(&rand);
getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight()));
getEditManager()->drawTerrain(ETerrainId::GRASS, &generator.rand);
getEditManager()->drawTerrain(ETerrainId::GRASS, &rand);
const auto * tmpl = mapGenOptions.getMapTemplate();
zones.clear();
for(const auto & option : tmpl->getZones())
{
auto zone = std::make_shared<Zone>(*this, generator);
auto zone = std::make_shared<Zone>(*this, generator, rand);
zone->setOptions(*option.second);
zones[zone->getId()] = zone;
}
@ -106,7 +109,7 @@ void RmgMap::initTiles(CMapGenerator & generator)
rmg::ZoneOptions options;
options.setId(waterId);
options.setType(ETemplateZoneType::WATER);
auto zone = std::make_shared<Zone>(*this, generator);
auto zone = std::make_shared<Zone>(*this, generator, rand);
zone->setOptions(options);
zones[zone->getId()] = zone;
break;
@ -115,12 +118,19 @@ void RmgMap::initTiles(CMapGenerator & generator)
void RmgMap::addModificators()
{
bool hasObjectDistributor = false;
bool hasRockFiller = false;
for(auto & z : getZones())
{
auto zone = z.second;
zone->addModificator<ObjectManager>();
zone->addModificator<ObjectDistributor>();
if (!hasObjectDistributor)
{
zone->addModificator<ObjectDistributor>();
hasObjectDistributor = true;
}
zone->addModificator<TreasurePlacer>();
zone->addModificator<ObstaclePlacer>();
zone->addModificator<TerrainPainter>();
@ -139,6 +149,7 @@ void RmgMap::addModificators()
{
zone->addModificator<TownPlacer>();
zone->addModificator<MinePlacer>();
zone->addModificator<QuestArtifactPlacer>();
zone->addModificator<ConnectionsPlacer>();
zone->addModificator<RoadPlacer>();
zone->addModificator<RiverPlacer>();
@ -147,12 +158,42 @@ void RmgMap::addModificators()
if(zone->isUnderground())
{
zone->addModificator<RockPlacer>();
if (!hasRockFiller)
{
zone->addModificator<RockFiller>();
hasRockFiller = true;
}
}
}
}
CMap & RmgMap::map() const
int RmgMap::levels() const
{
return mapInstance->levels();
}
int RmgMap::width() const
{
return mapInstance->width;
}
int RmgMap::height() const
{
return mapInstance->height;
}
PlayerInfo & RmgMap::getPlayer(int playerId)
{
return mapInstance->players.at(playerId);
}
std::shared_ptr<MapProxy> RmgMap::getMapProxy() const
{
return mapProxy;
}
CMap& RmgMap::getMap(const CMapGenerator*) const
{
return *mapInstance;
}
@ -239,13 +280,18 @@ void RmgMap::setRoad(const int3& tile, RoadId roadType)
tiles[tile.x][tile.y][tile.z].setRoadType(roadType);
}
TileInfo RmgMap::getTile(const int3& tile) const
TileInfo RmgMap::getTileInfo(const int3& tile) const
{
assertOnMap(tile);
return tiles[tile.x][tile.y][tile.z];
}
TerrainTile & RmgMap::getTile(const int3& tile) const
{
return mapInstance->getTile(tile);
}
TRmgTemplateZoneId RmgMap::getZoneID(const int3& tile) const
{
assertOnMap(tile);
@ -276,6 +322,7 @@ float RmgMap::getNearestObjectDistance(const int3 &tile) const
void RmgMap::registerZone(FactionID faction)
{
//FIXME: Protect with lock guard?
zonesPerFaction[faction]++;
zonesTotal++;
}
@ -321,7 +368,7 @@ void RmgMap::dump(bool zoneId) const
else
{
char t = '?';
switch (getTile(int3(i, j, k)).getTileType())
switch (getTileInfo(int3(i, j, k)).getTileType())
{
case ETileType::FREE:
t = ' '; break;

View File

@ -11,6 +11,7 @@
#pragma once
#include "../int3.h"
#include "../GameConstants.h"
#include "threadpool/MapProxy.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -20,12 +21,15 @@ class TileInfo;
class CMapGenOptions;
class Zone;
class CMapGenerator;
class MapProxy;
class playerInfo;
class RmgMap
{
public:
mutable std::unique_ptr<CMap> mapInstance;
CMap & map() const;
std::shared_ptr<MapProxy> getMapProxy() const;
CMap & getMap(const CMapGenerator *) const; //limited access
RmgMap(const CMapGenOptions& mapGenOptions);
~RmgMap() = default;
@ -44,11 +48,17 @@ public:
bool isUsed(const int3 &tile) const;
bool isRoad(const int3 &tile) const;
bool isOnMap(const int3 & tile) const;
int levels() const;
int width() const;
int height() const;
PlayerInfo & getPlayer(int playerId);
void setOccupied(const int3 &tile, ETileType::ETileType state);
void setRoad(const int3 &tile, RoadId roadType);
TileInfo getTile(const int3 & tile) const;
TileInfo getTileInfo(const int3 & tile) const;
TerrainTile & getTile(const int3 & tile) const;
float getNearestObjectDistance(const int3 &tile) const;
void setNearestObjectDistance(int3 &tile, float value);
@ -57,13 +67,15 @@ public:
void setZoneID(const int3& tile, TRmgTemplateZoneId zid);
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>>;
using ZonePair = std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>>;
using ZoneVector = std::vector<ZonePair>;
Zones & getZones();
void registerZone(FactionID faction);
ui32 getZoneCount(FactionID faction);
ui32 getTotalZoneCount() const;
void initTiles(CMapGenerator & generator);
void initTiles(CMapGenerator & generator, CRandomGenerator & rand);
void addModificators();
bool isAllowedSpell(const SpellID & sid) const;
@ -74,6 +86,9 @@ private:
void assertOnMap(const int3 &tile) const; //throws
private:
std::shared_ptr<MapProxy> mapProxy;
Zones zones;
std::map<FactionID, ui32> zonesPerFaction;
ui32 zonesTotal; //zones that have their main town only

View File

@ -310,7 +310,7 @@ void Object::Instance::finalize(RmgMap & map)
//If no specific template was defined for this object, select any matching
if (!dObject.appearance)
{
const auto * terrainType = map.map().getTile(getPosition(true)).terType;
const auto * terrainType = map.getTile(getPosition(true)).terType;
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId());
if (templates.empty())
{
@ -336,7 +336,7 @@ void Object::Instance::finalize(RmgMap & map)
map.setOccupied(tile, ETileType::ETileType::USED);
}
map.getEditManager()->insertObject(&dObject);
map.getMapProxy()->insertObject(&dObject);
}
void Object::finalize(RmgMap & map)

View File

@ -1,39 +0,0 @@
/*
* TerrainPainter.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 "TerrainPainter.h"
#include "TownPlacer.h"
#include "WaterAdopter.h"
#include "WaterProxy.h"
#include "ConnectionsPlacer.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
VCMI_LIB_NAMESPACE_BEGIN
void TerrainPainter::process()
{
initTerrainType(zone, generator);
paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType());
}
void TerrainPainter::init()
{
DEPENDENCY(TownPlacer);
DEPENDENCY_ALL(WaterAdopter);
POSTFUNCTION_ALL(WaterProxy);
POSTFUNCTION_ALL(ConnectionsPlacer);
POSTFUNCTION(ObjectManager);
}
VCMI_LIB_NAMESPACE_END

View File

@ -13,8 +13,6 @@
#include "RmgMap.h"
#include "Functions.h"
#include "TileInfo.h"
#include "../mapping/CMap.h"
#include "../CStopWatch.h"
#include "CMapGenerator.h"
#include "RmgPath.h"
@ -25,13 +23,14 @@ std::function<bool(const int3 &)> AREA_NO_FILTER = [](const int3 & t)
return true;
};
Zone::Zone(RmgMap & map, CMapGenerator & generator)
: townType(ETownType::NEUTRAL)
Zone::Zone(RmgMap & map, CMapGenerator & generator, CRandomGenerator & r)
: finished(false)
, townType(ETownType::NEUTRAL)
, terrainType(ETerrainId::GRASS)
, map(map)
, generator(generator)
{
rand.setSeed(r.nextInt());
}
bool Zone::isUnderground() const
@ -87,6 +86,7 @@ rmg::Area & Zone::area()
rmg::Area & Zone::areaPossible()
{
//FIXME: make const, only modify via mutex-protected interface
return dAreaPossible;
}
@ -97,6 +97,7 @@ rmg::Area & Zone::areaUsed()
void Zone::clearTiles()
{
//Lock lock(mx);
dArea.clear();
dAreaPossible.clear();
dAreaFree.clear();
@ -105,6 +106,7 @@ void Zone::clearTiles()
void Zone::initFreeTiles()
{
rmg::Tileset possibleTiles;
//Lock lock(mx);
vstd::copy_if(dArea.getTiles(), vstd::set_inserter(possibleTiles), [this](const int3 &tile) -> bool
{
return map.isPossible(tile);
@ -154,12 +156,12 @@ rmg::Path Zone::searchPath(const rmg::Area & src, bool onlyStraight, const std::
return 2;
return 3;
};
auto area = (dAreaPossible + dAreaFree).getSubarea(areafilter);
rmg::Path freePath(area);
rmg::Path resultPath(area);
freePath.connect(dAreaFree);
//connect to all pieces
auto goals = connectedAreas(src, onlyStraight);
for(auto & goal : goals)
@ -167,18 +169,23 @@ rmg::Path Zone::searchPath(const rmg::Area & src, bool onlyStraight, const std::
auto path = freePath.search(goal, onlyStraight, movementCost);
if(path.getPathArea().empty())
return rmg::Path::invalid();
freePath.connect(path.getPathArea());
resultPath.connect(path.getPathArea());
}
return resultPath;
}
rmg::Path Zone::searchPath(const int3 & src, bool onlyStraight, const std::function<bool(const int3 &)> & areafilter) const
///connect current tile to any other free tile within zone
{
return searchPath(rmg::Area({src}), onlyStraight, areafilter);
return searchPath(rmg::Area({ src }), onlyStraight, areafilter);
}
TModificators Zone::getModificators()
{
return modificators;
}
void Zone::connectPath(const rmg::Path & path)
@ -197,6 +204,7 @@ void Zone::fractalize()
rmg::Area tilesToIgnore; //will be erased in this iteration
const float minDistance = 10 * 10; //squared
float blockDistance = minDistance * 0.25f;
if(type != ETemplateZoneType::JUNCTION)
{
@ -206,7 +214,7 @@ void Zone::fractalize()
{
//link tiles in random order
std::vector<int3> tilesToMakePath = possibleTiles.getTilesVector();
RandomGeneratorUtil::randomShuffle(tilesToMakePath, generator.rand);
RandomGeneratorUtil::randomShuffle(tilesToMakePath, getRand());
int3 nodeFound(-1, -1, -1);
@ -231,7 +239,7 @@ void Zone::fractalize()
tilesToIgnore.clear();
}
}
Lock lock(areaMutex);
//cut straight paths towards the center. A* is too slow for that.
auto areas = connectedAreas(clearedTiles, false);
for(auto & area : areas)
@ -260,7 +268,6 @@ void Zone::fractalize()
}
//now block most distant tiles away from passages
float blockDistance = minDistance * 0.25f;
auto areaToBlock = dArea.getSubarea([this, blockDistance](const int3 & t)
{
auto distance = static_cast<float>(dAreaFree.distanceSqr(t));
@ -268,6 +275,8 @@ void Zone::fractalize()
});
dAreaPossible.subtract(areaToBlock);
dAreaFree.subtract(areaToBlock);
lock.unlock();
for(const auto & t : areaToBlock.getTiles())
map.setOccupied(t, ETileType::BLOCKED);
}
@ -281,126 +290,9 @@ void Zone::initModificators()
logGlobal->info("Zone %d modificators initialized", getId());
}
void Zone::processModificators()
CRandomGenerator& Zone::getRand()
{
for(auto & modificator : modificators)
{
try
{
modificator->run();
}
catch (const rmgException & e)
{
logGlobal->info("Zone %d, modificator %s - FAILED: %s", getId(), e.what());
throw e;
}
}
logGlobal->info("Zone %d filled successfully", getId());
}
Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator)
{
}
void Modificator::setName(const std::string & n)
{
name = n;
}
const std::string & Modificator::getName() const
{
return name;
}
bool Modificator::isFinished() const
{
return finished;
}
void Modificator::run()
{
started = true;
if(!finished)
{
for(auto * modificator : preceeders)
{
if(!modificator->started)
modificator->run();
}
logGlobal->info("Modificator zone %d - %s - started", zone.getId(), getName());
CStopWatch processTime;
try
{
process();
}
catch(rmgException &e)
{
logGlobal->error("Modificator %s, exception: %s", getName(), e.what());
}
#ifdef RMG_DUMP
dump();
#endif
finished = true;
logGlobal->info("Modificator zone %d - %s - done (%d ms)", zone.getId(), getName(), processTime.getDiff());
}
}
void Modificator::dependency(Modificator * modificator)
{
if(modificator && modificator != this)
{
if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end())
preceeders.push_back(modificator);
}
}
void Modificator::postfunction(Modificator * modificator)
{
if(modificator && modificator != this)
{
if(std::find(modificator->preceeders.begin(), modificator->preceeders.end(), this) == modificator->preceeders.end())
modificator->preceeders.push_back(this);
}
}
void Modificator::dump()
{
std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
auto & mapInstance = map.map();
int levels = mapInstance.levels();
int width = mapInstance.width;
int height = mapInstance.height;
for(int z = 0; z < levels; z++)
{
for(int j=0; j<height; j++)
{
for(int i=0; i<width; i++)
{
out << dump(int3(i, j, z));
}
out << std::endl;
}
out << std::endl;
}
out << std::endl;
}
char Modificator::dump(const int3 & t)
{
if(zone.freePaths().contains(t))
return '.'; //free path
if(zone.areaPossible().contains(t))
return ' '; //possible
if(zone.areaUsed().contains(t))
return 'U'; //used
if(zone.area().contains(t))
{
if(map.shouldBeBlocked(t))
return '#'; //obstacle
else
return '^'; //visitable points?
}
return '?';
return rand;
}
VCMI_LIB_NAMESPACE_END

View File

@ -13,71 +13,31 @@
#include "../GameConstants.h"
#include "float3.h"
#include "../int3.h"
#include "../CRandomGenerator.h"
#include "CRmgTemplate.h"
#include "RmgArea.h"
#include "RmgPath.h"
#include "RmgObject.h"
#include "modificators/Modificator.h"
//uncomment to generate dumps afger every step of map generation
//#define RMG_DUMP
#define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);}
#define DEPENDENCY(x) dependency(zone.getModificator<x>());
#define POSTFUNCTION(x) postfunction(zone.getModificator<x>());
#define DEPENDENCY_ALL(x) for(auto & z : map.getZones()) \
{ \
dependency(z.second->getModificator<x>()); \
}
#define POSTFUNCTION_ALL(x) for(auto & z : map.getZones()) \
{ \
postfunction(z.second->getModificator<x>()); \
}
VCMI_LIB_NAMESPACE_BEGIN
class RmgMap;
class CMapGenerator;
class Zone;
class Modificator
{
public:
Modificator() = delete;
Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator);
virtual void process() = 0;
virtual void init() {/*override to add dependencies*/}
virtual char dump(const int3 &);
virtual ~Modificator() = default;
void setName(const std::string & n);
const std::string & getName() const;
void run();
void dependency(Modificator * modificator);
void postfunction(Modificator * modificator);
protected:
RmgMap & map;
CMapGenerator & generator;
Zone & zone;
bool isFinished() const;
private:
std::string name;
bool started = false;
bool finished = false;
std::list<Modificator*> preceeders; //must be ordered container
void dump();
};
class Modificator;
class CRandomGenerator;
extern std::function<bool(const int3 &)> AREA_NO_FILTER;
typedef std::list<std::shared_ptr<Modificator>> TModificators;
class Zone : public rmg::ZoneOptions
{
public:
Zone(RmgMap & map, CMapGenerator & generator);
Zone(RmgMap & map, CMapGenerator & generator, CRandomGenerator & rand);
Zone(const Zone &) = delete;
void setOptions(const rmg::ZoneOptions & options);
@ -93,7 +53,7 @@ public:
rmg::Area & areaPossible();
rmg::Area & freePaths();
rmg::Area & areaUsed();
void initFreeTiles();
void clearTiles();
void fractalize();
@ -107,6 +67,8 @@ public:
rmg::Path searchPath(const rmg::Area & src, bool onlyStraight, const std::function<bool(const int3 &)> & areafilter = AREA_NO_FILTER) const;
rmg::Path searchPath(const int3 & src, bool onlyStraight, const std::function<bool(const int3 &)> & areafilter = AREA_NO_FILTER) const;
TModificators getModificators();
template<class T>
T* getModificator()
{
@ -123,12 +85,18 @@ public:
}
void initModificators();
void processModificators();
CRandomGenerator & getRand();
public:
boost::recursive_mutex areaMutex;
using Lock = boost::unique_lock<boost::recursive_mutex>;
protected:
CMapGenerator & generator;
CRandomGenerator rand;
RmgMap & map;
std::list<std::unique_ptr<Modificator>> modificators;
TModificators modificators;
bool finished;
//placement info
int3 pos;
@ -137,11 +105,11 @@ protected:
rmg::Area dAreaPossible;
rmg::Area dAreaFree; //core paths of free tiles that all other objects will be linked to
rmg::Area dAreaUsed;
std::vector<int3> possibleQuestArtifactPos;
//template info
si32 townType;
TerrainId terrainType;
};
VCMI_LIB_NAMESPACE_END

View File

@ -10,50 +10,66 @@
#include "StdInc.h"
#include "ConnectionsPlacer.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../TerrainHandler.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../TerrainHandler.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "TileInfo.h"
#include "../TileInfo.h"
#include "WaterAdopter.h"
#include "WaterProxy.h"
#include "TownPlacer.h"
#include <boost/interprocess/sync/scoped_lock.hpp>
VCMI_LIB_NAMESPACE_BEGIN
void ConnectionsPlacer::process()
{
collectNeighbourZones();
for(auto & c : dConnections)
auto diningPhilosophers = [this](std::function<void(const rmg::ZoneConnection&)> f)
{
for (auto& c : dConnections)
{
if (c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
continue;
auto otherZone = map.getZones().at(c.getZoneB());
auto* cp = otherZone->getModificator<ConnectionsPlacer>();
while (cp)
{
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock_t{});
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock_t{});
if (lock1.owns_lock() && lock2.owns_lock())
{
if (!vstd::contains(dCompleted, c))
{
f(c);
}
break;
}
}
}
};
diningPhilosophers([this](const rmg::ZoneConnection& c)
{
if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
continue;
if(vstd::contains(dCompleted, c))
continue;
selfSideDirectConnection(c);
}
createBorder(map, zone);
for(auto & c : dConnections)
});
createBorder();
diningPhilosophers([this](const rmg::ZoneConnection& c)
{
if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
continue;
if(vstd::contains(dCompleted, c))
continue;
selfSideIndirectConnection(c);
}
});
}
void ConnectionsPlacer::init()
@ -97,11 +113,11 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
int3 borderPos;
while(!directConnectionIterator->second.empty())
{
borderPos = *RandomGeneratorUtil::nextItem(directConnectionIterator->second, generator.rand);
borderPos = *RandomGeneratorUtil::nextItem(directConnectionIterator->second, zone.getRand());
guardPos = zone.areaPossible().nearest(borderPos);
assert(borderPos != guardPos);
float dist = map.getTile(guardPos).getNearestObjectDistance();
float dist = map.getTileInfo(guardPos).getNearestObjectDistance();
if (dist >= 3) //Don't place guards at adjacent tiles
{
@ -228,11 +244,12 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
int minDist = 3;
std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
rmg::Path path2(otherZone->area());
rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
{
auto ti = map.getTile(tile);
auto otherTi = map.getTile(tile - zShift);
auto ti = map.getTileInfo(tile);
auto otherTi = map.getTileInfo(tile - zShift);
float dist = ti.getNearestObjectDistance();
float otherDist = otherTi.getNearestObjectDistance();
if(dist < minDist || otherDist < minDist)
@ -296,4 +313,34 @@ void ConnectionsPlacer::collectNeighbourZones()
}
}
void ConnectionsPlacer::createBorder()
{
rmg::Area borderArea(zone.getArea().getBorder());
rmg::Area borderOutsideArea(zone.getArea().getBorderOutside());
auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & t)
{
auto tile = borderOutsideArea.nearest(t);
return map.isOnMap(tile) && map.getZones()[map.getZoneID(tile)]->getType() != ETemplateZoneType::WATER;
});
Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
for(const auto & tile : blockBorder.getTilesVector())
{
if(map.isPossible(tile))
{
map.setOccupied(tile, ETileType::BLOCKED);
zone.areaPossible().erase(tile);
}
map.foreachDirectNeighbour(tile, [this](int3 &nearbyPos)
{
if(map.isPossible(nearbyPos) && map.getZoneID(nearbyPos) == zone.getId())
{
map.setOccupied(nearbyPos, ETileType::BLOCKED);
zone.areaPossible().erase(nearbyPos);
}
});
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -9,8 +9,8 @@
*/
#pragma once
#include "Zone.h"
#include "RmgArea.h"
#include "../Zone.h"
#include "../RmgArea.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -27,6 +27,7 @@ public:
void selfSideDirectConnection(const rmg::ZoneConnection & connection);
void selfSideIndirectConnection(const rmg::ZoneConnection & connection);
void otherSideConnection(const rmg::ZoneConnection & connection);
void createBorder();
protected:
void collectNeighbourZones();

View File

@ -8,19 +8,19 @@
#include "MinePlacer.h"
#include "TownPlacer.h"
#include "ConnectionsPlacer.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "../spells/CSpellHandler.h" //for choosing random spells
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../../spells/CSpellHandler.h" //for choosing random spells
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "WaterAdopter.h"
#include "TileInfo.h"
#include "../TileInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -72,7 +72,7 @@ bool MinePlacer::placeMines(ObjectManager & manager)
}
//Shuffle mines to avoid patterns, but don't shuffle key objects like towns
RandomGeneratorUtil::randomShuffle(requiredObjects, generator.rand);
RandomGeneratorUtil::randomShuffle(requiredObjects, zone.getRand());
for (const auto& obj : requiredObjects)
{
manager.addRequiredObject(obj.first, obj.second);
@ -83,7 +83,7 @@ bool MinePlacer::placeMines(ObjectManager & manager)
{
for(auto * mine : createdMines)
{
for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc)
for(int rc = zone.getRand().nextInt(1, extraRes); rc > 0; --rc)
{
auto * resourse = dynamic_cast<CGResource *>(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create());
resourse->amount = CGResource::RANDOM_AMOUNT;

View File

@ -8,7 +8,7 @@
*
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -0,0 +1,159 @@
/*
* Modificator.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 "Modificator.h"
#include "../Functions.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../CStopWatch.h"
#include "../../mapping/CMap.h"
VCMI_LIB_NAMESPACE_BEGIN
Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator)
{
mapProxy = map.getMapProxy();
}
void Modificator::setName(const std::string & n)
{
name = n;
}
const std::string & Modificator::getName() const
{
return name;
}
bool Modificator::isReady()
{
Lock lock(mx, boost::try_to_lock_t{});
if (!lock.owns_lock())
{
return false;
}
else
{
//Check prerequisites
for (auto it = preceeders.begin(); it != preceeders.end();)
{
if ((*it)->isFinished()) //OK
{
//This preceeder won't be checked in the future
it = preceeders.erase(it);
}
else
{
return false;
}
}
//If a job is finished, it should be already erased from a queue
return !finished;
}
}
bool Modificator::isFinished()
{
Lock lock(mx, boost::try_to_lock_t{});
if (!lock.owns_lock())
{
return false;
}
else
{
return finished;
}
}
void Modificator::run()
{
Lock lock(mx);
if(!finished)
{
logGlobal->info("Modificator zone %d - %s - started", zone.getId(), getName());
CStopWatch processTime;
try
{
process();
}
catch(rmgException &e)
{
logGlobal->error("Modificator %s, exception: %s", getName(), e.what());
}
#ifdef RMG_DUMP
dump();
#endif
finished = true;
logGlobal->info("Modificator zone %d - %s - done (%d ms)", zone.getId(), getName(), processTime.getDiff());
}
}
void Modificator::dependency(Modificator * modificator)
{
if(modificator && modificator != this)
{
//TODO: use vstd::contains
if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end())
preceeders.push_back(modificator);
}
}
void Modificator::postfunction(Modificator * modificator)
{
if(modificator && modificator != this)
{
if(std::find(modificator->preceeders.begin(), modificator->preceeders.end(), this) == modificator->preceeders.end())
modificator->preceeders.push_back(this);
}
}
void Modificator::dump()
{
std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
int levels = map.levels();
int width = map.width();
int height = map.height();
for(int z = 0; z < levels; z++)
{
for(int j=0; j<height; j++)
{
for(int i=0; i<width; i++)
{
out << dump(int3(i, j, z));
}
out << std::endl;
}
out << std::endl;
}
out << std::endl;
}
char Modificator::dump(const int3 & t)
{
if(zone.freePaths().contains(t))
return '.'; //free path
if(zone.areaPossible().contains(t))
return ' '; //possible
if(zone.areaUsed().contains(t))
return 'U'; //used
if(zone.area().contains(t))
{
if(map.shouldBeBlocked(t))
return '#'; //obstacle
else
return '^'; //visitable points?
}
return '?';
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,81 @@
/*
* Modificator.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../../GameConstants.h"
#include "../../int3.h"
#include "../Zone.h"
#include "../threadpool/MapProxy.h"
class RmgMap;
class CMapGenerator;
class Zone;
class MapProxy;
#define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);}
#define DEPENDENCY(x) dependency(zone.getModificator<x>());
#define POSTFUNCTION(x) postfunction(zone.getModificator<x>());
#define DEPENDENCY_ALL(x) for(auto & z : map.getZones()) \
{ \
dependency(z.second->getModificator<x>()); \
}
#define POSTFUNCTION_ALL(x) for(auto & z : map.getZones()) \
{ \
postfunction(z.second->getModificator<x>()); \
}
VCMI_LIB_NAMESPACE_BEGIN
class Modificator
{
public:
Modificator() = delete;
Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator);
virtual void init() {/*override to add dependencies*/}
virtual char dump(const int3 &);
virtual ~Modificator() = default;
void setName(const std::string & n);
const std::string & getName() const;
bool isReady();
bool isFinished();
void run();
void dependency(Modificator * modificator);
void postfunction(Modificator * modificator);
protected:
RmgMap & map;
std::shared_ptr<MapProxy> mapProxy;
CMapGenerator & generator;
Zone & zone;
bool finished = false;
mutable boost::recursive_mutex externalAccessMutex; //Used to communicate between Modificators
using RecursiveLock = boost::unique_lock<boost::recursive_mutex>;
using Lock = boost::unique_lock<boost::shared_mutex>;
private:
virtual void process() = 0;
std::string name;
std::list<Modificator*> preceeders; //must be ordered container
mutable boost::shared_mutex mx; //Used only for task scheduling
void dump();
};
VCMI_LIB_NAMESPACE_END

View File

@ -11,32 +11,30 @@
#include "StdInc.h"
#include "ObjectDistributor.h"
#include "../VCMI_Lib.h"
#include "RmgMap.h"
#include "CMapGenerator.h"
#include "../../VCMI_Lib.h"
#include "../RmgMap.h"
#include "../CMapGenerator.h"
#include "TreasurePlacer.h"
#include "QuestArtifactPlacer.h"
#include "TownPlacer.h"
#include "TerrainPainter.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "../mapObjects/MapObjects.h"
#include "Functions.h"
#include "RmgObject.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../../mapObjects/MapObjects.h"
#include "../Functions.h"
#include "../RmgObject.h"
VCMI_LIB_NAMESPACE_BEGIN
void ObjectDistributor::process()
{
//Firts call will add objects to ALL zones, once they were added skip it
if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0)
{
ObjectDistributor::distributeLimitedObjects();
}
distributeLimitedObjects();
distributeSeerHuts();
}
void ObjectDistributor::init()
{
DEPENDENCY(TownPlacer);
DEPENDENCY(TerrainPainter);
//All of the terrain types need to be determined
DEPENDENCY_ALL(TerrainPainter);
POSTFUNCTION(TreasurePlacer);
}
@ -82,6 +80,8 @@ void ObjectDistributor::distributeLimitedObjects()
//We already know there are some templates
auto templates = handler->getTemplates(zone->getTerrainType());
//FIXME: Templates empty?! Maybe zone changed terrain type over time?
//Assume the template with fewest terrains is the most suitable
auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
{
@ -118,4 +118,54 @@ void ObjectDistributor::distributeLimitedObjects()
}
}
void ObjectDistributor::distributeSeerHuts()
{
//TODO: Move typedef outside the class?
//Copy by value to random shuffle
const auto & zoneMap = map.getZones();
RmgMap::ZoneVector zones(zoneMap.begin(), zoneMap.end());
RandomGeneratorUtil::randomShuffle(zones, zone.getRand());
const auto & possibleQuestArts = generator.getAllPossibleQuestArtifacts();
size_t availableArts = possibleQuestArts.size();
auto artIt = possibleQuestArts.begin();
for (int i = zones.size() - 1; i >= 0 ; i--)
{
size_t localArts = std::ceil((float)availableArts / (i + 1));
availableArts -= localArts;
auto * qap = zones[i].second->getModificator<QuestArtifactPlacer>();
if (qap)
{
for (;localArts > 0 && artIt != possibleQuestArts.end(); artIt++, localArts--)
{
qap->addRandomArtifact(*artIt);
}
}
}
}
void ObjectDistributor::distributePrisons()
{
//Copy by value to random shuffle
const auto & zoneMap = map.getZones();
RmgMap::ZoneVector zones(zoneMap.begin(), zoneMap.end());
RandomGeneratorUtil::randomShuffle(zones, zone.getRand());
size_t allowedPrisons = generator.getPrisonsRemaning();
for (int i = zones.size() - 1; i >= 0; i--)
{
auto zone = zones[i].second;
auto * tp = zone->getModificator<TreasurePlacer>();
if (tp)
{
tp->setMaxPrisons(std::ceil(float(allowedPrisons) / (i + 1)));
allowedPrisons -= tp->getMaxPrisons();
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -10,8 +10,8 @@
#pragma once
#include "Zone.h"
#include "RmgObject.h"
#include "../Zone.h"
#include "../RmgObject.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -21,6 +21,8 @@ class ObjectTemplate;
class ObjectDistributor : public Modificator
{
void distributeLimitedObjects();
void distributeSeerHuts();
void distributePrisons();
public:
MODIFICATOR(ObjectDistributor);

View File

@ -10,19 +10,24 @@
#include "StdInc.h"
#include "ObjectManager.h"
#include "CMapGenerator.h"
#include "TileInfo.h"
#include "RmgMap.h"
#include "../CMapGenerator.h"
#include "../TileInfo.h"
#include "../RmgMap.h"
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "WaterAdopter.h"
#include "../CCreatureHandler.h"
#include "../mapObjects/CommonConstructors.h"
#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "Functions.h"
#include "RmgObject.h"
#include "ConnectionsPlacer.h"
#include "TownPlacer.h"
#include "MinePlacer.h"
#include "TreasurePlacer.h"
#include "QuestArtifactPlacer.h"
#include "../../CCreatureHandler.h"
#include "../../mapObjects/CommonConstructors.h"
#include "../../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../Functions.h"
#include "../RmgObject.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -35,12 +40,16 @@ void ObjectManager::process()
void ObjectManager::init()
{
DEPENDENCY(WaterAdopter);
DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
DEPENDENCY(TownPlacer); //Only secondary towns
DEPENDENCY(MinePlacer);
POSTFUNCTION(RoadPlacer);
createDistancesPriorityQueue();
}
void ObjectManager::createDistancesPriorityQueue()
{
RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear();
for(const auto & tile : zone.areaPossible().getTilesVector())
{
@ -50,21 +59,25 @@ void ObjectManager::createDistancesPriorityQueue()
void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
{
RecursiveLock lock(externalAccessMutex);
requiredObjects.emplace_back(obj, strength);
}
void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength)
{
RecursiveLock lock(externalAccessMutex);
closeObjects.emplace_back(obj, strength);
}
void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget)
{
RecursiveLock lock(externalAccessMutex);
nearbyObjects.emplace_back(obj, nearbyTarget);
}
void ObjectManager::updateDistances(const rmg::Object & obj)
{
RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear();
for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
{
@ -76,6 +89,7 @@ void ObjectManager::updateDistances(const rmg::Object & obj)
const rmg::Area & ObjectManager::getVisitableArea() const
{
RecursiveLock lock(externalAccessMutex);
return objectsVisitableArea;
}
@ -83,6 +97,7 @@ std::vector<CGObjectInstance*> ObjectManager::getMines() const
{
std::vector<CGObjectInstance*> mines;
RecursiveLock lock(externalAccessMutex);
for(auto * object : objects)
{
if (object->ID == Obj::MINE)
@ -152,6 +167,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
}
}
//FIXME: Race condition for tiles? For Area?
if(result.valid())
obj.setPosition(result);
return result;
@ -161,14 +177,14 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
{
return findPlaceForObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
{
auto ti = map.getTile(tile);
auto ti = map.getTileInfo(tile);
float dist = ti.getNearestObjectDistance();
if(dist < min_dist)
return -1.f;
for(const auto & t : obj.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < min_dist)
if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
return -1.f;
}
@ -180,14 +196,14 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
{
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
{
auto ti = map.getTile(tile);
auto ti = map.getTileInfo(tile);
float dist = ti.getNearestObjectDistance();
if(dist < min_dist)
return -1.f;
for(const auto & t : obj.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < min_dist)
if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
return -1.f;
}
@ -242,13 +258,17 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
bool ObjectManager::createRequiredObjects()
{
logGlobal->trace("Creating required objects");
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
for(const auto & object : requiredObjects)
{
auto * obj = object.first;
//FIXME: Invalid dObject inside object?
rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
Zone::Lock lock(zone.areaMutex);
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
if(!path.valid())
@ -274,7 +294,7 @@ bool ObjectManager::createRequiredObjects()
continue;
}
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), generator.rand));
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
placeObject(rmgNearObject, false, false);
}
}
@ -282,7 +302,11 @@ bool ObjectManager::createRequiredObjects()
for(const auto & object : closeObjects)
{
auto * obj = object.first;
//TODO: Wrap into same area proxy?
Zone::Lock lock(zone.areaMutex);
auto possibleArea = zone.areaPossible();
rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
@ -318,7 +342,7 @@ bool ObjectManager::createRequiredObjects()
continue;
}
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), generator.rand));
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
placeObject(rmgNearObject, false, false);
}
}
@ -343,6 +367,8 @@ bool ObjectManager::createRequiredObjects()
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance)
{
object.finalize(map);
Zone::Lock lock(zone.areaMutex);
zone.areaPossible().subtract(object.getArea());
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping
@ -379,6 +405,21 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
m->areaIsolated().add(instance->getVisitablePosition() + int3(0, -1, 0));
}
}
switch (instance->object().ID)
{
case Obj::RANDOM_TREASURE_ART:
case Obj::RANDOM_MINOR_ART: //In OH3 quest artifacts have higher value than normal arts
{
if (auto * qap = zone.getModificator<QuestArtifactPlacer>())
{
qap->rememberPotentialArtifactToReplace(&instance->object());
}
break;
}
default:
break;
}
}
switch(object.instances().front()->object().ID)
@ -441,10 +482,10 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
}
if(!possibleCreatures.empty())
{
creId = *RandomGeneratorUtil::nextItem(possibleCreatures, generator.rand);
creId = *RandomGeneratorUtil::nextItem(possibleCreatures, zone.getRand());
amount = strength / VLC->creh->objects[creId]->getAIValue();
if (amount >= 4)
amount = static_cast<int>(amount * generator.rand.nextDouble(0.75, 1.25));
amount = static_cast<int>(amount * zone.getRand().nextDouble(0.75, 1.25));
}
else //just pick any available creature
{

View File

@ -10,8 +10,8 @@
#pragma once
#include "Zone.h"
#include "RmgObject.h"
#include "../Zone.h"
#include "../RmgObject.h"
#include <boost/heap/priority_queue.hpp> //A*
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -0,0 +1,101 @@
/*
* ObstaclePlacer.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 "ObstaclePlacer.h"
#include "ObjectManager.h"
#include "TreasurePlacer.h"
#include "RockPlacer.h"
#include "WaterRoutes.h"
#include "WaterProxy.h"
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "../RmgMap.h"
#include "../CMapGenerator.h"
#include "../../CRandomGenerator.h"
#include "../Functions.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapping/CMap.h"
#include "../../mapping/ObstacleProxy.h"
VCMI_LIB_NAMESPACE_BEGIN
void ObstaclePlacer::process()
{
manager = zone.getModificator<ObjectManager>();
if(!manager)
return;
collectPossibleObstacles(zone.getTerrainType());
blockedArea = zone.area().getSubarea([this](const int3 & t)
{
return map.shouldBeBlocked(t);
});
blockedArea.subtract(zone.areaUsed());
zone.areaPossible().subtract(blockedArea);
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
auto objs = createObstacles(zone.getRand());
mapProxy->insertObjects(objs);
}
void ObstaclePlacer::init()
{
DEPENDENCY(ObjectManager);
DEPENDENCY(TreasurePlacer);
DEPENDENCY(WaterRoutes);
DEPENDENCY(WaterProxy);
DEPENDENCY(RoadPlacer);
DEPENDENCY_ALL(RockPlacer);
}
bool ObstaclePlacer::isInTheMap(const int3& tile)
{
return map.isOnMap(tile);
}
void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
{
manager->placeObject(object, false, false);
}
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
{
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
}
void ObstaclePlacer::postProcess(const rmg::Object & object)
{
//river processing
riverManager = zone.getModificator<RiverPlacer>();
if(riverManager)
{
const auto objTypeName = object.instances().front()->object().typeName;
if(objTypeName == "mountain")
riverManager->riverSource().unite(object.getArea());
else if(objTypeName == "lake")
riverManager->riverSink().unite(object.getArea());
}
}
bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
{
if(prohibitedArea.overlap(objArea))
return true;
if(!zone.area().contains(objArea))
return true;
return false;
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,46 @@
/*
* ObstaclePlacer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "Modificator.h"
#include "../../mapping/ObstacleProxy.h"
VCMI_LIB_NAMESPACE_BEGIN
class CMap;
class CMapEditManager;
class RiverPlacer;
class ObjectManager;
class ObstaclePlacer: public Modificator, public ObstacleProxy
{
public:
MODIFICATOR(ObstaclePlacer);
void process() override;
void init() override;
bool isInTheMap(const int3& tile) override;
std::pair<bool, bool> verifyCoverage(const int3 & t) const override;
void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances) override;
void postProcess(const rmg::Object & object) override;
bool isProhibited(const rmg::Area & objArea) const override;
private:
rmg::Area prohibitedArea;
RiverPlacer * riverManager;
ObjectManager * manager;
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,149 @@
/*
* QuestArtifact.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 "QuestArtifactPlacer.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "TreasurePlacer.h"
#include "../CZonePlacer.h"
#include "../../VCMI_Lib.h"
#include "../../mapObjects/CObjectHandler.h"
#include "../../mapObjects/CommonConstructors.h"
#include "../../mapObjects/MapObjects.h"
VCMI_LIB_NAMESPACE_BEGIN
void QuestArtifactPlacer::process()
{
findZonesForQuestArts();
placeQuestArtifacts(zone.getRand());
}
void QuestArtifactPlacer::init()
{
DEPENDENCY_ALL(TreasurePlacer);
}
void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone)
{
RecursiveLock lock(externalAccessMutex);
questArtZones.push_back(otherZone);
}
void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id)
{
RecursiveLock lock(externalAccessMutex);
logGlobal->info("Need to place quest artifact artifact %s", VLC->artifacts()->getById(id)->getNameTranslated());
questArtifactsToPlace.emplace_back(id);
}
void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj)
{
RecursiveLock lock(externalAccessMutex);
artifactsToReplace.push_back(obj);
}
std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const
{
RecursiveLock lock(externalAccessMutex);
return artifactsToReplace;
}
void QuestArtifactPlacer::findZonesForQuestArts()
{
const auto& distances = generator.getZonePlacer()->getDistanceMap().at(zone.getId());
for (auto const& connectedZone : distances)
{
// Choose zones that are 1 or 2 connections away
if (vstd::iswithin(connectedZone.second, 1, 2))
{
addQuestArtZone(map.getZones().at(connectedZone.first));
}
}
logGlobal->info("Number of nearby zones suitable for quest artifacts: %d", questArtZones.size());
}
void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
{
for (const auto & artifactToPlace : questArtifactsToPlace)
{
RandomGeneratorUtil::randomShuffle(questArtZones, rand);
for (auto zone : questArtZones)
{
auto* qap = zone->getModificator<QuestArtifactPlacer>();
std::vector<CGObjectInstance *> artifactsToReplace = qap->getPossibleArtifactsToReplace();
if (artifactsToReplace.empty())
continue;
auto artifactToReplace = *RandomGeneratorUtil::nextItem(artifactsToReplace, rand);
logGlobal->info("Replacing %s at %s with the quest artifact %s",
artifactToReplace->getObjectName(),
artifactToReplace->getPosition().toString(),
VLC->artifacts()->getById(artifactToPlace)->getNameTranslated());
artifactToReplace->ID = Obj::ARTIFACT;
artifactToReplace->subID = artifactToPlace;
//Update appearance. Terrain is irrelevant.
auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, artifactToPlace);
auto templates = handler->getTemplates();
artifactToReplace->appearance = templates.front();
//FIXME: Instance name is still "randomArtifact"
for (auto z : map.getZones())
{
//Every qap has its OWN collection of artifacts
auto * localQap = zone->getModificator<QuestArtifactPlacer>();
if (localQap)
{
localQap->dropReplacedArtifact(artifactToReplace);
}
}
break;
}
}
}
void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
{
RecursiveLock lock(externalAccessMutex);
boost::remove(artifactsToReplace, obj);
}
size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const
{
RecursiveLock lock(externalAccessMutex);
return questArtifacts.size();
}
ArtifactID QuestArtifactPlacer::drawRandomArtifact()
{
RecursiveLock lock(externalAccessMutex);
if (!questArtifacts.empty())
{
ArtifactID ret = questArtifacts.back();
questArtifacts.pop_back();
RandomGeneratorUtil::randomShuffle(questArtifacts, zone.getRand());
return ret;
}
else
{
throw rmgException("No quest artifacts left for this zone!");
}
}
void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid)
{
RecursiveLock lock(externalAccessMutex);
questArtifacts.push_back(artid);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,51 @@
/*
* QuestArtifactPlacer, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../Zone.h"
#include "../Functions.h"
#include "../../mapObjects/ObjectTemplate.h"
VCMI_LIB_NAMESPACE_BEGIN
class CRandomGenerator;
class QuestArtifactPlacer : public Modificator
{
public:
MODIFICATOR(QuestArtifactPlacer);
void process() override;
void init() override;
void addQuestArtZone(std::shared_ptr<Zone> otherZone);
void findZonesForQuestArts();
void addQuestArtifact(const ArtifactID& id);
void rememberPotentialArtifactToReplace(CGObjectInstance* obj);
std::vector<CGObjectInstance*> getPossibleArtifactsToReplace() const;
void placeQuestArtifacts(CRandomGenerator & rand);
void dropReplacedArtifact(CGObjectInstance* obj);
size_t getMaxQuestArtifactCount() const;
ArtifactID drawRandomArtifact();
void addRandomArtifact(ArtifactID artid);
protected:
std::vector<std::shared_ptr<Zone>> questArtZones; //artifacts required for Seer Huts will be placed here - or not if null
std::vector<ArtifactID> questArtifactsToPlace;
std::vector<CGObjectInstance*> artifactsToReplace; //Common artifacts which may be replaced by quest artifacts from other zones
size_t maxQuestArtifacts;
std::vector<ArtifactID> questArtifacts;
};
VCMI_LIB_NAMESPACE_END

View File

@ -10,15 +10,15 @@
#include "StdInc.h"
#include "RiverPlacer.h"
#include "Functions.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../RiverHandler.h"
#include "../TerrainHandler.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "RmgPath.h"
#include "../Functions.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../RiverHandler.h"
#include "../../TerrainHandler.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../RmgPath.h"
#include "ObjectManager.h"
#include "ObstaclePlacer.h"
#include "WaterProxy.h"
@ -87,8 +87,8 @@ void RiverPlacer::init()
void RiverPlacer::drawRivers()
{
map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector());
map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(zone.getTerrainType())->river, &generator.rand);
auto tiles = rivers.getTilesVector();
mapProxy->drawRivers(zone.getRand(), tiles, zone.getTerrainType());
}
char RiverPlacer::dump(const int3 & t)
@ -137,7 +137,7 @@ void RiverPlacer::prepareHeightmap()
for(const auto & t : zone.area().getTilesVector())
{
heightMap[t] = generator.rand.nextInt(5);
heightMap[t] = zone.getRand().nextInt(5);
if(roads.contains(t))
heightMap[t] += 30.f;
@ -147,9 +147,9 @@ void RiverPlacer::prepareHeightmap()
}
//make grid
for(int j = 0; j < map.map().height; j += 2)
for(int j = 0; j < map.height(); j += 2)
{
for(int i = 0; i < map.map().width; i += 2)
for(int i = 0; i < map.width(); i += 2)
{
int3 t{i, j, zone.getPos().z};
if(zone.area().contains(t))
@ -183,13 +183,13 @@ void RiverPlacer::preprocess()
//looking outside map
if(!outOfMapInternal.empty())
{
auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), generator.rand);
auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), zone.getRand());
source.add(elem);
outOfMapInternal.erase(elem);
}
if(!outOfMapInternal.empty())
{
auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), generator.rand);
auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), zone.getRand());
sink.add(elem);
outOfMapInternal.erase(elem);
}
@ -294,7 +294,7 @@ void RiverPlacer::preprocess()
//decorative river
if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible().empty())
{
addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), generator.rand));
addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), zone.getRand()));
}
if(source.empty())

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -11,13 +11,14 @@
#include "StdInc.h"
#include "RoadPlacer.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../CModHandler.h"
#include "RmgPath.h"
#include "ObstaclePlacer.h"
#include "../Functions.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../threadpool/MapProxy.h"
#include "../../mapping/CMapEditManager.h"
#include "../../CModHandler.h"
#include "../RmgPath.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -76,17 +77,25 @@ void RoadPlacer::drawRoads(bool secondary)
|| (!secondary && generator.getConfig().defaultRoadType.empty()))
return;
zone.areaPossible().subtract(roads);
zone.freePaths().unite(roads);
map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
//RecursiveLock lock(externalAccessMutex);
{
//FIXME: double lock - unsafe
Zone::Lock lock(zone.areaMutex);
zone.areaPossible().subtract(roads);
zone.freePaths().unite(roads);
}
auto tiles = roads.getTilesVector();
std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName));
map.getEditManager()->drawRoad(roadType, &generator.rand);
mapProxy->drawRoads(zone.getRand(), tiles, roadType);
}
void RoadPlacer::addRoadNode(const int3& node)
{
RecursiveLock lock(externalAccessMutex);
roadNodes.insert(node);
}
@ -111,6 +120,7 @@ void RoadPlacer::connectRoads()
return;
//take any tile from road nodes as destination zone for all other road nodes
RecursiveLock lock(externalAccessMutex);
if(roads.empty())
roads.add(*roadNodes.begin());

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -0,0 +1,78 @@
/*
* RockFiller.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 "RockFiller.h"
#include "RockPlacer.h"
#include "TreasurePlacer.h"
#include "ObjectManager.h"
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "../RmgMap.h"
#include "../CMapGenerator.h"
#include "../Functions.h"
#include "../../TerrainHandler.h"
#include "../../CRandomGenerator.h"
#include "../lib/mapping/CMapEditManager.h"
#include "../TileInfo.h"
#include "../threadpool/MapProxy.h"
VCMI_LIB_NAMESPACE_BEGIN
class TileInfo;
void RockFiller::process()
{
processMap();
}
void RockFiller::processMap()
{
//Merge all areas
for(auto & z : map.getZones())
{
auto zone = z.second;
if(auto * m = zone->getModificator<RockPlacer>())
{
auto tiles = m->rockArea.getTilesVector();
mapProxy->drawTerrain(zone->getRand(), tiles, m->rockTerrain);
}
}
for(auto & z : map.getZones())
{
auto zone = z.second;
if(auto * m = zone->getModificator<RockPlacer>())
{
//Now make sure all accessible tiles have no additional rock on them
auto tiles = m->accessibleArea.getTilesVector();
mapProxy->drawTerrain(zone->getRand(), tiles, zone->getTerrainType());
m->postProcess();
}
}
}
void RockFiller::init()
{
DEPENDENCY_ALL(RockPlacer);
POSTFUNCTION_ALL(RoadPlacer);
}
char RockFiller::dump(const int3 & t)
{
if(!map.getTile(t).terType->isPassable())
{
return zone.area().contains(t) ? 'R' : 'E';
}
return Modificator::dump(t);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,28 @@
/*
* RockFiller.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN
class RockFiller: public Modificator
{
public:
MODIFICATOR(RockFiller);
void process() override;
void init() override;
char dump(const int3 &) override;
void processMap();
};
VCMI_LIB_NAMESPACE_END

View File

@ -14,77 +14,51 @@
#include "ObjectManager.h"
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "RmgMap.h"
#include "CMapGenerator.h"
#include "Functions.h"
#include "../TerrainHandler.h"
#include "../CRandomGenerator.h"
#include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h"
#include "../RmgMap.h"
#include "../CMapGenerator.h"
#include "../Functions.h"
#include "../../TerrainHandler.h"
#include "../../CRandomGenerator.h"
#include "../../mapping/CMapEditManager.h"
#include "../TileInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
class TileInfo;
void RockPlacer::process()
{
blockRock();
}
void RockPlacer::blockRock()
{
rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain;
assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable());
accessibleArea = zone.freePaths() + zone.areaUsed();
if(auto * m = zone.getModificator<ObjectManager>())
accessibleArea.unite(m->getVisitableArea());
//negative approach - create rock tiles first, then make sure all accessible tiles have no rock
rockArea = zone.area().getSubarea([this](const int3 & t)
{
return map.shouldBeBlocked(t);
});
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockPlacer>())
{
if(m != this && !m->isFinished())
return;
}
}
processMap();
}
void RockPlacer::processMap()
{
//merge all areas
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockPlacer>())
{
map.getEditManager()->getTerrainSelection().setSelection(m->rockArea.getTilesVector());
map.getEditManager()->drawTerrain(m->rockTerrain, &generator.rand);
}
}
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockPlacer>())
{
//now make sure all accessible tiles have no additional rock on them
map.getEditManager()->getTerrainSelection().setSelection(m->accessibleArea.getTilesVector());
map.getEditManager()->drawTerrain(z.second->getTerrainType(), &generator.rand);
m->postProcess();
}
}
}
void RockPlacer::postProcess()
{
//finally mark rock tiles as occupied, spawn no obstacles there
Zone::Lock lock(zone.areaMutex);
//Finally mark rock tiles as occupied, spawn no obstacles there
rockArea = zone.area().getSubarea([this](const int3 & t)
{
return !map.map().getTile(t).terType->isPassable();
return !map.getTile(t).terType->isPassable();
});
zone.areaUsed().unite(rockArea);
zone.areaPossible().subtract(rockArea);
//TODO: Might need mutex here as well
if(auto * m = zone.getModificator<RiverPlacer>())
m->riverProhibit().unite(rockArea);
if(auto * m = zone.getModificator<RoadPlacer>())
@ -93,13 +67,12 @@ void RockPlacer::postProcess()
void RockPlacer::init()
{
POSTFUNCTION_ALL(RoadPlacer);
DEPENDENCY(TreasurePlacer);
DEPENDENCY_ALL(TreasurePlacer);
}
char RockPlacer::dump(const int3 & t)
{
if(!map.map().getTile(t).terType->isPassable())
if(!map.getTile(t).terType->isPassable())
{
return zone.area().contains(t) ? 'R' : 'E';
}

View File

@ -9,12 +9,13 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN
class RockPlacer: public Modificator
{
friend class RockFiller;
public:
MODIFICATOR(RockPlacer);
@ -22,7 +23,7 @@ public:
void init() override;
char dump(const int3 &) override;
void processMap();
void blockRock();
void postProcess();
protected:

View File

@ -0,0 +1,105 @@
/*
* TerrainPainter.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 "TerrainPainter.h"
#include "TownPlacer.h"
#include "WaterAdopter.h"
#include "WaterProxy.h"
#include "ConnectionsPlacer.h"
#include "ObjectManager.h"
#include "../Functions.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../VCMI_Lib.h"
#include "../../TerrainHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
void TerrainPainter::process()
{
initTerrainType();
auto v = zone.getArea().getTilesVector();
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
}
void TerrainPainter::init()
{
DEPENDENCY(TownPlacer);
DEPENDENCY_ALL(WaterAdopter);
POSTFUNCTION_ALL(WaterProxy);
POSTFUNCTION_ALL(ConnectionsPlacer);
POSTFUNCTION(ObjectManager);
}
void TerrainPainter::initTerrainType()
{
if(zone.getType()==ETemplateZoneType::WATER)
{
//collect all water terrain types
std::vector<TerrainId> waterTerrains;
for(const auto & terrain : VLC->terrainTypeHandler->objects)
if(terrain->isWater())
waterTerrains.push_back(terrain->getId());
zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, zone.getRand()));
}
else
{
if(zone.isMatchTerrainToTown() && zone.getTownType() != ETownType::NEUTRAL)
{
auto terrainType = (*VLC->townh)[zone.getTownType()]->nativeTerrain;
if (terrainType <= ETerrainId::NONE)
{
logGlobal->warn("Town %s has invalid terrain type: %d", zone.getTownType(), terrainType);
zone.setTerrainType(ETerrainId::DIRT);
}
else
{
zone.setTerrainType(terrainType);
}
}
else
{
auto terrainTypes = zone.getTerrainTypes();
if (terrainTypes.empty())
{
logGlobal->warn("No terrain types found, falling back to DIRT");
zone.setTerrainType(ETerrainId::DIRT);
}
else
{
zone.setTerrainType(*RandomGeneratorUtil::nextItem(terrainTypes, zone.getRand()));
}
}
//Now, replace disallowed terrains on surface and in the underground
const auto & terrainType = VLC->terrainTypeHandler->getById(zone.getTerrainType());
if(zone.isUnderground())
{
if(!terrainType->isUnderground())
{
zone.setTerrainType(ETerrainId::SUBTERRANEAN);
}
}
else
{
if (!terrainType->isSurface())
{
zone.setTerrainType(ETerrainId::DIRT);
}
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -20,6 +20,8 @@ public:
void process() override;
void init() override;
void initTerrainType();
};
VCMI_LIB_NAMESPACE_END

View File

@ -10,20 +10,20 @@
#include "StdInc.h"
#include "TownPlacer.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "../spells/CSpellHandler.h" //for choosing random spells
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../../spells/CSpellHandler.h" //for choosing random spells
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "MinePlacer.h"
#include "WaterAdopter.h"
#include "TileInfo.h"
#include "../TileInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -52,7 +52,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
//set zone types to player faction, generate main town
logGlobal->info("Preparing playing zone");
int player_id = *zone.getOwner() - 1;
auto & playerInfo = map.map().players[player_id];
auto& playerInfo = map.getPlayer(player_id);
PlayerColor player(player_id);
if(playerInfo.canAnyonePlay())
{
@ -119,16 +119,16 @@ void TownPlacer::placeTowns(ObjectManager & manager)
if(!totalTowns) //if there's no town present, get random faction for dwellings and pandoras
{
//25% chance for neutral
if (generator.rand.nextInt(1, 100) <= 25)
if (zone.getRand().nextInt(1, 100) <= 25)
{
zone.setTownType(ETownType::NEUTRAL);
}
else
{
if(!zone.getTownTypes().empty())
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getTownTypes(), generator.rand));
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getTownTypes(), zone.getRand()));
else if(!zone.getMonsterTypes().empty())
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getMonsterTypes(), generator.rand)); //this happens in Clash of Dragons in treasure zones, where all towns are banned
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getMonsterTypes(), zone.getRand())); //this happens in Clash of Dragons in treasure zones, where all towns are banned
else //just in any case
zone.setTownType(getRandomTownType());
}
@ -140,11 +140,16 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
//towns are big objects and should be centered around visitable position
rmg::Object rmgObject(town);
rmgObject.setTemplate(zone.getTerrainType());
auto position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3 & t)
int3 position(-1, -1, -1);
{
float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number
}, ObjectManager::OptimizeType::WEIGHT);
Zone::Lock lock(zone.areaMutex);
position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3& t)
{
float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number
}, ObjectManager::OptimizeType::WEIGHT);
}
rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
manager.placeObject(rmgObject, false, true);
cleanupBoundaries(rmgObject);
@ -154,6 +159,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
{
Zone::Lock lock(zone.areaMutex);
for(const auto & t : rmgObject.getArea().getBorderOutside())
{
if(map.isOnMap(t))
@ -176,9 +182,9 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
if(!zone.areTownsSameType())
{
if(!zone.getTownTypes().empty())
subType = *RandomGeneratorUtil::nextItem(zone.getTownTypes(), generator.rand);
subType = *RandomGeneratorUtil::nextItem(zone.getTownTypes(), zone.getRand());
else
subType = *RandomGeneratorUtil::nextItem(zone.getDefaultTownTypes(), generator.rand); //it is possible to have zone with no towns allowed
subType = *RandomGeneratorUtil::nextItem(zone.getDefaultTownTypes(), zone.getRand()); //it is possible to have zone with no towns allowed
}
}
@ -229,7 +235,7 @@ si32 TownPlacer::getRandomTownType(bool matchUndergroundType)
townTypesAllowed = townTypesVerify;
}
return *RandomGeneratorUtil::nextItem(townTypesAllowed, generator.rand);
return *RandomGeneratorUtil::nextItem(townTypesAllowed, zone.getRand());
}
int TownPlacer::getTotalTowns() const

View File

@ -8,7 +8,7 @@
*
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -10,19 +10,21 @@
#include "StdInc.h"
#include "TreasurePlacer.h"
#include "CMapGenerator.h"
#include "Functions.h"
#include "../CMapGenerator.h"
#include "../Functions.h"
#include "ObjectManager.h"
#include "RoadPlacer.h"
#include "ConnectionsPlacer.h"
#include "RmgMap.h"
#include "TileInfo.h"
#include "../mapObjects/CommonConstructors.h"
#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
#include "../CCreatureHandler.h"
#include "../spells/CSpellHandler.h" //for choosing random spells
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../RmgMap.h"
#include "../TileInfo.h"
#include "../CZonePlacer.h"
#include "QuestArtifactPlacer.h"
#include "../../mapObjects/CommonConstructors.h"
#include "../../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
#include "../../CCreatureHandler.h"
#include "../../spells/CSpellHandler.h" //for choosing random spells
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -41,13 +43,9 @@ void TreasurePlacer::init()
POSTFUNCTION(RoadPlacer);
}
void TreasurePlacer::setQuestArtZone(Zone * otherZone)
{
questArtZone = otherZone;
}
void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
{
RecursiveLock lock(externalAccessMutex);
possibleObjects.push_back(oi);
}
@ -73,6 +71,7 @@ void TreasurePlacer::addAllPossibleObjects()
if (templates.empty())
continue;
//TODO: Reuse chooseRandomAppearance (eg. WoG treasure chests)
//Assume the template with fewest terrains is the most suitable
auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
{
@ -98,26 +97,28 @@ void TreasurePlacer::addAllPossibleObjects()
//prisons
//levels 1, 5, 10, 20, 30
static int prisonsLevels = std::min(generator.getConfig().prisonExperience.size(), generator.getConfig().prisonValues.size());
for(int i = 0; i < prisonsLevels; i++)
size_t prisonsLeft = getMaxPrisons();
for(int i = prisonsLevels - 1; i >= 0 ;i--)
{
oi.value = generator.getConfig().prisonValues[i];
if (oi.value > zone.getMaxTreasureValue())
{
continue;
}
oi.generateObject = [i, this]() -> CGObjectInstance *
{
std::vector<ui32> possibleHeroes;
for(int j = 0; j < map.map().allowedHeroes.size(); j++)
{
if(map.map().allowedHeroes[j])
possibleHeroes.push_back(j);
}
auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, generator.rand);
auto possibleHeroes = generator.getAllPossibleHeroes();
HeroTypeID hid = *RandomGeneratorUtil::nextItem(possibleHeroes, zone.getRand());
auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
auto * obj = dynamic_cast<CGHeroInstance *>(factory->create());
obj->subID = hid; //will be initialized later
obj->exp = generator.getConfig().prisonExperience[i];
obj->setOwner(PlayerColor::NEUTRAL);
map.map().allowedHeroes[hid] = false; //ban this hero
generator.decreasePrisonsRemaining();
generator.banHero(hid);
obj->appearance = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType()).front(); //can't init template with hero subID
return obj;
@ -125,7 +126,10 @@ void TreasurePlacer::addAllPossibleObjects()
oi.setTemplate(Obj::PRISON, 0, zone.getTerrainType());
oi.value = generator.getConfig().prisonValues[i];
oi.probability = 30;
oi.maxPerZone = generator.getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos.
//Distribute all allowed prisons, starting from the most valuable
oi.maxPerZone = (std::ceil((float)prisonsLeft / (i + 1)));
prisonsLeft -= oi.maxPerZone;
addObjectToRandomPool(oi);
}
@ -204,7 +208,7 @@ void TreasurePlacer::addAllPossibleObjects()
out.push_back(spell->id);
}
}
auto * a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, generator.rand));
auto * a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, zone.getRand()));
obj->storedArtifact = a;
return obj;
};
@ -318,7 +322,7 @@ void TreasurePlacer::addAllPossibleObjects()
spells.push_back(spell);
}
RandomGeneratorUtil::randomShuffle(spells, generator.rand);
RandomGeneratorUtil::randomShuffle(spells, zone.getRand());
for(int j = 0; j < std::min(12, static_cast<int>(spells.size())); j++)
{
obj->spells.push_back(spells[j]->id);
@ -347,7 +351,7 @@ void TreasurePlacer::addAllPossibleObjects()
spells.push_back(spell);
}
RandomGeneratorUtil::randomShuffle(spells, generator.rand);
RandomGeneratorUtil::randomShuffle(spells, zone.getRand());
for(int j = 0; j < std::min(15, static_cast<int>(spells.size())); j++)
{
obj->spells.push_back(spells[j]->id);
@ -375,7 +379,7 @@ void TreasurePlacer::addAllPossibleObjects()
spells.push_back(spell);
}
RandomGeneratorUtil::randomShuffle(spells, generator.rand);
RandomGeneratorUtil::randomShuffle(spells, zone.getRand());
for(int j = 0; j < std::min(60, static_cast<int>(spells.size())); j++)
{
obj->spells.push_back(spells[j]->id);
@ -388,44 +392,28 @@ void TreasurePlacer::addAllPossibleObjects()
oi.probability = 2;
addObjectToRandomPool(oi);
//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
//Seer huts with creatures or generic rewards
if(zone.getConnections().size()) //Unlikely, but...
{
static const int genericSeerHuts = 8;
int seerHutsPerType = 0;
const int questArtsRemaining = static_cast<int>(generator.getQuestArtsRemaning().size());
//general issue is that not many artifact types are available for quests
if(questArtsRemaining >= genericSeerHuts + static_cast<int>(creatures.size()))
auto * qap = zone.getModificator<QuestArtifactPlacer>();
if(!qap)
{
seerHutsPerType = questArtsRemaining / (genericSeerHuts + static_cast<int>(creatures.size()));
return; //TODO: throw?
}
else if(questArtsRemaining >= genericSeerHuts)
{
seerHutsPerType = 1;
}
oi.maxPerZone = seerHutsPerType;
RandomGeneratorUtil::randomShuffle(creatures, generator.rand);
const int questArtsRemaining = qap->getMaxQuestArtifactCount();
//Generate Seer Hut one by one. Duplicated oi possible and should work fine.
oi.maxPerZone = 1;
auto generateArtInfo = [this](const ArtifactID & id) -> ObjectInfo
{
ObjectInfo artInfo;
artInfo.probability = std::numeric_limits<ui16>::max(); //99,9% to spawn that art in first treasure pile
artInfo.maxPerZone = 1;
artInfo.value = 2000; //treasure art
artInfo.setTemplate(Obj::ARTIFACT, id, this->zone.getTerrainType());
artInfo.generateObject = [id]() -> CGObjectInstance *
{
auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, id);
return handler->create(handler->getTemplates().front());
};
return artInfo;
};
std::vector<ObjectInfo> possibleSeerHuts;
//14 creatures per town + 4 for each of gold / exp reward
possibleSeerHuts.reserve(14 + 4 + 4);
RandomGeneratorUtil::randomShuffle(creatures, zone.getRand());
for(int i = 0; i < std::min(static_cast<int>(creatures.size()), questArtsRemaining - genericSeerHuts); i++)
for(int i = 0; i < static_cast<int>(creatures.size()); i++)
{
auto * creature = creatures[i];
int creaturesAmount = creatureToCount(creature);
@ -433,9 +421,9 @@ void TreasurePlacer::addAllPossibleObjects()
if(!creaturesAmount)
continue;
int randomAppearance = chooseRandomAppearance(generator.rand, Obj::SEER_HUT, zone.getTerrainType());
int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
oi.generateObject = [creature, creaturesAmount, randomAppearance, this, qap]() -> CGObjectInstance *
{
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
@ -444,34 +432,46 @@ void TreasurePlacer::addAllPossibleObjects()
obj->rVal = creaturesAmount;
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
generator.banQuestArt(artid);
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
return obj;
};
oi.probability = 3;
oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) - 4000) / 3);
oi.probability = 3;
addObjectToRandomPool(oi);
if (oi.value > zone.getMaxTreasureValue())
{
continue;
}
else
{
possibleSeerHuts.push_back(oi);
}
}
static int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size());
for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar
{
int randomAppearance = chooseRandomAppearance(generator.rand, Obj::SEER_HUT, zone.getTerrainType());
int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
oi.value = generator.getConfig().questValues[i];
if (oi.value > zone.getMaxTreasureValue())
{
//Both variants have same value
continue;
}
oi.probability = 10;
oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance *
{
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
@ -481,21 +481,20 @@ void TreasurePlacer::addAllPossibleObjects()
obj->rVal = generator.getConfig().questRewardValues[i];
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
generator.banQuestArt(artid);
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
return obj;
};
addObjectToRandomPool(oi);
possibleSeerHuts.push_back(oi);
oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance *
{
auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
auto * obj = dynamic_cast<CGSeerHut *>(factory->create());
@ -504,28 +503,45 @@ void TreasurePlacer::addAllPossibleObjects()
obj->rVal = generator.getConfig().questRewardValues[i];
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand);
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
generator.banQuestArt(artid);
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
return obj;
};
addObjectToRandomPool(oi);
possibleSeerHuts.push_back(oi);
}
for (size_t i = 0; i < questArtsRemaining; i++)
{
addObjectToRandomPool(*RandomGeneratorUtil::nextItem(possibleSeerHuts, zone.getRand()));
}
}
}
size_t TreasurePlacer::getPossibleObjectsSize() const
{
RecursiveLock lock(externalAccessMutex);
return possibleObjects.size();
}
void TreasurePlacer::setMaxPrisons(size_t count)
{
RecursiveLock lock(externalAccessMutex);
maxPrisons = count;
}
size_t TreasurePlacer::getMaxPrisons() const
{
RecursiveLock lock(externalAccessMutex);
return maxPrisons;
}
bool TreasurePlacer::isGuardNeededForTreasure(int value)
{
return zone.getType() != ETemplateZoneType::WATER && value > minGuardedValue;
@ -537,7 +553,7 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
int maxValue = treasureInfo.max;
int minValue = treasureInfo.min;
const ui32 desiredValue = generator.rand.nextInt(minValue, maxValue);
const ui32 desiredValue =zone.getRand().nextInt(minValue, maxValue);
int currentValue = 0;
bool hasLargeObject = false;
@ -615,7 +631,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
bestPositions = accessibleArea.getTilesVector();
}
int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, generator.rand);
int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, zone.getRand());
instance.setPosition(nextPos - rmgObject.getPosition());
auto instanceAccessibleArea = instance.getAccessibleArea();
@ -670,7 +686,7 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
}
else
{
int r = generator.rand.nextInt(1, total);
int r = zone.getRand().nextInt(1, total);
auto sorter = [](const std::pair<ui32, ObjectInfo *> & rhs, const ui32 lhs) -> bool
{
return static_cast<int>(rhs.first) < lhs;
@ -756,6 +772,8 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
if(guarded)
guarded = manager.addGuard(rmgObject, value);
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
//TODO: Don't place
auto possibleArea = zone.areaPossible();
auto path = rmg::Path::invalid();
@ -763,13 +781,13 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
{
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile)
{
auto ti = map.getTile(tile);
auto ti = map.getTileInfo(tile);
if(ti.getNearestObjectDistance() < minDistance)
return -1.f;
for(const auto & t : rmgObject.getArea().getTilesVector())
{
if(map.getTile(t).getNearestObjectDistance() < minDistance)
if(map.getTileInfo(t).getNearestObjectDistance() < minDistance)
return -1.f;
}

View File

@ -1,5 +1,5 @@
/*
* TreasurePlacer.cpp, part of VCMI engine
* TreasurePlacer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -9,8 +9,8 @@
*/
#pragma once
#include "Zone.h"
#include "../mapObjects/ObjectTemplate.h"
#include "../Zone.h"
#include "../../mapObjects/ObjectTemplate.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -18,6 +18,7 @@ class CGObjectInstance;
class ObjectManager;
class RmgMap;
class CMapGenerator;
class CRandomGenerator;
struct ObjectInfo
{
@ -43,12 +44,12 @@ public:
char dump(const int3 &) override;
void createTreasures(ObjectManager & manager);
void setQuestArtZone(Zone * otherZone);
void addObjectToRandomPool(const ObjectInfo& oi);
void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects
size_t getPossibleObjectsSize() const;
void setMaxPrisons(size_t count);
size_t getMaxPrisons() const;
protected:
bool isGuardNeededForTreasure(int value);
@ -64,8 +65,8 @@ protected:
rmg::Area treasureArea;
rmg::Area treasureBlockArea;
rmg::Area guards;
Zone * questArtZone = nullptr; //artifacts required for Seer Huts will be placed here - or not if null
size_t maxPrisons;
};
VCMI_LIB_NAMESPACE_END

View File

@ -10,20 +10,20 @@
#include "StdInc.h"
#include "WaterAdopter.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "TreasurePlacer.h"
#include "TownPlacer.h"
#include "ConnectionsPlacer.h"
#include "TileInfo.h"
#include "../TileInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -35,7 +35,6 @@ void WaterAdopter::process()
void WaterAdopter::init()
{
//make dependencies
DEPENDENCY_ALL(WaterAdopter);
DEPENDENCY(TownPlacer);
POSTFUNCTION(ConnectionsPlacer);
POSTFUNCTION(TreasurePlacer);
@ -59,8 +58,8 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
if(waterContent == EWaterContent::NORMAL)
{
waterArea.unite(collectDistantTiles(zone, zone.getSize() - 1));
auto sliceStart = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], generator.rand);
auto sliceEnd = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], generator.rand);
auto sliceStart = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], zone.getRand());
auto sliceEnd = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], zone.getRand());
//at least 25% without water
bool endPassed = false;
@ -98,7 +97,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
const int coastLength = reverseDistanceMap[coastId].size() / (coastId + 3);
for(int coastIter = 0; coastIter < coastLength; ++coastIter)
{
int3 tile = *RandomGeneratorUtil::nextItem(reverseDistanceMap[coastId], generator.rand);
int3 tile = *RandomGeneratorUtil::nextItem(reverseDistanceMap[coastId], zone.getRand());
if(tilesChecked.find(tile) != tilesChecked.end())
continue;
@ -224,7 +223,11 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
}
}
map.getZones()[waterZoneId]->area().unite(waterArea);
{
Zone::Lock waterLock(map.getZones()[waterZoneId]->areaMutex);
map.getZones()[waterZoneId]->area().unite(waterArea);
}
Zone::Lock lock(zone.areaMutex);
zone.area().subtract(waterArea);
zone.areaPossible().subtract(waterArea);
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -10,23 +10,23 @@
#include "StdInc.h"
#include "WaterProxy.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../TerrainHandler.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../TerrainHandler.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "TreasurePlacer.h"
#include "TownPlacer.h"
#include "ConnectionsPlacer.h"
#include "TileInfo.h"
#include "../TileInfo.h"
#include "WaterAdopter.h"
#include "RmgArea.h"
#include "../RmgArea.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -38,7 +38,8 @@ void WaterProxy::process()
map.setOccupied(t, ETileType::POSSIBLE);
}
paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType());
auto v = zone.getArea().getTilesVector();
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
//check terrain type
for([[maybe_unused]] const auto & t : zone.area().getTilesVector())
@ -52,9 +53,10 @@ void WaterProxy::process()
if(z.second->getId() == zone.getId())
continue;
Zone::Lock lock(z.second->areaMutex);
for(const auto & t : z.second->area().getTilesVector())
{
if(map.map().getTile(t).terType->getId() == zone.getTerrainType())
if(map.getTile(t).terType->getId() == zone.getTerrainType())
{
z.second->areaPossible().erase(t);
z.second->area().erase(t);
@ -90,11 +92,13 @@ void WaterProxy::init()
const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const
{
RecursiveLock lock(externalAccessMutex);
return lakes;
}
void WaterProxy::collectLakes()
{
RecursiveLock lock(externalAccessMutex);
int lakeId = 0;
for(const auto & lake : connectedAreas(zone.getArea(), true))
{
@ -138,6 +142,8 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
if(map.isPossible(ct))
map.setOccupied(ct, ETileType::BLOCKED);
}
Zone::Lock lock(dst.areaMutex);
dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]);
continue;
}
@ -225,7 +231,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
if(sailingBoatTypes.empty())
return false;
auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, generator.rand))->create());
auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
rmg::Object rmgObject(*boat);
rmgObject.setTemplate(zone.getTerrainType());
@ -236,7 +242,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
auto boardingPositions = coast.getSubarea([&waterAvailable, this](const int3 & tile) //tiles where boarding is possible
{
//We don't want place boat right to any land object, especiallly the zone guard
if (map.getTile(tile).getNearestObjectDistance() <= 3)
if (map.getTileInfo(tile).getNearestObjectDistance() <= 3)
return false;
rmg::Area a({tile});
@ -288,7 +294,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route
if(!manager)
return false;
int subtype = chooseRandomAppearance(generator.rand, Obj::SHIPYARD, land.getTerrainType());
int subtype = chooseRandomAppearance(zone.getRand(), Obj::SHIPYARD, land.getTerrainType());
auto * shipyard = dynamic_cast<CGShipyard *>(VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create());
shipyard->tempOwner = PlayerColor::NEUTRAL;

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -11,22 +11,22 @@
#include "StdInc.h"
#include "WaterRoutes.h"
#include "WaterProxy.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "../mapping/CMapEditManager.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "RmgPath.h"
#include "RmgObject.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../mapObjects/CObjectClassesHandler.h"
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "Functions.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "TreasurePlacer.h"
#include "TownPlacer.h"
#include "ConnectionsPlacer.h"
#include "TileInfo.h"
#include "../TileInfo.h"
#include "WaterAdopter.h"
#include "RmgArea.h"
#include "../RmgArea.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -44,7 +44,9 @@ void WaterRoutes::process()
if(z.first != zone.getId())
result.push_back(wproxy->waterRoute(*z.second));
}
Zone::Lock lock(zone.areaMutex);
//prohibit to place objects on sealed off lakes
for(const auto & lake : wproxy->getLakes())
{

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "Zone.h"
#include "../Zone.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -0,0 +1,91 @@
/*
* BlockingQueue.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "StdInc.h"
VCMI_LIB_NAMESPACE_BEGIN
//Credit to https://github.com/Liam0205/toy-threadpool/tree/master/yuuki
template <typename T>
class DLL_LINKAGE BlockingQueue : protected std::queue<T>
{
using WriteLock = boost::unique_lock<boost::shared_mutex>;
using Readlock = boost::shared_lock<boost::shared_mutex>;
public:
BlockingQueue() = default;
~BlockingQueue()
{
clear();
}
BlockingQueue(const BlockingQueue&) = delete;
BlockingQueue(BlockingQueue&&) = delete;
BlockingQueue& operator=(const BlockingQueue&) = delete;
BlockingQueue& operator=(BlockingQueue&&) = delete;
public:
bool empty() const
{
Readlock lock(mx);
return std::queue<T>::empty();
}
size_t size() const
{
Readlock lock(mx);
return std::queue<T>::size();
}
public:
void clear()
{
WriteLock lock(mx);
while (!std::queue<T>::empty())
{
std::queue<T>::pop();
}
}
void push(const T& obj)
{
WriteLock lock(mx);
std::queue<T>::push(obj);
}
template <typename... Args>
void emplace(Args&&... args)
{
WriteLock lock(mx);
std::queue<T>::emplace(std::forward<Args>(args)...);
}
bool pop(T& holder)
{
WriteLock lock(mx);
if (std::queue<T>::empty())
{
return false;
}
else
{
holder = std::move(std::queue<T>::front());
std::queue<T>::pop();
return true;
}
}
private:
mutable boost::shared_mutex mx;
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,54 @@
/*
* MapProxy.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 "MapProxy.h"
#include "../../TerrainHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
MapProxy::MapProxy(RmgMap & map):
map(map)
{
}
void MapProxy::insertObject(CGObjectInstance * obj)
{
Lock lock(mx);
map.getEditManager()->insertObject(obj);
}
void MapProxy::insertObjects(std::set<CGObjectInstance*>& objects)
{
Lock lock(mx);
map.getEditManager()->insertObjects(objects);
}
void MapProxy::drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain)
{
Lock lock(mx);
map.getEditManager()->getTerrainSelection().setSelection(tiles);
map.getEditManager()->drawTerrain(terrain, &generator);
}
void MapProxy::drawRivers(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain)
{
Lock lock(mx);
map.getEditManager()->getTerrainSelection().setSelection(tiles);
map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(terrain)->river, &generator);
}
void MapProxy::drawRoads(CRandomGenerator & generator, std::vector<int3> & tiles, RoadId roadType)
{
Lock lock(mx);
map.getEditManager()->getTerrainSelection().setSelection(tiles);
map.getEditManager()->drawRoad(roadType, &generator);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,41 @@
/*
* MapProxy.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "StdInc.h"
#include "../../mapping/CMap.h"
#include "../RmgMap.h"
#include "../../mapping/CMapEditManager.h"
VCMI_LIB_NAMESPACE_BEGIN
class RmgMap;
class MapProxy
{
public:
MapProxy(RmgMap & map);
void insertObject(CGObjectInstance * obj);
void insertObjects(std::set<CGObjectInstance*>& objects);
void drawTerrain(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);
void drawRivers(CRandomGenerator & generator, std::vector<int3> & tiles, TerrainId terrain);
void drawRoads(CRandomGenerator & generator, std::vector<int3> & tiles, RoadId roadType);
private:
mutable boost::shared_mutex mx;
using Lock = boost::unique_lock<boost::shared_mutex>;
RmgMap & map;
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,192 @@
/*
* ThreadPool.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "BlockingQueue.h"
#include <boost/thread/future.hpp>
#include <boost/thread/condition_variable.hpp>
VCMI_LIB_NAMESPACE_BEGIN
typedef std::function<void()> TRMGfunction ;
typedef std::optional<TRMGfunction> TRMGJob;
//Credit to https://github.com/Liam0205/toy-threadpool/tree/master/yuuki
class DLL_LINKAGE ThreadPool
{
private:
using Lock = boost::unique_lock<boost::shared_mutex>;
mutable boost::shared_mutex mx;
mutable boost::condition_variable_any cv;
mutable boost::once_flag once;
bool isInitialized = false;
bool stopping = false;
bool canceling = false;
public:
ThreadPool();
~ThreadPool();
void init(size_t numThreads);
void spawn();
void terminate();
void cancel();
public:
bool initialized() const;
bool running() const;
int size() const;
private:
bool isRunning() const;
public:
auto async(std::function<void()>&& f) const -> boost::future<void>;
private:
std::vector<boost::thread> workers;
mutable BlockingQueue<TRMGfunction> tasks;
};
ThreadPool::ThreadPool() :
once(BOOST_ONCE_INIT)
{};
ThreadPool::~ThreadPool()
{
terminate();
}
inline void ThreadPool::init(size_t numThreads)
{
boost::call_once(once, [this, numThreads]()
{
Lock lock(mx);
stopping = false;
canceling = false;
workers.reserve(numThreads);
for (size_t i = 0; i < numThreads; ++i)
{
workers.emplace_back(std::bind(&ThreadPool::spawn, this));
}
isInitialized = true;
});
}
bool ThreadPool::isRunning() const
{
return isInitialized && !stopping && !canceling;
}
inline bool ThreadPool::initialized() const
{
Lock lock(mx);
return isInitialized;
}
inline bool ThreadPool::running() const
{
Lock lock(mx);
return isRunning();
}
inline int ThreadPool::size() const
{
Lock lock(mx);
return workers.size();
}
inline void ThreadPool::spawn()
{
while(true)
{
bool pop = false;
TRMGfunction task;
{
Lock lock(mx);
cv.wait(lock, [this, &pop, &task]
{
pop = tasks.pop(task);
return canceling || stopping || pop;
});
}
if (canceling || (stopping && !pop))
{
return;
}
task();
}
}
inline void ThreadPool::terminate()
{
{
Lock lock(mx);
if (isRunning())
{
stopping = true;
}
else
{
return;
}
}
cv.notify_all();
for (auto& worker : workers)
{
worker.join();
}
}
inline void ThreadPool::cancel()
{
{
Lock lock(mx);
if (running())
{
canceling = true;
}
else
{
return;
}
}
tasks.clear();
cv.notify_all();
for (auto& worker : workers)
{
worker.join();
}
}
auto ThreadPool::async(std::function<void()>&& f) const -> boost::future<void>
{
using TaskT = boost::packaged_task<void>;
{
Lock lock(mx);
if (stopping || canceling)
{
throw std::runtime_error("Delegating task to a threadpool that has been terminated or canceled.");
}
}
std::shared_ptr<TaskT> task = std::make_shared<TaskT>(f);
boost::future<void> fut = task->get_future();
tasks.emplace([task]() -> void
{
(*task)();
});
cv.notify_one();
return fut;
}
VCMI_LIB_NAMESPACE_END

View File

@ -16,7 +16,6 @@
#include "../lib/mapping/CMapEditManager.h"
#include "../lib/TerrainHandler.h"
#include "../lib/mapObjects/CObjectClassesHandler.h"
#include "../lib/rmg/ObstaclePlacer.h"
#include "../lib/CSkillHandler.h"
#include "../lib/spells/CSpellHandler.h"
#include "../lib/CHeroHandler.h"
@ -26,7 +25,7 @@
#include "maphandler.h"
#include "mainwindow.h"
#include "inspector/inspector.h"
#include "VCMI_Lib.h"
MapController::MapController(MainWindow * m): main(m)
{
@ -217,6 +216,18 @@ void MapController::setMap(std::unique_ptr<CMap> cmap)
}
);
_map->getEditManager()->getUndoManager().clearAll();
initObstaclePainters(_map.get());
}
void MapController::initObstaclePainters(CMap * map)
{
for (auto terrain : VLC->terrainTypeHandler->objects)
{
auto terrainId = terrain->getId();
_obstaclePainters[terrainId] = std::make_unique<EditorObstaclePlacer>(map);
_obstaclePainters[terrainId]->collectPossibleObstacles(terrainId);
}
}
void MapController::sceneForceUpdate()
@ -397,20 +408,24 @@ void MapController::commitObstacleFill(int level)
return;
//split by zones
std::map<TerrainId, ObstacleProxy> terrainSelected;
for (auto & painter : _obstaclePainters)
{
painter.second->clearBlockedArea();
}
for(auto & t : selection)
{
auto tl = _map->getTile(t);
if(tl.blocked || tl.visitable)
continue;
terrainSelected[tl.terType->getId()].blockedArea.add(t);
auto terrain = tl.terType->getId();
_obstaclePainters[terrain]->addBlockedTile(t);
}
for(auto & sel : terrainSelected)
for(auto & sel : _obstaclePainters)
{
sel.second.collectPossibleObstacles(sel.first);
sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault());
sel.second->placeObstacles(CRandomGenerator::getDefault());
}
_mapHandler->invalidateObjects();

View File

@ -13,6 +13,7 @@
#include "maphandler.h"
#include "mapview.h"
#include "../lib/mapping/CMap.h"
#include "../lib/rmg/modificators/ObstaclePlacer.h"
class MainWindow;
class MapController
@ -24,6 +25,7 @@ public:
~MapController();
void setMap(std::unique_ptr<CMap>);
void initObstaclePainters(CMap* map);
void repairMap();
@ -72,5 +74,7 @@ private:
std::vector<std::unique_ptr<CGObjectInstance>> _clipboard;
int _clipboardShiftIndex = 0;
std::map<TerrainId, std::unique_ptr<EditorObstaclePlacer>> _obstaclePainters;
void connectScenes();
};