Skip to content

Commit

Permalink
-Nodegen improvement: Paths from node a to node b are not generated i…
Browse files Browse the repository at this point in the history
…n tandem re-using the reverse of the other if one doesn't reach the destination.
  • Loading branch information
mostlikely4r committed Oct 22, 2024
1 parent 3b92faf commit 9dae093
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 112 deletions.
193 changes: 84 additions & 109 deletions playerbot/TravelNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,130 +215,118 @@ uint32 TravelNodePath::getPrice()
return taxiPath->price;
}

//Creates or appends the path from one node to another. Returns if the path.
TravelNodePath* TravelNode::buildPath(TravelNode* endNode, Unit* bot, bool postProcess, bool pathOnly)

uint32 TravelNode::getAreaTriggerId()
{
if (getMapId() != endNode->getMapId())
return nullptr;
for (auto link : *getLinks())
{
if (link.second->getPathType() != TravelNodePathType::areaTrigger)
continue;

if (pathOnly && hasLinkTo(endNode))
return nullptr;
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(link.second->getPathObject());
if (!atEntry)
continue;

TravelNodePath* returnNodePath;
WorldPosition inPos = WorldPosition(atEntry->mapid, atEntry->x, atEntry->y, atEntry->z - 4.0f, 0);

if (!hasPathTo(endNode)) //Create path if it doesn't exists
returnNodePath = setPathTo(endNode, TravelNodePath(), false);
else
returnNodePath = getPathTo(endNode); //Get the exsisting path.
if (*getPosition() == inPos)
return link.second->getPathObject();
}

if (returnNodePath->getComplete()) //Path is already complete. Return it.
return returnNodePath;
return 0;
}

