/* * ObstaclePlacer.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "../mapObjects/CObjectClassesHandler.h" #include "ObstaclePlacer.h" #include "ObjectManager.h" #include "TreasurePlacer.h" #include "RockPlacer.h" #include "WaterRoutes.h" #include "WaterProxy.h" #include "RoadPlacer.h" #include "RiverPlacer.h" #include "RmgMap.h" #include "CMapGenerator.h" #include "../CRandomGenerator.h" #include "Functions.h" void ObstaclePlacer::process() { 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()) { for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) { auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); if(handler->isStaticObject()) { for(auto temp : handler->getTemplates()) { if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid()) obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp); } } } } for(auto o : obstaclesBySize) { possibleObstacles.push_back(o); } boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool { return p1.first > p2.first; //bigger obstacles first }); auto blockedArea = zone.area().getSubarea([this](const int3 & t) { return map.shouldBeBlocked(t); }); blockedArea.subtract(zone.areaUsed()); zone.areaPossible().subtract(blockedArea); auto prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); //reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left auto blockedTiles = blockedArea.getTilesVector(); int tilePos = 0; 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; } if(weightedObjects.empty()) { tilePos += 1; continue; } auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, generator.rand); objIter->first->setPosition(objIter->second); manager->placeObject(*objIter->first, false, false); 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()); } 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(); } } } void ObstaclePlacer::init() { DEPENDENCY(ObjectManager); DEPENDENCY(TreasurePlacer); DEPENDENCY(WaterRoutes); DEPENDENCY(WaterProxy); DEPENDENCY(RoadPlacer); DEPENDENCY_ALL(RockPlacer); }