eqemu-server/zone/pathing.cpp

1925 lines
44 KiB
C++

#include "../common/global_define.h"
#include "client.h"
#include "doors.h"
#include "pathing.h"
#include "water_map.h"
#include "zone.h"
#include <fstream>
#include <list>
#include <math.h>
#include <sstream>
#include <string.h>
#ifdef _WINDOWS
#define snprintf _snprintf
#endif
//#define PATHDEBUG
extern Zone *zone;
float VectorDistance(glm::vec3 a, glm::vec3 b)
{
float xdist = a.x - b.x;
float ydist = a.y - b.y;
float zdist = a.z - b.z;
return sqrtf(xdist * xdist + ydist * ydist + zdist * zdist);
}
float VectorDistanceNoRoot(glm::vec3 a, glm::vec3 b)
{
float xdist = a.x - b.x;
float ydist = a.y - b.y;
float zdist = a.z - b.z;
return xdist * xdist + ydist * ydist + zdist * zdist;
}
PathManager* PathManager::LoadPathFile(const char* ZoneName)
{
FILE *PathFile = nullptr;
char LowerCaseZoneName[64];
char ZonePathFileName[256];
PathManager* Ret = nullptr;
strn0cpy(LowerCaseZoneName, ZoneName, 64);
strlwr(LowerCaseZoneName);
snprintf(ZonePathFileName, 250, "%s%s.path", Config->MapDir.c_str(), LowerCaseZoneName);
if((PathFile = fopen(ZonePathFileName, "rb")))
{
Ret = new PathManager();
if(Ret->loadPaths(PathFile))
{
Log(Logs::General, Logs::Status, "Path File %s loaded.", ZonePathFileName);
}
else
{
Log(Logs::General, Logs::Error, "Path File %s failed to load.", ZonePathFileName);
safe_delete(Ret);
}
fclose(PathFile);
}
else
{
Log(Logs::General, Logs::Error, "Path File %s not found.", ZonePathFileName);
}
return Ret;
}
PathManager::PathManager()
{
PathNodes = nullptr;
ClosedListFlag = nullptr;
Head.PathNodeCount = 0;
Head.version = 2;
QuickConnectTarget = -1;
}
PathManager::~PathManager()
{
safe_delete_array(PathNodes);
safe_delete_array(ClosedListFlag);
}
bool PathManager::loadPaths(FILE *PathFile)
{
char Magic[10];
fread(&Magic, 9, 1, PathFile);
if(strncmp(Magic, "EQEMUPATH", 9))
{
Log(Logs::General, Logs::Error, "Bad Magic String in .path file.");
return false;
}
fread(&Head, sizeof(Head), 1, PathFile);
Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld",
(long)Head.version, (long)Head.PathNodeCount);
if(Head.version != 2)
{
Log(Logs::General, Logs::Error, "Unsupported path file version.");
return false;
}
PathNodes = new PathNode[Head.PathNodeCount];
fread(PathNodes, sizeof(PathNode), Head.PathNodeCount, PathFile);
ClosedListFlag = new int[Head.PathNodeCount];
#ifdef PATHDEBUG
PrintPathing();
#endif
int MaxNodeID = Head.PathNodeCount - 1;
bool PathFileValid = true;
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
for(uint32 j = 0; j < PATHNODENEIGHBOURS; ++j)
{
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);
PathFileValid = false;
}
}
}
if(!PathFileValid)
{
safe_delete_array(PathNodes);
}
return PathFileValid;
}
void PathManager::PrintPathing()
{
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
printf("PathNode: %2d id %2d. (%8.3f, %8.3f, %8.3f), BestZ: %8.3f\n",
i, PathNodes[i].id, PathNodes[i].v.x, PathNodes[i].v.y, PathNodes[i].v.z, PathNodes[i].bestz);
if(PathNodes[i].Neighbours[0].id == -1)
{
printf(" NO NEIGHBOURS.\n");
continue;
}
for(int j=0; j<PATHNODENEIGHBOURS; j++)
{
if(PathNodes[i].Neighbours[j].id == -1)
break;
printf(" Neighbour: %2d, Distance %8.3f", PathNodes[i].Neighbours[j].id,
PathNodes[i].Neighbours[j].distance);
if(PathNodes[i].Neighbours[j].Teleport)
printf(" ***** TELEPORT *****");
if(PathNodes[i].Neighbours[j].DoorID >= 0)
printf(" ***** via door %i *****", PathNodes[i].Neighbours[j].DoorID);
printf("\n");
}
}
}
glm::vec3 PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ)
{
glm::vec3 Result;
if(NodeNumber < Head.PathNodeCount)
{
Result = PathNodes[NodeNumber].v;
if(!BestZ)
return Result;
Result.z = PathNodes[NodeNumber].bestz;
}
return Result;
}
std::deque<int> PathManager::FindRoute(int startID, int endID)
{
Log(Logs::Detail, Logs::None, "FindRoute from node %i to %i", startID, endID);
memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount);
std::deque<AStarNode> OpenList, ClosedList;
std::deque<int>Route;
AStarNode AStarEntry, CurrentNode;
AStarEntry.PathNodeID = startID;
AStarEntry.Parent = -1;
AStarEntry.HCost = 0;
AStarEntry.GCost = 0;
AStarEntry.Teleport = false;
OpenList.push_back(AStarEntry);
while(!OpenList.empty())
{
// The OpenList is maintained in sorted order, lowest to highest cost.
CurrentNode = (*OpenList.begin());
ClosedList.push_back(CurrentNode);
ClosedListFlag[CurrentNode.PathNodeID] = true;
OpenList.pop_front();
for(int i = 0; i < PATHNODENEIGHBOURS; ++i)
{
if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == -1)
break;
if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == CurrentNode.Parent)
continue;
if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == endID)
{
Route.push_back(CurrentNode.PathNodeID);
Route.push_back(endID);
std::deque<AStarNode>::iterator RouteIterator;
while(CurrentNode.PathNodeID != startID)
{
for(RouteIterator = ClosedList.begin(); RouteIterator != ClosedList.end(); ++RouteIterator)
{
if((*RouteIterator).PathNodeID == CurrentNode.Parent)
{
if(CurrentNode.Teleport)
Route.insert(Route.begin(), -1);
CurrentNode = (*RouteIterator);
Route.insert(Route.begin(), CurrentNode.PathNodeID);
break;
}
}
}
return Route;
}
if(ClosedListFlag[PathNodes[CurrentNode.PathNodeID].Neighbours[i].id])
continue;
AStarEntry.PathNodeID = PathNodes[CurrentNode.PathNodeID].Neighbours[i].id;
AStarEntry.Parent = CurrentNode.PathNodeID;
AStarEntry.Teleport = PathNodes[CurrentNode.PathNodeID].Neighbours[i].Teleport;
// HCost is the estimated cost to get from this node to the end.
AStarEntry.HCost = VectorDistance(PathNodes[PathNodes[CurrentNode.PathNodeID].Neighbours[i].id].v,
PathNodes[endID].v);
AStarEntry.GCost = CurrentNode.GCost + PathNodes[CurrentNode.PathNodeID].Neighbours[i].distance;
float FCost = AStarEntry.HCost + AStarEntry.GCost;
#ifdef PATHDEBUG
printf("Node: %i, Open Neighbour %i has HCost %8.3f, GCost %8.3f (Total Cost: %8.3f)\n",
CurrentNode.PathNodeID,
PathNodes[CurrentNode.PathNodeID].Neighbours[i].id,
AStarEntry.HCost,
AStarEntry.GCost,
AStarEntry.HCost + AStarEntry.GCost);
#endif
bool AlreadyInOpenList = false;
std::deque<AStarNode>::iterator OpenListIterator, InsertionPoint = OpenList.end();
for(OpenListIterator = OpenList.begin(); OpenListIterator != OpenList.end(); ++OpenListIterator)
{
if((*OpenListIterator).PathNodeID == PathNodes[CurrentNode.PathNodeID].Neighbours[i].id)
{
AlreadyInOpenList = true;
float GCostToNode = CurrentNode.GCost + PathNodes[CurrentNode.PathNodeID].Neighbours[i].distance;
if(GCostToNode < (*OpenListIterator).GCost)
{
(*OpenListIterator).Parent = CurrentNode.PathNodeID;
(*OpenListIterator).GCost = GCostToNode;
(*OpenListIterator).Teleport = PathNodes[CurrentNode.PathNodeID].Neighbours[i].Teleport;
}
break;
}
else if((InsertionPoint == OpenList.end()) && (((*OpenListIterator).HCost + (*OpenListIterator).GCost) > FCost))
{
InsertionPoint = OpenListIterator;
}
}
if(!AlreadyInOpenList)
OpenList.insert(InsertionPoint, AStarEntry);
}
}
Log(Logs::Detail, Logs::None, "Unable to find a route.");
return Route;
}
bool CheckLOSBetweenPoints(glm::vec3 start, glm::vec3 end) {
glm::vec3 hit;
if((zone->zonemap) && (zone->zonemap->LineIntersectsZone(start, end, 1, &hit)))
return false;
return true;
}
auto path_compare = [](const PathNodeSortStruct& a, const PathNodeSortStruct& b)
{
return a.Distance < b.Distance;
};
std::deque<int> PathManager::FindRoute(glm::vec3 Start, glm::vec3 End)
{
Log(Logs::Detail, Logs::None, "FindRoute(%8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f)", Start.x, Start.y, Start.z, End.x, End.y, End.z);
std::deque<int> noderoute;
float CandidateNodeRangeXY = RuleR(Pathing, CandidateNodeRangeXY);
float CandidateNodeRangeZ = RuleR(Pathing, CandidateNodeRangeZ);
// Find the nearest PathNode the Start has LOS to.
//
//
int ClosestPathNodeToStart = -1;
std::deque<PathNodeSortStruct> SortedByDistance;
PathNodeSortStruct TempNode;
for(uint32 i = 0 ; i < Head.PathNodeCount; ++i)
{
if ((std::abs(Start.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) &&
(std::abs(Start.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) &&
(std::abs(Start.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) {
TempNode.id = i;
TempNode.Distance = VectorDistanceNoRoot(Start, PathNodes[i].v);
SortedByDistance.push_back(TempNode);
}
}
std::sort(SortedByDistance.begin(), SortedByDistance.end(), path_compare);
for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator)
{
Log(Logs::Detail, Logs::None, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id);
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
{
ClosestPathNodeToStart = (*Iterator).id;
break;
}
}
if(ClosestPathNodeToStart <0 ) {
Log(Logs::Detail, Logs::None, "No LOS to any starting Path Node within range.");
return noderoute;
}
Log(Logs::Detail, Logs::None, "Closest Path Node To Start: %2d", ClosestPathNodeToStart);
// Find the nearest PathNode the end point has LOS to
int ClosestPathNodeToEnd = -1;
SortedByDistance.clear();
for(uint32 i = 0 ; i < Head.PathNodeCount; ++i)
{
if ((std::abs(End.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) &&
(std::abs(End.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) &&
(std::abs(End.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) {
TempNode.id = i;
TempNode.Distance = VectorDistanceNoRoot(End, PathNodes[i].v);
SortedByDistance.push_back(TempNode);
}
}
std::sort(SortedByDistance.begin(), SortedByDistance.end(), path_compare);
for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator)
{
Log(Logs::Detail, Logs::None, "Checking Reachability of Node %i from End Position.", PathNodes[(*Iterator).id].id);
Log(Logs::Detail, Logs::None, " (%8.3f, %8.3f, %8.3f) to (%8.3f, %8.3f, %8.3f)",
End.x, End.y, End.z,
PathNodes[(*Iterator).id].v.x, PathNodes[(*Iterator).id].v.y, PathNodes[(*Iterator).id].v.z);
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
{
ClosestPathNodeToEnd = (*Iterator).id;
break;
}
}
if(ClosestPathNodeToEnd < 0) {
Log(Logs::Detail, Logs::None, "No LOS to any end Path Node within range.");
return noderoute;
}
Log(Logs::Detail, Logs::None, "Closest Path Node To End: %2d", ClosestPathNodeToEnd);
if(ClosestPathNodeToStart == ClosestPathNodeToEnd)
{
noderoute.push_back(ClosestPathNodeToStart);
return noderoute;
}
noderoute = FindRoute(ClosestPathNodeToStart, ClosestPathNodeToEnd);
int NodesToAttemptToCull = RuleI(Pathing, CullNodesFromStart);
if(NodesToAttemptToCull > 0)
{
int CulledNodes = 0;
std::deque<int>::iterator First, Second;
while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull))
{
First = noderoute.begin();
Second = First;
++Second;
if((*Second) < 0)
break;
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Second)].v, 1.0f, nullptr)
&& NoHazards(Start, PathNodes[(*Second)].v))
{
noderoute.erase(First);
++CulledNodes;
}
else
break;
}
}
NodesToAttemptToCull = RuleI(Pathing, CullNodesFromEnd);
if(NodesToAttemptToCull > 0)
{
int CulledNodes = 0;
std::deque<int>::iterator First, Second;
while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull))
{
First = noderoute.end();
--First;
Second = First;
--Second;
if((*Second) < 0)
break;
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Second)].v, 1.0f, nullptr)
&& NoHazards(End, PathNodes[(*Second)].v))
{
noderoute.erase(First);
++CulledNodes;
}
else
break;
}
}
return noderoute;
}
const char* DigitToWord(int i)
{
switch(i) {
case 0:
return "zero";
case 1:
return "one";
case 2:
return "two";
case 3:
return "three";
case 4:
return "four";
case 5:
return "five";
case 6:
return "six";
case 7:
return "seven";
case 8:
return "eight";
case 9:
return "nine";
}
return "";
}
void PathManager::SpawnPathNodes()
{
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
auto npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
auto c = PathNodes[i].id / 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(PathNodes[i].v.x, PathNodes[i].v.y, PathNodes[i].v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
}
}
void PathManager::MeshTest()
{
// This will test connectivity between all path nodes
int TotalTests = 0;
int NoConnections = 0;
printf("Beginning Pathmanager connectivity tests.\n"); fflush(stdout);
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
for(uint32 j = 0; j < Head.PathNodeCount; ++j)
{
if(j == i)
continue;
std::deque<int> Route = FindRoute(PathNodes[i].id, PathNodes[j].id);
if(Route.empty())
{
++NoConnections;
printf("FindRoute(%i, %i) **** NO ROUTE FOUND ****\n", PathNodes[i].id, PathNodes[j].id);
}
++TotalTests;
}
}
printf("Executed %i route searches.\n", TotalTests);
printf("Failed to find %i routes.\n", NoConnections);
fflush(stdout);
}
void PathManager::SimpleMeshTest()
{
// This will test connectivity between the first path node and all other nodes
int TotalTests = 0;
int NoConnections = 0;
printf("Beginning Pathmanager connectivity tests.\n");
fflush(stdout);
for(uint32 j = 1; j < Head.PathNodeCount; ++j)
{
std::deque<int> Route = FindRoute(PathNodes[0].id, PathNodes[j].id);
if(Route.empty())
{
++NoConnections;
printf("FindRoute(%i, %i) **** NO ROUTE FOUND ****\n", PathNodes[0].id, PathNodes[j].id);
}
++TotalTests;
}
printf("Executed %i route searches.\n", TotalTests);
printf("Failed to find %i routes.\n", NoConnections);
fflush(stdout);
}
glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached)
{
glm::vec3 To(ToX, ToY, ToZ);
if (Speed <= 0) {
return To;
}
glm::vec3 From(GetX(), GetY(), GetZ());
if (DistanceSquared(To, From) < 1.0f) {
WaypointChanged = false;
NodeReached = true;
Route.clear();
return To;
}
if (Route.empty()) {
Route = zone->pathing->FindRoute(From, To);
PathingDestination = To;
WaypointChanged = true;
NodeReached = false;
return *Route.begin();
}
else {
bool SameDestination = DistanceSquared(To, PathingDestination) < 4.0f;
if (!SameDestination) {
//We had a route but our target position moved too much
Route = zone->pathing->FindRoute(From, To);
PathingDestination = To;
WaypointChanged = true;
NodeReached = false;
return *Route.begin();
}
else {
bool AtNextNode = DistanceSquared(From, *Route.begin()) < 4.0f;
if (AtNextNode) {
WaypointChanged = false;
NodeReached = true;
Route.pop_front();
if (Route.empty()) {
Route = zone->pathing->FindRoute(From, To);
PathingDestination = To;
WaypointChanged = true;
return *Route.begin();
}
else {
auto node = *Route.begin();
if (node.x == 1000000.0f && node.y == 1000000.0f && node.z == 1000000.0f) {
//If is identity node then is teleport node.
Route.pop_front();
if (Route.empty()) {
return To;
}
auto nextNode = *Route.begin();
Teleport(nextNode);
Route.pop_front();
if (Route.empty()) {
return To;
}
return *Route.begin();
}
return node;
}
}
else {
WaypointChanged = false;
NodeReached = false;
return *Route.begin();
}
}
}
}
int PathManager::FindNearestPathNode(glm::vec3 Position)
{
// Find the nearest PathNode we have LOS to.
//
//
float CandidateNodeRangeXY = RuleR(Pathing, CandidateNodeRangeXY);
float CandidateNodeRangeZ = RuleR(Pathing, CandidateNodeRangeZ);
int ClosestPathNodeToStart = -1;
std::deque<PathNodeSortStruct> SortedByDistance;
PathNodeSortStruct TempNode;
for(uint32 i = 0 ; i < Head.PathNodeCount; ++i)
{
if ((std::abs(Position.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) &&
(std::abs(Position.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) &&
(std::abs(Position.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) {
TempNode.id = i;
TempNode.Distance = VectorDistanceNoRoot(Position, PathNodes[i].v);
SortedByDistance.push_back(TempNode);
}
}
std::sort(SortedByDistance.begin(), SortedByDistance.end(), path_compare);
for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator)
{
Log(Logs::Detail, Logs::None, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id);
if(!zone->zonemap->LineIntersectsZone(Position, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
{
ClosestPathNodeToStart = (*Iterator).id;
break;
}
}
if(ClosestPathNodeToStart <0 ) {
Log(Logs::Detail, Logs::None, "No LOS to any starting Path Node within range.");
return -1;
}
return ClosestPathNodeToStart;
}
bool PathManager::NoHazards(glm::vec3 From, glm::vec3 To)
{
// Test the Z coordinate at the mid point.
//
glm::vec3 MidPoint((From.x + To.x) / 2, (From.y + To.y) / 2, From.z);
float NewZ = zone->zonemap->FindBestZ(MidPoint, nullptr);
if (std::abs(NewZ - From.z) > RuleR(Pathing, ZDiffThresholdNew)) {
Log(Logs::Detail, Logs::None, " HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Z Change is %8.3f",
From.x, From.y, From.z, MidPoint.x, MidPoint.y, MidPoint.z, NewZ - From.z);
return false;
}
else
{
Log(Logs::Detail, Logs::None, "No HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Z Change is %8.3f",
From.x, From.y, From.z, MidPoint.x, MidPoint.y, MidPoint.z, NewZ - From.z);
}
return true;
}
bool PathManager::NoHazardsAccurate(glm::vec3 From, glm::vec3 To)
{
float stepx, stepy, stepz, curx, cury, curz;
glm::vec3 cur = From;
float last_z = From.z;
float step_size = 1.0;
curx = From.x;
cury = From.y;
curz = From.z;
do
{
stepx = (float)To.x - curx;
stepy = (float)To.y - cury;
stepz = (float)To.z - curz;
float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz);
stepx = (stepx / factor)*step_size;
stepy = (stepy / factor)*step_size;
stepz = (stepz / factor)*step_size;
glm::vec3 TestPoint(curx, cury, curz);
float NewZ = zone->zonemap->FindBestZ(TestPoint, nullptr);
if (std::abs(NewZ - last_z) > 5.0f) {
Log(Logs::Detail, Logs::None, " HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Best Z %8.3f, Z Change is %8.3f",
From.x, From.y, From.z, TestPoint.x, TestPoint.y, TestPoint.z, NewZ, NewZ - From.z);
return false;
}
last_z = NewZ;
if (zone->watermap)
{
auto from = glm::vec3(From.x, From.y, From.z);
auto to = glm::vec3(To.x, To.y, To.z);
if (zone->watermap->InLiquid(from) || zone->watermap->InLiquid(to))
{
break;
}
auto testPointNewZ = glm::vec3(TestPoint.x, TestPoint.y, NewZ);
if (zone->watermap->InLiquid(testPointNewZ))
{
glm::vec3 TestPointWater(TestPoint.x, TestPoint.y, NewZ - 0.5f);
glm::vec3 TestPointWaterDest = TestPointWater;
glm::vec3 hit;
TestPointWaterDest.z -= 500;
float best_z2 = -999990;
if (zone->zonemap->LineIntersectsZone(TestPointWater, TestPointWaterDest, 1.0f, &hit))
{
best_z2 = hit.z;
}
if (best_z2 == -999990)
{
Log(Logs::Detail, Logs::None, " HAZARD DETECTED, really deep water/lava!");
return false;
}
else
{
if (std::abs(NewZ - best_z2) > RuleR(Pathing, ZDiffThresholdNew)) {
Log(Logs::Detail, Logs::None,
" HAZARD DETECTED, water is fairly deep at %8.3f units deep",
std::abs(NewZ - best_z2));
return false;
}
else
{
Log(Logs::Detail, Logs::None,
" HAZARD NOT DETECTED, water is shallow at %8.3f units deep",
std::abs(NewZ - best_z2));
}
}
}
else
{
Log(Logs::Detail, Logs::None, "Hazard point not in water or lava!");
}
}
else
{
Log(Logs::Detail, Logs::None, "No water map loaded for hazards!");
}
curx += stepx;
cury += stepy;
curz += stepz;
cur.x = curx;
cur.y = cury;
cur.z = curz;
if (std::abs(curx - To.x) < step_size)
cur.x = To.x;
if (std::abs(cury - To.y) < step_size)
cur.y = To.y;
if (std::abs(curz - To.z) < step_size)
cur.z = To.z;
} while (cur.x != To.x || cur.y != To.y || cur.z != To.z);
return true;
}
void Mob::PrintRoute()
{
//printf("Route is : ");
//
//for(auto Iterator = Route.begin(); Iterator !=Route.end(); ++Iterator)
//{
// printf("%i, ", (*Iterator));
//}
//
//printf("\n");
}
void PathManager::OpenDoors(int Node1, int Node2, Mob *ForWho)
{
if(!ForWho || (Node1 >= Head.PathNodeCount) || (Node2 >= Head.PathNodeCount) || (Node1 < 0) || (Node2 < 0))
return;
for(int i = 0; i < PATHNODENEIGHBOURS; ++i)
{
if(PathNodes[Node1].Neighbours[i].id == -1)
return;
if(PathNodes[Node1].Neighbours[i].id != Node2)
continue;
if(PathNodes[Node1].Neighbours[i].DoorID >= 0)
{
Doors *d = entity_list.FindDoor(PathNodes[Node1].Neighbours[i].DoorID);
if(d && !d->IsDoorOpen() )
{
Log(Logs::Detail, Logs::None, "Opening door %i for %s", PathNodes[Node1].Neighbours[i].DoorID, ForWho->GetName());
d->ForceOpen(ForWho);
}
return;
}
}
}
//this assumes that the first point in the list is the player's
//current position, I dont know how well it works if its not.
void Client::SendPathPacket(std::vector<FindPerson_Point> &points) {
if(points.size() < 2) {
//empty length packet == not found.
EQApplicationPacket outapp(OP_FindPersonReply, 0);
QueuePacket(&outapp);
return;
}
int len = sizeof(FindPersonResult_Struct) + (points.size()+1) * sizeof(FindPerson_Point);
auto outapp = new EQApplicationPacket(OP_FindPersonReply, len);
FindPersonResult_Struct* fpr=(FindPersonResult_Struct*)outapp->pBuffer;
std::vector<FindPerson_Point>::iterator cur, end;
cur = points.begin();
end = points.end();
unsigned int r;
for(r = 0; cur != end; ++cur, r++) {
fpr->path[r] = *cur;
}
//put the last element into the destination field
--cur;
fpr->path[r] = *cur;
fpr->dest = *cur;
FastQueuePacket(&outapp);
}
PathNode* PathManager::FindPathNodeByCoordinates(float x, float y, float z)
{
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
if((PathNodes[i].v.x == x) && (PathNodes[i].v.y == y) && (PathNodes[i].v.z == z))
return &PathNodes[i];
return nullptr;
}
int PathManager::GetRandomPathNode()
{
return zone->random.Int(0, Head.PathNodeCount - 1);
}
void PathManager::ShowPathNodeNeighbours(Client *c)
{
if(!c || !c->GetTarget())
return;
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
c->Message(0, "Unable to find path node.");
return;
}
c->Message(0, "Path node %4i", Node->id);
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
char Name[64];
if(PathNodes[i].id < 10)
sprintf(Name, "%s000", DigitToWord(PathNodes[i].id));
else if(PathNodes[i].id < 100)
sprintf(Name, "%s_%s000", DigitToWord(PathNodes[i].id / 10), DigitToWord(PathNodes[i].id % 10));
else
sprintf(Name, "%s_%s_%s000", DigitToWord(PathNodes[i].id/100), DigitToWord((PathNodes[i].id % 100)/10),
DigitToWord(((PathNodes[i].id % 100) %10)));
Mob *m = entity_list.GetMob(Name);
if(m)
m->SendIllusionPacket(151);
}
std::stringstream Neighbours;
for(int i = 0; i < PATHNODENEIGHBOURS; ++i)
{
if(Node->Neighbours[i].id == -1)
break;
Neighbours << Node->Neighbours[i].id << ", ";
char Name[64];
if(Node->Neighbours[i].id < 10)
sprintf(Name, "%s000", DigitToWord(Node->Neighbours[i].id));
else if(Node->Neighbours[i].id < 100)
sprintf(Name, "%s_%s000", DigitToWord(Node->Neighbours[i].id / 10), DigitToWord(Node->Neighbours[i].id % 10));
else
sprintf(Name, "%s_%s_%s000", DigitToWord(Node->Neighbours[i].id/100), DigitToWord((Node->Neighbours[i].id % 100)/10),
DigitToWord(((Node->Neighbours[i].id % 100) %10)));
Mob *m = entity_list.GetMob(Name);
if(m)
m->SendIllusionPacket(46);
}
c->Message(0, "Neighbours: %s", Neighbours.str().c_str());
}
void PathManager::NodeInfo(Client *c)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
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);
bool neighbour = false;
for(int x = 0; x < 50; ++x)
{
if(Node->Neighbours[x].id != -1)
{
if(!neighbour)
{
c->Message(0, "Neighbours found:");
neighbour = true;
}
c->Message(0, "id: %i, distance: %.2f, door id: %i, is teleport: %i",
Node->Neighbours[x].id, Node->Neighbours[x].distance,
Node->Neighbours[x].DoorID, Node->Neighbours[x].Teleport);
}
}
if(!neighbour)
{
c->Message(0, "No neighbours found!");
}
return;
}
void PathManager::DumpPath(std::string filename)
{
std::ofstream o_file;
std::string file_to_write = StringFormat("%s%s", Config->MapDir.c_str(), filename.c_str());
o_file.open(file_to_write.c_str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out);
o_file.write("EQEMUPATH", 9);
o_file.write((const char*)&Head, sizeof(Head));
o_file.write((const char*)PathNodes, (sizeof(PathNode)*Head.PathNodeCount));
o_file.close();
}
int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 requested_id)
{
int32 new_id = -1;
if(requested_id != 0)
{
new_id = requested_id;
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
if(PathNodes[i].id == requested_id)
{
new_id = -1;
break;
}
}
}
if(new_id == -1)
{
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
if(PathNodes[i].id - new_id > 1) {
new_id = PathNodes[i].id - 1;
break;
}
if(PathNodes[i].id > new_id)
new_id = PathNodes[i].id;
}
new_id++;
}
PathNode new_node;
new_node.v.x = x;
new_node.v.y = y;
new_node.v.z = z;
new_node.bestz = best_z;
new_node.id = (uint16)new_id;
for(int x = 0; x < PATHNODENEIGHBOURS; ++x)
{
new_node.Neighbours[x].id = -1;
new_node.Neighbours[x].distance = 0.0;
new_node.Neighbours[x].DoorID = -1;
new_node.Neighbours[x].Teleport = 0;
}
Head.PathNodeCount++;
if(Head.PathNodeCount > 1)
{
auto t_PathNodes = new PathNode[Head.PathNodeCount];
for(uint32 x = 0; x < (Head.PathNodeCount - 1); ++x)
{
t_PathNodes[x].v.x = PathNodes[x].v.x;
t_PathNodes[x].v.y = PathNodes[x].v.y;
t_PathNodes[x].v.z = PathNodes[x].v.z;
t_PathNodes[x].bestz = PathNodes[x].bestz;
t_PathNodes[x].id = PathNodes[x].id;
for(int n = 0; n < PATHNODENEIGHBOURS; ++n)
{
t_PathNodes[x].Neighbours[n].distance = PathNodes[x].Neighbours[n].distance;
t_PathNodes[x].Neighbours[n].DoorID = PathNodes[x].Neighbours[n].DoorID;
t_PathNodes[x].Neighbours[n].id = PathNodes[x].Neighbours[n].id;
t_PathNodes[x].Neighbours[n].Teleport = PathNodes[x].Neighbours[n].Teleport;
}
}
int32 index = (Head.PathNodeCount - 1);
t_PathNodes[index].v.x = new_node.v.x;
t_PathNodes[index].v.y = new_node.v.y;
t_PathNodes[index].v.z = new_node.v.z;
t_PathNodes[index].bestz = new_node.bestz;
t_PathNodes[index].id = new_node.id;
for(int n = 0; n < PATHNODENEIGHBOURS; ++n)
{
t_PathNodes[index].Neighbours[n].distance = new_node.Neighbours[n].distance;
t_PathNodes[index].Neighbours[n].DoorID = new_node.Neighbours[n].DoorID;
t_PathNodes[index].Neighbours[n].id = new_node.Neighbours[n].id;
t_PathNodes[index].Neighbours[n].Teleport = new_node.Neighbours[n].Teleport;
}
delete[] PathNodes;
PathNodes = t_PathNodes;
auto npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
if(new_id < 10)
sprintf(npc_type->name, "%s", DigitToWord(new_id));
else if(new_id < 100)
sprintf(npc_type->name, "%s_%s", DigitToWord(new_id/10), DigitToWord(new_id % 10));
else
sprintf(npc_type->name, "%s_%s_%s", DigitToWord(new_id/100), DigitToWord((new_id % 100)/10),
DigitToWord(((new_id % 100) %10)));
sprintf(npc_type->lastname, "%i", new_id);
npc_type->cur_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 151;
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(new_node.v.x, new_node.v.y, new_node.v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
safe_delete_array(ClosedListFlag);
ClosedListFlag = new int[Head.PathNodeCount];
return new_id;
}
else
{
PathNodes = new PathNode[Head.PathNodeCount];
PathNodes[0].v.x = new_node.v.x;
PathNodes[0].v.y = new_node.v.y;
PathNodes[0].v.z = new_node.v.z;
PathNodes[0].bestz = new_node.bestz;
PathNodes[0].id = new_node.id;
for(int n = 0; n < PATHNODENEIGHBOURS; ++n)
{
PathNodes[0].Neighbours[n].distance = new_node.Neighbours[n].distance;
PathNodes[0].Neighbours[n].DoorID = new_node.Neighbours[n].DoorID;
PathNodes[0].Neighbours[n].id = new_node.Neighbours[n].id;
PathNodes[0].Neighbours[n].Teleport = new_node.Neighbours[n].Teleport;
}
auto npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
if(new_id < 10)
sprintf(npc_type->name, "%s", DigitToWord(new_id));
else if(new_id < 100)
sprintf(npc_type->name, "%s_%s", DigitToWord(new_id/10), DigitToWord(new_id % 10));
else
sprintf(npc_type->name, "%s_%s_%s", DigitToWord(new_id/100), DigitToWord((new_id % 100)/10),
DigitToWord(((new_id % 100) %10)));
sprintf(npc_type->lastname, "%i", new_id);
npc_type->cur_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 151;
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(new_node.v.x, new_node.v.y, new_node.v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
ClosedListFlag = new int[Head.PathNodeCount];
return new_id;
}
}
bool PathManager::DeleteNode(Client *c)
{
if(!c)
{
return false;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return false;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return false;
}
return DeleteNode(Node->id);
}
bool PathManager::DeleteNode(int32 id)
{
//if the current list is > 1 in size create a new list of size current size - 1
//transfer all but the current node to this new list and delete our current list
//set this new list to be our current list
//else if the size is 1 just delete our current list and set it to zero.
//go through and delete all ref in neighbors...
if(Head.PathNodeCount > 1)
{
auto t_PathNodes = new PathNode[Head.PathNodeCount - 1];
uint32 index = 0;
for(uint32 x = 0; x < Head.PathNodeCount; x++)
{
if(PathNodes[x].id != id)
{
t_PathNodes[index].id = PathNodes[x].id;
t_PathNodes[index].v.x = PathNodes[x].v.x;
t_PathNodes[index].v.y = PathNodes[x].v.y;
t_PathNodes[index].v.z = PathNodes[x].v.z;
t_PathNodes[index].bestz = PathNodes[x].bestz;
for(int n = 0; n < PATHNODENEIGHBOURS; ++n)
{
t_PathNodes[index].Neighbours[n].distance = PathNodes[x].Neighbours[n].distance;
t_PathNodes[index].Neighbours[n].DoorID = PathNodes[x].Neighbours[n].DoorID;
t_PathNodes[index].Neighbours[n].id = PathNodes[x].Neighbours[n].id;
t_PathNodes[index].Neighbours[n].Teleport = PathNodes[x].Neighbours[n].Teleport;
}
index++;
}
}
Head.PathNodeCount--;
delete[] PathNodes;
PathNodes = t_PathNodes;
for(uint32 y = 0; y < Head.PathNodeCount; ++y)
{
for(int n = 0; n < PATHNODENEIGHBOURS; ++n)
{
if(PathNodes[y].Neighbours[n].id == id)
{
PathNodes[y].Neighbours[n].Teleport = 0;
PathNodes[y].Neighbours[n].DoorID = -1;
PathNodes[y].Neighbours[n].distance = 0.0;
PathNodes[y].Neighbours[n].id = -1;
}
}
}
safe_delete_array(ClosedListFlag);
ClosedListFlag = new int[Head.PathNodeCount];
}
else
{
delete[] PathNodes;
PathNodes = nullptr;
}
return true;
}
void PathManager::ConnectNodeToNode(Client *c, int32 Node2, int32 teleport, int32 doorid)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
c->Message(0, "Connecting %i to %i", Node->id, Node2);
if(doorid == 0)
ConnectNodeToNode(Node->id, Node2, teleport);
else
ConnectNodeToNode(Node->id, Node2, teleport, doorid);
}
void PathManager::ConnectNodeToNode(int32 Node1, int32 Node2, int32 teleport, int32 doorid)
{
PathNode *a = nullptr;
PathNode *b = nullptr;
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
if(PathNodes[x].id == Node1)
{
a = &PathNodes[x];
if(b)
break;
}
else if(PathNodes[x].id == Node2)
{
b = &PathNodes[x];
if(a)
break;
}
}
if(a == nullptr || b == nullptr)
return;
bool connect_a_to_b = true;
if(NodesConnected(a, b))
connect_a_to_b = false;
bool connect_b_to_a = true;
if(NodesConnected(b, a))
connect_b_to_a = false;
if(connect_a_to_b)
{
for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i)
{
if(a->Neighbours[a_i].id == -1)
{
a->Neighbours[a_i].id = b->id;
a->Neighbours[a_i].DoorID = doorid;
a->Neighbours[a_i].Teleport = teleport;
a->Neighbours[a_i].distance = VectorDistance(a->v, b->v);
break;
}
}
}
if(connect_b_to_a)
{
for(int b_i = 0; b_i < PATHNODENEIGHBOURS; ++b_i)
{
if(b->Neighbours[b_i].id == -1)
{
b->Neighbours[b_i].id = a->id;
b->Neighbours[b_i].DoorID = doorid;
b->Neighbours[b_i].Teleport = teleport;
b->Neighbours[b_i].distance = VectorDistance(a->v, b->v);
break;
}
}
}
}
void PathManager::ConnectNode(Client *c, int32 Node2, int32 teleport, int32 doorid)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
c->Message(0, "Connecting %i to %i", Node->id, Node2);
if(doorid == 0)
ConnectNode(Node->id, Node2, teleport);
else
ConnectNode(Node->id, Node2, teleport, doorid);
}
void PathManager::ConnectNode(int32 Node1, int32 Node2, int32 teleport, int32 doorid)
{
PathNode *a = nullptr;
PathNode *b = nullptr;
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
if(PathNodes[x].id == Node1)
{
a = &PathNodes[x];
if(b)
break;
}
else if(PathNodes[x].id == Node2)
{
b = &PathNodes[x];
if(a)
break;
}
}
if(a == nullptr || b == nullptr)
return;
bool connect_a_to_b = true;
if(NodesConnected(a, b))
connect_a_to_b = false;
if(connect_a_to_b)
{
for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i)
{
if(a->Neighbours[a_i].id == -1)
{
a->Neighbours[a_i].id = b->id;
a->Neighbours[a_i].DoorID = doorid;
a->Neighbours[a_i].Teleport = teleport;
a->Neighbours[a_i].distance = VectorDistance(a->v, b->v);
break;
}
}
}
}
void PathManager::DisconnectNodeToNode(Client *c, int32 Node2)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
DisconnectNodeToNode(Node->id, Node2);
}
void PathManager::DisconnectNodeToNode(int32 Node1, int32 Node2)
{
PathNode *a = nullptr;
PathNode *b = nullptr;
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
if(PathNodes[x].id == Node1)
{
a = &PathNodes[x];
if(b)
break;
}
else if(PathNodes[x].id == Node2)
{
b = &PathNodes[x];
if(a)
break;
}
}
if(a == nullptr || b == nullptr)
return;
bool disconnect_a_from_b = false;
if(NodesConnected(a, b))
disconnect_a_from_b = true;
bool disconnect_b_from_a = false;
if(NodesConnected(b, a))
disconnect_b_from_a = true;
if(disconnect_a_from_b)
{
for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i)
{
if(a->Neighbours[a_i].id == b->id)
{
a->Neighbours[a_i].distance = 0.0;
a->Neighbours[a_i].DoorID = -1;
a->Neighbours[a_i].id = -1;
a->Neighbours[a_i].Teleport = 0;
break;
}
}
}
if(disconnect_b_from_a)
{
for(int b_i = 0; b_i < PATHNODENEIGHBOURS; ++b_i)
{
if(b->Neighbours[b_i].id == a->id)
{
b->Neighbours[b_i].distance = 0.0;
b->Neighbours[b_i].DoorID = -1;
b->Neighbours[b_i].id = -1;
b->Neighbours[b_i].Teleport = 0;
break;
}
}
}
}
void PathManager::MoveNode(Client *c)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
Node->v.x = c->GetX();
Node->v.y = c->GetY();
Node->v.z = c->GetZ();
if(zone->zonemap)
{
glm::vec3 loc(c->GetX(), c->GetY(), c->GetZ());
Node->bestz = zone->zonemap->FindBestZ(loc, nullptr);
}
else
{
Node->bestz = Node->v.z;
}
}
void PathManager::DisconnectAll(Client *c)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
for(int x = 0; x < PATHNODENEIGHBOURS; ++x)
{
Node->Neighbours[x].distance = 0;
Node->Neighbours[x].Teleport = 0;
Node->Neighbours[x].DoorID = -1;
Node->Neighbours[x].id = -1;
}
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
if(PathNodes[i].id == Node->id)
continue;
for(int ix = 0; ix < PATHNODENEIGHBOURS; ++ix)
{
if(PathNodes[i].Neighbours[ix].id == Node->id)
{
PathNodes[i].Neighbours[ix].distance = 0;
PathNodes[i].Neighbours[ix].Teleport = 0;
PathNodes[i].Neighbours[ix].id = -1;
PathNodes[i].Neighbours[ix].DoorID = -1;
}
}
}
}
//checks if anything in a points to b
bool PathManager::NodesConnected(PathNode *a, PathNode *b)
{
if(!a)
return false;
if(!b)
return false;
for(int x = 0; x < PATHNODENEIGHBOURS; ++x)
{
if(a->Neighbours[x].id == b->id)
return true;
}
return false;
}
bool PathManager::CheckLosFN(glm::vec3 a, glm::vec3 b)
{
if(zone->zonemap)
{
glm::vec3 hit;
glm::vec3 myloc;
glm::vec3 oloc;
myloc.x = a.x;
myloc.y = a.y;
myloc.z = a.z;
oloc.x = b.x;
oloc.y = b.y;
oloc.z = b.z;
if(zone->zonemap->LineIntersectsZone(myloc, oloc, 1.0f, nullptr))
{
return false;
}
}
return true;
}
void PathManager::ProcessNodesAndSave(std::string filename)
{
if(zone->zonemap)
{
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
for(int in = 0; in < PATHNODENEIGHBOURS; ++in)
{
PathNodes[i].Neighbours[in].distance = 0.0;
PathNodes[i].Neighbours[in].DoorID = -1;
PathNodes[i].Neighbours[in].id = -1;
PathNodes[i].Neighbours[in].Teleport = 0;
}
}
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
for(uint32 y = 0; y < Head.PathNodeCount; ++y)
{
if(y == x) //can't connect to ourselves.
continue;
if(!NodesConnected(&PathNodes[x], &PathNodes[y]))
{
if(VectorDistance(PathNodes[x].v, PathNodes[y].v) <= 200)
{
if(CheckLosFN(PathNodes[x].v, PathNodes[y].v))
{
if(NoHazardsAccurate(PathNodes[x].v, PathNodes[y].v))
{
ConnectNodeToNode(PathNodes[x].id, PathNodes[y].id, 0, 0);
}
}
}
}
}
}
}
DumpPath(filename);
}
void PathManager::ResortConnections()
{
NeighbourNode Neigh[PATHNODENEIGHBOURS];
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
int index = 0;
for(int y = 0; y < PATHNODENEIGHBOURS; ++y)
{
Neigh[y].distance = 0;
Neigh[y].DoorID = -1;
Neigh[y].id = -1;
Neigh[y].Teleport = 0;
}
for(int z = 0; z < PATHNODENEIGHBOURS; ++z)
{
if(PathNodes[x].Neighbours[z].id != -1)
{
Neigh[index].id = PathNodes[x].Neighbours[z].id;
Neigh[index].distance = PathNodes[x].Neighbours[z].distance;
Neigh[index].DoorID = PathNodes[x].Neighbours[z].DoorID;
Neigh[index].Teleport = PathNodes[x].Neighbours[z].Teleport;
index++;
}
}
for(int i = 0; i < PATHNODENEIGHBOURS; ++i)
{
PathNodes[x].Neighbours[i].distance = 0;
PathNodes[x].Neighbours[i].DoorID = -1;
PathNodes[x].Neighbours[i].id = -1;
PathNodes[x].Neighbours[i].Teleport = 0;
}
for(int z = 0; z < PATHNODENEIGHBOURS; ++z)
{
PathNodes[x].Neighbours[z].distance = Neigh[z].distance;
PathNodes[x].Neighbours[z].DoorID = Neigh[z].DoorID;
PathNodes[x].Neighbours[z].id = Neigh[z].id;
PathNodes[x].Neighbours[z].Teleport = Neigh[z].Teleport;
}
}
}
void PathManager::QuickConnect(Client *c, bool set)
{
if(!c)
{
return;
}
if(!c->GetTarget())
{
c->Message(0, "You must target a node.");
return;
}
PathNode *Node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if(!Node)
{
return;
}
if(set)
{
c->Message(0, "Setting %i to the quick connect target", Node->id);
QuickConnectTarget = Node->id;
}
else
{
if(QuickConnectTarget >= 0)
{
ConnectNodeToNode(QuickConnectTarget, Node->id);
}
}
}
struct InternalPathSort
{
int16 old_id;
int16 new_id;
};
void PathManager::SortNodes()
{
std::vector<InternalPathSort> sorted_vals;
for(uint32 x = 0; x < Head.PathNodeCount; ++x)
{
InternalPathSort tmp;
tmp.old_id = PathNodes[x].id;
sorted_vals.push_back(tmp);
}
auto t_PathNodes = new PathNode[Head.PathNodeCount];
memcpy(t_PathNodes, PathNodes, sizeof(PathNode)*Head.PathNodeCount);
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
{
for(size_t j = 0; j < sorted_vals.size(); ++j)
{
if(sorted_vals[j].old_id == PathNodes[i].id)
{
if(i != PathNodes[i].id)
{
printf("Assigning new id of index %i differs from old id %i\n", i, PathNodes[i].id);
}
sorted_vals[j].new_id = i;
}
}
t_PathNodes[i].id = i;
}
for(uint32 y = 0; y < Head.PathNodeCount; ++y)
{
for(int z = 0; z < PATHNODENEIGHBOURS; ++z)
{
if(PathNodes[y].Neighbours[z].id != -1)
{
int new_val = -1;
for(size_t c = 0; c < sorted_vals.size(); ++c)
{
if(PathNodes[y].Neighbours[z].id == sorted_vals[c].old_id)
{
new_val = sorted_vals[c].new_id;
break;
}
}
if(new_val != -1)
{
if(t_PathNodes[y].Neighbours[z].id != new_val)
{
printf("changing neighbor value to %i from %i\n", new_val, t_PathNodes[y].Neighbours[z].id);
}
t_PathNodes[y].Neighbours[z].id = new_val;
}
}
}
}
safe_delete_array(PathNodes);
PathNodes = t_PathNodes;
}