1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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/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
@ -135,9 +136,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/rmg/WaterProxy.cpp
${MAIN_LIB_DIR}/rmg/WaterRoutes.cpp
${MAIN_LIB_DIR}/rmg/RockPlacer.cpp
${MAIN_LIB_DIR}/rmg/RockFiller.cpp
${MAIN_LIB_DIR}/rmg/ObstaclePlacer.cpp
${MAIN_LIB_DIR}/rmg/RiverPlacer.cpp
${MAIN_LIB_DIR}/rmg/TerrainPainter.cpp
${MAIN_LIB_DIR}/rmg/threadpool/MapProxy.cpp
${MAIN_LIB_DIR}/serializer/BinaryDeserializer.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/MapReaderH3M.h
${MAIN_LIB_DIR}/mapping/MapFormatJson.h
${MAIN_LIB_DIR}/mapping/ObstacleProxy.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/WaterRoutes.h
${MAIN_LIB_DIR}/rmg/RockPlacer.h
${MAIN_LIB_DIR}/rmg/RockFiller.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/threadpool/BlockingQueue.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/BinarySerializer.h

View File

@ -101,7 +101,7 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const
void CMapGenerator::initPrisonsRemaining()
{
allowedPrisons = 0;
for (auto isAllowed : map->map().allowedHeroes)
for (auto isAllowed : map->getMap(this).allowedHeroes)
{
if (isAllowed)
allowedPrisons++;
@ -133,7 +133,7 @@ std::unique_ptr<CMap> CMapGenerator::generate()
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();
@ -164,7 +164,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]);
@ -263,10 +263,10 @@ 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()
@ -374,7 +374,7 @@ 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");
@ -383,13 +383,14 @@ void CMapGenerator::fillZones()
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();
}
@ -434,9 +435,9 @@ 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->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));
}
return ret;
@ -445,13 +446,13 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
void CMapGenerator::banQuestArt(const ArtifactID & id)
{
//TODO: Protect with mutex
map->map().allowedArtifact[id] = false;
map->getMap(this).allowedArtifact[id] = false;
}
void CMapGenerator::banHero(const HeroTypeID & id)
{
//TODO: Protect with mutex
map->map().allowedHeroes[id] = false;
map->getMap(this).allowedHeroes[id] = false;
}
Zone * CMapGenerator::getZoneWater() const

View File

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

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
@ -224,8 +224,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
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 +784,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
@ -863,7 +862,7 @@ 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);
paintZoneTerrain(*zone.second, *rand, map.getMapProxy(), ETerrainId::SUBTERRANEAN);
}
}
logGlobal->info("Finished zone colouring");

View File

@ -25,6 +25,7 @@
#include "WaterAdopter.h"
#include "WaterProxy.h"
#include "TownPlacer.h"
#include <boost/interprocess/sync/scoped_lock.hpp>
VCMI_LIB_NAMESPACE_BEGIN
@ -101,7 +102,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
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 +229,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)

View File

@ -51,6 +51,7 @@ void createBorder(RmgMap & gen, Zone & zone)
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())
{
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();
map.getEditManager()->getTerrainSelection().setSelection(v);
map.getEditManager()->drawTerrain(terrain, &generator);
mapProxy->drawTerrain(generator, v, 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)
{
if(map.map().twoLevel)
if(map.levels())
{
//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);
if(!map.map().getTile(tile).terType->isPassable())
if(!map.getTile(tile).terType->isPassable())
{
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 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);

View File

@ -19,6 +19,7 @@ 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)
@ -45,18 +46,16 @@ bool Modificator::isReady()
{
if ((*it)->isFinished()) //OK
{
//This preceeder won't be checked in the future
it = preceeders.erase(it);
}
else if (!(*it)->isReady())
{
return false;
}
else
{
++it;
return false;
}
}
//If a job is finished, it should be already erased from a queue
return !finished;
}
}
@ -102,6 +101,7 @@ 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);
}
@ -119,10 +119,9 @@ void Modificator::postfunction(Modificator * modificator)
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;
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++)

View File

@ -12,14 +12,13 @@
#include "../GameConstants.h"
#include "../int3.h"
#include "threadpool/JobProvider.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>());
@ -57,14 +56,38 @@ public:
protected:
RmgMap & map;
std::shared_ptr<MapProxy> mapProxy;
CMapGenerator & generator;
Zone & zone;
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>;
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:
virtual void process() = 0;

View File

