diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 36e931505..97b94240a 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -13,6 +13,8 @@ #include "CZonePlacer.h" #include "../mapObjects/CObjectClassesHandler.h" +static const int3 dirs4[] = {int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0)}; + void CMapGenerator::foreach_neighbour(const int3 &pos, std::function foo) { for(const int3 &dir : dirs) @@ -23,6 +25,16 @@ void CMapGenerator::foreach_neighbour(const int3 &pos, std::function foo) +{ + for(const int3 &dir : dirs4) + { + int3 n = pos + dir; + if(map->isInTheMap(n)) + foo(n); + } +} + CMapGenerator::CMapGenerator() : zonesTotal(0), monolithIndex(0) @@ -421,8 +433,8 @@ void CMapGenerator::createConnections() setOccupied (guardPos, ETileType::FREE); //just in case monster is too weak to spawn zoneA->addMonster (this, guardPos, connection.getGuardStrength(), false, true); //zones can make paths only in their own area - zoneA->crunchPath (this, guardPos, posA, zoneA->getId(), zoneA->getFreePaths()); //make connection towards our zone center - zoneB->crunchPath (this, guardPos, posB, zoneB->getId(), zoneB->getFreePaths()); //make connection towards other zone center + zoneA->crunchRoad(this, guardPos, posA, zoneA->getFreePaths()); //make connection towards our zone center + zoneB->crunchRoad(this, guardPos, posB, zoneB->getFreePaths()); //make connection towards other zone center break; //we're done with this connection } } diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 3d46a1c2d..bca10f076 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -66,6 +66,7 @@ public: void createConnections(); void findZonesForQuestArts(); void foreach_neighbour(const int3 &pos, std::function foo); + void foreachDirectNeighbour(const int3 &pos, std::function foo); bool isBlocked(const int3 &tile) const; bool shouldBeBlocked(const int3 &tile) const; diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 5220113bd..2fa921761 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -575,7 +575,7 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen) //logGlobal->infoStream() << boost::format ("Zone %d subdivided fractally") %id; } -bool CRmgTemplateZone::crunchPath (CMapGenerator* gen, const int3 &src, const int3 &dst, TRmgTemplateZoneId zone, std::set* clearedTiles) +bool CRmgTemplateZone::crunchPath(CMapGenerator* gen, const int3 &src, const int3 &dst, std::set* clearedTiles, bool forRoad) { /* make shortest path with free tiles, reachning dst or closest already free tile. Avoid blocks. @@ -596,7 +596,8 @@ do not leave zone border } auto lastDistance = distance; - gen->foreach_neighbour (currentPos, [this, gen, ¤tPos, dst, &distance, &result, &end, clearedTiles](int3 &pos) + + auto processNeighbours = [this, gen, ¤tPos, dst, &distance, &result, &end, clearedTiles](int3 &pos) { if (!result) //not sure if lambda is worth it... { @@ -628,15 +629,21 @@ do not leave zone border } } } - }); - + }; + + if(forRoad) + gen->foreachDirectNeighbour(currentPos,processNeighbours); + else + gen->foreach_neighbour (currentPos,processNeighbours); + int3 anotherPos(-1, -1, -1); - if (!(result || distance < lastDistance)) //we do not advance, use more advaced pathfinding algorithm? + if (!(result || distance < lastDistance)) //we do not advance, use more advanced pathfinding algorithm? { //try any nearby tiles, even if its not closer than current float lastDistance = 2 * distance; //start with significantly larger value - gen->foreach_neighbour(currentPos, [this, gen, ¤tPos, dst, &lastDistance, &anotherPos, &end, clearedTiles](int3 &pos) + + auto processNeighbours2 = [this, gen, ¤tPos, dst, &lastDistance, &anotherPos, &end, clearedTiles](int3 &pos) { if (currentPos.dist2dSQ(dst) < lastDistance) //try closest tiles from all surrounding unused tiles { @@ -651,7 +658,14 @@ do not leave zone border } } } - }); + }; + + if(forRoad) + gen->foreachDirectNeighbour(currentPos,processNeighbours2); + else + gen->foreach_neighbour (currentPos,processNeighbours2); + + if (anotherPos.valid()) { if (clearedTiles) @@ -671,6 +685,26 @@ do not leave zone border return result; } +bool CRmgTemplateZone::crunchRoad(CMapGenerator* gen, const int3& src, const int3& dst, std::set* clearedTiles) +{ + std::set currentClearedTiles; + + if(crunchPath(gen, src, dst, ¤tClearedTiles, true)) + { + roads.insert(std::begin(currentClearedTiles), std::end(currentClearedTiles)); + + if(nullptr != clearedTiles) + clearedTiles->insert(std::begin(currentClearedTiles), std::end(currentClearedTiles)); + + return true; + } + else + { + return false; + } +} + + void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength) { requiredObjects.push_back(std::make_pair(obj, strength)); @@ -904,7 +938,7 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m gen->setOccupied(tile, ETileType::BLOCKED); //so that crunch path doesn't cut through objects } - if (!crunchPath (gen, closestTile, closestFreeTile, id)) + if (!crunchPath (gen, closestTile, closestFreeTile)) { //we can't connect this pile, just block it off and start over for (auto treasure : treasures) @@ -1239,7 +1273,22 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen) logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") %id; //TODO CLEANUP! return false; + } + + switch (obj.first->ID) + { + case Obj::TOWN: + case Obj::MONOLITH_TWO_WAY: + case Obj::SUBTERRANEAN_GATE: + { + crunchRoad(gen, this->pos, pos + obj.first->getVisitableOffset(), &freePaths); + } + break; + + default: + break; } + placeObject (gen, obj.first, pos); guardObject (gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY), true); @@ -1434,7 +1483,7 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen) void CRmgTemplateZone::drawRoads(CMapGenerator* gen) { std::vector tiles; - for (auto tile : freePaths) + for (auto tile : roads) { tiles.push_back (tile); } @@ -1715,7 +1764,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, { //crunching path may fail if center of the zone is directly over wide object //make sure object is accessible before surrounding it with blocked tiles - if (crunchPath (gen, tile, findClosestTile(freePaths, tile), id, addToFreePaths ? &freePaths : nullptr)) + if (crunchPath (gen, tile, findClosestTile(freePaths, tile), addToFreePaths ? &freePaths : nullptr)) { guardTile = tile; break; diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h index d1a16a407..0726c3d16 100644 --- a/lib/rmg/CRmgTemplateZone.h +++ b/lib/rmg/CRmgTemplateZone.h @@ -167,7 +167,8 @@ public: void createTreasures(CMapGenerator* gen); void createObstacles1(CMapGenerator* gen); void createObstacles2(CMapGenerator* gen); - bool crunchPath (CMapGenerator* gen, const int3 &src, const int3 &dst, TRmgTemplateZoneId zone, std::set* clearedTiles = nullptr); + bool crunchPath(CMapGenerator* gen, const int3 &src, const int3 &dst, std::set* clearedTiles = nullptr, bool forRoad = false); + bool crunchRoad(CMapGenerator* gen, const int3 &src, const int3 &dst, std::set* clearedTiles = nullptr); std::vector getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object); void addConnection(TRmgTemplateZoneId otherZone); @@ -217,6 +218,8 @@ private: std::vector connections; //list of adjacent zones std::set freePaths; //core paths of free tiles that all other objects will be linked to + std::set roads; + void drawRoads(CMapGenerator* gen); bool pointIsIn(int x, int y);