implemented taxinodes
This commit is contained in:
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;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ClientlessRouteRecorderMgr.h"
|
||||
#include "ClientlessLuaEngine.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Creature.h"
|
||||
#include "Group.h"
|
||||
#include "ObjectAccessor.h"
|
||||
@@ -33,10 +34,13 @@
|
||||
#include "Unit.h"
|
||||
#include "World.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
|
||||
namespace VianoBotCommands
|
||||
{
|
||||
@@ -718,6 +722,18 @@ namespace VianoBotCommands
|
||||
return handler && handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr;
|
||||
}
|
||||
|
||||
static bool IsClientlessTravelNodeType(std::string const& type)
|
||||
{
|
||||
return type == "ground"
|
||||
|| type == "taxi"
|
||||
|| type == "ship"
|
||||
|| type == "zeppelin"
|
||||
|| type == "tram"
|
||||
|| type == "portal"
|
||||
|| type == "elevator"
|
||||
|| type == "location"; // legacy C8.x node type
|
||||
}
|
||||
|
||||
static bool HandleClientlessNodeAdd(ChatHandler* handler, char const* args)
|
||||
{
|
||||
Player* player = GetClientlessCommandPlayer(handler);
|
||||
@@ -726,14 +742,23 @@ static bool HandleClientlessNodeAdd(ChatHandler* handler, char const* args)
|
||||
|
||||
std::istringstream input(args ? args : "");
|
||||
std::string key;
|
||||
std::string type = "location";
|
||||
std::string type = "ground";
|
||||
input >> key;
|
||||
if (!(input >> type))
|
||||
type = "location";
|
||||
type = "ground";
|
||||
|
||||
if (key.empty())
|
||||
{
|
||||
handler->SendSysMessage("Usage: .clientless node add <key> [type]");
|
||||
handler->SendSysMessage(
|
||||
"Usage: .clientless node add <key> [ground|taxi|ship|zeppelin|tram|portal|elevator]");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsClientlessTravelNodeType(type))
|
||||
{
|
||||
handler->PSendSysMessage(
|
||||
"Unknown travel node type '%s'. Allowed: ground, taxi, ship, zeppelin, tram, portal, elevator.",
|
||||
type.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -838,6 +863,105 @@ static bool HandleClientlessNodeList(ChatHandler* handler, char const* /*args*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool HandleClientlessNodeListNearby(ChatHandler* handler, char const* args)
|
||||
{
|
||||
Player* player = GetClientlessCommandPlayer(handler);
|
||||
if (!player || !player->IsInWorld())
|
||||
return true;
|
||||
|
||||
float radius = 300.0f;
|
||||
std::istringstream input(args ? args : "");
|
||||
if (input >> radius)
|
||||
{
|
||||
if (radius <= 0.0f)
|
||||
{
|
||||
handler->SendSysMessage("Usage: .clientless node list_nearby [radius>0]");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
struct NearbyNode
|
||||
{
|
||||
std::string Key;
|
||||
std::string Type;
|
||||
float X = 0.0f;
|
||||
float Y = 0.0f;
|
||||
float Z = 0.0f;
|
||||
float Distance = 0.0f;
|
||||
};
|
||||
|
||||
QueryResult result = WorldDatabase.PQuery(
|
||||
"SELECT n.node_key,n.node_type,w.position_x,w.position_y,w.position_z "
|
||||
"FROM vcore_clientless_travel_node n "
|
||||
"INNER JOIN vcore_clientless_world_point w ON w.point_key=n.world_point_key "
|
||||
"WHERE n.enabled=1 AND w.enabled=1 AND w.map=%u",
|
||||
uint32(player->GetMapId()));
|
||||
|
||||
if (!result)
|
||||
{
|
||||
handler->PSendSysMessage(
|
||||
"No travel nodes on map %u within %.1f yd.",
|
||||
uint32(player->GetMapId()), radius);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<NearbyNode> nodes;
|
||||
|
||||
do
|
||||
{
|
||||
Field* f = result->Fetch();
|
||||
|
||||
NearbyNode node;
|
||||
node.Key = f[0].GetString();
|
||||
node.Type = f[1].GetString();
|
||||
node.X = f[2].GetFloat();
|
||||
node.Y = f[3].GetFloat();
|
||||
node.Z = f[4].GetFloat();
|
||||
|
||||
float const dx = node.X - player->GetPositionX();
|
||||
float const dy = node.Y - player->GetPositionY();
|
||||
float const dz = node.Z - player->GetPositionZ();
|
||||
node.Distance = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (node.Distance <= radius)
|
||||
nodes.push_back(std::move(node));
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
std::sort(nodes.begin(), nodes.end(),
|
||||
[](NearbyNode const& left, NearbyNode const& right)
|
||||
{
|
||||
return left.Distance < right.Distance;
|
||||
});
|
||||
|
||||
if (nodes.empty())
|
||||
{
|
||||
handler->PSendSysMessage(
|
||||
"No travel nodes within %.1f yd on map %u.",
|
||||
radius, uint32(player->GetMapId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"Travel nodes within %.1f yd on map %u: %u",
|
||||
radius, uint32(player->GetMapId()), uint32(nodes.size()));
|
||||
|
||||
for (NearbyNode const& node : nodes)
|
||||
{
|
||||
handler->PSendSysMessage(
|
||||
"[%.1f yd] %s type=%s x=%.2f y=%.2f z=%.2f",
|
||||
node.Distance,
|
||||
node.Key.c_str(),
|
||||
node.Type.c_str(),
|
||||
node.X,
|
||||
node.Y,
|
||||
node.Z);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleClientlessRouteRemove(ChatHandler* handler, char const* args)
|
||||
{
|
||||
std::istringstream input(args ? args : "");
|
||||
@@ -984,6 +1108,211 @@ static bool HandleClientlessRouteStatus(ChatHandler* handler, char const* /*args
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool HandleClientlessTaxiImport(ChatHandler* handler, char const* /*args*/)
|
||||
{
|
||||
std::vector<uint32> importedTaxiNodes;
|
||||
uint32 createdPoints = 0;
|
||||
uint32 updatedPoints = 0;
|
||||
uint32 createdNodes = 0;
|
||||
uint32 updatedNodes = 0;
|
||||
uint32 createdEdges = 0;
|
||||
uint32 existingEdges = 0;
|
||||
|
||||
for (uint32 taxiNodeId = 1; taxiNodeId < sTaxiNodesStore.GetNumRows(); ++taxiNodeId)
|
||||
{
|
||||
TaxiNodesEntry const* taxiNode = sTaxiNodesStore.LookupEntry(taxiNodeId);
|
||||
if (!taxiNode)
|
||||
continue;
|
||||
|
||||
uint8 const field = uint8((taxiNodeId - 1) / 8);
|
||||
uint32 const submask = 1u << ((taxiNodeId - 1) % 8);
|
||||
if ((sTaxiNodesMask[field] & submask) == 0)
|
||||
continue;
|
||||
|
||||
std::string const key = "taxi_" + std::to_string(taxiNodeId);
|
||||
|
||||
QueryResult pointExists = WorldDatabase.PQuery(
|
||||
"SELECT id FROM vcore_clientless_world_point WHERE point_key='%s' LIMIT 1",
|
||||
key.c_str());
|
||||
|
||||
if (pointExists)
|
||||
{
|
||||
WorldDatabase.DirectPExecute(
|
||||
"UPDATE vcore_clientless_world_point "
|
||||
"SET category='travel_taxi',map=%u,position_x=%.6f,position_y=%.6f,position_z=%.6f,"
|
||||
"orientation=0,enabled=1,comment='Auto-imported from TaxiNodes.dbc node %u' "
|
||||
"WHERE point_key='%s'",
|
||||
uint32(taxiNode->ContinentID),
|
||||
taxiNode->Pos.X,
|
||||
taxiNode->Pos.Y,
|
||||
taxiNode->Pos.Z,
|
||||
taxiNodeId,
|
||||
key.c_str());
|
||||
++updatedPoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldDatabase.DirectPExecute(
|
||||
"INSERT INTO vcore_clientless_world_point "
|
||||
"(point_key,category,map,position_x,position_y,position_z,orientation,enabled,comment) "
|
||||
"VALUES ('%s','travel_taxi',%u,%.6f,%.6f,%.6f,0,1,"
|
||||
"'Auto-imported from TaxiNodes.dbc node %u')",
|
||||
key.c_str(),
|
||||
uint32(taxiNode->ContinentID),
|
||||
taxiNode->Pos.X,
|
||||
taxiNode->Pos.Y,
|
||||
taxiNode->Pos.Z,
|
||||
taxiNodeId);
|
||||
++createdPoints;
|
||||
}
|
||||
|
||||
QueryResult nodeExists = WorldDatabase.PQuery(
|
||||
"SELECT id FROM vcore_clientless_travel_node WHERE node_key='%s' LIMIT 1",
|
||||
key.c_str());
|
||||
|
||||
if (nodeExists)
|
||||
{
|
||||
WorldDatabase.DirectPExecute(
|
||||
"UPDATE vcore_clientless_travel_node "
|
||||
"SET node_type='taxi',world_point_key='%s',enabled=1,"
|
||||
"comment='Auto-imported TaxiNodes.dbc node %u' "
|
||||
"WHERE node_key='%s'",
|
||||
key.c_str(), taxiNodeId, key.c_str());
|
||||
++updatedNodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldDatabase.DirectPExecute(
|
||||
"INSERT INTO vcore_clientless_travel_node "
|
||||
"(node_key,node_type,world_point_key,enabled,comment) "
|
||||
"VALUES ('%s','taxi','%s',1,'Auto-imported TaxiNodes.dbc node %u')",
|
||||
key.c_str(), key.c_str(), taxiNodeId);
|
||||
++createdNodes;
|
||||
}
|
||||
|
||||
importedTaxiNodes.push_back(taxiNodeId);
|
||||
}
|
||||
|
||||
for (uint32 sourceId : importedTaxiNodes)
|
||||
{
|
||||
std::string const fromKey = "taxi_" + std::to_string(sourceId);
|
||||
|
||||
for (uint32 destinationId : importedTaxiNodes)
|
||||
{
|
||||
if (sourceId == destinationId)
|
||||
continue;
|
||||
|
||||
uint32 pathId = 0;
|
||||
uint32 pathCost = 0;
|
||||
sObjectMgr->GetTaxiPath(sourceId, destinationId, pathId, pathCost);
|
||||
if (!pathId)
|
||||
continue;
|
||||
|
||||
std::string const toKey = "taxi_" + std::to_string(destinationId);
|
||||
|
||||
QueryResult edgeExists = WorldDatabase.PQuery(
|
||||
"SELECT id FROM vcore_clientless_travel_edge "
|
||||
"WHERE from_node_key='%s' AND to_node_key='%s' AND edge_type='taxi' LIMIT 1",
|
||||
fromKey.c_str(), toKey.c_str());
|
||||
|
||||
if (edgeExists)
|
||||
{
|
||||
++existingEdges;
|
||||
continue;
|
||||
}
|
||||
|
||||
WorldDatabase.DirectPExecute(
|
||||
"INSERT INTO vcore_clientless_travel_edge "
|
||||
"(from_node_key,to_node_key,edge_type,cost,bidirectional,enabled,comment) "
|
||||
"VALUES ('%s','%s','taxi',1,0,1,"
|
||||
"'Auto-imported TaxiPath id=%u price=%u')",
|
||||
fromKey.c_str(),
|
||||
toKey.c_str(),
|
||||
pathId,
|
||||
pathCost);
|
||||
|
||||
++createdEdges;
|
||||
}
|
||||
}
|
||||
|
||||
bool const graphLoaded = sClientlessTravelGraphMgr->Reload();
|
||||
sClientlessWorldPointMgr->Reload();
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"Taxi import complete: taxiNodes=%u points(created=%u updated=%u) "
|
||||
"nodes(created=%u updated=%u) taxiEdges(created=%u existing=%u).",
|
||||
uint32(importedTaxiNodes.size()),
|
||||
createdPoints,
|
||||
updatedPoints,
|
||||
createdNodes,
|
||||
updatedNodes,
|
||||
createdEdges,
|
||||
existingEdges);
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"Travel graph reloaded: %u node(s), %u directed edge(s)%s. "
|
||||
"Taxi execution is intentionally not implemented yet.",
|
||||
sClientlessTravelGraphMgr->GetNodeCount(),
|
||||
sClientlessTravelGraphMgr->GetEdgeCount(),
|
||||
graphLoaded ? "" : " (no usable nodes)");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool HandleClientlessTaxiInfo(ChatHandler* handler, char const* args)
|
||||
{
|
||||
std::istringstream input(args ? args : "");
|
||||
uint32 taxiNodeId = 0;
|
||||
input >> taxiNodeId;
|
||||
|
||||
if (!taxiNodeId)
|
||||
{
|
||||
handler->SendSysMessage("Usage: .clientless taxi info <TaxiNodeId>");
|
||||
return true;
|
||||
}
|
||||
|
||||
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(taxiNodeId);
|
||||
if (!node)
|
||||
{
|
||||
handler->PSendSysMessage("TaxiNode %u does not exist in TaxiNodes.dbc.", taxiNodeId);
|
||||
return true;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"TaxiNode %u: map=%u x=%.2f y=%.2f z=%.2f vcoreKey=taxi_%u",
|
||||
taxiNodeId,
|
||||
uint32(node->ContinentID),
|
||||
node->Pos.X,
|
||||
node->Pos.Y,
|
||||
node->Pos.Z,
|
||||
taxiNodeId);
|
||||
|
||||
uint32 outgoing = 0;
|
||||
for (uint32 destinationId = 1; destinationId < sTaxiNodesStore.GetNumRows(); ++destinationId)
|
||||
{
|
||||
if (destinationId == taxiNodeId || !sTaxiNodesStore.LookupEntry(destinationId))
|
||||
continue;
|
||||
|
||||
uint32 pathId = 0;
|
||||
uint32 cost = 0;
|
||||
sObjectMgr->GetTaxiPath(taxiNodeId, destinationId, pathId, cost);
|
||||
if (!pathId)
|
||||
continue;
|
||||
|
||||
handler->PSendSysMessage(
|
||||
" -> taxi_%u path=%u cost=%u",
|
||||
destinationId,
|
||||
pathId,
|
||||
cost);
|
||||
++outgoing;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage("Outgoing taxi edges: %u.", outgoing);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleClientlessTravelReload(ChatHandler* handler, char const* /*args*/)
|
||||
{
|
||||
bool const loaded = sClientlessTravelGraphMgr->Reload();
|
||||
@@ -1478,6 +1807,7 @@ static bool HandleClientlessTravelReload(ChatHandler* handler, char const* /*arg
|
||||
{ "remove", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessNodeRemove, "" },
|
||||
{ "force-remove", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessNodeForceRemove, "" },
|
||||
{ "list", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessNodeList, "" },
|
||||
{ "list_nearby", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessNodeListNearby, "" },
|
||||
} },
|
||||
{ "route", rbac::RBAC_PERM_COMMAND_GM, false, nullptr, "", {
|
||||
{ "remove", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessRouteRemove, "" },
|
||||
@@ -1490,6 +1820,10 @@ static bool HandleClientlessTravelReload(ChatHandler* handler, char const* /*arg
|
||||
{ "cancel", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessRouteCancel, "" },
|
||||
{ "status", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessRouteStatus, "" },
|
||||
} },
|
||||
{ "taxi", rbac::RBAC_PERM_COMMAND_GM, false, nullptr, "", {
|
||||
{ "import", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessTaxiImport, "" },
|
||||
{ "info", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessTaxiInfo, "" },
|
||||
} },
|
||||
{ "travelreload", rbac::RBAC_PERM_COMMAND_GM, false, &HandleClientlessTravelReload, "" },
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user