@ -27,18 +27,28 @@ 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)
//Do that only once
auto lockVec = tryLockAll<ObjectDistributor>();
if (!lockVec.empty())
{
for(auto & z : map.getZones())
{
if(auto * m = z.second->getModificator<ObjectDistributor>())
{
if(m->isFinished())
return;
}
}
distributeLimitedObjects();
distributeSeerHuts();
finished = true;
}
}
void ObjectDistributor::init()
{
DEPENDENCY(TownPlacer);
DEPENDENCY(TerrainPainter);
//All of the terrain types need to be determined
DEPENDENCY_ALL(TerrainPainter);
POSTFUNCTION(TreasurePlacer);
}
@ -84,6 +94,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
{

View File

@ -16,6 +16,9 @@
#include "RoadPlacer.h"
#include "RiverPlacer.h"
#include "WaterAdopter.h"
#include "ConnectionsPlacer.h"
#include "TownPlacer.h"
#include "MinePlacer.h"
#include "TreasurePlacer.h"
#include "QuestArtifactPlacer.h"
#include "../CCreatureHandler.h"
@ -37,13 +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()
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear();
for(const auto & tile : zone.areaPossible().getTilesVector())
{
@ -53,25 +59,25 @@ void ObjectManager::createDistancesPriorityQueue()
void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
requiredObjects.emplace_back(obj, strength);
}
void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
closeObjects.emplace_back(obj, strength);
}
void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
nearbyObjects.emplace_back(obj, nearbyTarget);
}
void ObjectManager::updateDistances(const rmg::Object & obj)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
tilesByDistance.clear();
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
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return objectsVisitableArea;
}
std::vector<CGObjectInstance*> ObjectManager::getMines() const
{
Lock lock(externalAccessMutex);
std::vector<CGObjectInstance*> mines;
RecursiveLock lock(externalAccessMutex);
for(auto * object : objects)
{
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())
obj.setPosition(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)
{
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;
}
@ -189,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;
}
@ -251,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())
@ -291,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));
@ -352,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

View File

@ -9,7 +9,6 @@
*/
#include "StdInc.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "ObstaclePlacer.h"
#include "ObjectManager.h"
#include "TreasurePlacer.h"
@ -24,186 +23,16 @@
#include "Functions.h"
#include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.h"
#include "../mapping/ObstacleProxy.h"
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()
{
manager = zone.getModificator<ObjectManager>();
if(!manager)
return;
riverManager = zone.getModificator<RiverPlacer>();
collectPossibleObstacles(zone.getTerrainType());
blockedArea = zone.area().getSubarea([this](const int3 & t)
@ -212,10 +41,12 @@ void ObstaclePlacer::process()
});
blockedArea.subtract(zone.areaUsed());
zone.areaPossible().subtract(blockedArea);
//TODO: Set prohibited area in ObstacleProxy :?
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
placeObstacles(&map.map(), generator.rand);
auto objs = createObstacles(generator.rand);
mapProxy->insertObjects(objs);
}
void ObstaclePlacer::init()
@ -228,9 +59,9 @@ void ObstaclePlacer::init()
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*> &)
@ -238,9 +69,15 @@ 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;
@ -262,8 +99,4 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
return false;
}
void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set<CGObjectInstance*> &)
{
}
VCMI_LIB_NAMESPACE_END

View File

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

View File

@ -33,26 +33,26 @@ void QuestArtifactPlacer::init()
void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
questArtZones.push_back(otherZone);
}
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());
questArtifactsToPlace.emplace_back(id);
}
void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
artifactsToReplace.push_back(obj);
}
std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return artifactsToReplace;
}
@ -113,19 +113,19 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator * rand)
void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
boost::remove(artifactsToReplace, obj);
}
size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return questArtifacts.size();
}
ArtifactID QuestArtifactPlacer::drawRandomArtifact()
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
if (!questArtifacts.empty())
{
ArtifactID ret = questArtifacts.back();
@ -141,6 +141,6 @@ ArtifactID QuestArtifactPlacer::drawRandomArtifact()
void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
questArtifacts.push_back(artid);
}

View File

@ -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(generator.rand, tiles, zone.getTerrainType());
}
char RiverPlacer::dump(const int3 & t)
@ -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))

View File

@ -28,6 +28,7 @@
#include "WaterProxy.h"
#include "WaterRoutes.h"
#include "RockPlacer.h"
#include "RockFiller.h"
#include "ObstaclePlacer.h"
#include "RiverPlacer.h"
#include "TerrainPainter.h"
@ -40,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);
}
@ -121,7 +123,7 @@ void RmgMap::addModificators()
auto zone = z.second;
zone->addModificator<ObjectManager>();
zone->addModificator<ObjectDistributor>();
zone->addModificator<ObjectDistributor>(); //FIXME: Only one is needed for map
zone->addModificator<TreasurePlacer>();
zone->addModificator<ObstaclePlacer>();
zone->addModificator<TerrainPainter>();
@ -149,12 +151,38 @@ void RmgMap::addModificators()
if(zone->isUnderground())
{
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;
}
@ -241,13 +269,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);
@ -278,6 +311,7 @@ float RmgMap::getNearestObjectDistance(const int3 &tile) const
void RmgMap::registerZone(FactionID faction)
{
//FIXME: Protect with lock guard?
zonesPerFaction[faction]++;
zonesTotal++;
}
@ -323,7 +357,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);
@ -76,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

