fixed travel to location
This commit is contained in:
1 parent
b14c864814
commit
4c04cd88de
7 files changed
+528
-4
No files matched your search
@@ -329,6 +329,219 @@ bool ClientlessTravelGraphMgr::PlanRoute(
|
||||
}
|
||||
|
||||
|
||||
bool ClientlessTravelGraphMgr::PlanRouteToPosition(
|
||||
Player const* player,
|
||||
uint32 destinationMapId,
|
||||
float destinationX,
|
||||
float destinationY,
|
||||
float destinationZ,
|
||||
float startRadius,
|
||||
float anchorRadius,
|
||||
std::vector<ClientlessTravelRouteStep>& route,
|
||||
std::string& anchorNode,
|
||||
std::string& error)
|
||||
{
|
||||
route.clear();
|
||||
anchorNode.clear();
|
||||
error.clear();
|
||||
|
||||
EnsureLoaded();
|
||||
|
||||
if (!player || !player->IsInWorld())
|
||||
{
|
||||
error = "player is not in world";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startRadius <= 0.0f)
|
||||
startRadius = 500.0f;
|
||||
|
||||
if (anchorRadius <= 0.0f)
|
||||
anchorRadius = 1000.0f;
|
||||
|
||||
struct QueueItem
|
||||
{
|
||||
float Cost;
|
||||
std::string Node;
|
||||
|
||||
bool operator>(QueueItem const& other) const
|
||||
{
|
||||
return Cost > other.Cost;
|
||||
}
|
||||
};
|
||||
|
||||
struct Previous
|
||||
{
|
||||
std::string Node;
|
||||
std::string EdgeType;
|
||||
uint32 EdgeDatabaseId = 0;
|
||||
bool EdgeReverse = false;
|
||||
};
|
||||
|
||||
std::priority_queue<QueueItem, std::vector<QueueItem>, std::greater<QueueItem>> open;
|
||||
std::unordered_map<std::string, float> distance;
|
||||
std::unordered_map<std::string, Previous> previous;
|
||||
|
||||
for (auto const& pair : _nodes)
|
||||
distance[pair.first] = std::numeric_limits<float>::infinity();
|
||||
|
||||
// Multi-source Dijkstra: every graph node close enough to the player's
|
||||
// actual position is a possible entry point. Seed cost includes the local
|
||||
// distance needed to reach that node, so nearer usable entries are still
|
||||
// preferred without letting an isolated node block the route entirely.
|
||||
uint32 startCandidateCount = 0;
|
||||
for (auto const& pair : _nodes)
|
||||
{
|
||||
ClientlessTravelNode const& node = pair.second;
|
||||
if (node.MapId != player->GetMapId())
|
||||
continue;
|
||||
|
||||
float const dx = node.X - player->GetPositionX();
|
||||
float const dy = node.Y - player->GetPositionY();
|
||||
float const dz = node.Z - player->GetPositionZ();
|
||||
float const localStartDistance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (localStartDistance > startRadius)
|
||||
continue;
|
||||
|
||||
distance[node.Key] = localStartDistance;
|
||||
open.push({ localStartDistance, node.Key });
|
||||
++startCandidateCount;
|
||||
}
|
||||
|
||||
if (startCandidateCount == 0)
|
||||
{
|
||||
error = "no travel node near current position";
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!open.empty())
|
||||
{
|
||||
QueueItem current = open.top();
|
||||
open.pop();
|
||||
|
||||
if (current.Cost != distance[current.Node])
|
||||
continue;
|
||||
|
||||
auto edgeItr = _edges.find(current.Node);
|
||||
if (edgeItr == _edges.end())
|
||||
continue;
|
||||
|
||||
for (ClientlessTravelEdge const& edge : edgeItr->second)
|
||||
{
|
||||
float const candidate = current.Cost + edge.Cost;
|
||||
if (candidate >= distance[edge.ToNode])
|
||||
continue;
|
||||
|
||||
distance[edge.ToNode] = candidate;
|
||||
previous[edge.ToNode] = {
|
||||
current.Node, edge.Type, edge.DatabaseId, edge.Reverse
|
||||
};
|
||||
open.push({ candidate, edge.ToNode });
|
||||
}
|
||||
}
|
||||
|
||||
// The destination anchor's primary job is to get the bot close to the
|
||||
// requested Location. Do not combine macro-route cost and final local
|
||||
// distance into one score: that can make the current/start node win simply
|
||||
// because reaching it costs zero, leaving MMAP with a very long final leg.
|
||||
//
|
||||
// Instead choose the reachable node closest to the destination first.
|
||||
// Graph cost is only a tie-breaker between equally suitable anchors.
|
||||
float bestDestinationDistance = std::numeric_limits<float>::infinity();
|
||||
float bestGraphCost = std::numeric_limits<float>::infinity();
|
||||
|
||||
for (auto const& pair : _nodes)
|
||||
{
|
||||
ClientlessTravelNode const& node = pair.second;
|
||||
if (node.MapId != destinationMapId)
|
||||
continue;
|
||||
|
||||
auto distanceItr = distance.find(node.Key);
|
||||
if (distanceItr == distance.end() || !std::isfinite(distanceItr->second))
|
||||
continue;
|
||||
|
||||
float const dx = node.X - destinationX;
|
||||
float const dy = node.Y - destinationY;
|
||||
float const dz = node.Z - destinationZ;
|
||||
float const localDestinationDistance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (localDestinationDistance > anchorRadius)
|
||||
continue;
|
||||
|
||||
float const graphCost = distanceItr->second;
|
||||
|
||||
constexpr float DistanceTieEpsilon = 0.5f;
|
||||
bool const closerToDestination =
|
||||
localDestinationDistance + DistanceTieEpsilon < bestDestinationDistance;
|
||||
|
||||
bool const sameDestinationDistance =
|
||||
std::fabs(localDestinationDistance - bestDestinationDistance) <= DistanceTieEpsilon;
|
||||
|
||||
if (!closerToDestination &&
|
||||
!(sameDestinationDistance && graphCost < bestGraphCost))
|
||||
continue;
|
||||
|
||||
bestDestinationDistance = localDestinationDistance;
|
||||
bestGraphCost = graphCost;
|
||||
anchorNode = node.Key;
|
||||
}
|
||||
|
||||
if (anchorNode.empty())
|
||||
{
|
||||
error = "no reachable travel node near destination";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reconstruct until we reach whichever seeded start candidate actually won.
|
||||
// Seed roots deliberately have no Previous entry.
|
||||
std::vector<std::string> nodeKeys;
|
||||
std::string cursor = anchorNode;
|
||||
nodeKeys.push_back(cursor);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto previousItr = previous.find(cursor);
|
||||
if (previousItr == previous.end())
|
||||
break;
|
||||
|
||||
cursor = previousItr->second.Node;
|
||||
nodeKeys.push_back(cursor);
|
||||
}
|
||||
|
||||
std::reverse(nodeKeys.begin(), nodeKeys.end());
|
||||
route.reserve(nodeKeys.size());
|
||||
|
||||
for (std::size_t i = 0; i < nodeKeys.size(); ++i)
|
||||
{
|
||||
ClientlessTravelNode const& node = _nodes.at(nodeKeys[i]);
|
||||
|
||||
ClientlessTravelRouteStep step;
|
||||
step.NodeKey = node.Key;
|
||||
step.NodeType = node.Type;
|
||||
step.WorldPointKey = node.WorldPointKey;
|
||||
step.MapId = node.MapId;
|
||||
step.X = node.X;
|
||||
step.Y = node.Y;
|
||||
step.Z = node.Z;
|
||||
step.O = node.O;
|
||||
|
||||
if (i == 0)
|
||||
step.EdgeType = "start";
|
||||
else
|
||||
{
|
||||
Previous const& prev = previous.at(nodeKeys[i]);
|
||||
step.EdgeType = prev.EdgeType;
|
||||
step.EdgeDatabaseId = prev.EdgeDatabaseId;
|
||||
step.EdgeReverse = prev.EdgeReverse;
|
||||
}
|
||||
|
||||
route.push_back(std::move(step));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<ClientlessTravelEdgePoint> const*
|
||||
ClientlessTravelGraphMgr::GetEdgePoints(uint32 edgeDatabaseId) const
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user