1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +02:00

Parallel RMG works fine for maps without water.

This commit is contained in:
Tomasz Zieliński
2023-05-19 20:30:15 +02:00
parent 19010dd834
commit 73d9f5bd0a
36 changed files with 521 additions and 411 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/MapFormatH3M.cpp
${MAIN_LIB_DIR}/mapping/MapReaderH3M.cpp ${MAIN_LIB_DIR}/mapping/MapReaderH3M.cpp
${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp ${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp
${MAIN_LIB_DIR}/mapping/ObstacleProxy.cpp
${MAIN_LIB_DIR}/registerTypes/RegisterTypes.cpp ${MAIN_LIB_DIR}/registerTypes/RegisterTypes.cpp
${MAIN_LIB_DIR}/registerTypes/TypesClientPacks1.cpp ${MAIN_LIB_DIR}/registerTypes/TypesClientPacks1.cpp
@ -135,9 +136,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/rmg/WaterProxy.cpp ${MAIN_LIB_DIR}/rmg/WaterProxy.cpp
${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp ${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp
${MAIN_LIB_DIR}/rmg/RockPlacer.cpp ${MAIN_LIB_DIR}/rmg/RockPlacer.cpp
${MAIN_LIB_DIR}/rmg/RockFiller.cpp
${MAIN_LIB_DIR}/rmg/ObstaclePlacer.cpp ${MAIN_LIB_DIR}/rmg/ObstaclePlacer.cpp
${MAIN_LIB_DIR}/rmg/RiverPlacer.cpp ${MAIN_LIB_DIR}/rmg/RiverPlacer.cpp
${MAIN_LIB_DIR}/rmg/TerrainPainter.cpp ${MAIN_LIB_DIR}/rmg/TerrainPainter.cpp
${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.cpp
${MAIN_LIB_DIR}/serializer/BinaryDeserializer.cpp ${MAIN_LIB_DIR}/serializer/BinaryDeserializer.cpp
${MAIN_LIB_DIR}/serializer/BinarySerializer.cpp ${MAIN_LIB_DIR}/serializer/BinarySerializer.cpp
@ -387,6 +390,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/mapping/MapFormatH3M.h ${MAIN_LIB_DIR}/mapping/MapFormatH3M.h
${MAIN_LIB_DIR}/mapping/MapReaderH3M.h ${MAIN_LIB_DIR}/mapping/MapReaderH3M.h
${MAIN_LIB_DIR}/mapping/MapFormatJson.h ${MAIN_LIB_DIR}/mapping/MapFormatJson.h
${MAIN_LIB_DIR}/mapping/ObstacleProxy.h
${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h ${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h
@ -421,13 +425,14 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/rmg/WaterProxy.h ${MAIN_LIB_DIR}/rmg/WaterProxy.h
${MAIN_LIB_DIR}/rmg/WaterRoutes.h ${MAIN_LIB_DIR}/rmg/WaterRoutes.h
${MAIN_LIB_DIR}/rmg/RockPlacer.h ${MAIN_LIB_DIR}/rmg/RockPlacer.h
${MAIN_LIB_DIR}/rmg/RockFiller.h
${MAIN_LIB_DIR}/rmg/ObstaclePlacer.h ${MAIN_LIB_DIR}/rmg/ObstaclePlacer.h
${MAIN_LIB_DIR}/rmg/RiverPlacer.h ${MAIN_LIB_DIR}/rmg/RiverPlacer.h
${MAIN_LIB_DIR}/rmg/TerrainPainter.h ${MAIN_LIB_DIR}/rmg/TerrainPainter.h
${MAIN_LIB_DIR}/rmg/float3.h ${MAIN_LIB_DIR}/rmg/float3.h
${MAIN_LIB_DIR}/rmg/threadpool/BlockingQueue.h ${MAIN_LIB_DIR}/rmg/threadpool/BlockingQueue.h
${MAIN_LIB_DIR}/rmg/threadpool/ThreadPool.h ${MAIN_LIB_DIR}/rmg/threadpool/ThreadPool.h
${MAIN_LIB_DIR}/rmg/threadpool/JobProvider.h ${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.h
${MAIN_LIB_DIR}/serializer/BinaryDeserializer.h ${MAIN_LIB_DIR}/serializer/BinaryDeserializer.h
${MAIN_LIB_DIR}/serializer/BinarySerializer.h ${MAIN_LIB_DIR}/serializer/BinarySerializer.h

View File

@ -101,7 +101,7 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const
void CMapGenerator::initPrisonsRemaining() void CMapGenerator::initPrisonsRemaining()
{ {
allowedPrisons = 0; allowedPrisons = 0;
for (auto isAllowed : map->map().allowedHeroes) for (auto isAllowed : map->getMap(this).allowedHeroes)
{ {
if (isAllowed) if (isAllowed)
allowedPrisons++; allowedPrisons++;
@ -133,7 +133,7 @@ std::unique_ptr<CMap> CMapGenerator::generate()
initQuestArtsRemaining(); initQuestArtsRemaining();
genZones(); genZones();
Load::Progress::step(); 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(); map->addModificators();
Load::Progress::step(3); Load::Progress::step(3);
fillZones(); fillZones();
@ -164,7 +164,7 @@ std::string CMapGenerator::getMapDescription() const
std::stringstream ss; std::stringstream ss;
ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") + ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() % ", 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()] % static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
monsterStrengthStr[monsterStrengthIndex]); monsterStrengthStr[monsterStrengthIndex]);
@ -263,10 +263,10 @@ void CMapGenerator::addPlayerInfo()
teamNumbers[j].erase(itTeam); teamNumbers[j].erase(itTeam);
} }
teamsTotal.insert(player.team.getNum()); 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() void CMapGenerator::genZones()
@ -374,7 +374,7 @@ void CMapGenerator::fillZones()
} }
auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand); 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"); logGlobal->info("Zones filled successfully");
@ -383,13 +383,14 @@ void CMapGenerator::fillZones()
void CMapGenerator::addHeaderInfo() void CMapGenerator::addHeaderInfo()
{ {
map->map().version = EMapFormat::VCMI; auto& m = map->getMap(this);
map->map().width = mapGenOptions.getWidth(); m.version = EMapFormat::VCMI;
map->map().height = mapGenOptions.getHeight(); m.width = mapGenOptions.getWidth();
map->map().twoLevel = mapGenOptions.getHasTwoLevels(); m.height = mapGenOptions.getHeight();
map->map().name = VLC->generaltexth->allTexts[740]; m.twoLevel = mapGenOptions.getHasTwoLevels();
map->map().description = getMapDescription(); m.name = VLC->generaltexth->allTexts[740];
map->map().difficulty = 1; m.description = getMapDescription();
m.difficulty = 1;
addPlayerInfo(); addPlayerInfo();
} }
@ -434,9 +435,9 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
{ {
//Skip heroes that were banned, including the ones placed in prisons //Skip heroes that were banned, including the ones placed in prisons
std::vector<HeroTypeID> ret; std::vector<HeroTypeID> ret;
for (int j = 0; j < map->map().allowedHeroes.size(); j++) for (int j = 0; j < map->getMap(this).allowedHeroes.size(); j++)
{ {
if (map->map().allowedHeroes[j]) if (map->getMap(this).allowedHeroes[j])
ret.push_back(HeroTypeID(j)); ret.push_back(HeroTypeID(j));
} }
return ret; return ret;
@ -445,13 +446,13 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
void CMapGenerator::banQuestArt(const ArtifactID & id) void CMapGenerator::banQuestArt(const ArtifactID & id)
{ {
//TODO: Protect with mutex //TODO: Protect with mutex
map->map().allowedArtifact[id] = false; map->getMap(this).allowedArtifact[id] = false;
} }
void CMapGenerator::banHero(const HeroTypeID & id) void CMapGenerator::banHero(const HeroTypeID & id)
{ {
//TODO: Protect with mutex //TODO: Protect with mutex
map->map().allowedHeroes[id] = false; map->getMap(this).allowedHeroes[id] = false;
} }
Zone * CMapGenerator::getZoneWater() const Zone * CMapGenerator::getZoneWater() const

View File

@ -15,7 +15,6 @@
#include "CMapGenOptions.h" #include "CMapGenOptions.h"
#include "../int3.h" #include "../int3.h"
#include "CRmgTemplate.h" #include "CRmgTemplate.h"
#include "threadpool/JobProvider.h"
#include "../LoadProgress.h" #include "../LoadProgress.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN

View File

@ -38,7 +38,7 @@ CZonePlacer::CZonePlacer(RmgMap & map)
int3 CZonePlacer::cords(const float3 & f) const 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 float CZonePlacer::getDistance (float distance) const
@ -224,8 +224,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
auto zoneType = zone->getType(); auto zoneType = zone->getType();
auto existingZoneType = existingZone->getType(); auto existingZoneType = existingZone->getType();
if ((zoneType == ETemplateZoneType::PLAYER_START || zoneType == ETemplateZoneType::CPU_START) && if (zone->getOwner() && existingZone->getOwner()) //Players participate in game
(existingZoneType == ETemplateZoneType::PLAYER_START || existingZoneType == ETemplateZoneType::CPU_START))
{ {
int firstPlayer = zone->getOwner().value(); int firstPlayer = zone->getOwner().value();
int secondPlayer = existingZone->getOwner().value(); int secondPlayer = existingZone->getOwner().value();
@ -785,7 +784,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
zone->setPos(int3(total.x / size, total.y / size, total.z / size)); 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 1. Create Voronoi diagram
@ -863,7 +862,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
//make sure that terrain inside zone is not a rock //make sure that terrain inside zone is not a rock
//FIXME: reorder actions? //FIXME: reorder actions?
paintZoneTerrain(*zone.second, *rand, map, ETerrainId::SUBTERRANEAN); paintZoneTerrain(*zone.second, *rand, map.getMapProxy(), ETerrainId::SUBTERRANEAN);
} }
} }
logGlobal->info("Finished zone colouring"); logGlobal->info("Finished zone colouring");

View File

@ -25,6 +25,7 @@
#include "WaterAdopter.h" #include "WaterAdopter.h"
#include "WaterProxy.h" #include "WaterProxy.h"
#include "TownPlacer.h" #include "TownPlacer.h"
#include <boost/interprocess/sync/scoped_lock.hpp>
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -101,7 +102,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
guardPos = zone.areaPossible().nearest(borderPos); guardPos = zone.areaPossible().nearest(borderPos);
assert(borderPos != guardPos); 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 if (dist >= 3) //Don't place guards at adjacent tiles
{ {
@ -228,11 +229,12 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true); bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
int minDist = 3; int minDist = 3;
std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
rmg::Path path2(otherZone->area()); rmg::Path path2(otherZone->area());
rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile) rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
{ {
auto ti = map.getTile(tile); auto ti = map.getTileInfo(tile);
auto otherTi = map.getTile(tile - zShift); auto otherTi = map.getTileInfo(tile - zShift);
float dist = ti.getNearestObjectDistance(); float dist = ti.getNearestObjectDistance();
float otherDist = otherTi.getNearestObjectDistance(); float otherDist = otherTi.getNearestObjectDistance();
if(dist < minDist || otherDist < minDist) if(dist < minDist || otherDist < minDist)

View File

@ -51,6 +51,7 @@ void createBorder(RmgMap & gen, Zone & zone)
return gen.isOnMap(tile) && gen.getZones()[gen.getZoneID(tile)]->getType() != ETemplateZoneType::WATER; return gen.isOnMap(tile) && gen.getZones()[gen.getZoneID(tile)]->getType() != ETemplateZoneType::WATER;
}); });
Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
for(const auto & tile : blockBorder.getTilesVector()) for(const auto & tile : blockBorder.getTilesVector())
{ {
if(gen.isPossible(tile)) if(gen.isPossible(tile))
@ -70,11 +71,10 @@ void createBorder(RmgMap & gen, Zone & zone)
} }
} }
void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrain) void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, std::shared_ptr<MapProxy> mapProxy, TerrainId terrain)
{ {
auto v = zone.getArea().getTilesVector(); auto v = zone.getArea().getTilesVector();
map.getEditManager()->getTerrainSelection().setSelection(v); mapProxy->drawTerrain(generator, v, terrain);
map.getEditManager()->drawTerrain(terrain, &generator);
} }
int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain) int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain)
@ -153,15 +153,15 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator) void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator)
{ {
if(map.map().twoLevel) if(map.levels())
{ {
//finally mark rock tiles as occupied, spawn no obstacles there //finally mark rock tiles as occupied, spawn no obstacles there
for(int x = 0; x < map.map().width; x++) for(int x = 0; x < map.width(); x++)
{ {
for(int y = 0; y < map.map().height; y++) for(int y = 0; y < map.height(); y++)
{ {
int3 tile(x, y, 1); int3 tile(x, y, 1);
if(!map.map().getTile(tile).terType->isPassable()) if(!map.getTile(tile).terType->isPassable())
{ {
map.setOccupied(tile, ETileType::USED); map.setOccupied(tile, ETileType::USED);
} }

View File

@ -43,7 +43,7 @@ rmg::Tileset collectDistantTiles(const Zone & zone, int distance);
void createBorder(RmgMap & gen, Zone & zone); void createBorder(RmgMap & gen, Zone & zone);
void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, TerrainId terrainType); void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, std::shared_ptr<MapProxy> mapProxy, TerrainId terrainType);
void initTerrainType(Zone & zone, CMapGenerator & gen); void initTerrainType(Zone & zone, CMapGenerator & gen);

View File

@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator) Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator)
{ {
mapProxy = map.getMapProxy();
} }
void Modificator::setName(const std::string & n) void Modificator::setName(const std::string & n)
@ -45,18 +46,16 @@ bool Modificator::isReady()
{ {
if ((*it)->isFinished()) //OK if ((*it)->isFinished()) //OK
{ {
//This preceeder won't be checked in the future
it = preceeders.erase(it); it = preceeders.erase(it);
} }
else if (!(*it)->isReady())
{
return false;
}
else else
{ {
++it; return false;
} }
} }
//If a job is finished, it should be already erased from a queue
return !finished; return !finished;
} }
} }
@ -102,6 +101,7 @@ void Modificator::dependency(Modificator * modificator)
{ {
if(modificator && modificator != this) if(modificator && modificator != this)
{ {
//TODO: use vstd::contains
if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end()) if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end())
preceeders.push_back(modificator); preceeders.push_back(modificator);
} }
@ -119,10 +119,9 @@ void Modificator::postfunction(Modificator * modificator)
void Modificator::dump() void Modificator::dump()
{ {
std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName())); 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 = map.levels();
int levels = mapInstance.levels(); int width = map.width();
int width = mapInstance.width; int height = map.height();
int height = mapInstance.height;
for(int z = 0; z < levels; z++) for(int z = 0; z < levels; z++)
{ {
for(int j=0; j<height; j++) for(int j=0; j<height; j++)

View File

@ -12,14 +12,13 @@
#include "../GameConstants.h" #include "../GameConstants.h"
#include "../int3.h" #include "../int3.h"
#include "threadpool/JobProvider.h"
#include "Zone.h" #include "Zone.h"
#include "threadpool/MapProxy.h"
class RmgMap; class RmgMap;
class CMapGenerator; class CMapGenerator;
class Zone; class Zone;
class MapProxy;
#define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);} #define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);}
#define DEPENDENCY(x) dependency(zone.getModificator<x>()); #define DEPENDENCY(x) dependency(zone.getModificator<x>());
@ -57,14 +56,38 @@ public:
protected: protected:
RmgMap & map; RmgMap & map;
std::shared_ptr<MapProxy> mapProxy;
CMapGenerator & generator; CMapGenerator & generator;
Zone & zone; Zone & zone;
bool finished = false; bool finished = false;
mutable boost::shared_mutex externalAccessMutex; //Used to communicate between Modificators 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>; using Lock = boost::unique_lock<boost::shared_mutex>;
template <typename TModificator>
std::vector<RecursiveLock> tryLockAll()
{
std::vector<RecursiveLock> locks;
for (auto & zone : map.getZones())
{
if (auto * m = zone.second->getModificator<TModificator>())
{
RecursiveLock lock(m->externalAccessMutex, boost::try_to_lock_t{});
if (lock.owns_lock())
{
locks.emplace_back(std::move(lock));
}
else //return empty
{
return std::vector<RecursiveLock>();
}
}
}
return locks;
}
private: private:
virtual void process() = 0; virtual void process() = 0;

View File

@ -27,18 +27,28 @@ VCMI_LIB_NAMESPACE_BEGIN
void ObjectDistributor::process() void ObjectDistributor::process()
{ {
//Firts call will add objects to ALL zones, once they were added skip it //Do that only once
if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0) auto lockVec = tryLockAll<ObjectDistributor>();
if (!lockVec.empty())
{ {
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<ObjectDistributor>())
{
if(m->isFinished())
return;
}
}
distributeLimitedObjects(); distributeLimitedObjects();
distributeSeerHuts(); distributeSeerHuts();
finished = true;
} }
} }
void ObjectDistributor::init() void ObjectDistributor::init()
{ {
DEPENDENCY(TownPlacer); //All of the terrain types need to be determined
DEPENDENCY(TerrainPainter); DEPENDENCY_ALL(TerrainPainter);
POSTFUNCTION(TreasurePlacer); POSTFUNCTION(TreasurePlacer);
} }
@ -84,6 +94,8 @@ void ObjectDistributor::distributeLimitedObjects()
//We already know there are some templates //We already know there are some templates
auto templates = handler->getTemplates(zone->getTerrainType()); 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 //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 auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
{ {

View File

@ -16,6 +16,9 @@
#include "RoadPlacer.h" #include "RoadPlacer.h"
#include "RiverPlacer.h" #include "RiverPlacer.h"
#include "WaterAdopter.h" #include "WaterAdopter.h"
#include "ConnectionsPlacer.h"
#include "TownPlacer.h"
#include "MinePlacer.h"
#include "TreasurePlacer.h" #include "TreasurePlacer.h"
#include "QuestArtifactPlacer.h" #include "QuestArtifactPlacer.h"
#include "../CCreatureHandler.h" #include "../CCreatureHandler.h"
@ -37,13 +40,16 @@ void ObjectManager::process()
void ObjectManager::init() void ObjectManager::init()
{ {
DEPENDENCY(WaterAdopter); DEPENDENCY(WaterAdopter);
DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
DEPENDENCY(TownPlacer); //Only secondary towns
DEPENDENCY(MinePlacer);
POSTFUNCTION(RoadPlacer); POSTFUNCTION(RoadPlacer);
createDistancesPriorityQueue(); createDistancesPriorityQueue();
} }
void ObjectManager::createDistancesPriorityQueue() void ObjectManager::createDistancesPriorityQueue()
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear(); tilesByDistance.clear();
for(const auto & tile : zone.areaPossible().getTilesVector()) for(const auto & tile : zone.areaPossible().getTilesVector())
{ {
@ -53,25 +59,25 @@ void ObjectManager::createDistancesPriorityQueue()
void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength) void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
requiredObjects.emplace_back(obj, strength); requiredObjects.emplace_back(obj, strength);
} }
void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength) void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
closeObjects.emplace_back(obj, strength); closeObjects.emplace_back(obj, strength);
} }
void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget) void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
nearbyObjects.emplace_back(obj, nearbyTarget); nearbyObjects.emplace_back(obj, nearbyTarget);
} }
void ObjectManager::updateDistances(const rmg::Object & obj) void ObjectManager::updateDistances(const rmg::Object & obj)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear(); tilesByDistance.clear();
for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
{ {
@ -83,15 +89,15 @@ void ObjectManager::updateDistances(const rmg::Object & obj)
const rmg::Area & ObjectManager::getVisitableArea() const const rmg::Area & ObjectManager::getVisitableArea() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return objectsVisitableArea; return objectsVisitableArea;
} }
std::vector<CGObjectInstance*> ObjectManager::getMines() const std::vector<CGObjectInstance*> ObjectManager::getMines() const
{ {
Lock lock(externalAccessMutex);
std::vector<CGObjectInstance*> mines; std::vector<CGObjectInstance*> mines;
RecursiveLock lock(externalAccessMutex);
for(auto * object : objects) for(auto * object : objects)
{ {
if (object->ID == Obj::MINE) if (object->ID == Obj::MINE)
@ -161,6 +167,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
} }
} }
//FIXME: Race condition for tiles? For Area?
if(result.valid()) if(result.valid())
obj.setPosition(result); obj.setPosition(result);
return result; return result;
@ -170,14 +177,14 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
{ {
return findPlaceForObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile) 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(); float dist = ti.getNearestObjectDistance();
if(dist < min_dist) if(dist < min_dist)
return -1.f; return -1.f;
for(const auto & t : obj.getArea().getTilesVector()) for(const auto & t : obj.getArea().getTilesVector())
{ {
if(map.getTile(t).getNearestObjectDistance() < min_dist) if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
return -1.f; return -1.f;
} }
@ -189,14 +196,14 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
{ {
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile) 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(); float dist = ti.getNearestObjectDistance();
if(dist < min_dist) if(dist < min_dist)
return -1.f; return -1.f;
for(const auto & t : obj.getArea().getTilesVector()) for(const auto & t : obj.getArea().getTilesVector())
{ {
if(map.getTile(t).getNearestObjectDistance() < min_dist) if(map.getTileInfo(t).getNearestObjectDistance() < min_dist)
return -1.f; return -1.f;
} }
@ -252,12 +259,16 @@ bool ObjectManager::createRequiredObjects()
{ {
logGlobal->trace("Creating required objects"); logGlobal->trace("Creating required objects");
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
for(const auto & object : requiredObjects) for(const auto & object : requiredObjects)
{ {
auto * obj = object.first; auto * obj = object.first;
//FIXME: Invalid dObject inside object?
rmg::Object rmgObject(*obj); rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType()); rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); 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); auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
if(!path.valid()) if(!path.valid())
@ -291,7 +302,11 @@ bool ObjectManager::createRequiredObjects()
for(const auto & object : closeObjects) for(const auto & object : closeObjects)
{ {
auto * obj = object.first; auto * obj = object.first;
//TODO: Wrap into same area proxy?
Zone::Lock lock(zone.areaMutex);
auto possibleArea = zone.areaPossible(); auto possibleArea = zone.areaPossible();
rmg::Object rmgObject(*obj); rmg::Object rmgObject(*obj);
rmgObject.setTemplate(zone.getTerrainType()); rmgObject.setTemplate(zone.getTerrainType());
bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
@ -352,6 +367,8 @@ bool ObjectManager::createRequiredObjects()
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance) void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance)
{ {
object.finalize(map); object.finalize(map);
Zone::Lock lock(zone.areaMutex);
zone.areaPossible().subtract(object.getArea()); zone.areaPossible().subtract(object.getArea());
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition()); bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping

View File

@ -9,7 +9,6 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "ObstaclePlacer.h" #include "ObstaclePlacer.h"
#include "ObjectManager.h" #include "ObjectManager.h"
#include "TreasurePlacer.h" #include "TreasurePlacer.h"
@ -24,186 +23,16 @@
#include "Functions.h" #include "Functions.h"
#include "../mapping/CMapEditManager.h" #include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h" #include "../mapping/CMap.h"
#include "../mapping/ObstacleProxy.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
void ObstacleProxy::collectPossibleObstacles(TerrainId terrain)
{
//get all possible obstacles for this terrain
for(auto primaryID : VLC->objtypeh->knownObjects())
{
for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
{
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
if(handler->isStaticObject())
{
for(const auto & temp : handler->getTemplates())
{
if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid())
obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
}
}
}
}
for(const auto & o : obstaclesBySize)
{
possibleObstacles.emplace_back(o);
}
boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool
{
return p1.first > p2.first; //bigger obstacles first
});
}
int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, 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)
{
if(!possibleObstacle.first)
continue;
auto shuffledObstacles = possibleObstacle.second;
RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand);
for(const auto & temp : shuffledObstacles)
{
auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
auto * obj = handler->create(temp);
allObjects.emplace_back(*obj);
rmg::Object * rmgObject = &allObjects.back();
for(const auto & offset : obj->getBlockedOffsets())
{
rmgObject->setPosition(tile - offset);
if(!map->isInTheMap(rmgObject->getPosition()))
continue;
if(!rmgObject->getArea().getSubarea([map](const int3 & t)
{
return !map->isInTheMap(t);
}).empty())
continue;
if(isProhibited(rmgObject->getArea()))
continue;
int coverageBlocked = 0;
int coveragePossible = 0;
//do not use area intersection in optimization purposes
for(const auto & t : rmgObject->getArea().getTilesVector())
{
auto coverage = verifyCoverage(t);
if(coverage.first)
++coverageBlocked;
if(coverage.second)
++coveragePossible;
}
int coverageOverlap = possibleObstacle.first - coverageBlocked - coveragePossible;
int weight = possibleObstacle.first + coverageBlocked - coverageOverlap * possibleObstacle.first;
assert(coverageOverlap >= 0);
if(weight > maxWeight)
{
weightedObjects.clear();
maxWeight = weight;
weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
if(weight > 0)
break;
}
else if(weight == maxWeight)
weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
}
}
if(maxWeight > 0)
break;
}
return maxWeight;
}
void ObstacleProxy::placeObstacles(CMap * map, CRandomGenerator & rand)
{
//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
auto blockedTiles = blockedArea.getTilesVector();
int tilePos = 0;
std::set<CGObjectInstance*> objs;
while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size())
{
auto tile = blockedArea.getTilesVector()[tilePos];
std::list<rmg::Object> allObjects;
std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
int maxWeight = getWeightedObjects(tile, map, rand, allObjects, weightedObjects);
if(weightedObjects.empty())
{
tilePos += 1;
continue;
}
auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
objIter->first->setPosition(objIter->second);
placeObject(*objIter->first, objs);
blockedArea.subtract(objIter->first->getArea());
tilePos = 0;
postProcess(*objIter->first);
if(maxWeight < 0)
logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString());
for(auto & o : allObjects)
{
if(&o != objIter->first)
o.clear();
}
}
finalInsertion(map->getEditManager(), objs);
}
void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
{
manager->insertObjects(instances); //insert as one operation - for undo purposes
}
std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
{
return {blockedArea.contains(t), false};
}
void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
{
for (auto * instance : object.instances())
{
instances.insert(&instance->object());
}
}
void ObstacleProxy::postProcess(const rmg::Object & object)
{
}
bool ObstacleProxy::isProhibited(const rmg::Area & objArea) const
{
return false;
}
void ObstaclePlacer::process() void ObstaclePlacer::process()
{ {
manager = zone.getModificator<ObjectManager>(); manager = zone.getModificator<ObjectManager>();
if(!manager) if(!manager)
return; return;
riverManager = zone.getModificator<RiverPlacer>();
collectPossibleObstacles(zone.getTerrainType()); collectPossibleObstacles(zone.getTerrainType());
blockedArea = zone.area().getSubarea([this](const int3 & t) blockedArea = zone.area().getSubarea([this](const int3 & t)
@ -213,9 +42,11 @@ void ObstaclePlacer::process()
blockedArea.subtract(zone.areaUsed()); blockedArea.subtract(zone.areaUsed());
zone.areaPossible().subtract(blockedArea); zone.areaPossible().subtract(blockedArea);
//TODO: Set prohibited area in ObstacleProxy :?
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
placeObstacles(&map.map(), generator.rand); auto objs = createObstacles(generator.rand);
mapProxy->insertObjects(objs);
} }
void ObstaclePlacer::init() void ObstaclePlacer::init()
@ -228,9 +59,9 @@ void ObstaclePlacer::init()
DEPENDENCY_ALL(RockPlacer); DEPENDENCY_ALL(RockPlacer);
} }
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const bool ObstaclePlacer::isInTheMap(const int3& tile)
{ {
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)}; return map.isOnMap(tile);
} }
void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &) void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
@ -238,9 +69,15 @@ void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance
manager->placeObject(object, false, false); 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) void ObstaclePlacer::postProcess(const rmg::Object & object)
{ {
//river processing //river processing
riverManager = zone.getModificator<RiverPlacer>();
if(riverManager) if(riverManager)
{ {
const auto objTypeName = object.instances().front()->object().typeName; const auto objTypeName = object.instances().front()->object().typeName;
@ -262,8 +99,4 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
return false; return false;
} }
void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set<CGObjectInstance*> &)
{
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -9,7 +9,9 @@
*/ */
#pragma once #pragma once
#include "Zone.h"
#include "Modificator.h"
#include "../mapping/ObstacleProxy.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -17,38 +19,6 @@ class CMap;
class CMapEditManager; class CMapEditManager;
class RiverPlacer; class RiverPlacer;
class ObjectManager; 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 class ObstaclePlacer: public Modificator, public ObstacleProxy
{ {
public: public:
@ -57,6 +27,8 @@ public:
void process() override; void process() override;
void init() override; void init() override;
bool isInTheMap(const int3& tile) override;
std::pair<bool, bool> verifyCoverage(const int3 & t) const override; std::pair<bool, bool> verifyCoverage(const int3 & t) const override;
void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances) override; void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances) override;
@ -65,8 +37,6 @@ public:
bool isProhibited(const rmg::Area & objArea) const override; bool isProhibited(const rmg::Area & objArea) const override;
void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances) override;
private: private:
rmg::Area prohibitedArea; rmg::Area prohibitedArea;
RiverPlacer * riverManager; RiverPlacer * riverManager;

View File

@ -33,26 +33,26 @@ void QuestArtifactPlacer::init()
void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone) void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
questArtZones.push_back(otherZone); questArtZones.push_back(otherZone);
} }
void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id) void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
logGlobal->info("Need to place quest artifact artifact %s", VLC->artifacts()->getById(id)->getNameTranslated()); logGlobal->info("Need to place quest artifact artifact %s", VLC->artifacts()->getById(id)->getNameTranslated());
questArtifactsToPlace.emplace_back(id); questArtifactsToPlace.emplace_back(id);
} }
void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj) void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
artifactsToReplace.push_back(obj); artifactsToReplace.push_back(obj);
} }
std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return artifactsToReplace; return artifactsToReplace;
} }
@ -113,19 +113,19 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator * rand)
void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj) void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
boost::remove(artifactsToReplace, obj); boost::remove(artifactsToReplace, obj);
} }
size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return questArtifacts.size(); return questArtifacts.size();
} }
ArtifactID QuestArtifactPlacer::drawRandomArtifact() ArtifactID QuestArtifactPlacer::drawRandomArtifact()
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
if (!questArtifacts.empty()) if (!questArtifacts.empty())
{ {
ArtifactID ret = questArtifacts.back(); ArtifactID ret = questArtifacts.back();
@ -141,6 +141,6 @@ ArtifactID QuestArtifactPlacer::drawRandomArtifact()
void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid) void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
questArtifacts.push_back(artid); questArtifacts.push_back(artid);
} }

