diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a2ca6f4b7..cf70bcbeb 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4382,9 +4382,6 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer; - Message(0, "Client Update Position (%.2f, %.2f, %.2f, %u) (%u)", ppu->x_pos, ppu->y_pos, ppu->z_pos, ppu->heading, ppu->animation); - Message(0, "Client Update Deltas (%.2f, %.2f, %.2f, %u)", ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading); - /* Boat handling */ if (ppu->spawn_id != GetID()) { /* If player is controlling boat */ diff --git a/zone/mob.cpp b/zone/mob.cpp index 9bfc30d38..6b40615a3 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -445,7 +445,7 @@ Mob::Mob(const char* in_name, AssistAggro = false; npc_assist_cap = 0; - PathRecalcTimer.reset(new Timer(1000)); + PathRecalcTimer.reset(new Timer(2000)); } Mob::~Mob() diff --git a/zone/pathfinder_waypoint.cpp b/zone/pathfinder_waypoint.cpp index 10c8c97a8..c845a4d88 100644 --- a/zone/pathfinder_waypoint.cpp +++ b/zone/pathfinder_waypoint.cpp @@ -5,12 +5,14 @@ #include #include #include +#include #include #include "pathfinder_waypoint.h" #include "zone.h" #include "client.h" #include "../common/eqemu_logsys.h" +#include "../common/string_util.h" #include "../common/rulesys.h" extern Zone *zone; @@ -36,12 +38,6 @@ struct PathFileHeader { }; #pragma pack() -struct Node -{ - glm::vec3 v; - float bestz; -}; - struct Edge { float distance; @@ -49,6 +45,14 @@ struct Edge int door_id; }; +struct Node +{ + int id; + glm::vec3 v; + float bestz; + std::map edges; +}; + template class distance_heuristic : public boost::astar_heuristic { @@ -85,7 +89,6 @@ private: }; typedef boost::geometry::model::point Point; -typedef boost::geometry::model::box Box; typedef std::pair RTreeValue; typedef boost::adjacency_list> GraphType; @@ -96,50 +99,15 @@ struct PathfinderWaypoint::Implementation { boost::geometry::index::rtree> Tree; GraphType Graph; std::vector Nodes; - std::map, Edge> Edges; + std::string FileName; }; PathfinderWaypoint::PathfinderWaypoint(const std::string &path) { - PathFileHeader Head; m_impl.reset(new Implementation()); m_impl->PathFileValid = false; - Head.PathNodeCount = 0; - Head.version = 2; - - FILE *f = fopen(path.c_str(), "rb"); - if (f) { - char Magic[10]; - - fread(&Magic, 9, 1, f); - - if (strncmp(Magic, "EQEMUPATH", 9)) - { - Log(Logs::General, Logs::Error, "Bad Magic String in .path file."); - fclose(f); - return; - } - - fread(&Head, sizeof(Head), 1, f); - - Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld", - (long)Head.version, (long)Head.PathNodeCount); - - if (Head.version == 2) - { - LoadV2(f, Head); - return; - } - else if (Head.version == 3) { - LoadV3(f, Head); - return; - } - else { - Log(Logs::General, Logs::Error, "Unsupported path file version."); - fclose(f); - return; - } - } + m_impl->FileName = path; + Load(path); } PathfinderWaypoint::~PathfinderWaypoint() @@ -151,21 +119,15 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g std::vector result_start_n; m_impl->Tree.query(boost::geometry::index::nearest(Point(start.x, start.y, start.z), 1), std::back_inserter(result_start_n)); if (result_start_n.size() == 0) { - IPath Route; - Route.push_back(start); - Route.push_back(end); - return Route; + return IPath(); } - + std::vector result_end_n; m_impl->Tree.query(boost::geometry::index::nearest(Point(end.x, end.y, end.z), 1), std::back_inserter(result_end_n)); if (result_end_n.size() == 0) { - IPath Route; - Route.push_back(start); - Route.push_back(end); - return Route; + return IPath(); } - + auto &nearest_start = *result_start_n.begin(); auto &nearest_end = *result_end_n.begin(); @@ -175,7 +137,7 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g Route.push_back(end); return Route; } - + std::vector p(boost::num_vertices(m_impl->Graph)); try { boost::astar_search(m_impl->Graph, nearest_start.second, @@ -194,8 +156,10 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g break; } else { - auto iter = m_impl->Edges.find(std::make_pair(p[v], p[v + 1])); - if (iter != m_impl->Edges.end()) { + auto &node = m_impl->Nodes[v]; + + auto iter = node.edges.find(p[v + 1]); + if (iter != node.edges.end()) { auto &edge = iter->second; if (edge.teleport) { Route.push_front(m_impl->Nodes[v].v); @@ -210,14 +174,14 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g } } } + Route.push_front(start); - return Route; } - + IPath Route; - Route.push_back(start); - Route.push_back(end); + Route.push_front(start); + Route.push_back(glm::vec3(nearest_start.first.get<0>(), nearest_start.first.get<1>(), nearest_start.first.get<2>())); return Route; } @@ -231,95 +195,186 @@ void PathfinderWaypoint::DebugCommand(Client *c, const Seperator *sep) if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { c->Message(0, "Syntax: #path shownodes: Spawns a npc to represent every npc node."); + c->Message(0, "#path show: Plots a path from the user to their target."); c->Message(0, "#path info node_id: Gives information about node info (requires shownode target)."); - c->Message(0, "#path dump file_name: Dumps the current zone->pathing to a file of your naming."); - c->Message(0, "#path add [requested_id]: Adds a node at your current location will try to take the requested id if possible."); - c->Message(0, "#path connect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node and connects that node back (requires shownode target)."); - c->Message(0, "#path sconnect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node (requires shownode target)."); - c->Message(0, "#path qconnect [set]: short cut connect, connects the targeted node to the node you set with #path qconnect set (requires shownode target)."); - c->Message(0, "#path disconnect [all]/disconnect_from_id: Disconnects the currently targeted node to disconnect from disconnect from id's node (requires shownode target), if passed all as the second argument it will disconnect this node from every other node."); - c->Message(0, "#path move: Moves your targeted node to your current position"); - c->Message(0, "#path process file_name: processes the map file and tries to automatically generate a rudimentary path setup and then dumps the current zone->pathing to a file of your naming."); - c->Message(0, "#path resort [nodes]: resorts the connections/nodes after you've manually altered them so they'll work."); return; } - + if(!strcasecmp(sep->arg[1], "shownodes")) { ShowNodes(); return; } - + if (!strcasecmp(sep->arg[1], "show")) { if (c->GetTarget() != nullptr) { auto target = c->GetTarget(); - glm::vec3 end(target->GetX(), target->GetY(), target->GetZ()); glm::vec3 start(c->GetX(), c->GetY(), c->GetZ()); - + glm::vec3 end(target->GetX(), target->GetY(), target->GetZ()); + ShowPath(c, start, end); } - + return; } + + if (!strcasecmp(sep->arg[1], "reload")) + { + Load(m_impl->FileName); + return; + } + + if (!strcasecmp(sep->arg[1], "info")) + { + NodeInfo(c); + return; + } +} + +void PathfinderWaypoint::Load(const std::string &filename) { + PathFileHeader Head; + Head.PathNodeCount = 0; + Head.version = 2; + + FILE *f = fopen(filename.c_str(), "rb"); + if (f) { + char Magic[10]; + + fread(&Magic, 9, 1, f); + + if (strncmp(Magic, "EQEMUPATH", 9)) + { + Log(Logs::General, Logs::Error, "Bad Magic String in .path file."); + fclose(f); + return; + } + + fread(&Head, sizeof(Head), 1, f); + + Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld", + (long)Head.version, (long)Head.PathNodeCount); + + if (Head.version == 2) + { + LoadV2(f, Head); + return; + } + else if (Head.version == 3) { + LoadV3(f, Head); + return; + } + else { + Log(Logs::General, Logs::Error, "Unsupported path file version."); + fclose(f); + return; + } + } } void PathfinderWaypoint::LoadV2(FILE *f, const PathFileHeader &header) { std::unique_ptr PathNodes(new PathNode[header.PathNodeCount]); - + fread(PathNodes.get(), sizeof(PathNode), header.PathNodeCount, f); - + int MaxNodeID = header.PathNodeCount - 1; - + m_impl->PathFileValid = true; - + m_impl->Nodes.reserve(header.PathNodeCount); for (uint32 i = 0; i < header.PathNodeCount; ++i) { auto &n = PathNodes[i]; - - Point p = Point(n.v.x, n.v.y, n.v.z); - m_impl->Tree.insert(std::make_pair(p, i)); - - boost::add_vertex(m_impl->Graph); Node node; + node.id = i; node.v = n.v; node.bestz = n.bestz; m_impl->Nodes.push_back(node); } - + auto weightmap = boost::get(boost::edge_weight, m_impl->Graph); for (uint32 i = 0; i < header.PathNodeCount; ++i) { for (uint32 j = 0; j < 50; ++j) { + auto &node = m_impl->Nodes[i]; if (PathNodes[i].Neighbours[j].id > MaxNodeID) { Log(Logs::General, Logs::Error, "Path Node %i, Neighbour %i (%i) out of range.", i, j, PathNodes[i].Neighbours[j].id); m_impl->PathFileValid = false; } - + if (PathNodes[i].Neighbours[j].id > 0) { - GraphType::edge_descriptor e; - bool inserted; - boost::tie(e, inserted) = boost::add_edge(PathNodes[i].id, PathNodes[i].Neighbours[j].id, m_impl->Graph); - weightmap[e] = PathNodes[i].Neighbours[j].distance; - Edge edge; edge.distance = PathNodes[i].Neighbours[j].distance; edge.door_id = PathNodes[i].Neighbours[j].DoorID; edge.teleport = PathNodes[i].Neighbours[j].Teleport; - - m_impl->Edges[std::make_pair(PathNodes[i].id, PathNodes[i].Neighbours[j].id)] = edge; + + node.edges[PathNodes[i].Neighbours[j].id] = edge; } } } - + + BuildGraph(); fclose(f); } void PathfinderWaypoint::LoadV3(FILE *f, const PathFileHeader &header) { + m_impl->Nodes.reserve(header.PathNodeCount); + + uint32 edge_count = 0; + fread(&edge_count, sizeof(uint32), 1, f); + + for (int i = 0; i < header.PathNodeCount; ++i) + { + uint32 id = 0; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float best_z = 0.0f; + + fread(&id, sizeof(uint32), 1, f); + fread(&x, sizeof(float), 1, f); + fread(&y, sizeof(float), 1, f); + fread(&z, sizeof(float), 1, f); + fread(&best_z, sizeof(float), 1, f); + + Node n; + n.id = id; + n.bestz = best_z; + n.v.x = x; + n.v.y = y; + n.v.z = z; + + m_impl->Nodes.push_back(n); + } + + for (uint32 j = 0; j < edge_count; ++j) { + uint32 from = 0; + uint32 to = 0; + int8 teleport = 0; + float distance = 0.0f; + int32 door_id = 0; + + fread(&from, sizeof(uint32), 1, f); + fread(&to, sizeof(uint32), 1, f); + fread(&teleport, sizeof(int8), 1, f); + fread(&distance, sizeof(float), 1, f); + fread(&door_id, sizeof(int32), 1, f); + + Edge e; + e.teleport = teleport > 0 ? true : false; + e.distance = distance; + e.door_id = door_id; + + auto &n = m_impl->Nodes[from]; + n.edges[to] = e; + } + + m_impl->PathFileValid = true; + + BuildGraph(); fclose(f); } @@ -327,42 +382,7 @@ void PathfinderWaypoint::ShowNodes() { for (size_t i = 0; i < m_impl->Nodes.size(); ++i) { - auto npc_type = new NPCType; - memset(npc_type, 0, sizeof(NPCType)); - - auto c = i / 1000u; - sprintf(npc_type->name, "Node%u", c); - npc_type->cur_hp = 4000000; - npc_type->max_hp = 4000000; - npc_type->race = 2254; - npc_type->gender = 2; - npc_type->class_ = 9; - npc_type->deity = 1; - npc_type->level = 75; - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = 1; - npc_type->light = 0; - npc_type->runspeed = 0; - npc_type->d_melee_texture1 = 1; - npc_type->d_melee_texture2 = 1; - npc_type->merchanttype = 1; - npc_type->bodytype = 1; - - npc_type->STR = 150; - npc_type->STA = 150; - npc_type->DEX = 150; - npc_type->AGI = 150; - npc_type->INT = 150; - npc_type->WIS = 150; - npc_type->CHA = 150; - - npc_type->findable = 1; - auto position = glm::vec4(m_impl->Nodes[i].v.x, m_impl->Nodes[i].v.y, m_impl->Nodes[i].v.z, 0.0f); - auto npc = new NPC(npc_type, nullptr, position, FlyMode1); - npc->GiveNPCTypeData(npc_type); - - entity_list.AddNPC(npc, true, true); + ShowNode(m_impl->Nodes[i]); } } @@ -385,3 +405,150 @@ void PathfinderWaypoint::ShowPath(Client *c, const glm::vec3 &start, const glm:: c->SendPathPacket(points); } + +void PathfinderWaypoint::NodeInfo(Client *c) +{ + if (!c->GetTarget()) { + return; + } + + auto node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if (node == nullptr) { + return; + } + + c->Message(0, "Pathing node: %i at (%.2f, %.2f, %.2f) with bestz %.2f", + node->id, node->v.x, node->v.y, node->v.z, node->bestz); + + for (auto &edge : node->edges) { + c->Message(0, "id: %i, distance: %.2f, door id: %i, is teleport: %i", + edge.first, + edge.second.distance, + edge.second.door_id, + edge.second.teleport); + } +} + +Node *PathfinderWaypoint::FindPathNodeByCoordinates(float x, float y, float z) +{ + for (auto &node : m_impl->Nodes) { + auto dist = Distance(glm::vec3(x, y, z), node.v); + + if (dist < 0.1) { + return &node; + } + } + + return nullptr; +} + +void PathfinderWaypoint::BuildGraph() +{ + m_impl->Graph = GraphType(); + m_impl->Tree = boost::geometry::index::rtree>(); + + for (auto &node : m_impl->Nodes) { + Point p = Point(node.v.x, node.v.y, node.v.z); + m_impl->Tree.insert(std::make_pair(p, node.id)); + boost::add_vertex(m_impl->Graph); + } + + //Populate edges now that we've created all the nodes + auto weightmap = boost::get(boost::edge_weight, m_impl->Graph); + for (auto &node : m_impl->Nodes) { + for (auto &edge : node.edges) { + GraphType::edge_descriptor e; + bool inserted; + boost::tie(e, inserted) = boost::add_edge(node.id, edge.first, m_impl->Graph); + weightmap[e] = edge.second.distance; + } + } +} + +std::string DigitToWord(int i) +{ + std::string digit = std::to_string(i); + std::string ret; + for (size_t idx = 0; idx < digit.length(); ++idx) { + if (!ret.empty()) { + ret += "_"; + } + + switch (digit[idx]) { + case '0': + ret += "Zero"; + break; + case '1': + ret += "One"; + break; + case '2': + ret += "Two"; + break; + case '3': + ret += "Three"; + break; + case '4': + ret += "Four"; + break; + case '5': + ret += "Five"; + break; + case '6': + ret += "Six"; + break; + case '7': + ret += "Seven"; + break; + case '8': + ret += "Eight"; + break; + case '9': + ret += "Nine"; + break; + default: + break; + } + } + + return ret; +} + +void PathfinderWaypoint::ShowNode(const Node &n) { + auto npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + + sprintf(npc_type->name, "%s", DigitToWord(n.id).c_str()); + sprintf(npc_type->lastname, "%i", n.id); + npc_type->cur_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 2254; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity = 1; + npc_type->level = 75; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 0; + npc_type->runspeed = 0; + npc_type->d_melee_texture1 = 1; + npc_type->d_melee_texture2 = 1; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + npc_type->show_name = true; + + npc_type->STR = 150; + npc_type->STA = 150; + npc_type->DEX = 150; + npc_type->AGI = 150; + npc_type->INT = 150; + npc_type->WIS = 150; + npc_type->CHA = 150; + + npc_type->findable = 1; + auto position = glm::vec4(n.v.x, n.v.y, n.v.z, 0.0f); + auto npc = new NPC(npc_type, nullptr, position, FlyMode1); + npc->GiveNPCTypeData(npc_type); + + entity_list.AddNPC(npc, true, true); +} diff --git a/zone/pathfinder_waypoint.h b/zone/pathfinder_waypoint.h index 5d10ea25a..f4132e44c 100644 --- a/zone/pathfinder_waypoint.h +++ b/zone/pathfinder_waypoint.h @@ -3,6 +3,7 @@ #include "pathfinder_interface.h" struct PathFileHeader; +struct Node; class PathfinderWaypoint : public IPathfinder { @@ -15,10 +16,15 @@ public: virtual void DebugCommand(Client *c, const Seperator *sep); private: + void Load(const std::string &filename); void LoadV2(FILE *f, const PathFileHeader &header); void LoadV3(FILE *f, const PathFileHeader &header); void ShowNodes(); void ShowPath(Client *c, const glm::vec3 &start, const glm::vec3 &end); + void NodeInfo(Client *c); + Node *FindPathNodeByCoordinates(float x, float y, float z); + void BuildGraph(); + void ShowNode(const Node &n); struct Implementation; std::unique_ptr m_impl; diff --git a/zone/pathing.cpp b/zone/pathing.cpp index a915a73a0..a5d439d94 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -26,7 +26,12 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa PathingDestination = To; WaypointChanged = true; NodeReached = false; - return (*Route.begin()).pos; + if (Route.empty()) { + return From; + } + else { + return (*Route.begin()).pos; + } } else { if (PathRecalcTimer->Check()) {