1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-10 00:43:59 +02:00

Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Xilmi 2024-12-05 14:55:20 +01:00
commit 3f073507a1
10 changed files with 78 additions and 26 deletions

View File

@ -24,6 +24,23 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
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, onlyStraight, 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) rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
{ {
uint32_t distanceSq = distance * distance; uint32_t distanceSq = distance * distance;

View File

@ -34,6 +34,8 @@ public:
} }
}; };
void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src, bool onlyStraight = true);
rmg::Tileset collectDistantTiles(const Zone & zone, int distance); rmg::Tileset collectDistantTiles(const Zone & zone, int distance);
int chooseRandomAppearance(vstd::RNG & generator, si32 ObjID, TerrainId terrain); int chooseRandomAppearance(vstd::RNG & generator, si32 ObjID, TerrainId terrain);

View File

@ -116,8 +116,7 @@ Path Path::search(const Tileset & dst, bool straight, std::function<float(const
if(!result.dArea->contains(pos)) if(!result.dArea->contains(pos))
return; 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 float distance = distances[currentNode] + movementCost; //we prefer to use already free paths
int bestDistanceSoFar = std::numeric_limits<int>::max(); int bestDistanceSoFar = std::numeric_limits<int>::max();
auto it = distances.find(pos); auto it = distances.find(pos);
@ -190,4 +189,21 @@ const Area & Path::getPathArea() const
return dPath; 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 dist = border.distanceSqr(dst);
if(dist > 1.0f)
{
ret /= dist;
}
return ret;
};
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -21,7 +21,8 @@ namespace rmg
class Path class Path
{ {
public: public:
const static std::function<float(const int3 &, const int3 &)> DEFAULT_MOVEMENT_FUNCTION; using MoveCostFunction = std::function<float(const int3 &, const int3 &)>;
const static MoveCostFunction DEFAULT_MOVEMENT_FUNCTION;
Path(const Area & area); Path(const Area & area);
Path(const Area & area, const int3 & src); Path(const Area & area, const int3 & src);
@ -42,6 +43,7 @@ public:
const Area & getPathArea() const; const Area & getPathArea() const;
static Path invalid(); static Path invalid();
static MoveCostFunction createCurvedCostFunction(const Area & border);
private: private:

View File

@ -121,6 +121,11 @@ ThreadSafeProxy<const rmg::Area> Zone::areaUsed() const
return ThreadSafeProxy<const rmg::Area>(dAreaUsed, areaMutex); return ThreadSafeProxy<const rmg::Area>(dAreaUsed, areaMutex);
} }
rmg::Area Zone::areaForRoads() const
{
return areaPossible() + freePaths();
}
void Zone::clearTiles() void Zone::clearTiles()
{ {
Lock lock(areaMutex); Lock lock(areaMutex);
@ -299,7 +304,6 @@ void Zone::fractalize()
logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance); logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance);
Lock lock(areaMutex); Lock lock(areaMutex);
// FIXME: Do not access Area directly
rmg::Area clearedTiles(dAreaFree); rmg::Area clearedTiles(dAreaFree);
rmg::Area possibleTiles(dAreaPossible); rmg::Area possibleTiles(dAreaPossible);

View File

@ -93,6 +93,8 @@ public:
ThreadSafeProxy<rmg::Area> areaUsed(); ThreadSafeProxy<rmg::Area> areaUsed();
ThreadSafeProxy<const rmg::Area> areaUsed() const; ThreadSafeProxy<const rmg::Area> areaUsed() const;
rmg::Area areaForRoads() const;
void initFreeTiles(); void initFreeTiles();
void clearTiles(); void clearTiles();
void fractalize(); void fractalize();

View File

@ -185,8 +185,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
return 1.f / (1.f + border.distanceSqr(d)); return 1.f / (1.f + border.distanceSqr(d));
}; };
auto ourArea = zone.areaPossible() + zone.freePaths(); auto ourArea = zone.areaForRoads();
auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); auto theirArea = otherZone->areaForRoads();
theirArea.add(potentialPos); theirArea.add(potentialPos);
rmg::Path ourPath(ourArea); rmg::Path ourPath(ourArea);
rmg::Path theirPath(theirArea); rmg::Path theirPath(theirArea);
@ -282,20 +282,18 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
rmg::Area border(zone.area()->getBorder()); rmg::Area border(zone.area()->getBorder());
border.unite(otherZone->area()->getBorder()); border.unite(otherZone->area()->getBorder());
auto costFunction = [&border](const int3 & s, const int3 & d) auto localCostFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder());
{ auto otherCostFunction = rmg::Path::createCurvedCostFunction(otherZone->area()->getBorder());
return 1.f / (1.f + border.distanceSqr(d));
};
auto ourArea = zone.areaPossible() + zone.freePaths(); auto ourArea = zone.areaForRoads();
auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); auto theirArea = otherZone->areaForRoads();
theirArea.add(guardPos); theirArea.add(guardPos);
rmg::Path ourPath(ourArea); rmg::Path ourPath(ourArea);
rmg::Path theirPath(theirArea); rmg::Path theirPath(theirArea);
ourPath.connect(zone.freePaths().get()); ourPath.connect(zone.freePaths().get());
ourPath = ourPath.search(guardPos, true, costFunction); ourPath = ourPath.search(guardPos, true, localCostFunction);
theirPath.connect(otherZone->freePaths().get()); theirPath.connect(otherZone->freePaths().get());
theirPath = theirPath.search(guardPos, true, costFunction); theirPath = theirPath.search(guardPos, true, otherCostFunction);
if(ourPath.valid() && theirPath.valid()) if(ourPath.valid() && theirPath.valid())
{ {
@ -417,12 +415,15 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
if(path1.valid() && path2.valid()) if(path1.valid() && path2.valid())
{ {
zone.connectPath(path1);
otherZone->connectPath(path2);
manager.placeObject(rmgGate1, guarded1, true, allowRoad); manager.placeObject(rmgGate1, guarded1, true, allowRoad);
managerOther.placeObject(rmgGate2, guarded2, true, allowRoad); managerOther.placeObject(rmgGate2, guarded2, true, allowRoad);
replaceWithCurvedPath(path1, zone, rmgGate1.getVisitablePosition());
replaceWithCurvedPath(path2, *otherZone, rmgGate2.getVisitablePosition());
zone.connectPath(path1);
otherZone->connectPath(path2);
assert(otherZone->getModificator<ConnectionsPlacer>()); assert(otherZone->getModificator<ConnectionsPlacer>());
otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);

View File

@ -344,7 +344,7 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
{ {
int3 pos; int3 pos;
auto possibleArea = searchArea; auto possibleArea = searchArea;
auto cachedArea = zone.areaPossible() + zone.freePaths(); auto cachedArea = zone.areaForRoads();
while(true) while(true)
{ {
pos = findPlaceForObject(possibleArea, obj, weightFunction, optimizer); pos = findPlaceForObject(possibleArea, obj, weightFunction, optimizer);
@ -419,6 +419,9 @@ bool ObjectManager::createMonoliths()
return false; return false;
} }
// Once it can be created, replace with curved path
replaceWithCurvedPath(path, zone, rmgObject.getVisitablePosition());
zone.connectPath(path); zone.connectPath(path);
placeObject(rmgObject, guarded, true, objInfo.createRoad); placeObject(rmgObject, guarded, true, objInfo.createRoad);
} }
@ -449,6 +452,11 @@ bool ObjectManager::createRequiredObjects()
logGlobal->error("Failed to fill zone %d due to lack of space", zone.getId()); logGlobal->error("Failed to fill zone %d due to lack of space", zone.getId());
return false; return false;
} }
if (objInfo.createRoad)
{
// Once valid path can be created, replace with curved path
replaceWithCurvedPath(path, zone, rmgObject.getVisitablePosition());
}
zone.connectPath(path); zone.connectPath(path);
placeObject(rmgObject, guarded, true, objInfo.createRoad); placeObject(rmgObject, guarded, true, objInfo.createRoad);

View File

@ -212,7 +212,7 @@ void RiverPlacer::preprocess()
{ {
auto river = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river; auto river = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river;
auto & a = neighbourZonesTiles[connectedToWaterZoneId]; auto & a = neighbourZonesTiles[connectedToWaterZoneId];
auto availableArea = zone.areaPossible() + zone.freePaths(); auto availableArea = zone.areaForRoads();
for(const auto & tileToProcess : availableArea.getTilesVector()) for(const auto & tileToProcess : availableArea.getTilesVector())
{ {
int templateId = -1; int templateId = -1;

View File

@ -266,9 +266,9 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
rmg::Object rmgObject(*boat); rmg::Object rmgObject(*boat);
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand()); 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 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 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 //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()); rmgObject.setTemplate(land.getTerrainType(), zone.getRand());
bool guarded = manager->addGuard(rmgObject, guard); bool guarded = manager->addGuard(rmgObject, guard);
auto waterAvailable = zone.areaPossible() + zone.freePaths(); auto waterAvailable = zone.areaForRoads();
waterAvailable.intersect(lake.area); waterAvailable.intersect(lake.area);
rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles 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 auto boardingPositions = coast.getSubarea([&waterAvailable](const int3 & tile) //tiles where boarding is possible
{ {
rmg::Area a({tile}); rmg::Area a({tile});