View File

@ -87,8 +87,8 @@ void RiverPlacer::init()
void RiverPlacer::drawRivers() void RiverPlacer::drawRivers()
{ {
map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector()); auto tiles = rivers.getTilesVector();
map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(zone.getTerrainType())->river, &generator.rand); mapProxy->drawRivers(generator.rand, tiles, zone.getTerrainType());
} }
char RiverPlacer::dump(const int3 & t) char RiverPlacer::dump(const int3 & t)
@ -147,9 +147,9 @@ void RiverPlacer::prepareHeightmap()
} }
//make grid //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}; int3 t{i, j, zone.getPos().z};
if(zone.area().contains(t)) if(zone.area().contains(t))

View File

@ -28,6 +28,7 @@
#include "WaterProxy.h" #include "WaterProxy.h"
#include "WaterRoutes.h" #include "WaterRoutes.h"
#include "RockPlacer.h" #include "RockPlacer.h"
#include "RockFiller.h"
#include "ObstaclePlacer.h" #include "ObstaclePlacer.h"
#include "RiverPlacer.h" #include "RiverPlacer.h"
#include "TerrainPainter.h" #include "TerrainPainter.h"
@ -40,6 +41,7 @@ RmgMap::RmgMap(const CMapGenOptions& mapGenOptions) :
mapGenOptions(mapGenOptions), zonesTotal(0) mapGenOptions(mapGenOptions), zonesTotal(0)
{ {
mapInstance = std::make_unique<CMap>(); mapInstance = std::make_unique<CMap>();
mapProxy = std::make_shared<MapProxy>(*this);
getEditManager()->getUndoManager().setUndoRedoLimit(0); getEditManager()->getUndoManager().setUndoRedoLimit(0);
} }
@ -121,7 +123,7 @@ void RmgMap::addModificators()
auto zone = z.second; auto zone = z.second;
zone->addModificator<ObjectManager>(); zone->addModificator<ObjectManager>();
zone->addModificator<ObjectDistributor>(); zone->addModificator<ObjectDistributor>(); //FIXME: Only one is needed for map
zone->addModificator<TreasurePlacer>(); zone->addModificator<TreasurePlacer>();
zone->addModificator<ObstaclePlacer>(); zone->addModificator<ObstaclePlacer>();
zone->addModificator<TerrainPainter>(); zone->addModificator<TerrainPainter>();
@ -149,12 +151,38 @@ void RmgMap::addModificators()
if(zone->isUnderground()) if(zone->isUnderground())
{ {
zone->addModificator<RockPlacer>(); zone->addModificator<RockPlacer>();
zone->addModificator<RockFiller>(); //FIXME: Only one is needed for map
} }
} }
} }
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; return *mapInstance;
} }
@ -241,13 +269,18 @@ void RmgMap::setRoad(const int3& tile, RoadId roadType)
tiles[tile.x][tile.y][tile.z].setRoadType(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); assertOnMap(tile);
return tiles[tile.x][tile.y][tile.z]; 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 TRmgTemplateZoneId RmgMap::getZoneID(const int3& tile) const
{ {
assertOnMap(tile); assertOnMap(tile);
@ -278,6 +311,7 @@ float RmgMap::getNearestObjectDistance(const int3 &tile) const
void RmgMap::registerZone(FactionID faction) void RmgMap::registerZone(FactionID faction)
{ {
//FIXME: Protect with lock guard?
zonesPerFaction[faction]++; zonesPerFaction[faction]++;
zonesTotal++; zonesTotal++;
} }
@ -323,7 +357,7 @@ void RmgMap::dump(bool zoneId) const
else else
{ {
char t = '?'; char t = '?';
switch (getTile(int3(i, j, k)).getTileType()) switch (getTileInfo(int3(i, j, k)).getTileType())
{ {
case ETileType::FREE: case ETileType::FREE:
t = ' '; break; t = ' '; break;

View File

@ -11,6 +11,7 @@
#pragma once #pragma once
#include "../int3.h" #include "../int3.h"
#include "../GameConstants.h" #include "../GameConstants.h"
#include "threadpool/MapProxy.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -20,12 +21,15 @@ class TileInfo;
class CMapGenOptions; class CMapGenOptions;
class Zone; class Zone;
class CMapGenerator; class CMapGenerator;
class MapProxy;
class playerInfo;
class RmgMap class RmgMap
{ {
public: public:
mutable std::unique_ptr<CMap> mapInstance; 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(const CMapGenOptions& mapGenOptions);
~RmgMap() = default; ~RmgMap() = default;
@ -45,10 +49,16 @@ public:
bool isRoad(const int3 &tile) const; bool isRoad(const int3 &tile) const;
bool isOnMap(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 setOccupied(const int3 &tile, ETileType::ETileType state);
void setRoad(const int3 &tile, RoadId roadType); 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; float getNearestObjectDistance(const int3 &tile) const;
void setNearestObjectDistance(int3 &tile, float value); void setNearestObjectDistance(int3 &tile, float value);
@ -76,6 +86,9 @@ private:
void assertOnMap(const int3 &tile) const; //throws void assertOnMap(const int3 &tile) const; //throws
private: private:
std::shared_ptr<MapProxy> mapProxy;
Zones zones; Zones zones;
std::map<FactionID, ui32> zonesPerFaction; std::map<FactionID, ui32> zonesPerFaction;
ui32 zonesTotal; //zones that have their main town only ui32 zonesTotal; //zones that have their main town only

View File

@ -182,7 +182,7 @@ std::list<Object::Instance*> Object::instances()
{ {
std::list<Object::Instance*> result; std::list<Object::Instance*> result;
for(auto & i : dInstances) for(auto & i : dInstances)
result.push_back(&i); result.push_back(&i); //FIXME: Sterta zosta�a uskzodzona :? Mo�e w innym miejscu?
return result; return result;
} }
@ -310,7 +310,7 @@ void Object::Instance::finalize(RmgMap & map)
//If no specific template was defined for this object, select any matching //If no specific template was defined for this object, select any matching
if (!dObject.appearance) 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()); auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId());
if (templates.empty()) if (templates.empty())
{ {
@ -336,7 +336,7 @@ void Object::Instance::finalize(RmgMap & map)
map.setOccupied(tile, ETileType::ETileType::USED); map.setOccupied(tile, ETileType::ETileType::USED);
} }
map.getEditManager()->insertObject(&dObject); map.getMapProxy()->insertObject(&dObject);
} }
void Object::finalize(RmgMap & map) void Object::finalize(RmgMap & map)

View File

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

91
lib/rmg/RockFiller.cpp Normal file
View File

@ -0,0 +1,91 @@
/*
* 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 "../mapping/CMapEditManager.h"
#include "TileInfo.h"
#include "threadpool/MapProxy.h"
VCMI_LIB_NAMESPACE_BEGIN
class TileInfo;
void RockFiller::process()
{
//Do that only once
auto lockVec = tryLockAll<RockFiller>();
if (!lockVec.empty())
{
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockFiller>())
{
if(m->isFinished())
return;
}
}
logGlobal->info("Processing RockFiller for the whole map");
processMap();
finished = true; //Block other placers immediately
}
}
void RockFiller::processMap()
{
//Merge all areas
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockPlacer>())
{
auto tiles = m->rockArea.getTilesVector();
mapProxy->drawTerrain(generator.rand, tiles, m->rockTerrain);
}
}
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<RockPlacer>())
{
//Now make sure all accessible tiles have no additional rock on them
auto tiles = m->accessibleArea.getTilesVector();
mapProxy->drawTerrain(generator.rand, tiles, z.second->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

28
lib/rmg/RockFiller.h Normal file
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

@ -20,11 +20,17 @@
#include "../TerrainHandler.h" #include "../TerrainHandler.h"
#include "../CRandomGenerator.h" #include "../CRandomGenerator.h"
#include "../mapping/CMapEditManager.h" #include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h" #include "TileInfo.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class TileInfo;
void RockPlacer::process() void RockPlacer::process()
{
blockRock();
}
void RockPlacer::blockRock()
{ {
rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain; rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain;
assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable()); assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable());
@ -38,53 +44,21 @@ void RockPlacer::process()
{ {
return map.shouldBeBlocked(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() 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) 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.areaUsed().unite(rockArea);
zone.areaPossible().subtract(rockArea); zone.areaPossible().subtract(rockArea);
//TODO: Might need mutex here as well
if(auto * m = zone.getModificator<RiverPlacer>()) if(auto * m = zone.getModificator<RiverPlacer>())
m->riverProhibit().unite(rockArea); m->riverProhibit().unite(rockArea);
if(auto * m = zone.getModificator<RoadPlacer>()) if(auto * m = zone.getModificator<RoadPlacer>())
@ -93,13 +67,12 @@ void RockPlacer::postProcess()
void RockPlacer::init() void RockPlacer::init()
{ {
POSTFUNCTION_ALL(RoadPlacer); DEPENDENCY_ALL(TreasurePlacer);
DEPENDENCY(TreasurePlacer);
} }
char RockPlacer::dump(const int3 & t) 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'; return zone.area().contains(t) ? 'R' : 'E';
} }

View File

@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class RockPlacer: public Modificator class RockPlacer: public Modificator
{ {
friend class RockFiller;
public: public:
MODIFICATOR(RockPlacer); MODIFICATOR(RockPlacer);
@ -22,7 +23,7 @@ public:
void init() override; void init() override;
char dump(const int3 &) override; char dump(const int3 &) override;
void processMap(); void blockRock();
void postProcess(); void postProcess();
protected: protected:

View File

@ -23,8 +23,10 @@ VCMI_LIB_NAMESPACE_BEGIN
void TerrainPainter::process() void TerrainPainter::process()
{ {
//TODO: Make member methods
initTerrainType(zone, generator); initTerrainType(zone, generator);
paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType()); paintZoneTerrain(zone, generator.rand, mapProxy, zone.getTerrainType());
} }
void TerrainPainter::init() void TerrainPainter::init()

View File

@ -52,7 +52,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
//set zone types to player faction, generate main town //set zone types to player faction, generate main town
logGlobal->info("Preparing playing zone"); logGlobal->info("Preparing playing zone");
int player_id = *zone.getOwner() - 1; int player_id = *zone.getOwner() - 1;
auto & playerInfo = map.map().players[player_id]; auto& playerInfo = map.getPlayer(player_id);
PlayerColor player(player_id); PlayerColor player(player_id);
if(playerInfo.canAnyonePlay()) if(playerInfo.canAnyonePlay())
{ {
@ -140,11 +140,16 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
//towns are big objects and should be centered around visitable position //towns are big objects and should be centered around visitable position
rmg::Object rmgObject(town); rmg::Object rmgObject(town);
rmgObject.setTemplate(zone.getTerrainType()); rmgObject.setTemplate(zone.getTerrainType());
auto position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3 & t)
int3 position(-1, -1, -1);
{
Zone::Lock lock(zone.areaMutex);
position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3& t)
{ {
float distance = zone.getPos().dist2dSQ(t); float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number return 100000.f - distance; //some big number
}, ObjectManager::OptimizeType::WEIGHT); }, ObjectManager::OptimizeType::WEIGHT);
}
rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
manager.placeObject(rmgObject, false, true); manager.placeObject(rmgObject, false, true);
cleanupBoundaries(rmgObject); cleanupBoundaries(rmgObject);
@ -154,6 +159,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject) void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
{ {
Zone::Lock lock(zone.areaMutex);
for(const auto & t : rmgObject.getArea().getBorderOutside()) for(const auto & t : rmgObject.getArea().getBorderOutside())
{ {
if(map.isOnMap(t)) if(map.isOnMap(t))

View File

@ -45,7 +45,7 @@ void TreasurePlacer::init()
void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi) void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
@ -526,19 +526,19 @@ void TreasurePlacer::addAllPossibleObjects()
size_t TreasurePlacer::getPossibleObjectsSize() const size_t TreasurePlacer::getPossibleObjectsSize() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return possibleObjects.size(); return possibleObjects.size();
} }
void TreasurePlacer::setMaxPrisons(size_t count) void TreasurePlacer::setMaxPrisons(size_t count)
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
maxPrisons = count; maxPrisons = count;
} }
size_t TreasurePlacer::getMaxPrisons() const size_t TreasurePlacer::getMaxPrisons() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return maxPrisons; return maxPrisons;
} }
@ -772,6 +772,8 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
if(guarded) if(guarded)
guarded = manager.addGuard(rmgObject, value); 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 possibleArea = zone.areaPossible();
auto path = rmg::Path::invalid(); auto path = rmg::Path::invalid();
@ -779,13 +781,13 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
{ {
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile) 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) if(ti.getNearestObjectDistance() < minDistance)
return -1.f; return -1.f;
for(const auto & t : rmgObject.getArea().getTilesVector()) for(const auto & t : rmgObject.getArea().getTilesVector())
{ {
if(map.getTile(t).getNearestObjectDistance() < minDistance) if(map.getTileInfo(t).getNearestObjectDistance() < minDistance)
return -1.f; return -1.f;
} }

View File

@ -225,6 +225,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
} }
map.getZones()[waterZoneId]->area().unite(waterArea); map.getZones()[waterZoneId]->area().unite(waterArea);
Zone::Lock lock(zone.areaMutex);
zone.area().subtract(waterArea); zone.area().subtract(waterArea);
zone.areaPossible().subtract(waterArea); zone.areaPossible().subtract(waterArea);
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap); distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);

View File

@ -38,7 +38,7 @@ void WaterProxy::process()
map.setOccupied(t, ETileType::POSSIBLE); map.setOccupied(t, ETileType::POSSIBLE);
} }
paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType()); paintZoneTerrain(zone, generator.rand, mapProxy, zone.getTerrainType());
//check terrain type //check terrain type
for([[maybe_unused]] const auto & t : zone.area().getTilesVector()) for([[maybe_unused]] const auto & t : zone.area().getTilesVector())
@ -52,9 +52,10 @@ void WaterProxy::process()
if(z.second->getId() == zone.getId()) if(z.second->getId() == zone.getId())
continue; continue;
Zone::Lock lock(z.second->areaMutex);
for(const auto & t : z.second->area().getTilesVector()) 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->areaPossible().erase(t);
z.second->area().erase(t); z.second->area().erase(t);
@ -90,13 +91,13 @@ void WaterProxy::init()
const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
return lakes; return lakes;
} }
void WaterProxy::collectLakes() void WaterProxy::collectLakes()
{ {
Lock lock(externalAccessMutex); RecursiveLock lock(externalAccessMutex);
int lakeId = 0; int lakeId = 0;
for(const auto & lake : connectedAreas(zone.getArea(), true)) for(const auto & lake : connectedAreas(zone.getArea(), true))
{ {
@ -140,6 +141,8 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
if(map.isPossible(ct)) if(map.isPossible(ct))
map.setOccupied(ct, ETileType::BLOCKED); map.setOccupied(ct, ETileType::BLOCKED);
} }
Zone::Lock lock(dst.areaMutex);
dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]); dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]);
continue; continue;
} }
@ -223,7 +226,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 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 //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; return false;
rmg::Area a({tile}); rmg::Area a({tile});

View File

@ -45,6 +45,8 @@ void WaterRoutes::process()
result.push_back(wproxy->waterRoute(*z.second)); result.push_back(wproxy->waterRoute(*z.second));
} }
Zone::Lock lock(zone.areaMutex);
//prohibit to place objects on sealed off lakes //prohibit to place objects on sealed off lakes
for(const auto & lake : wproxy->getLakes()) for(const auto & lake : wproxy->getLakes())
{ {

View File

@ -86,6 +86,7 @@ rmg::Area & Zone::area()
rmg::Area & Zone::areaPossible() rmg::Area & Zone::areaPossible()
{ {
//FIXME: make const, only modify via mutex-protected interface
return dAreaPossible; return dAreaPossible;
} }
@ -96,6 +97,7 @@ rmg::Area & Zone::areaUsed()
void Zone::clearTiles() void Zone::clearTiles()
{ {
//Lock lock(mx);
dArea.clear(); dArea.clear();
dAreaPossible.clear(); dAreaPossible.clear();
dAreaFree.clear(); dAreaFree.clear();
@ -104,6 +106,7 @@ void Zone::clearTiles()
void Zone::initFreeTiles() void Zone::initFreeTiles()
{ {
rmg::Tileset possibleTiles; rmg::Tileset possibleTiles;
//Lock lock(mx);
vstd::copy_if(dArea.getTiles(), vstd::set_inserter(possibleTiles), [this](const int3 &tile) -> bool vstd::copy_if(dArea.getTiles(), vstd::set_inserter(possibleTiles), [this](const int3 &tile) -> bool
{ {
return map.isPossible(tile); return map.isPossible(tile);
@ -201,6 +204,7 @@ void Zone::fractalize()
rmg::Area tilesToIgnore; //will be erased in this iteration rmg::Area tilesToIgnore; //will be erased in this iteration
const float minDistance = 10 * 10; //squared const float minDistance = 10 * 10; //squared
float blockDistance = minDistance * 0.25f;
if(type != ETemplateZoneType::JUNCTION) if(type != ETemplateZoneType::JUNCTION)
{ {
@ -235,7 +239,7 @@ void Zone::fractalize()
tilesToIgnore.clear(); tilesToIgnore.clear();
} }
} }
Lock lock(areaMutex);
//cut straight paths towards the center. A* is too slow for that. //cut straight paths towards the center. A* is too slow for that.
auto areas = connectedAreas(clearedTiles, false); auto areas = connectedAreas(clearedTiles, false);
for(auto & area : areas) for(auto & area : areas)
@ -264,7 +268,6 @@ void Zone::fractalize()
} }
//now block most distant tiles away from passages //now block most distant tiles away from passages
float blockDistance = minDistance * 0.25f;
auto areaToBlock = dArea.getSubarea([this, blockDistance](const int3 & t) auto areaToBlock = dArea.getSubarea([this, blockDistance](const int3 & t)
{ {
auto distance = static_cast<float>(dAreaFree.distanceSqr(t)); auto distance = static_cast<float>(dAreaFree.distanceSqr(t));
@ -272,6 +275,8 @@ void Zone::fractalize()
}); });
dAreaPossible.subtract(areaToBlock); dAreaPossible.subtract(areaToBlock);
dAreaFree.subtract(areaToBlock); dAreaFree.subtract(areaToBlock);
lock.unlock();
for(const auto & t : areaToBlock.getTiles()) for(const auto & t : areaToBlock.getTiles())
map.setOccupied(t, ETileType::BLOCKED); map.setOccupied(t, ETileType::BLOCKED);
} }

View File

@ -13,7 +13,6 @@
#include "../GameConstants.h" #include "../GameConstants.h"
#include "float3.h" #include "float3.h"
#include "../int3.h" #include "../int3.h"
#include "threadpool/JobProvider.h"
#include "CRmgTemplate.h" #include "CRmgTemplate.h"
#include "RmgArea.h" #include "RmgArea.h"
#include "RmgPath.h" #include "RmgPath.h"
@ -88,6 +87,10 @@ public:
void initModificators(); void initModificators();
public:
boost::recursive_mutex areaMutex;
using Lock = boost::unique_lock<boost::recursive_mutex>;
protected: protected:
CMapGenerator & generator; CMapGenerator & generator;
RmgMap & map; RmgMap & map;
@ -107,6 +110,11 @@ protected:
si32 townType; si32 townType;
TerrainId terrainType; TerrainId terrainType;
/*
private:
mutable boost::shared_mutex mx; //Used for area access
using Lock = boost::unique_lock<boost::shared_mutex>;
*/
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -1,30 +0,0 @@
/*
* JobProvider.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 "../../GameConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
typedef std::function<void()> TRMGfunction ;
typedef std::optional<TRMGfunction> TRMGJob;
class DLL_LINKAGE IJobProvider
{
public:
//TODO: Think about some mutex protection
virtual TRMGJob getNextJob() = 0;
virtual bool hasJobs() = 0;
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,57 @@
/*
* 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
*
*/
#pragma once
#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

@ -11,12 +11,14 @@
#pragma once #pragma once
#include "BlockingQueue.h" #include "BlockingQueue.h"
#include "JobProvider.h"
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/thread/condition_variable.hpp> #include <boost/thread/condition_variable.hpp>
VCMI_LIB_NAMESPACE_BEGIN 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 //Credit to https://github.com/Liam0205/toy-threadpool/tree/master/yuuki
class DLL_LINKAGE ThreadPool class DLL_LINKAGE ThreadPool

View File

@ -397,20 +397,24 @@ void MapController::commitObstacleFill(int level)
return; return;
//split by zones //split by zones
std::map<TerrainId, ObstacleProxy> terrainSelected;
std::map<TerrainId, std::unique_ptr<EditorObstaclePlacer>> terrainSelected;
for(auto & t : selection) for(auto & t : selection)
{ {
auto tl = _map->getTile(t); auto tl = _map->getTile(t);
if(tl.blocked || tl.visitable) if(tl.blocked || tl.visitable)
continue; continue;
terrainSelected[tl.terType->getId()].blockedArea.add(t); auto terrain = tl.terType->getId();
//FIXME: This map can be populated once at launch, not need to collect objects every time
terrainSelected[terrain] = std::make_unique<EditorObstaclePlacer>(_map.get());
terrainSelected[terrain]->addBlockedTile(t);
} }
for(auto & sel : terrainSelected) for(auto & sel : terrainSelected)
{ {
sel.second.collectPossibleObstacles(sel.first); sel.second->collectPossibleObstacles(sel.first);
sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault()); sel.second->placeObstacles(CRandomGenerator::getDefault());
} }
_mapHandler->invalidateObjects(); _mapHandler->invalidateObjects();