std::vector<WorldPosition> path = returnNodePath->getPath();
bool TravelNode::isAreaTriggerTarget(uint32 areaTriggerId)
{
for (uint32 i = 0; i < sAreaTriggerStore.GetNumRows(); i++)
{
if (areaTriggerId && areaTriggerId != i)
continue;

if (path.empty())
path = { *getPosition() }; //Start the path from the current Node.
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(i);
if (!atEntry)
continue;

WorldPosition* endPos = endNode->getPosition(); //Build the path to the end Node.
AreaTrigger const* at = sObjectMgr.GetAreaTrigger(i);
if (!at)
continue;

path = endPos->getPathFromPath(path, bot); //Pathfind from the existing path to the end Node.
WorldPosition outPos = WorldPosition(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation);

bool canPath = endPos->isPathTo(path); //Check if we reached our destination.
if (*getPosition() == outPos)
return true;
}

if (!canPath && endNode->hasLinkTo(this)) //Unable to find a path? See if the reverse is possible.
{
TravelNodePath backNodePath = *endNode->getPathTo(this);
return false;
}

if (backNodePath.getPathType() == TravelNodePathType::walk)
{
std::vector<WorldPosition> bPath = backNodePath.getPath();
//Creates or appends the path from one node to another. Returns if the path.
TravelNodePath* TravelNode::buildPath(TravelNode* endNode, Unit* bot, bool postProcess)
{
if (getMapId() != endNode->getMapId())
return nullptr;

if (!backNodePath.getComplete()) //Build it if it's not already complete.
{
if (bPath.empty())
bPath = { *endNode->getPosition() }; //Start the path from the end Node.
TravelNodePath* returnNodePath;

WorldPosition* thisPos = getPosition(); //Build the path to this Node.
returnNodePath = getPathTo(endNode);

bPath = thisPos->getPathFromPath(bPath, bot); //Pathfind from the existing path to the this Node.
if (returnNodePath->getComplete())
return returnNodePath;

canPath = thisPos->isPathTo(bPath); //Check if we reached our destination.
}
else
canPath = true;
std::vector<WorldPosition> path = returnNodePath->getPath();

if (canPath)
{
std::reverse(bPath.begin(), bPath.end());
path = bPath;
}
}
}
WorldPosition startPos = *getPosition();
WorldPosition endPos = *endNode->getPosition();

//Transports are (probably?) not solid at this moment. We need to walk over them so we need extra code for this.
//Some portals are 'too' solid so we can't properly walk in them. Again we need to bypass this.
if (!isTransport() && !isPortal() && (endNode->isPortal() || endNode->isTransport()))
if (path.empty())
path = { startPos };

path = endPos.getPathFromPath(path, bot);
bool canPath = endPos.isPathTo(path);

if (canPath && path.size() == 2 && this->getDistance(endNode) > 5.0f) //Very small path probably bad pathfinder or flying. Stop using it.
canPath = false;

//Cheat a little for walk -> portal/transport.
if (!canPath && !isTransport() && !getAreaTriggerId() && (endNode->getAreaTriggerId() || endNode->isTransport()))
{
if (endNode->isTransport() && path.back().isInWater()) //Do not swim to boats.
canPath = false;
else if (!canPath && endPos->isPathTo(path, 20.0f)) //Cheat a little for transports and portals.
if (endPos.isPathTo(path, 20.0f))
{
path.push_back(*endPos);
path.push_back(endPos);
canPath = true;
}
}

if (!endNode->hasPathTo(this) || !endNode->getPathTo(this)->getComplete())
{
std::vector<WorldPosition> reversePath = path;
reverse(reversePath.begin(), reversePath.end());

TravelNodePath* backNodePath = endNode->setPathTo(this, TravelNodePath(), false);

backNodePath->setComplete(canPath);
TravelNodePath* backNodePath; //Get/Build the reverse path.

if(!pathOnly)
endNode->setLinkTo(this, true);
if (!endNode->hasPathTo(this))
backNodePath = endNode->buildPath(this, bot, postProcess);
else
backNodePath = endNode->getPathTo(this);

backNodePath->setPath(reversePath);
if (!canPath)
{
std::vector<WorldPosition> backPath = backNodePath->getPath();

backNodePath->calculateCost(!postProcess);
}
if (startPos.isPathTo(backPath))
{
std::reverse(backPath.begin(), backPath.end());
path = backPath;
canPath = true;
}
}

if (isTransport() && path.size() > 1)
{
WorldPosition secondPos = *std::next(path.begin()); //This is to prevent bots from jumping in the water from a transport. Need to remove this when transports are properly handled.
if (secondPos.getTerrain() && secondPos.isInWater())
canPath = false;
}

returnNodePath->setPath(path);
returnNodePath->setComplete(canPath);

if (!pathOnly && canPath && !hasLinkTo(endNode))
setLinkTo(endNode, true);
if (canPath && !hasLinkTo(endNode))
setLinkTo(endNode);

returnNodePath->setPath(path);

if (!pathOnly && !returnNodePath->getCalculated())
if (!returnNodePath->getCalculated())
{
returnNodePath->calculateCost(!postProcess);
}

if (!pathOnly && canPath && endNode->hasPathTo(this) && !endNode->hasLinkTo(this))
{
TravelNodePath* backNodePath = endNode->getPathTo(this);

std::vector<WorldPosition> reversePath = path;
reverse(reversePath.begin(), reversePath.end());
backNodePath->setPath(reversePath);
endNode->setLinkTo(this, true);

if (!backNodePath->getCalculated())
{
backNodePath->calculateCost(!postProcess);
}
}

return returnNodePath;
}

Expand Down Expand Up @@ -2330,13 +2318,13 @@ void TravelNodeMap::generateNodes()
generatePortalNodes();
}

void TravelNodeMap::generateWalkPathMap(uint32 mapId, bool pathOnly)
void TravelNodeMap::generateWalkPathMap(uint32 mapId)
{
for (auto& startNode : sTravelNodeMap.getNodes(WorldPosition(mapId, 1, 1)))
{
if (!pathOnly && startNode->isLinked())
if (startNode->isLinked())
continue;

for (auto& endNode : sTravelNodeMap.getNodes(*startNode->getPosition(), 2000.0f))
{
if (endNode->isTransport() && endNode->isLinked())
Expand All @@ -2348,21 +2336,17 @@ void TravelNodeMap::generateWalkPathMap(uint32 mapId, bool pathOnly)
if (startNode->hasCompletePathTo(endNode))
continue;

if (pathOnly && startNode->hasPathTo(endNode))
continue;

if (startNode->getMapId() != endNode->getMapId())
continue;

startNode->buildPath(endNode, nullptr, false, pathOnly);
startNode->buildPath(endNode, nullptr, false);
}

if(!pathOnly)
startNode->setLinked(true);
startNode->setLinked(true);
}
}

