From 927dfa55652e9c5c0eff3a1d264a7bb2b573d422 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Thu, 4 Jun 2015 09:02:56 +0200 Subject: [PATCH] Added correct connections for Subterranean Gates --- lib/rmg/CMapGenerator.cpp | 10 +--- lib/rmg/CRmgTemplateZone.cpp | 112 ++++++++++++++++++++++++++++++++++- lib/rmg/CRmgTemplateZone.h | 9 ++- 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index a49e2e129..e09a61cd8 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -551,14 +551,8 @@ void CMapGenerator::createConnections() if (withinZone) { - auto gate1 = new CGSubterraneanGate; - gate1->ID = Obj::SUBTERRANEAN_GATE; - gate1->subID = 0; - zoneA->placeAndGuardObject(this, gate1, tile, connection.getGuardStrength()); - auto gate2 = new CGSubterraneanGate(*gate1); - zoneB->placeAndGuardObject(this, gate2, otherTile, connection.getGuardStrength()); - //TODO: connect gates with middle of zone / free paths. At this point free paths do not exist yet. - + zoneA->placeSubterraneanGate(this, tile, connection.getGuardStrength()); + zoneB->placeSubterraneanGate(this, otherTile, connection.getGuardStrength()); stop = true; //we are done, go to next connection } } diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index a19895c3b..f7caf6adc 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -608,6 +608,15 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen) //logGlobal->infoStream() << boost::format ("Zone %d subdivided fractally") %id; } +void CRmgTemplateZone::connectLater(CMapGenerator* gen) +{ + for (const int3 node : tilesToConnectLater) + { + if (!connectWithCenter(gen, node, true)) + logGlobal->errorStream() << boost::format("Failed to connect node %s with center of the zone") % node; + } +} + bool CRmgTemplateZone::crunchPath(CMapGenerator* gen, const int3 &src, const int3 &dst, bool onlyStraight, std::set* clearedTiles) { /* @@ -767,7 +776,7 @@ bool CRmgTemplateZone::createRoad(CMapGenerator* gen, const int3& src, const int auto foo = [gen, this, &open, &closed, &cameFrom, ¤tNode, &distances, &dst, &directNeighbourFound, movementCost](int3& pos) -> void { - int distance = distances[currentNode] + movementCost; + float distance = distances[currentNode] + movementCost; int bestDistanceSoFar = 1e6; //FIXME: boost::limits auto it = distances.find(pos); if (it != distances.end()) @@ -816,7 +825,6 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl std::map distances; int3 currentNode = src; - gen->setRoad(src, ERoadType::NO_ROAD); //just in case zone guard already has road under it. Road under nodes will be added at very end cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition distances[src] = 0; @@ -833,7 +841,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl vstd::erase_if_present(open, currentNode); closed.insert(currentNode); - if (gen->isFree(currentNode)) + if (gen->isFree(currentNode)) //we reached free paths, stop { // Trace the path using the saved parent information and return path int3 backTracking = currentNode; @@ -884,6 +892,84 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl return false; } +bool CRmgTemplateZone::connectWithCenter(CMapGenerator* gen, const int3& src, bool onlyStraight) +///connect current tile to any other free tile within zone +{ + //A* algorithm taken from Wiki http://en.wikipedia.org/wiki/A*_search_algorithm + + std::set closed; // The set of nodes already evaluated. + std::set open{ src }; // The set of tentative nodes to be evaluated, initially containing the start node + std::map cameFrom; // The map of navigated nodes. + std::map distances; + + int3 currentNode = src; + + cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition + distances[src] = 0; + // Cost from start along best known path. + // Estimated total cost from start to goal through y. + + while (open.size()) + { + int3 currentNode = *boost::min_element(open, [&distances](const int3 &pos1, const int3 &pos2) -> bool + { + return distances[pos1] < distances[pos2]; + }); + + vstd::erase_if_present(open, currentNode); + closed.insert(currentNode); + + if (currentNode == pos) //we reached center of the zone, stop + { + // Trace the path using the saved parent information and return path + int3 backTracking = currentNode; + while (cameFrom[backTracking].valid()) + { + gen->setOccupied(backTracking, ETileType::FREE); + backTracking = cameFrom[backTracking]; + } + return true; + } + else + { + auto foo = [gen, this, &open, &closed, &cameFrom, ¤tNode, &distances](int3& pos) -> void + { + float movementCost = 0; + if (gen->isFree(pos)) + movementCost = 1; + else if (gen->isPossible(pos)) + movementCost = 2; + else + return; + + float distance = distances[currentNode] + movementCost; //we prefer to use already free paths + int bestDistanceSoFar = 1e6; //FIXME: boost::limits + auto it = distances.find(pos); + if (it != distances.end()) + bestDistanceSoFar = it->second; + + if (distance < bestDistanceSoFar || !vstd::contains(closed, pos)) + { + auto obj = gen->map->getTile(pos).topVisitableObj(); + if (vstd::contains(this->tileinfo, pos)) + { + cameFrom[pos] = currentNode; + open.insert(pos); + distances[pos] = distance; + } + } + }; + + if (onlyStraight) + gen->foreachDirectNeighbour(currentNode, foo); + else + gen->foreach_neighbour(currentNode, foo); + } + + } + return false; +} + void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength) { @@ -894,6 +980,11 @@ void CRmgTemplateZone::addCloseObject(CGObjectInstance * obj, si32 strength) closeObjects.push_back(std::make_pair(obj, strength)); } +void CRmgTemplateZone::addToConnectLater(const int3& src) +{ + tilesToConnectLater.insert(src); +} + bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles, bool zoneGuard) { //precalculate actual (randomized) monster strength based on this post @@ -1255,6 +1346,8 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen) //first town in zone goes in the middle placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); cutPathAroundTown(town); + setPos(town->visitablePos() + (0, 1, 0)); //new center of zone that paths connect to + gen->setOccupied (this->getPos(), ETileType::FREE); } else addRequiredObject (town); @@ -1300,6 +1393,8 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen) //towns are big objects and should be centered around visitable position placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); //generate no guards, but free path to entrance cutPathAroundTown(town); + setPos(town->visitablePos() + (0, 1, 0)); //new center of zone that paths connect to + gen->setOccupied (getPos(), ETileType::FREE); totalTowns++; //register MAIN town of zone only @@ -1753,6 +1848,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen) addAllPossibleObjects (gen); + connectLater(gen); //ideally this should work after fractalize, but fails fractalize(gen); placeMines(gen); createRequiredObjects(gen); @@ -2001,6 +2097,16 @@ void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* guardObject(gen, object, str, zoneGuard); } +void CRmgTemplateZone::placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength) +{ + auto gate = new CGSubterraneanGate; + gate->ID = Obj::SUBTERRANEAN_GATE; + gate->subID = 0; + placeObject (gen, gate, pos, true); + addToConnectLater (getAccessibleOffset (gen, gate->appearance, pos)); //guard will be placed on accessibleOffset + guardObject (gen, gate, guardStrength, true); +} + std::vector CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object) { //get all tiles from which this object can be accessed diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h index 5b0a74ef8..00f50c23e 100644 --- a/lib/rmg/CRmgTemplateZone.h +++ b/lib/rmg/CRmgTemplateZone.h @@ -156,6 +156,7 @@ public: void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); + void addToConnectLater(const int3& src); bool addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles = true, bool zoneGuard = false); bool createTreasurePile(CMapGenerator* gen, int3 &pos, float minDistance, const CTreasureInfo& treasureInfo); bool fill (CMapGenerator* gen); @@ -166,12 +167,14 @@ public: void initTerrainType (CMapGenerator* gen); void createBorder(CMapGenerator* gen); void fractalize(CMapGenerator* gen); + void connectLater(CMapGenerator* gen); bool createRequiredObjects(CMapGenerator* gen); void createTreasures(CMapGenerator* gen); void createObstacles1(CMapGenerator* gen); void createObstacles2(CMapGenerator* gen); bool crunchPath(CMapGenerator* gen, const int3 &src, const int3 &dst, bool onlyStraight, std::set* clearedTiles = nullptr); bool connectPath(CMapGenerator* gen, const int3& src, bool onlyStraight); + bool connectWithCenter(CMapGenerator* gen, const int3& src, bool onlyStraight); std::vector getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object); @@ -184,6 +187,9 @@ public: ObjectInfo getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 desiredValue, ui32 maxValue, ui32 currentValue); + void placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength); + void placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, bool updateDistance = true); + bool guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard = false, bool addToFreePaths = false); void placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard = false); void addRoadNode(const int3 & node); void connectRoads(CMapGenerator * gen); //fills "roads" according to "roadNodes" @@ -226,6 +232,7 @@ private: std::set roadNodes; //tiles to be connected with roads std::set roads; //all tiles with roads + std::set tilesToConnectLater; //will be connected after paths are fractalized bool createRoad(CMapGenerator* gen, const int3 &src, const int3 &dst); void drawRoads(CMapGenerator * gen); //actually updates tiles @@ -240,6 +247,4 @@ private: void setTemplateForObject(CMapGenerator* gen, CGObjectInstance* obj); bool areAllTilesAvailable(CMapGenerator* gen, CGObjectInstance* obj, int3& tile, std::set& tilesBlockedByObject) const; void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos); - void placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, bool updateDistance = true); - bool guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard = false, bool addToFreePaths = false); };