From 855ab0f0e969046c1c064d2dab33250c57135f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 21 Nov 2024 22:00:24 +0100 Subject: [PATCH 1/5] First attempt to enforce curved paths --- lib/rmg/RmgPath.cpp | 20 +++++++++++++++++++ lib/rmg/RmgPath.h | 4 +++- lib/rmg/modificators/ConnectionsPlacer.cpp | 17 ++++++++-------- lib/rmg/modificators/ObjectManager.cpp | 23 ++++++++++++++++++++++ lib/rmg/modificators/RoadPlacer.cpp | 2 +- 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lib/rmg/RmgPath.cpp b/lib/rmg/RmgPath.cpp index 134f08267..62f31a8fc 100644 --- a/lib/rmg/RmgPath.cpp +++ b/lib/rmg/RmgPath.cpp @@ -190,4 +190,24 @@ const Area & Path::getPathArea() const return dPath; } +Path::MoveCostFunction Path::createCurvedCostFunction(const Area & border) +{ + // Capture by value to ensure the Area object persists + return [border = border](const int3& src, const int3& dst) -> float + { + // Route main roads far from border + //float ret = dst.dist2d(src); + float ret = dst.dist2d(src); + + float dist = border.distanceSqr(dst); + //int3 closestTile = border.nearest(dst); + //float dist = dst.chebdist2d(closestTile); + if(dist > 1.0f) + { + ret /= dist * dist; + } + return ret; + }; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/RmgPath.h b/lib/rmg/RmgPath.h index a76df8cfa..8cfe9e59f 100644 --- a/lib/rmg/RmgPath.h +++ b/lib/rmg/RmgPath.h @@ -21,7 +21,8 @@ namespace rmg class Path { public: - const static std::function DEFAULT_MOVEMENT_FUNCTION; + typedef std::function MoveCostFunction; + const static MoveCostFunction DEFAULT_MOVEMENT_FUNCTION; Path(const Area & area); Path(const Area & area, const int3 & src); @@ -42,6 +43,7 @@ public: const Area & getPathArea() const; static Path invalid(); + static MoveCostFunction createCurvedCostFunction(const Area & border); private: diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index 79c4ff438..a36de74ef 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -278,24 +278,23 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con assert(zone.getModificator()); auto & manager = *zone.getModificator(); auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true); - + rmg::Area border(zone.area()->getBorder()); border.unite(otherZone->area()->getBorder()); - - auto costFunction = [&border](const int3 & s, const int3 & d) - { - return 1.f / (1.f + border.distanceSqr(d)); - }; - + + auto localCostFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); + auto otherCostFunction = rmg::Path::createCurvedCostFunction(otherZone->area()->getBorder()); + + // TODO: helper function for this sum? auto ourArea = zone.areaPossible() + zone.freePaths(); auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); theirArea.add(guardPos); rmg::Path ourPath(ourArea); rmg::Path theirPath(theirArea); ourPath.connect(zone.freePaths().get()); - ourPath = ourPath.search(guardPos, true, costFunction); + ourPath = ourPath.search(guardPos, true, localCostFunction); theirPath.connect(otherZone->freePaths().get()); - theirPath = theirPath.search(guardPos, true, costFunction); + theirPath = theirPath.search(guardPos, true, otherCostFunction); if(ourPath.valid() && theirPath.valid()) { diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index c256a85c1..32e89cb67 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -419,6 +419,11 @@ bool ObjectManager::createMonoliths() return false; } + // Once it can be created, replace with curved path + auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); + rmg::Path curvedPath(zone.areaPossible() + zone.freePaths()); + path = curvedPath.search(rmgObject.getVisitablePosition(), true, costFunction); + zone.connectPath(path); placeObject(rmgObject, guarded, true, objInfo.createRoad); } @@ -449,6 +454,24 @@ bool ObjectManager::createRequiredObjects() logGlobal->error("Failed to fill zone %d due to lack of space", zone.getId()); return false; } + if (objInfo.createRoad) + { + // Once valid path can be created, replace with curved path + + auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); + auto pathArea = zone.areaPossible() + zone.freePaths(); + rmg::Path curvedPath(pathArea); + curvedPath.connect(zone.freePaths().get()); + curvedPath = curvedPath.search(rmgObject.getVisitablePosition(), false, costFunction); + if (curvedPath.valid()) + { + path = curvedPath; + } + else + { + logGlobal->warn("Failed to create curved path for required object at %s", rmgObject.getPosition().toString()); + } + } zone.connectPath(path); placeObject(rmgObject, guarded, true, objInfo.createRoad); diff --git a/lib/rmg/modificators/RoadPlacer.cpp b/lib/rmg/modificators/RoadPlacer.cpp index a2a404766..482515bc9 100644 --- a/lib/rmg/modificators/RoadPlacer.cpp +++ b/lib/rmg/modificators/RoadPlacer.cpp @@ -91,7 +91,7 @@ bool RoadPlacer::createRoad(const int3 & destination) float dist = border.distanceSqr(dst); if(dist > 1.0f) { - ret /= dist; + ret /= dist * dist; } return ret; } From 9277c5f4e777f0218d620019bf322bb7c13b1273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 21 Nov 2024 22:30:21 +0100 Subject: [PATCH 2/5] Fixed main issue with custom path routing --- lib/rmg/RmgPath.cpp | 10 +++------- lib/rmg/modificators/RoadPlacer.cpp | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/rmg/RmgPath.cpp b/lib/rmg/RmgPath.cpp index 62f31a8fc..b36628f98 100644 --- a/lib/rmg/RmgPath.cpp +++ b/lib/rmg/RmgPath.cpp @@ -116,8 +116,7 @@ Path Path::search(const Tileset & dst, bool straight, std::functioncontains(pos)) return; - float movementCost = moveCostFunction(currentNode, pos) + currentNode.dist2d(pos); - + float movementCost = moveCostFunction(currentNode, pos); float distance = distances[currentNode] + movementCost; //we prefer to use already free paths int bestDistanceSoFar = std::numeric_limits::max(); auto it = distances.find(pos); @@ -196,15 +195,12 @@ Path::MoveCostFunction Path::createCurvedCostFunction(const Area & border) return [border = border](const int3& src, const int3& dst) -> float { // Route main roads far from border - //float ret = dst.dist2d(src); float ret = dst.dist2d(src); - float dist = border.distanceSqr(dst); - //int3 closestTile = border.nearest(dst); - //float dist = dst.chebdist2d(closestTile); + if(dist > 1.0f) { - ret /= dist * dist; + ret /= dist; } return ret; }; diff --git a/lib/rmg/modificators/RoadPlacer.cpp b/lib/rmg/modificators/RoadPlacer.cpp index 482515bc9..a2a404766 100644 --- a/lib/rmg/modificators/RoadPlacer.cpp +++ b/lib/rmg/modificators/RoadPlacer.cpp @@ -91,7 +91,7 @@ bool RoadPlacer::createRoad(const int3 & destination) float dist = border.distanceSqr(dst); if(dist > 1.0f) { - ret /= dist * dist; + ret /= dist; } return ret; } From f95bb58433f972d24f7867672e9fdf433b240262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 22 Nov 2024 09:28:37 +0100 Subject: [PATCH 3/5] Implement curved paths for all road node objects --- lib/rmg/Functions.cpp | 17 +++++++++++++++++ lib/rmg/Functions.h | 2 ++ lib/rmg/Zone.cpp | 5 +++++ lib/rmg/Zone.h | 2 ++ lib/rmg/modificators/ConnectionsPlacer.cpp | 19 +++++++++++-------- lib/rmg/modificators/ObjectManager.cpp | 21 +++------------------ lib/rmg/modificators/RiverPlacer.cpp | 2 +- lib/rmg/modificators/WaterProxy.cpp | 8 ++++---- 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index fd855e6be..62e56154c 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -24,6 +24,23 @@ VCMI_LIB_NAMESPACE_BEGIN +void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src) +{ + auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); + auto pathArea = zone.areaForRoads(); + rmg::Path curvedPath(pathArea); + curvedPath.connect(zone.freePaths().get()); + curvedPath = curvedPath.search(src, false, costFunction); + if (curvedPath.valid()) + { + path = curvedPath; + } + else + { + logGlobal->warn("Failed to create curved path to %s", src.toString()); + } +} + rmg::Tileset collectDistantTiles(const Zone& zone, int distance) { uint32_t distanceSq = distance * distance; diff --git a/lib/rmg/Functions.h b/lib/rmg/Functions.h index 2756d0897..012178401 100644 --- a/lib/rmg/Functions.h +++ b/lib/rmg/Functions.h @@ -34,6 +34,8 @@ public: } }; +void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src); + rmg::Tileset collectDistantTiles(const Zone & zone, int distance); int chooseRandomAppearance(vstd::RNG & generator, si32 ObjID, TerrainId terrain); diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index 273dc226e..8a4972e65 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -121,6 +121,11 @@ ThreadSafeProxy Zone::areaUsed() const return ThreadSafeProxy(dAreaUsed, areaMutex); } +rmg::Area Zone::areaForRoads() const +{ + return areaPossible() + freePaths(); +} + void Zone::clearTiles() { Lock lock(areaMutex); diff --git a/lib/rmg/Zone.h b/lib/rmg/Zone.h index 4ae2c7a13..5b18a6fe8 100644 --- a/lib/rmg/Zone.h +++ b/lib/rmg/Zone.h @@ -92,6 +92,8 @@ public: ThreadSafeProxy freePaths() const; ThreadSafeProxy areaUsed(); ThreadSafeProxy areaUsed() const; + + rmg::Area areaForRoads() const; void initFreeTiles(); void clearTiles(); diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index a36de74ef..98311055c 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -185,8 +185,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con return 1.f / (1.f + border.distanceSqr(d)); }; - auto ourArea = zone.areaPossible() + zone.freePaths(); - auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); + auto ourArea = zone.areaForRoads(); + auto theirArea = otherZone->areaForRoads(); theirArea.add(potentialPos); rmg::Path ourPath(ourArea); rmg::Path theirPath(theirArea); @@ -285,9 +285,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con auto localCostFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); auto otherCostFunction = rmg::Path::createCurvedCostFunction(otherZone->area()->getBorder()); - // TODO: helper function for this sum? - auto ourArea = zone.areaPossible() + zone.freePaths(); - auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); + auto ourArea = zone.areaForRoads(); + auto theirArea = otherZone->areaForRoads(); theirArea.add(guardPos); rmg::Path ourPath(ourArea); rmg::Path theirPath(theirArea); @@ -417,11 +416,15 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c if(path1.valid() && path2.valid()) { - zone.connectPath(path1); - otherZone->connectPath(path2); - manager.placeObject(rmgGate1, guarded1, true, allowRoad); managerOther.placeObject(rmgGate2, guarded2, true, allowRoad); + + // FIXME: Guard can still be placed on the object + replaceWithCurvedPath(path1, zone, rmgGate1.getVisitablePosition()); + replaceWithCurvedPath(path2, *otherZone, rmgGate2.getVisitablePosition()); + + zone.connectPath(path1); + otherZone->connectPath(path2); assert(otherZone->getModificator()); otherZone->getModificator()->otherSideConnection(connection); diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 32e89cb67..13b8b3000 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -344,7 +344,7 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg { int3 pos; auto possibleArea = searchArea; - auto cachedArea = zone.areaPossible() + zone.freePaths(); + auto cachedArea = zone.areaForRoads(); while(true) { pos = findPlaceForObject(possibleArea, obj, weightFunction, optimizer); @@ -420,9 +420,7 @@ bool ObjectManager::createMonoliths() } // Once it can be created, replace with curved path - auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); - rmg::Path curvedPath(zone.areaPossible() + zone.freePaths()); - path = curvedPath.search(rmgObject.getVisitablePosition(), true, costFunction); + replaceWithCurvedPath(path, zone, rmgObject.getVisitablePosition()); zone.connectPath(path); placeObject(rmgObject, guarded, true, objInfo.createRoad); @@ -457,20 +455,7 @@ bool ObjectManager::createRequiredObjects() if (objInfo.createRoad) { // Once valid path can be created, replace with curved path - - auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); - auto pathArea = zone.areaPossible() + zone.freePaths(); - rmg::Path curvedPath(pathArea); - curvedPath.connect(zone.freePaths().get()); - curvedPath = curvedPath.search(rmgObject.getVisitablePosition(), false, costFunction); - if (curvedPath.valid()) - { - path = curvedPath; - } - else - { - logGlobal->warn("Failed to create curved path for required object at %s", rmgObject.getPosition().toString()); - } + replaceWithCurvedPath(path, zone, rmgObject.getVisitablePosition()); } zone.connectPath(path); diff --git a/lib/rmg/modificators/RiverPlacer.cpp b/lib/rmg/modificators/RiverPlacer.cpp index dd4150627..9983d2011 100644 --- a/lib/rmg/modificators/RiverPlacer.cpp +++ b/lib/rmg/modificators/RiverPlacer.cpp @@ -212,7 +212,7 @@ void RiverPlacer::preprocess() { auto river = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river; auto & a = neighbourZonesTiles[connectedToWaterZoneId]; - auto availableArea = zone.areaPossible() + zone.freePaths(); + auto availableArea = zone.areaForRoads(); for(const auto & tileToProcess : availableArea.getTilesVector()) { int templateId = -1; diff --git a/lib/rmg/modificators/WaterProxy.cpp b/lib/rmg/modificators/WaterProxy.cpp index 544c6f197..5172910fa 100644 --- a/lib/rmg/modificators/WaterProxy.cpp +++ b/lib/rmg/modificators/WaterProxy.cpp @@ -266,9 +266,9 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout rmg::Object rmgObject(*boat); rmgObject.setTemplate(zone.getTerrainType(), zone.getRand()); - auto waterAvailable = zone.areaPossible() + zone.freePaths(); + auto waterAvailable = zone.areaForRoads(); rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles - coast.intersect(land.areaPossible() + land.freePaths()); //having only available land tiles + coast.intersect(land.areaForRoads()); //having only available land tiles 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 @@ -332,10 +332,10 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool rmgObject.setTemplate(land.getTerrainType(), zone.getRand()); bool guarded = manager->addGuard(rmgObject, guard); - auto waterAvailable = zone.areaPossible() + zone.freePaths(); + auto waterAvailable = zone.areaForRoads(); waterAvailable.intersect(lake.area); rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles - coast.intersect(land.areaPossible() + land.freePaths()); //having only available land tiles + coast.intersect(land.areaForRoads()); //having only available land tiles auto boardingPositions = coast.getSubarea([&waterAvailable](const int3 & tile) //tiles where boarding is possible { rmg::Area a({tile}); From c8fcc6ffcae6b48166e8ae7454aea2de761067dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 23 Nov 2024 08:25:55 +0100 Subject: [PATCH 4/5] Fix broken roads to portals or gates by the lower zone border --- lib/rmg/Functions.cpp | 4 ++-- lib/rmg/Functions.h | 2 +- lib/rmg/Zone.cpp | 1 - lib/rmg/modificators/ConnectionsPlacer.cpp | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index 62e56154c..2d76d2f49 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -24,13 +24,13 @@ VCMI_LIB_NAMESPACE_BEGIN -void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src) +void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src, bool onlyStraight) { auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder()); auto pathArea = zone.areaForRoads(); rmg::Path curvedPath(pathArea); curvedPath.connect(zone.freePaths().get()); - curvedPath = curvedPath.search(src, false, costFunction); + curvedPath = curvedPath.search(src, onlyStraight, costFunction); if (curvedPath.valid()) { path = curvedPath; diff --git a/lib/rmg/Functions.h b/lib/rmg/Functions.h index 012178401..dfec5c3d7 100644 --- a/lib/rmg/Functions.h +++ b/lib/rmg/Functions.h @@ -34,7 +34,7 @@ public: } }; -void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src); +void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src, bool onlyStraight = true); rmg::Tileset collectDistantTiles(const Zone & zone, int distance); diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index 8a4972e65..b59b26183 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -304,7 +304,6 @@ void Zone::fractalize() logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance); Lock lock(areaMutex); - // FIXME: Do not access Area directly rmg::Area clearedTiles(dAreaFree); rmg::Area possibleTiles(dAreaPossible); diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index 98311055c..ba67ca49f 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -419,7 +419,6 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c manager.placeObject(rmgGate1, guarded1, true, allowRoad); managerOther.placeObject(rmgGate2, guarded2, true, allowRoad); - // FIXME: Guard can still be placed on the object replaceWithCurvedPath(path1, zone, rmgGate1.getVisitablePosition()); replaceWithCurvedPath(path2, *otherZone, rmgGate2.getVisitablePosition()); From 82501dc553b59f8962c09e6a2efcffb56c907bfd Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 2 Dec 2024 13:15:27 +0100 Subject: [PATCH 5/5] Update lib/rmg/RmgPath.h Co-authored-by: Ivan Savenko --- lib/rmg/RmgPath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmg/RmgPath.h b/lib/rmg/RmgPath.h index 8cfe9e59f..7707e1abb 100644 --- a/lib/rmg/RmgPath.h +++ b/lib/rmg/RmgPath.h @@ -21,7 +21,7 @@ namespace rmg class Path { public: - typedef std::function MoveCostFunction; + using MoveCostFunction = std::function; const static MoveCostFunction DEFAULT_MOVEMENT_FUNCTION; Path(const Area & area);