void TravelNodeMap::generateWalkPaths(bool pathOnly)
void TravelNodeMap::generateWalkPaths()
{
//Pathfinder
std::vector<WorldPosition> ppath;
Expand All @@ -2380,7 +2364,7 @@ void TravelNodeMap::generateWalkPaths(bool pathOnly)
for (auto& map : nodeMaps)
{
uint32 mapId = map.first;
calculations.push_back(std::async([this,mapId, pathOnly] { generateWalkPathMap(mapId, pathOnly); }));
calculations.push_back(std::async([this,mapId] { generateWalkPathMap(mapId); }));
bar.step();
}

Expand Down Expand Up @@ -2796,15 +2780,6 @@ void TravelNodeMap::generateAll()
sLog.outString("-Calculating coverage"); //This prevents crashes when bots from multiple maps try to calculate this on the fly.
for (auto& node : getNodes())
node->hasRouteTo(node);

if (false) //Only use this for debugging purposes. This will generate path attempts to see on the map if anything usefull be can be done with those.
{
hasToGen = true;
hasToFullGen = true;
generateWalkPaths(true);
hasToGen = false;
hasToFullGen = false;
}
}

void TravelNodeMap::printMap()
Expand Down
8 changes: 5 additions & 3 deletions playerbot/TravelNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ namespace ai
bool isTransport() { for (auto link : *getLinks()) if (link.second->getPathType() == TravelNodePathType::transport) return true; return false; }
uint32 getTransportId() { for (auto link : *getLinks()) if (link.second->getPathType() == TravelNodePathType::transport) return link.second->getPathObject(); return false; }
bool isPortal() { for (auto link : *getLinks()) if (link.second->getPathType() == TravelNodePathType::areaTrigger || link.second->getPathType() == TravelNodePathType::staticPortal) return true; return false; }
uint32 getAreaTriggerId();
bool isAreaTriggerTarget(uint32 areaTriggerId = 0);

//WorldLocation shortcuts
uint32 getMapId() { return point.getMapId(); }
Expand All @@ -169,7 +171,7 @@ namespace ai
bool hasPathTo(TravelNode* node) { return paths.find(node) != paths.end(); }
TravelNodePath* getPathTo(TravelNode* node) { return &paths[node]; }
bool hasCompletePathTo(TravelNode* node) { return hasPathTo(node) && getPathTo(node)->getComplete(); }
TravelNodePath* buildPath(TravelNode* endNode, Unit* bot, bool postProcess = false, bool pathOnly = false);
TravelNodePath* buildPath(TravelNode* endNode, Unit* bot, bool postProcess = false);

void setLinkTo(TravelNode* node, float distance = 0.1f) {
if (this != node)
Expand Down Expand Up @@ -385,8 +387,8 @@ namespace ai
void generatePortalNodes(); //Create node at static portal (ie. dalaran->ironforge) and the desination of teleport spell (ie. teleport to ironforge)
void generateNodes(); //Call all above methods.

void generateWalkPathMap(uint32 mapId, bool pathOnly = false); //Pathfind from all nodes to all nodes in a specific map. Create a path for all attemps and a link for all paths that actually reach the end node.
void generateWalkPaths(bool pathOnly = false); //Call above method for all maps async.
void generateWalkPathMap(uint32 mapId); //Pathfind from all nodes to all nodes in a specific map. Create a path for all attemps and a link for all paths that actually reach the end node.
void generateWalkPaths(); //Call above method for all maps async.

//Helper nodes take a long time to generate and have limited impact. Disable is generation time becomes an issue. It does make some places connected to the network though like some caves and tauren start.
void generateHelperNodes(uint32 mapId); //For all nodes, objects and creatures, that can't be directly reached from one of the 5 nearby nodes, place a node on one of the paths of the nearby nodes so they can be reached or else place a node at the object. Also generate walkPaths for each node and the entire map again afterwards.
Expand Down

0 comments on commit 9dae093

Please sign in to comment.