diff --git a/lib/rmg/ObstaclePlacer.cpp b/lib/rmg/ObstaclePlacer.cpp index a0c051fdb..1e7874075 100644 --- a/lib/rmg/ObstaclePlacer.cpp +++ b/lib/rmg/ObstaclePlacer.cpp @@ -22,22 +22,10 @@ #include "CMapGenerator.h" #include "../CRandomGenerator.h" #include "Functions.h" +#include "../mapping/CMapEditManager.h" -void ObstaclePlacer::process() +void ObstacleProxy::collectPossibleObstacles(const Terrain & terrain) { - auto * manager = zone.getModificator(); - if(!manager) - return; - - auto * riverManager = zone.getModificator(); - - typedef std::vector> ObstacleVector; - //obstacleVector possibleObstacles; - - std::map obstaclesBySize; - typedef std::pair ObstaclePair; - std::vector possibleObstacles; - //get all possible obstacles for this terrain for(auto primaryID : VLC->objtypeh->knownObjects()) { @@ -48,7 +36,7 @@ void ObstaclePlacer::process() { for(auto temp : handler->getTemplates()) { - if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid()) + if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid()) obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp); } } @@ -62,122 +50,172 @@ void ObstaclePlacer::process() { return p1.first > p2.first; //bigger obstacles first }); - - auto blockedArea = zone.area().getSubarea([this](const int3 & t) +} + +int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list & allObjects, std::vector> & weightedObjects) +{ + int maxWeight = std::numeric_limits::min(); + for(int i = 0; i < possibleObstacles.size(); ++i) { - return map.shouldBeBlocked(t); - }); - blockedArea.subtract(zone.areaUsed()); - zone.areaPossible().subtract(blockedArea); - - - auto prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); - + if(!possibleObstacles[i].first) + continue; + + auto shuffledObstacles = possibleObstacles[i].second; + RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand); + + for(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(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(auto & t : rmgObject->getArea().getTilesVector()) + { + auto coverage = verifyCoverage(t); + if(coverage.first) + ++coverageBlocked; + if(coverage.second) + ++coveragePossible; + } + + int coverageOverlap = possibleObstacles[i].first - coverageBlocked - coveragePossible; + int weight = possibleObstacles[i].first + coverageBlocked - coverageOverlap * possibleObstacles[i].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 objs; + while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size()) { auto tile = blockedArea.getTilesVector()[tilePos]; - + std::list allObjects; - std::vector> weightedObjects; //obj + position - int maxWeight = std::numeric_limits::min(); - for(int i = 0; i < possibleObstacles.size(); ++i) - { - if(!possibleObstacles[i].first) - continue; - - auto shuffledObstacles = possibleObstacles[i].second; - RandomGeneratorUtil::randomShuffle(shuffledObstacles, generator.rand); - - for(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(auto & offset : obj->getBlockedOffsets()) - { - rmgObject->setPosition(tile - offset); - if(!map.isOnMap(rmgObject->getPosition())) - continue; - - if(!rmgObject->getArea().getSubarea([this](const int3 & t) - { - return !map.isOnMap(t); - }).empty()) - continue; - - if(prohibitedArea.overlap(rmgObject->getArea())) - continue; - - if(!zone.area().contains(rmgObject->getArea())) - continue; - - int coverageBlocked = 0; - int coveragePossible = 0; - //do not use area intersection in optimization purposes - for(auto & t : rmgObject->getArea().getTilesVector()) - { - if(map.shouldBeBlocked(t)) - ++coverageBlocked; - if(zone.areaPossible().contains(t)) - ++coveragePossible; - } - - int coverageOverlap = possibleObstacles[i].first - coverageBlocked - coveragePossible; - int weight = possibleObstacles[i].first + coverageBlocked - coverageOverlap * possibleObstacles[i].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; - } - + std::vector> weightedObjects; + int maxWeight = getWeightedObjects(tile, map, rand, allObjects, weightedObjects); + if(weightedObjects.empty()) { tilePos += 1; continue; } - - auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, generator.rand); + + auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand); objIter->first->setPosition(objIter->second); - manager->placeObject(*objIter->first, false, false); + placeObject(*objIter->first, objs); + blockedArea.subtract(objIter->first->getArea()); tilePos = 0; - - //river processing - if(riverManager) - { - if(objIter->first->instances().front()->object().typeName == "mountain") - riverManager->riverSource().unite(objIter->first->getArea()); - if(objIter->first->instances().front()->object().typeName == "lake") - riverManager->riverSink().unite(objIter->first->getArea()); - } - + + 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 & instances) +{ + manager->insertObjects(instances); //insert as one operation - for undo purposes +} + +std::pair ObstacleProxy::verifyCoverage(const int3 & t) const +{ + std::pair result(false, false); + if(blockedArea.contains(t)) + result.first = true; + return result; +} + +void ObstacleProxy::placeObject(rmg::Object & object, std::set & 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(); + if(!manager) + return; + + riverManager = zone.getModificator(); + + collectPossibleObstacles(zone.getTerrainType()); + + blockedArea = zone.area().getSubarea([this](const int3 & t) + { + return map.shouldBeBlocked(t); + }); + blockedArea.subtract(zone.areaUsed()); + zone.areaPossible().subtract(blockedArea); + + prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); + + placeObstacles(&map.map(), generator.rand); } void ObstaclePlacer::init() @@ -189,3 +227,45 @@ void ObstaclePlacer::init() DEPENDENCY(RoadPlacer); DEPENDENCY_ALL(RockPlacer); } + +std::pair ObstaclePlacer::verifyCoverage(const int3 & t) const +{ + std::pair result(false, false); + if(map.shouldBeBlocked(t)) + result.first = true; + if(zone.areaPossible().contains(t)) + result.second = true; + return result; +} + +void ObstaclePlacer::placeObject(rmg::Object & object, std::set &) +{ + manager->placeObject(object, false, false); +} + +void ObstaclePlacer::postProcess(const rmg::Object & object) +{ + //river processing + if(riverManager) + { + if(object.instances().front()->object().typeName == "mountain") + riverManager->riverSource().unite(object.getArea()); + if(object.instances().front()->object().typeName == "lake") + riverManager->riverSink().unite(object.getArea()); + } +} + +bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const +{ + if(prohibitedArea.overlap(objArea)) + return true; + + if(!zone.area().contains(objArea)) + return true; + + return false; +} + +void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set &) +{ +} diff --git a/lib/rmg/ObstaclePlacer.h b/lib/rmg/ObstaclePlacer.h index cb1242048..b98d042aa 100644 --- a/lib/rmg/ObstaclePlacer.h +++ b/lib/rmg/ObstaclePlacer.h @@ -11,11 +11,61 @@ #pragma once #include "Zone.h" -class ObstaclePlacer: public Modificator +class CMap; +class CMapEditManager; +class RiverPlacer; +class ObjectManager; +class DLL_LINKAGE ObstacleProxy +{ +public: + ObstacleProxy() = default; + virtual ~ObstacleProxy() = default; + + rmg::Area blockedArea; + + void collectPossibleObstacles(const Terrain & terrain); + + void placeObstacles(CMap * map, CRandomGenerator & rand); + + virtual std::pair verifyCoverage(const int3 & t) const; + + virtual void placeObject(rmg::Object & object, std::set & instances); + + virtual void postProcess(const rmg::Object & object); + + virtual bool isProhibited(const rmg::Area & objArea) const; + + virtual void finalInsertion(CMapEditManager * manager, std::set & instances); + +protected: + int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list & allObjects, std::vector> & weightedObjects); + + typedef std::vector> ObstacleVector; + std::map obstaclesBySize; + typedef std::pair ObstaclePair; + std::vector possibleObstacles; +}; + +class ObstaclePlacer: public Modificator, public ObstacleProxy { public: MODIFICATOR(ObstaclePlacer); void process() override; void init() override; + + std::pair verifyCoverage(const int3 & t) const override; + + void placeObject(rmg::Object & object, std::set & instances) override; + + void postProcess(const rmg::Object & object) override; + + bool isProhibited(const rmg::Area & objArea) const override; + + void finalInsertion(CMapEditManager * manager, std::set & instances) override; + +private: + rmg::Area prohibitedArea; + RiverPlacer * riverManager; + ObjectManager * manager; };