implemented taxinodes
clang PCH / build (push) Canceled after 0s
GCC PCH / build (push) Canceled after 0s

This commit is contained in:
Nico committed 2026-10-14 16:03:14 +00:00
1 parent 4c04cd88de
commit 24fb292cee
4 files changed
+547 -4

No files matched your search

@@ -95,6 +95,22 @@ namespace
float parsed = std::strtof(value.c_str(), &end);
return end && end != value.c_str() ? parsed : fallback;
}
bool ParseTaxiNodeKey(std::string const& key, uint32& taxiNodeId)
{
static std::string const Prefix = "taxi_";
if (key.rfind(Prefix, 0) != 0 || key.size() <= Prefix.size())
return false;
char* end = nullptr;
unsigned long const parsed = std::strtoul(key.c_str() + Prefix.size(), &end, 10);
if (!end || *end != '\0' || parsed == 0 || parsed > 0xFFFFFFFFul)
return false;
taxiNodeId = uint32(parsed);
return true;
}
}
ClientlessActionExecutorMgr* ClientlessActionExecutorMgr::instance()
@@ -480,6 +496,10 @@ bool ClientlessActionExecutorMgr::StartTravelToNode(
record.Route.clear();
record.RouteStep = 0;
record.EdgePath.clear();
record.TaxiActive = false;
record.TaxiPathId = 0;
record.TaxiSourceNodeId = 0;
record.TaxiDestinationNodeId = 0;
std::string error;
if (!sClientlessTravelGraphMgr->PlanRoute(
player, destinationNode, startRadius, record.Route, error))
@@ -611,6 +631,10 @@ bool ClientlessActionExecutorMgr::StartTravelToLocation(
record.Route.clear();
record.RouteStep = 0;
record.EdgePath.clear();
record.TaxiActive = false;
record.TaxiPathId = 0;
record.TaxiSourceNodeId = 0;
record.TaxiDestinationNodeId = 0;
std::string anchorNode;
std::string error;
@@ -725,7 +749,7 @@ bool ClientlessActionExecutorMgr::StartRouteStep(
ClientlessTravelRouteStep const& step = record.Route[record.RouteStep];
if (step.EdgeType != "start" && step.EdgeType != "ground")
if (step.EdgeType != "start" && step.EdgeType != "ground" && step.EdgeType != "taxi")
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.TargetName = "node:" + step.NodeKey;
@@ -741,6 +765,103 @@ bool ClientlessActionExecutorMgr::StartRouteStep(
return false;
}
if (step.EdgeType == "taxi")
{
if (record.RouteStep == 0)
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.Detail = "taxi edge has no source route step";
record.Active = false;
return false;
}
ClientlessTravelRouteStep const& sourceStep = record.Route[record.RouteStep - 1];
uint32 sourceTaxiNodeId = 0;
uint32 destinationTaxiNodeId = 0;
if (!ParseTaxiNodeKey(sourceStep.NodeKey, sourceTaxiNodeId) ||
!ParseTaxiNodeKey(step.NodeKey, destinationTaxiNodeId))
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.TargetName = "node:" + step.NodeKey;
record.Status.Detail = "taxi edge endpoints are not taxi_<id> nodes";
record.Active = false;
return false;
}
uint32 taxiPathId = 0;
uint32 taxiCost = 0;
sObjectMgr->GetTaxiPath(
sourceTaxiNodeId,
destinationTaxiNodeId,
taxiPathId,
taxiCost);
if (!taxiPathId)
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.TargetName = "node:" + step.NodeKey;
record.Status.Detail =
"Trinity has no taxi path " +
std::to_string(sourceTaxiNodeId) + " -> " +
std::to_string(destinationTaxiNodeId);
record.Active = false;
return false;
}
sClientlessTravelMgr->CancelTravel(characterGuid);
record.EdgePath.clear();
if (!player->ActivateTaxiPathTo(taxiPathId))
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.TargetName = "node:" + step.NodeKey;
record.Status.Detail =
"ActivateTaxiPathTo failed for path=" +
std::to_string(taxiPathId) +
" source=" + std::to_string(sourceTaxiNodeId) +
" destination=" + std::to_string(destinationTaxiNodeId);
record.Active = false;
TC_LOG_WARN("server.adventurebot",
"[AdventureBot] taxi-start-failed guid=%u name='%s' path=%u source=%u destination=%u cost=%u",
characterGuid,
player->GetName().c_str(),
taxiPathId,
sourceTaxiNodeId,
destinationTaxiNodeId,
taxiCost);
return false;
}
record.TaxiActive = true;
record.TaxiPathId = taxiPathId;
record.TaxiSourceNodeId = sourceTaxiNodeId;
record.TaxiDestinationNodeId = destinationTaxiNodeId;
record.Status.State = ClientlessActionExecutionState::Moving;
record.Status.TargetName = "node:" + step.NodeKey;
record.Status.TargetGuid = 0;
record.Status.TargetEntry = 0;
record.Status.Detail =
"taxi path " + std::to_string(taxiPathId) +
" " + std::to_string(sourceTaxiNodeId) +
" -> " + std::to_string(destinationTaxiNodeId);
TC_LOG_INFO("server.adventurebot",
"[AdventureBot] taxi-started guid=%u name='%s' routeStep=%u/%u path=%u source=%u destination=%u cost=%u",
characterGuid,
player->GetName().c_str(),
uint32(record.RouteStep + 1),
uint32(record.Route.size()),
taxiPathId,
sourceTaxiNodeId,
destinationTaxiNodeId,
taxiCost);
return true;
}
if (step.MapId != player->GetMapId())
{
record.Status.State = ClientlessActionExecutionState::Failed;
@@ -886,6 +1007,82 @@ void ClientlessActionExecutorMgr::UpdateTravelToNode(
uint32 characterGuid,
Record& record)
{
if (record.TaxiActive)
{
if (!player || !player->IsInWorld())
return;
if (player->IsInFlight())
return;
if (record.RouteStep >= record.Route.size())
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.Detail = "taxi route step index became invalid";
record.Active = false;
record.TaxiActive = false;
return;
}
ClientlessTravelRouteStep const& destination = record.Route[record.RouteStep];
if (player->GetMapId() != destination.MapId)
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.Detail =
"taxi ended on unexpected map; expected=" +
std::to_string(destination.MapId) +
" actual=" + std::to_string(player->GetMapId());
record.Active = false;
record.TaxiActive = false;
return;
}
float const dx = player->GetPositionX() - destination.X;
float const dy = player->GetPositionY() - destination.Y;
float const dz = player->GetPositionZ() - destination.Z;
float const remaining = std::sqrt(dx * dx + dy * dy + dz * dz);
constexpr float TaxiArrivalSanityRadius = 120.0f;
if (remaining > TaxiArrivalSanityRadius)
{
record.Status.State = ClientlessActionExecutionState::Failed;
record.Status.Detail =
"taxi stopped away from destination node; remaining=" +
std::to_string(remaining);
record.Active = false;
record.TaxiActive = false;
TC_LOG_WARN("server.adventurebot",
"[AdventureBot] taxi-ended-away-from-node guid=%u name='%s' path=%u source=%u destination=%u remaining=%.2f",
characterGuid,
player->GetName().c_str(),
record.TaxiPathId,
record.TaxiSourceNodeId,
record.TaxiDestinationNodeId,
remaining);
return;
}
TC_LOG_INFO("server.adventurebot",
"[AdventureBot] taxi-complete guid=%u name='%s' path=%u source=%u destination=%u remaining=%.2f",
characterGuid,
player->GetName().c_str(),
record.TaxiPathId,
record.TaxiSourceNodeId,
record.TaxiDestinationNodeId,
remaining);
record.TaxiActive = false;
record.TaxiPathId = 0;
record.TaxiSourceNodeId = 0;
record.TaxiDestinationNodeId = 0;
++record.RouteStep;
StartRouteStep(player, characterGuid, record);
return;
}
if (!record.EdgePath.empty())
{
ClientlessTravelEdgePoint const& destination = record.EdgePath.back();
@@ -54,6 +54,12 @@ private:
std::vector<ClientlessTravelEdgePoint> EdgePath;
// C8.10b: active server-side taxi edge.
bool TaxiActive = false;
uint32 TaxiPathId = 0;
uint32 TaxiSourceNodeId = 0;
uint32 TaxiDestinationNodeId = 0;
bool FinalLocalLegPending = false;
bool FinalLocalLegActive = false;
uint32 FinalMapId = 0;
@@ -18,6 +18,12 @@
class Player;
// Canonical Vcore travel-node types:
// ground, taxi, ship, zeppelin, tram, portal, elevator
//
// Node type describes what the place is. Edge type describes how travel occurs.
// Ground edges may connect any node type; specialized edge executors may impose
// stricter endpoint rules.
struct TC_GAME_API ClientlessTravelNode
{
uint32 DatabaseId = 0;