@ -182,7 +182,7 @@ std::list<Object::Instance*> Object::instances()
{
std::list<Object::Instance*> result;
for(auto & i : dInstances)
result.push_back(&i);
result.push_back(&i); //FIXME: Sterta zosta³a uskzodzona :? Mo¿e w innym miejscu?
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 (!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

@ -11,10 +11,11 @@
#include "StdInc.h"
#include "RoadPlacer.h"
#include "ObjectManager.h"
#include "ObstaclePlacer.h"
#include "Functions.h"
#include "CMapGenerator.h"
#include "RmgMap.h"
#include "../mapping/CMap.h"
#include "threadpool/MapProxy.h"
#include "../mapping/CMapEditManager.h"
#include "../CModHandler.h"
#include "RmgPath.h"
@ -76,19 +77,25 @@ void RoadPlacer::drawRoads(bool secondary)
|| (!secondary && generator.getConfig().defaultRoadType.empty()))
return;
Lock lock(externalAccessMutex);
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(generator.rand, tiles, roadType);
}
void RoadPlacer::addRoadNode(const int3& node)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
roadNodes.insert(node);
}
@ -113,7 +120,7 @@ void RoadPlacer::connectRoads()
return;
//take any tile from road nodes as destination zone for all other road nodes
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
if(roads.empty())
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,71 +20,45 @@
#include "../TerrainHandler.h"
#include "../CRandomGenerator.h"
#include "../mapping/CMapEditManager.h"
#include "../mapping/CMap.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

@ -15,6 +15,7 @@ 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

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

View File

@ -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())
{
@ -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))

View File

@ -45,7 +45,7 @@ void TreasurePlacer::init()
void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
possibleObjects.push_back(oi);
}
@ -526,19 +526,19 @@ void TreasurePlacer::addAllPossibleObjects()
size_t TreasurePlacer::getPossibleObjectsSize() const
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return possibleObjects.size();
}
void TreasurePlacer::setMaxPrisons(size_t count)
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
maxPrisons = count;
}
size_t TreasurePlacer::getMaxPrisons() const
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return maxPrisons;
}
@ -772,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();
@ -779,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

@ -225,6 +225,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
}
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

@ -38,7 +38,7 @@ void WaterProxy::process()
map.setOccupied(t, ETileType::POSSIBLE);
}
paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType());
paintZoneTerrain(zone, generator.rand, mapProxy, zone.getTerrainType());
//check terrain type
for([[maybe_unused]] const auto & t : zone.area().getTilesVector())
@ -52,9 +52,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,13 +91,13 @@ void WaterProxy::init()
const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
return lakes;
}
void WaterProxy::collectLakes()
{
Lock lock(externalAccessMutex);
RecursiveLock lock(externalAccessMutex);
int lakeId = 0;
for(const auto & lake : connectedAreas(zone.getArea(), true))
{
@ -140,6 +141,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;
}
@ -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
{
//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});

View File

@ -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

@ -86,6 +86,7 @@ rmg::Area & Zone::area()
rmg::Area & Zone::areaPossible()
{
//FIXME: make const, only modify via mutex-protected interface
return dAreaPossible;
}
@ -96,6 +97,7 @@ rmg::Area & Zone::areaUsed()
void Zone::clearTiles()
{
//Lock lock(mx);
dArea.clear();
dAreaPossible.clear();
dAreaFree.clear();
@ -104,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);
@ -201,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)
{
@ -235,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)
@ -264,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));
@ -272,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);
}

View File

@ -13,7 +13,6 @@
#include "../GameConstants.h"
#include "float3.h"
#include "../int3.h"
#include "threadpool/JobProvider.h"
#include "CRmgTemplate.h"
#include "RmgArea.h"
#include "RmgPath.h"
@ -87,6 +86,10 @@ public:
}
void initModificators();
public:
boost::recursive_mutex areaMutex;
using Lock = boost::unique_lock<boost::recursive_mutex>;
protected:
CMapGenerator & generator;
@ -106,7 +109,12 @@ protected:
//template info
si32 townType;
TerrainId terrainType;
/*
private:
mutable boost::shared_mutex mx; //Used for area access
using Lock = boost::unique_lock<boost::shared_mutex>;
*/
};
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
#include "BlockingQueue.h"
#include "JobProvider.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

View File

@ -397,20 +397,24 @@ void MapController::commitObstacleFill(int level)
return;
//split by zones
std::map<TerrainId, ObstacleProxy> terrainSelected;
std::map<TerrainId, std::unique_ptr<EditorObstaclePlacer>> terrainSelected;
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();
//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)
{
sel.second.collectPossibleObstacles(sel.first);
sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault());
sel.second->collectPossibleObstacles(sel.first);
sel.second->placeObstacles(CRandomGenerator::getDefault());
}
_mapHandler->invalidateObjects();