diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 9533bf174..bd727d1f4 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -119,12 +119,11 @@ RandomMapTab::RandomMapTab(): std::string cbRoadType = "selectRoad_" + road->getJsonKey(); addCallback(cbRoadType, [&, road](bool on) { - mapGenOptions->setRoadEnabled(road->getJsonKey(), on); + mapGenOptions->setRoadEnabled(road->getId(), on); updateMapInfoByHost(); }); } - build(config); updateMapInfoByHost(); @@ -313,7 +312,7 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr opts) { if(auto w = widget(r->getJsonKey())) { - w->setSelected(opts->isRoadEnabled(r->getJsonKey())); + w->setSelected(opts->isRoadEnabled(r->getId())); } } } diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index e59e7dc63..3201943eb 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -26,6 +26,9 @@ CMapGenOptions::CMapGenOptions() waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr) { resetPlayersMap(); + setRoadEnabled(RoadId(Road::DIRT_ROAD), true); + setRoadEnabled(RoadId(Road::GRAVEL_ROAD), true); + setRoadEnabled(RoadId(Road::COBBLESTONE_ROAD), true); } si32 CMapGenOptions::getWidth() const @@ -233,17 +236,26 @@ void CMapGenOptions::setMapTemplate(const std::string & name) setMapTemplate(VLC->tplh->getTemplate(name)); } -void CMapGenOptions::setRoadEnabled(const std::string & roadName, bool enable) +void CMapGenOptions::setRoadEnabled(const RoadId & roadType, bool enable) { - if(enable) - disabledRoads.erase(roadName); + if (enable) + { + enabledRoads.insert(roadType); + } else - disabledRoads.insert(roadName); + { + enabledRoads.erase(roadType); + } } -bool CMapGenOptions::isRoadEnabled(const std::string & roadName) const +bool CMapGenOptions::isRoadEnabled(const RoadId & roadType) const { - return !disabledRoads.count(roadName); + return enabledRoads.count(roadType); +} + +bool CMapGenOptions::isRoadEnabled() const +{ + return !enabledRoads.empty(); } void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & team) diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index 14e1e5560..aa5683ede 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -110,8 +110,9 @@ public: EMonsterStrength::EMonsterStrength getMonsterStrength() const; void setMonsterStrength(EMonsterStrength::EMonsterStrength value); - bool isRoadEnabled(const std::string & roadName) const; - void setRoadEnabled(const std::string & roadName, bool enable); + bool isRoadEnabled(const RoadId & roadType) const; + bool isRoadEnabled() const; + void setRoadEnabled(const RoadId & roadType, bool enable); /// The first player colors belong to standard players and the last player colors belong to comp only players. /// All standard players are by default of type EPlayerType::AI. @@ -156,7 +157,7 @@ private: EWaterContent::EWaterContent waterContent; EMonsterStrength::EMonsterStrength monsterStrength; std::map players; - std::set disabledRoads; + std::set enabledRoads; const CRmgTemplate * mapTemplate; @@ -187,7 +188,7 @@ public: setMapTemplate(templateName); } - h & disabledRoads; + h & enabledRoads; } } }; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index b1de0801e..2960632cf 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -80,11 +80,6 @@ void CMapGenerator::loadConfig() config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer(); config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer(); config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer(); - //override config with game options - if(!mapGenOptions.isRoadEnabled(config.secondaryRoadType)) - config.secondaryRoadType = ""; - if(!mapGenOptions.isRoadEnabled(config.defaultRoadType)) - config.defaultRoadType = config.secondaryRoadType; config.singleThread = randomMapJson["singleThread"].Bool(); } diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 7a545fc5a..ccd0881c6 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -435,11 +435,12 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) } } -ZoneConnection::ZoneConnection() - : zoneA(-1), +ZoneConnection::ZoneConnection(): + zoneA(-1), zoneB(-1), guardStrength(0), - connectionType(EConnectionType::EConnectionType::GUARDED) + connectionType(rmg::EConnectionType::GUARDED), + hasRoad(rmg::ERoadOption::ROAD_TRUE) { } @@ -475,10 +476,15 @@ int ZoneConnection::getGuardStrength() const return guardStrength; } -EConnectionType::EConnectionType ZoneConnection::getConnectionType() const +rmg::EConnectionType ZoneConnection::getConnectionType() const { return connectionType; } + +rmg::ERoadOption ZoneConnection::getRoadOption() const +{ + return hasRoad; +} bool operator==(const ZoneConnection & l, const ZoneConnection & r) { @@ -495,10 +501,18 @@ void ZoneConnection::serializeJson(JsonSerializeFormat & handler) "wide" }; + static const std::vector roadOptions = + { + "true", + "false", + "random" + }; + handler.serializeId("a", zoneA, -1); handler.serializeId("b", zoneB, -1); handler.serializeInt("guard", guardStrength, 0); handler.serializeEnum("type", connectionType, connectionTypes); + handler.serializeEnum("road", hasRoad, roadOptions); } } diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 05efe9496..3f5a91197 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -67,20 +67,24 @@ public: void serializeJson(JsonSerializeFormat & handler); }; -namespace EConnectionType -{ - enum class EConnectionType - { - GUARDED = 0, //default - FICTIVE, - REPULSIVE, - WIDE - }; -} - namespace rmg { +enum class EConnectionType +{ + GUARDED = 0, //default + FICTIVE, + REPULSIVE, + WIDE +}; + +enum class ERoadOption +{ + ROAD_TRUE, + ROAD_FALSE, + ROAD_RANDOM +}; + class DLL_LINKAGE ZoneConnection { public: @@ -91,7 +95,8 @@ public: TRmgTemplateZoneId getZoneB() const; TRmgTemplateZoneId getOtherZoneId(TRmgTemplateZoneId id) const; int getGuardStrength() const; - EConnectionType::EConnectionType getConnectionType() const; + rmg::EConnectionType getConnectionType() const; + rmg::ERoadOption getRoadOption() const; void serializeJson(JsonSerializeFormat & handler); @@ -100,7 +105,8 @@ private: TRmgTemplateZoneId zoneA; TRmgTemplateZoneId zoneB; int guardStrength; - EConnectionType::EConnectionType connectionType; + rmg::EConnectionType connectionType; + rmg::ERoadOption hasRoad; }; class DLL_LINKAGE ZoneOptions diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 77f8ff0d0..abfb73421 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -75,7 +75,7 @@ void CZonePlacer::findPathsBetweenZones() for (auto & connection : connectedZoneIds) { - if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) + if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE) { //Do not consider virtual connections for graph distance continue; @@ -536,7 +536,7 @@ void CZonePlacer::attractConnectedZones(TZoneMap & zones, TForceVector & forces, for (const auto & connection : zone.second->getConnections()) { - if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) + if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE) { continue; } @@ -625,7 +625,7 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces //TODO: Consider z plane? for (auto& connection : zone.second->getConnections()) { - if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) + if (connection.getConnectionType() == rmg::EConnectionType::REPULSIVE) { auto & otherZone = zones[connection.getOtherZoneId(zone.second->getId())]; float3 otherZoneCenter = otherZone->getCenter(); @@ -693,7 +693,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista for (const auto& connection : firstZone->getConnections()) { //FIXME: Should we also exclude fictive connections? - if (connection.getConnectionType() != EConnectionType::EConnectionType::REPULSIVE) + if (connection.getConnectionType() != rmg::EConnectionType::REPULSIVE) { connectedZones.insert(connection.getOtherZoneId(firstZone->getId())); } @@ -740,7 +740,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista float maxDistance = 0; for (auto con : misplacedZone->getConnections()) { - if (con.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) + if (con.getConnectionType() == rmg::EConnectionType::REPULSIVE) { continue; } diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index ea75a0143..9b4178b4c 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -98,6 +98,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con bool success = false; auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); auto & otherZone = map.getZones().at(otherZoneId); + bool createRoad = shouldGenerateRoad(connection); //1. Try to make direct connection //Do if it's not prohibited by terrain settings @@ -110,7 +111,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con if (directConnectionIterator != dNeighbourZones.end()) { - if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE) + if (connection.getConnectionType() == rmg::EConnectionType::WIDE) { for (auto borderPos : directConnectionIterator->second) { @@ -159,8 +160,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con } } - if (connection.getConnectionType() == EConnectionType::EConnectionType::FICTIVE || - connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE) + if (connection.getConnectionType() == rmg::EConnectionType::FICTIVE || + connection.getConnectionType() == rmg::EConnectionType::REPULSIVE) { //Fictive or repulsive connections are not real, take no action dCompleted.push_back(connection); @@ -254,15 +255,18 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con otherZone->getModificator()->updateDistances(guardPos); } - assert(zone.getModificator()); - zone.getModificator()->addRoadNode(guardPos); - - assert(otherZone->getModificator()); - otherZone->getModificator()->addRoadNode(roadNode); - - assert(otherZone->getModificator()); - otherZone->getModificator()->otherSideConnection(connection); - + if (createRoad) + { + assert(zone.getModificator()); + zone.getModificator()->addRoadNode(guardPos); + + assert(otherZone->getModificator()); + otherZone->getModificator()->addRoadNode(roadNode); + + assert(otherZone->getModificator()); + otherZone->getModificator()->otherSideConnection(connection); + } + success = true; } } @@ -274,7 +278,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con { if(generator.getZoneWater() && generator.getZoneWater()->getModificator()) { - if(generator.getZoneWater()->getModificator()->waterKeepConnection(connection.getZoneA(), connection.getZoneB())) + if(generator.getZoneWater()->getModificator()->waterKeepConnection(connection, createRoad)) { assert(otherZone->getModificator()); otherZone->getModificator()->otherSideConnection(connection); @@ -292,6 +296,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c bool success = false; auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); auto & otherZone = map.getZones().at(otherZoneId); + + bool allowRoad = shouldGenerateRoad(connection); //3. place subterrain gates if(zone.isUnderground() != otherZone->isUnderground()) @@ -341,8 +347,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c zone.connectPath(path1); otherZone->connectPath(path2); - manager.placeObject(rmgGate1, guarded1, true); - managerOther.placeObject(rmgGate2, guarded2, true); + manager.placeObject(rmgGate1, guarded1, true, allowRoad); + managerOther.placeObject(rmgGate2, guarded2, true, allowRoad); assert(otherZone->getModificator()); otherZone->getModificator()->otherSideConnection(connection); @@ -359,8 +365,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c auto * teleport1 = factory->create(); auto * teleport2 = factory->create(); - zone.getModificator()->addRequiredObject(teleport1, connection.getGuardStrength()); - otherZone->getModificator()->addRequiredObject(teleport2, connection.getGuardStrength()); + RequiredObjectInfo obj1(teleport1, connection.getGuardStrength(), allowRoad); + RequiredObjectInfo obj2(teleport2, connection.getGuardStrength(), allowRoad); + zone.getModificator()->addRequiredObject(obj1); + otherZone->getModificator()->addRequiredObject(obj2); assert(otherZone->getModificator()); otherZone->getModificator()->otherSideConnection(connection); @@ -386,6 +394,12 @@ void ConnectionsPlacer::collectNeighbourZones() } } +bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const +{ + return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE || + (connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM && zone.getRand().nextDouble() >= 0.5f); +} + void ConnectionsPlacer::createBorder() { rmg::Area borderArea(zone.getArea().getBorder()); @@ -401,7 +415,7 @@ void ConnectionsPlacer::createBorder() { auto otherZone = connection.getOtherZoneId(zone.getId()); - if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE) + if (connection.getConnectionType() == rmg::EConnectionType::WIDE) { auto sharedBorder = borderArea.getSubarea([this, otherZone, &borderOutsideArea](const int3 & t) { diff --git a/lib/rmg/modificators/ConnectionsPlacer.h b/lib/rmg/modificators/ConnectionsPlacer.h index abb7cafa9..744a91b3c 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.h +++ b/lib/rmg/modificators/ConnectionsPlacer.h @@ -28,6 +28,8 @@ public: void selfSideIndirectConnection(const rmg::ZoneConnection & connection); void otherSideConnection(const rmg::ZoneConnection & connection); void createBorder(); + + bool shouldGenerateRoad(const rmg::ZoneConnection& connection) const; protected: void collectNeighbourZones(); diff --git a/lib/rmg/modificators/MinePlacer.cpp b/lib/rmg/modificators/MinePlacer.cpp index 758028f6c..880e0db9a 100644 --- a/lib/rmg/modificators/MinePlacer.cpp +++ b/lib/rmg/modificators/MinePlacer.cpp @@ -63,8 +63,11 @@ bool MinePlacer::placeMines(ObjectManager & manager) createdMines.push_back(mine); - if(!i && (res == EGameResID::WOOD || res == EGameResID::ORE)) - manager.addCloseObject(mine, rmginfo.value); //only first wood&ore mines are close + if (!i && (res == EGameResID::WOOD || res == EGameResID::ORE)) + { + //only first wood & ore mines are close + manager.addCloseObject(RequiredObjectInfo(mine, rmginfo.value)); + } else requiredObjects.push_back(std::pair(mine, rmginfo.value)); } @@ -74,7 +77,7 @@ bool MinePlacer::placeMines(ObjectManager & manager) RandomGeneratorUtil::randomShuffle(requiredObjects, zone.getRand()); for (const auto& obj : requiredObjects) { - manager.addRequiredObject(obj.first, obj.second); + manager.addRequiredObject(RequiredObjectInfo(obj.first, obj.second)); } //create extra resources @@ -84,9 +87,13 @@ bool MinePlacer::placeMines(ObjectManager & manager) { for(int rc = zone.getRand().nextInt(1, extraRes); rc > 0; --rc) { - auto * resourse = dynamic_cast(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create()); - resourse->amount = CGResource::RANDOM_AMOUNT; - manager.addNearbyObject(resourse, mine); + auto * resource = dynamic_cast(VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create()); + resource->amount = CGResource::RANDOM_AMOUNT; + + RequiredObjectInfo roi; + roi.obj = resource; + roi.nearbyTarget = mine; + manager.addNearbyObject(roi); } } } diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 5de046baf..2e75c38a1 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -57,22 +57,22 @@ void ObjectManager::createDistancesPriorityQueue() } } -void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength) +void ObjectManager::addRequiredObject(const RequiredObjectInfo & info) { RecursiveLock lock(externalAccessMutex); - requiredObjects.emplace_back(obj, strength); + requiredObjects.emplace_back(info); } -void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength) +void ObjectManager::addCloseObject(const RequiredObjectInfo & info) { RecursiveLock lock(externalAccessMutex); - closeObjects.emplace_back(obj, strength); + closeObjects.emplace_back(info); } -void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget) +void ObjectManager::addNearbyObject(const RequiredObjectInfo & info) { RecursiveLock lock(externalAccessMutex); - nearbyObjects.emplace_back(obj, nearbyTarget); + nearbyObjects.emplace_back(info); } void ObjectManager::updateDistances(const rmg::Object & obj) @@ -331,13 +331,11 @@ bool ObjectManager::createRequiredObjects() logGlobal->trace("Creating required objects"); //RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop? - for(const auto & object : requiredObjects) + for(const auto & objInfo : requiredObjects) { - auto * obj = object.first; - //FIXME: Invalid dObject inside object? - rmg::Object rmgObject(*obj); + rmg::Object rmgObject(*objInfo.obj); rmgObject.setTemplate(zone.getTerrainType()); - bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); + bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY)); Zone::Lock lock(zone.areaMutex); auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE); @@ -349,14 +347,14 @@ bool ObjectManager::createRequiredObjects() } zone.connectPath(path); - placeObject(rmgObject, guarded, true); + placeObject(rmgObject, guarded, true, objInfo.createRoad); for(const auto & nearby : nearbyObjects) { - if(nearby.second != obj) + if(nearby.nearbyTarget != nearby.obj) continue; - rmg::Object rmgNearObject(*nearby.first); + rmg::Object rmgNearObject(*nearby.obj); rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); possibleArea.intersect(zone.areaPossible()); if(possibleArea.empty()) @@ -366,21 +364,18 @@ bool ObjectManager::createRequiredObjects() } rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand())); - placeObject(rmgNearObject, false, false); + placeObject(rmgNearObject, false, false, nearby.createRoad); } } - for(const auto & object : closeObjects) + for(const auto & objInfo : closeObjects) { - auto * obj = object.first; - - //TODO: Wrap into same area proxy? Zone::Lock lock(zone.areaMutex); auto possibleArea = zone.areaPossible(); - rmg::Object rmgObject(*obj); + rmg::Object rmgObject(*objInfo.obj); rmgObject.setTemplate(zone.getTerrainType()); - bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); + bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY)); auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, [this, &rmgObject](const int3 & tile) { @@ -401,10 +396,10 @@ bool ObjectManager::createRequiredObjects() for(const auto & nearby : nearbyObjects) { - if(nearby.second != obj) + if(nearby.nearbyTarget != objInfo.obj) continue; - rmg::Object rmgNearObject(*nearby.first); + rmg::Object rmgNearObject(*nearby.obj); rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); possibleArea.intersect(zone.areaPossible()); if(possibleArea.empty()) @@ -420,10 +415,10 @@ bool ObjectManager::createRequiredObjects() //create object on specific positions //TODO: implement guards - for (const auto &obj : instantObjects) + for (const auto &objInfo : instantObjects) { - rmg::Object rmgObject(*obj.first); - rmgObject.setPosition(obj.second); + rmg::Object rmgObject(*objInfo.obj); + rmgObject.setPosition(objInfo.pos); placeObject(rmgObject, false, false); } @@ -435,7 +430,7 @@ bool ObjectManager::createRequiredObjects() return true; } -void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance) +void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/) { object.finalize(map); @@ -492,25 +487,27 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD break; } } - - switch(object.instances().front()->object().ID) + + if (createRoad) { - case Obj::TOWN: - case Obj::RANDOM_TOWN: - case Obj::MONOLITH_TWO_WAY: - case Obj::MONOLITH_ONE_WAY_ENTRANCE: + if (auto* m = zone.getModificator()) + m->addRoadNode(object.instances().front()->getVisitablePosition()); + } + + //TODO: Add road node to these objects: + /* + case Obj::MONOLITH_ONE_WAY_ENTRANCE: + case Obj::RANDOM_TOWN: case Obj::MONOLITH_ONE_WAY_EXIT: - case Obj::SUBTERRANEAN_GATE: - case Obj::SHIPYARD: - if(auto * m = zone.getModificator()) - m->addRoadNode(object.instances().front()->getVisitablePosition()); - break; - + */ + + switch (object.instances().front()->object().ID) + { case Obj::WATER_WHEEL: - if(auto * m = zone.getModificator()) + if (auto* m = zone.getModificator()) m->addRiverNode(object.instances().front()->getVisitablePosition()); break; - + default: break; } @@ -613,4 +610,20 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard return true; } +RequiredObjectInfo::RequiredObjectInfo(): + obj(nullptr), + nearbyTarget(nullptr), + guardStrength(0), + createRoad(true) +{} + +RequiredObjectInfo::RequiredObjectInfo(CGObjectInstance* obj, ui32 guardStrength, bool createRoad, CGObjectInstance* nearbyTarget): + obj(obj), + nearbyTarget(nearbyTarget), + guardStrength(guardStrength), + createRoad(createRoad) +{} + VCMI_LIB_NAMESPACE_END + + diff --git a/lib/rmg/modificators/ObjectManager.h b/lib/rmg/modificators/ObjectManager.h index 402743048..f346a921a 100644 --- a/lib/rmg/modificators/ObjectManager.h +++ b/lib/rmg/modificators/ObjectManager.h @@ -29,6 +29,18 @@ struct DistanceMaximizeFunctor } }; +struct RequiredObjectInfo +{ + RequiredObjectInfo(); + RequiredObjectInfo(CGObjectInstance* obj, ui32 guardStrength = 0, bool createRoad = false, CGObjectInstance* nearbyTarget = nullptr); + + CGObjectInstance* obj; + CGObjectInstance* nearbyTarget; + int3 pos; + ui32 guardStrength; + bool createRoad; +}; + class ObjectManager: public Modificator { public: @@ -45,9 +57,9 @@ public: void process() override; void init() override; - void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); - void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); - void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget); + void addRequiredObject(const RequiredObjectInfo & info); + void addCloseObject(const RequiredObjectInfo & info); + void addNearbyObject(const RequiredObjectInfo & info); bool createRequiredObjects(); @@ -59,7 +71,7 @@ public: CGCreature * chooseGuard(si32 strength, bool zoneGuard = false); bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false); - void placeObject(rmg::Object & object, bool guarded, bool updateDistance); + void placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad = false); void updateDistances(const rmg::Object & obj); void updateDistances(const int3& pos); @@ -72,10 +84,10 @@ public: protected: //content info - std::vector> requiredObjects; - std::vector> closeObjects; - std::vector> instantObjects; - std::vector> nearbyObjects; + std::vector requiredObjects; + std::vector closeObjects; + std::vector instantObjects; + std::vector nearbyObjects; std::vector objects; rmg::Area objectsVisitableArea; diff --git a/lib/rmg/modificators/RoadPlacer.cpp b/lib/rmg/modificators/RoadPlacer.cpp index dd9768fc1..e78808cfc 100644 --- a/lib/rmg/modificators/RoadPlacer.cpp +++ b/lib/rmg/modificators/RoadPlacer.cpp @@ -79,25 +79,41 @@ bool RoadPlacer::createRoad(const int3 & dst) } void RoadPlacer::drawRoads(bool secondary) -{ - if((secondary && generator.getConfig().secondaryRoadType.empty()) - || (!secondary && generator.getConfig().defaultRoadType.empty())) - return; - - //RecursiveLock lock(externalAccessMutex); +{ { - //FIXME: double lock - unsafe + //Clean space under roads even if they won't be eventually generated Zone::Lock lock(zone.areaMutex); zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); } + if (!generator.getMapGenOptions().isRoadEnabled()) + { + return; + } + + if((secondary && generator.getConfig().secondaryRoadType.empty()) + || (!secondary && generator.getConfig().defaultRoadType.empty())) + return; + + //TODO: Allow custom road type for object + //TODO: Remove these default types + auto tiles = roads.getTilesVector(); std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName)); - mapProxy->drawRoads(zone.getRand(), tiles, roadType); + + //If our road type is not enabled, choose highest below it + for (int8_t bestRoad = roadType.getNum(); bestRoad > RoadId(Road::NO_ROAD).getNum(); bestRoad--) + { + if (generator.getMapGenOptions().isRoadEnabled(RoadId(bestRoad))) + { + mapProxy->drawRoads(zone.getRand(), tiles, RoadId(bestRoad)); + return; + } + } } void RoadPlacer::addRoadNode(const int3& node) diff --git a/lib/rmg/modificators/TownPlacer.cpp b/lib/rmg/modificators/TownPlacer.cpp index 883508c2c..ecb12d7b6 100644 --- a/lib/rmg/modificators/TownPlacer.cpp +++ b/lib/rmg/modificators/TownPlacer.cpp @@ -152,7 +152,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town) }, 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); + manager.placeObject(rmgObject, false, true, true); cleanupBoundaries(rmgObject); zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town return position; @@ -216,7 +216,9 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player placeMainTown(manager, *town); } else - manager.addRequiredObject(town); + { + manager.addRequiredObject(RequiredObjectInfo(town, 0, true)); + } totalTowns++; } } diff --git a/lib/rmg/modificators/WaterProxy.cpp b/lib/rmg/modificators/WaterProxy.cpp index cf4e58684..023a13af1 100644 --- a/lib/rmg/modificators/WaterProxy.cpp +++ b/lib/rmg/modificators/WaterProxy.cpp @@ -133,6 +133,8 @@ RouteInfo WaterProxy::waterRoute(Zone & dst) if(adopter->getCoastTiles().empty()) return result; + + bool createRoad = false; //block zones are not connected by template for(auto& lake : lakes) @@ -162,17 +164,23 @@ RouteInfo WaterProxy::waterRoute(Zone & dst) int zoneTowns = 0; if(auto * m = dst.getModificator()) zoneTowns = m->getTotalTowns(); + + if (vstd::contains(lake.keepRoads, dst.getId())) + { + createRoad = true; + } + //FIXME: Why are Shipyards not allowed in zones with no towns? if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns) { - if(placeShipyard(dst, lake, generator.getConfig().shipyardGuard, result)) + if(placeShipyard(dst, lake, generator.getConfig().shipyardGuard, createRoad, result)) { logGlobal->info("Shipyard successfully placed at zone %d", dst.getId()); } else { logGlobal->warn("Shipyard placement failed, trying boat at zone %d", dst.getId()); - if(placeBoat(dst, lake, result)) + if(placeBoat(dst, lake, createRoad, result)) { logGlobal->warn("Boat successfully placed at zone %d", dst.getId()); } @@ -184,7 +192,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst) } else { - if(placeBoat(dst, lake, result)) + if(placeBoat(dst, lake, createRoad, result)) { logGlobal->info("Boat successfully placed at zone %d", dst.getId()); } @@ -199,21 +207,29 @@ RouteInfo WaterProxy::waterRoute(Zone & dst) return result; } -bool WaterProxy::waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB) +bool WaterProxy::waterKeepConnection(const rmg::ZoneConnection & connection, bool createRoad) { + const auto & zoneA = connection.getZoneA(); + const auto & zoneB = connection.getZoneB(); + for(auto & lake : lakes) { if(lake.neighbourZones.count(zoneA) && lake.neighbourZones.count(zoneB)) { lake.keepConnections.insert(zoneA); lake.keepConnections.insert(zoneB); + if (createRoad) + { + lake.keepRoads.insert(zoneA); + lake.keepRoads.insert(zoneB); + } return true; } } return false; } -bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) +bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, RouteInfo & info) { auto * manager = zone.getModificator(); if(!manager) @@ -284,7 +300,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) zone.connectPath(path); land.connectPath(landPath); - manager->placeObject(rmgObject, false, true); + manager->placeObject(rmgObject, false, true, createRoad); land.getModificator()->updateDistances(rmgObject); //Keep land objects away from the boat break; } @@ -292,7 +308,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) return !boardingPositions.empty(); } -bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info) +bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool createRoad, RouteInfo & info) { auto * manager = land.getModificator(); if(!manager) @@ -372,7 +388,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route info.boarding = boardingPosition; info.water = shipPositions; - manager->placeObject(rmgObject, guarded, true); + manager->placeObject(rmgObject, guarded, true, createRoad); zone.areaPossible().subtract(shipyardOutToBlock); for(const auto & i : shipyardOutToBlock.getTilesVector()) diff --git a/lib/rmg/modificators/WaterProxy.h b/lib/rmg/modificators/WaterProxy.h index 4d2c11a3b..a370ec74a 100644 --- a/lib/rmg/modificators/WaterProxy.h +++ b/lib/rmg/modificators/WaterProxy.h @@ -34,9 +34,10 @@ public: std::map reverseDistanceMap; std::map neighbourZones; //zones boardered. Area - part of land std::set keepConnections; + std::set keepRoads; }; - bool waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB); + bool waterKeepConnection(const rmg::ZoneConnection & connection, bool createRoad); RouteInfo waterRoute(Zone & dst); void process() override; @@ -47,8 +48,8 @@ public: protected: void collectLakes(); - bool placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info); - bool placeBoat(Zone & land, const Lake & lake, RouteInfo & info); + bool placeShipyard(Zone & land, const Lake & lake, si32 guard, bool createRoad, RouteInfo & info); + bool placeBoat(Zone & land, const Lake & lake, bool createRoad, RouteInfo & info); protected: std::vector lakes; //disconnected parts of zone. Used to work with water zones