Files
mod-playerbots/src/TravelMgr.cpp
2024-08-04 12:04:06 +02:00

4272 lines
140 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "TravelMgr.h"
#include <iomanip>
#include <numeric>
#include "CellImpl.h"
#include "ChatHelper.h"
#include "MMapFactory.h"
#include "MapMgr.h"
#include "PathGenerator.h"
#include "Playerbots.h"
#include "StrategyContext.h"
#include "TransportMgr.h"
#include "VMapFactory.h"
#include "VMapMgr2.h"
WorldPosition::WorldPosition(std::string const str)
{
std::stringstream out(str);
out >> this->m_mapId;
out >> this->m_positionX;
out >> this->m_positionY;
out >> this->m_positionZ;
out >> this->m_orientation;
}
WorldPosition::WorldPosition(uint32 mapId, const Position& pos)
: WorldLocation(mapId, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation())
{
}
WorldPosition::WorldPosition(WorldObject const* wo)
{
if (wo)
{
set(WorldLocation(wo->GetMapId(), wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ(),
wo->GetOrientation()));
}
}
WorldPosition::WorldPosition(std::vector<WorldPosition*> list, WorldPositionConst conType)
{
uint32 size = list.size();
if (!size)
return;
if (size == 1)
set(*list.front());
else if (conType == WP_RANDOM)
set(*list[urand(0, size - 1)]);
else if (conType == WP_CENTROID)
{
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0]->getMapId(), 0, 0, 0, 0),
[size](WorldLocation i, WorldPosition* j)
{
i.m_positionX += j->getX() / size;
i.m_positionY += j->getY() / size;
i.m_positionZ += j->getZ() / size;
i.NormalizeOrientation(i.m_orientation += j->getO() / size);
return i;
}));
}
else if (conType == WP_MEAN_CENTROID)
{
WorldPosition pos = WorldPosition(list, WP_CENTROID);
set(*pos.closestSq(list));
}
}
WorldPosition::WorldPosition(std::vector<WorldPosition> list, WorldPositionConst conType)
{
uint32 size = list.size();
if (!size)
return;
if (size == 1)
set(list.front());
else if (conType == WP_RANDOM)
set(list[urand(0, size - 1)]);
else if (conType == WP_CENTROID)
{
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0].getMapId(), 0, 0, 0, 0),
[size](WorldLocation i, WorldPosition& j)
{
i.m_positionX += j.getX() / size;
i.m_positionY += j.getY() / size;
i.m_positionZ += j.getZ() / size;
i.NormalizeOrientation(i.m_orientation += j.getO() / size);
return i;
}));
}
else if (conType == WP_MEAN_CENTROID)
{
WorldPosition pos = WorldPosition(list, WP_CENTROID);
set(pos.closestSq(list));
}
}
WorldPosition::WorldPosition(uint32 mapid, GridCoord grid)
: WorldLocation(mapid, (int32(grid.x_coord) - CENTER_GRID_ID - 0.5) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET,
(int32(grid.y_coord) - CENTER_GRID_ID - 0.5) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET, 0, 0)
{
}
WorldPosition::WorldPosition(uint32 mapid, CellCoord cell)
: WorldLocation(
mapid, (int32(cell.x_coord) - CENTER_GRID_CELL_ID - 0.5) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET,
(int32(cell.y_coord) - CENTER_GRID_CELL_ID - 0.5) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET, 0, 0)
{
}
WorldPosition::WorldPosition(uint32 mapid, mGridCoord grid)
: WorldLocation(mapid, (32 - grid.first) * SIZE_OF_GRIDS, (32 - grid.second) * SIZE_OF_GRIDS, 0, 0)
{
}
void WorldPosition::set(const WorldLocation& pos) { WorldRelocate(pos); }
void WorldPosition::set(const WorldPosition& pos)
{
WorldRelocate(pos.m_mapId, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
}
void WorldPosition::set(const WorldObject* pos)
{
WorldRelocate(pos->GetMapId(), pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation());
}
void WorldPosition::setMapId(uint32 id)
{
m_mapId = id;
}
void WorldPosition::setX(float x) { m_positionX = x; }
void WorldPosition::setY(float y) { m_positionY = y; }
void WorldPosition::setZ(float z) { m_positionZ = z; }
void WorldPosition::setO(float o) { m_orientation = o; }
WorldPosition::operator bool() const
{
return GetMapId() != 0 || GetPositionX() != 0 || GetPositionY() != 0 || GetPositionZ() != 0;
}
bool operator==(WorldPosition const& p1, const WorldPosition& p2)
{
return p1.GetMapId() == p2.GetMapId() && p2.GetPositionX() == p1.GetPositionX() &&
p2.GetPositionY() == p1.GetPositionY() && p2.GetPositionZ() == p1.GetPositionZ() &&
p2.GetOrientation() == p1.GetOrientation();
}
bool operator!=(WorldPosition const& p1, const WorldPosition& p2) { return !(p1 == p2); }
WorldPosition& WorldPosition::operator+=(WorldPosition const& p1)
{
m_positionX += p1.GetPositionX();
m_positionY += p1.GetPositionY();
m_positionZ += p1.GetPositionZ();
return *this;
}
WorldPosition& WorldPosition::operator-=(WorldPosition const& p1)
{
m_positionX -= p1.GetPositionX();
m_positionY -= p1.GetPositionY();
m_positionZ -= p1.GetPositionZ();
return *this;
}
uint32 WorldPosition::getMapId() { return GetMapId(); }
float WorldPosition::getX() { return GetPositionX(); }
float WorldPosition::getY() { return GetPositionY(); }
float WorldPosition::getZ() { return GetPositionZ(); }
float WorldPosition::getO() { return GetOrientation(); }
bool WorldPosition::isOverworld()
{
return GetMapId() == 0 || GetMapId() == 1 || GetMapId() == 530 || GetMapId() == 571;
}
bool WorldPosition::isInWater()
{
return getMap() ? getMap()->IsInWater(PHASEMASK_NORMAL, GetPositionX(), GetPositionY(), GetPositionZ(),
DEFAULT_COLLISION_HEIGHT)
: false;
};
bool WorldPosition::isUnderWater()
{
return getMap() ? getMap()->IsUnderWater(PHASEMASK_NORMAL, GetPositionX(), GetPositionY(), GetPositionZ(),
DEFAULT_COLLISION_HEIGHT)
: false;
};
WorldPosition WorldPosition::relPoint(WorldPosition* center)
{
return WorldPosition(GetMapId(), GetPositionX() - center->GetPositionX(), GetPositionY() - center->GetPositionY(),
GetPositionZ() - center->GetPositionZ(), GetOrientation());
}
WorldPosition WorldPosition::offset(WorldPosition* center)
{
return WorldPosition(GetMapId(), GetPositionX() + center->GetPositionX(), GetPositionY() + center->GetPositionY(),
GetPositionZ() + center->GetPositionZ(), GetOrientation());
}
float WorldPosition::size()
{
return sqrt(pow(GetPositionX(), 2.0) + pow(GetPositionY(), 2.0) + pow(GetPositionZ(), 2.0));
}
float WorldPosition::distance(WorldPosition* center)
{
if (GetMapId() == center->getMapId())
return relPoint(center).size();
// this -> mapTransfer | mapTransfer -> center
return sTravelMgr->mapTransDistance(*this, *center);
};
float WorldPosition::fDist(WorldPosition* center)
{
if (GetMapId() == center->getMapId())
return sqrt(sqDistance2d(center));
// this -> mapTransfer | mapTransfer -> center
return sTravelMgr->fastMapTransDistance(*this, *center);
};
float mapTransfer::fDist(WorldPosition start, WorldPosition end)
{
return start.fDist(pointFrom) + portalLength + pointTo.fDist(end);
}
// When moving from this along list return last point that falls within range.
// Distance is move distance along path.
WorldPosition WorldPosition::lastInRange(std::vector<WorldPosition> list, float minDist, float maxDist)
{
WorldPosition rPoint;
float startDist = 0.0f;
// Enter the path at the closest point.
for (auto& p : list)
{
float curDist = distance(p);
if (startDist < curDist || p == list.front())
startDist = curDist + 0.1f;
}
float totalDist = 0.0f;
// Follow the path from the last nearest point
// Return last point in range.
for (auto& p : list)
{
float curDist = distance(p);
if (totalDist > 0) // We have started the path. Keep counting.
totalDist += p.distance(std::prev(&p, 1));
if (curDist == startDist) // Start the path here.
totalDist = startDist;
if (minDist > 0 && totalDist < minDist)
continue;
if (maxDist > 0 && totalDist > maxDist)
continue; // We do not break here because the path may loop back and have a second startDist point.
rPoint = p;
}
return rPoint;
};
// Todo: remove or adjust to above standard.
WorldPosition WorldPosition::firstOutRange(std::vector<WorldPosition> list, float minDist, float maxDist)
{
WorldPosition rPoint;
for (auto& p : list)
{
if (minDist > 0 && distance(p) < minDist)
return p;
if (maxDist > 0 && distance(p) > maxDist)
return p;
rPoint = p;
}
return rPoint;
}
// Returns true if (on the x-y plane) the position is inside the three points.
bool WorldPosition::isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3)
{
if (getMapId() != p1->getMapId() != p2->getMapId() != p3->getMapId())
return false;
float d1, d2, d3;
bool has_neg, has_pos;
d1 = mSign(p1, p2);
d2 = mSign(p2, p3);
d3 = mSign(p3, p1);
has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
return !(has_neg && has_pos);
}
MapEntry const* WorldPosition::getMapEntry() { return sMapStore.LookupEntry(GetMapId()); };
uint32 WorldPosition::getInstanceId()
{
if (Map* map = sMapMgr->FindBaseMap(getMapId()))
return map->GetInstanceId();
return 0;
}
Map* WorldPosition::getMap()
{
return sMapMgr->FindMap(GetMapId(), getMapEntry()->Instanceable() ? getInstanceId() : 0);
}
float WorldPosition::getHeight() // remove const - whipowill
{
return getMap()->GetHeight(getX(), getY(), getZ());
}
G3D::Vector3 WorldPosition::getVector3() { return G3D::Vector3(GetPositionX(), GetPositionY(), GetPositionZ()); }
std::string const WorldPosition::print()
{
std::ostringstream out;
out << GetMapId() << std::fixed << std::setprecision(2);
out << ';' << GetPositionX();
out << ';' << GetPositionY();
out << ';' << GetPositionZ();
out << ';' << GetOrientation();
return out.str();
}
std::string const WorldPosition::to_string()
{
std::stringstream out;
out << GetMapId();
out << GetPositionX();
out << GetPositionY();
out << GetPositionZ();
out << GetOrientation();
return out.str();
};
void WorldPosition::printWKT(std::vector<WorldPosition> points, std::ostringstream& out, uint32 dim, bool loop)
{
switch (dim)
{
case 0:
if (points.size() == 1)
out << "\"POINT(";
else
out << "\"MULTIPOINT(";
break;
case 1:
out << "\"LINESTRING(";
break;
case 2:
out << "\"POLYGON((";
}
for (auto& p : points)
out << p.getDisplayX() << " " << p.getDisplayY() << (!loop && &p == &points.back() ? "" : ",");
if (loop)
out << points.front().getDisplayX() << " " << points.front().getDisplayY();
out << (dim == 2 ? "))\"," : ")\",");
}
WorldPosition WorldPosition::getDisplayLocation()
{
WorldPosition pos = sTravelNodeMap->getMapOffset(getMapId());
return offset(const_cast<WorldPosition*>(&pos));
}
uint16 WorldPosition::getAreaId() { return sMapMgr->GetAreaId(PHASEMASK_NORMAL, getMapId(), getX(), getY(), getZ()); }
AreaTableEntry const* WorldPosition::getArea()
{
uint16 areaId = getAreaId();
if (!areaId)
return nullptr;
return sAreaTableStore.LookupEntry(areaId);
}
std::string const WorldPosition::getAreaName(bool fullName, bool zoneName)
{
if (!isOverworld())
{
MapEntry const* map = sMapStore.LookupEntry(getMapId());
if (map)
return map->name[0];
}
AreaTableEntry const* area = getArea();
if (!area)
return "";
std::string areaName = area->area_name[0];
if (fullName)
{
uint16 zoneId = area->zone;
while (zoneId > 0)
{
AreaTableEntry const* parentArea = sAreaTableStore.LookupEntry(zoneId);
if (!parentArea)
break;
std::string const subAreaName = parentArea->area_name[0];
if (zoneName)
areaName = subAreaName;
else
areaName = subAreaName + " " + areaName;
zoneId = parentArea->zone;
}
}
return std::move(areaName);
}
std::set<Transport*> WorldPosition::getTransports(uint32 entry)
{
/*
if(!entry)
return getMap()->m_transports;
else
{
*/
std::set<Transport*> transports;
/*
for (auto transport : getMap()->m_transports)
if(transport->GetEntry() == entry)
transports.insert(transport);
return transports;
}
*/
return transports;
}
std::vector<GridCoord> WorldPosition::getGridCoord(WorldPosition secondPos)
{
std::vector<GridCoord> retVec;
int lx = std::min(getGridCoord().x_coord, secondPos.getGridCoord().x_coord);
int ly = std::min(getGridCoord().y_coord, secondPos.getGridCoord().y_coord);
int ux = std::max(getGridCoord().x_coord, secondPos.getGridCoord().x_coord);
int uy = std::max(getGridCoord().y_coord, secondPos.getGridCoord().y_coord);
int32 border = 1;
lx = std::min(std::max(border, lx), MAX_NUMBER_OF_GRIDS - border);
ly = std::min(std::max(border, ly), MAX_NUMBER_OF_GRIDS - border);
ux = std::min(std::max(border, ux), MAX_NUMBER_OF_GRIDS - border);
uy = std::min(std::max(border, uy), MAX_NUMBER_OF_GRIDS - border);
for (int x = lx - border; x <= ux + border; x++)
{
for (int y = ly - border; y <= uy + border; y++)
{
retVec.push_back(GridCoord(x, y));
}
}
return retVec;
}
std::vector<WorldPosition> WorldPosition::fromGridCoord(GridCoord gridCoord)
{
std::vector<WorldPosition> retVec;
GridCoord g;
for (uint32 d = 0; d < 4; d++)
{
g = gridCoord;
if (d == 1 || d == 2)
g.inc_x(1);
if (d == 2 || d == 3)
g.inc_y(1);
retVec.push_back(WorldPosition(getMapId(), g));
}
return retVec;
}
std::vector<WorldPosition> WorldPosition::fromCellCoord(CellCoord cellcoord)
{
std::vector<WorldPosition> retVec;
CellCoord p;
for (uint32 d = 0; d < 4; d++)
{
p = cellcoord;
if (d == 1 || d == 2)
p.inc_x(1);
if (d == 2 || d == 3)
p.inc_y(1);
retVec.push_back(WorldPosition(getMapId(), p));
}
return retVec;
}
std::vector<WorldPosition> WorldPosition::gridFromCellCoord(CellCoord cellCoord)
{
Cell c(cellCoord);
return fromGridCoord(GridCoord(c.GridX(), c.GridY()));
}
std::vector<std::pair<int32, int32>> WorldPosition::getmGridCoords(WorldPosition secondPos)
{
std::vector<mGridCoord> retVec;
int lx = std::min(getmGridCoord().first, secondPos.getmGridCoord().first);
int ly = std::min(getmGridCoord().second, secondPos.getmGridCoord().second);
int ux = std::max(getmGridCoord().first, secondPos.getmGridCoord().first);
int uy = std::max(getmGridCoord().second, secondPos.getmGridCoord().second);
int border = 1;
// lx = std::min(std::max(border, lx), MAX_NUMBER_OF_GRIDS - border);
// ly = std::min(std::max(border, ly), MAX_NUMBER_OF_GRIDS - border);
// ux = std::min(std::max(border, ux), MAX_NUMBER_OF_GRIDS - border);
// uy = std::min(std::max(border, uy), MAX_NUMBER_OF_GRIDS - border);
for (int x = lx - border; x <= ux + border; x++)
{
for (int y = ly - border; y <= uy + border; y++)
{
retVec.push_back(std::make_pair(x, y));
}
}
return retVec;
}
std::vector<WorldPosition> WorldPosition::frommGridCoord(mGridCoord GridCoord)
{
std::vector<WorldPosition> retVec;
mGridCoord g;
for (uint32 d = 0; d < 4; d++)
{
g = GridCoord;
if (d == 1 || d == 2)
g.second++;
if (d == 2 || d == 3)
g.first++;
retVec.push_back(WorldPosition(getMapId(), g));
}
return retVec;
}
void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y)
{
std::string const fileName = "load_map_grid.csv";
if (isOverworld() && false || false)
{
if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y))
if (sPlayerbotAIConfig->hasLog(fileName))
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00,\"mmap\", " << x << "," << y << "," << (sTravelMgr->isBadMmap(mapId, x, y) ? "0" : "1")
<< ",";
printWKT(fromGridCoord(GridCoord(x, y)), out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
else
{
// This needs to be disabled or maps will not load.
// Needs more testing to check for impact on movement.
if (false)
if (!sTravelMgr->isBadVmap(mapId, x, y))
{
// load VMAPs for current map/grid...
const MapEntry* i_mapEntry = sMapStore.LookupEntry(mapId);
const char* mapName = i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap(
(sWorld->GetDataPath() + "vmaps").c_str(), mapId, x, y);
switch (vmapLoadResult)
{
case VMAP::VMAP_LOAD_RESULT_OK:
// LOG_ERROR("playerbots", "VMAP loaded name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
// mapName, mapId, x, y, x, y);
break;
case VMAP::VMAP_LOAD_RESULT_ERROR:
// LOG_ERROR("playerbots", "Could not load VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{},
// y:{})", mapName, mapId, x, y, x, y);
sTravelMgr->addBadVmap(mapId, x, y);
break;
case VMAP::VMAP_LOAD_RESULT_IGNORED:
sTravelMgr->addBadVmap(mapId, x, y);
// LOG_INFO("playerbots", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
// mapName, mapId, x, y, x, y);
break;
}
if (sPlayerbotAIConfig->hasLog(fileName))
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00,\"vmap\", " << x << "," << y << ", " << (sTravelMgr->isBadVmap(mapId, x, y) ? "0" : "1")
<< ",";
printWKT(frommGridCoord(mGridCoord(x, y)), out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
if (!sTravelMgr->isBadMmap(mapId, x, y))
{
// load navmesh
if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y))
sTravelMgr->addBadMmap(mapId, x, y);
if (sPlayerbotAIConfig->hasLog(fileName))
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00,\"mmap\", " << x << "," << y << "," << (sTravelMgr->isBadMmap(mapId, x, y) ? "0" : "1")
<< ",";
printWKT(fromGridCoord(GridCoord(x, y)), out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
}
}
void WorldPosition::loadMapAndVMaps(WorldPosition secondPos)
{
for (auto& grid : getmGridCoords(secondPos))
{
loadMapAndVMap(getMapId(), grid.first, grid.second);
}
}
std::vector<WorldPosition> WorldPosition::fromPointsArray(std::vector<G3D::Vector3> path)
{
std::vector<WorldPosition> retVec;
for (auto p : path)
retVec.push_back(WorldPosition(getMapId(), p.x, p.y, p.z, getO()));
return retVec;
}
// A single pathfinding attempt from one position to another. Returns pathfinding status and path.
std::vector<WorldPosition> WorldPosition::getPathStepFrom(WorldPosition startPos, Unit* bot)
{
if (!bot)
return {};
// Load mmaps and vmaps between the two points.
loadMapAndVMaps(startPos);
PathGenerator path(bot);
path.CalculatePath(startPos.getX(), startPos.getY(), startPos.getZ());
Movement::PointsArray points = path.GetPath();
PathType type = path.GetPathType();
if (sPlayerbotAIConfig->hasLog("pathfind_attempt_point.csv"))
{
std::ostringstream out;
out << std::fixed << std::setprecision(1);
printWKT({startPos, *this}, out);
sPlayerbotAIConfig->log("pathfind_attempt_point.csv", out.str().c_str());
}
if (sPlayerbotAIConfig->hasLog("pathfind_attempt.csv") && (type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL))
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr() << "+00,";
out << std::fixed << std::setprecision(1) << type << ",";
printWKT(fromPointsArray(points), out, 1);
sPlayerbotAIConfig->log("pathfind_attempt.csv", out.str().c_str());
}
if (type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL)
return fromPointsArray(points);
return {};
}
bool WorldPosition::cropPathTo(std::vector<WorldPosition>& path, float maxDistance)
{
if (path.empty())
return false;
auto bestPos = std::min_element(path.begin(), path.end(),
[this](WorldPosition i, WorldPosition j)
{ return this->sqDistance(i) < this->sqDistance(j); });
bool insRange = this->sqDistance(*bestPos) <= maxDistance * maxDistance;
if (bestPos == path.end())
return insRange;
path.erase(std::next(bestPos), path.end());
return insRange;
}
// A sequential series of pathfinding attempts. Returns the complete path and if the patfinder eventually found a way to
// the destination.
std::vector<WorldPosition> WorldPosition::getPathFromPath(std::vector<WorldPosition> startPath, Unit* bot,
uint8 maxAttempt)
{
// We start at the end of the last path.
WorldPosition currentPos = startPath.back();
// No pathfinding across maps.
if (getMapId() != currentPos.getMapId())
return {};
std::vector<WorldPosition> subPath, fullPath = startPath;
// Limit the pathfinding attempts
for (uint32 i = 0; i < maxAttempt; i++)
{
// Try to pathfind to this position.
subPath = getPathStepFrom(currentPos, bot);
// If we could not find a path return what we have now.
if (subPath.empty() || currentPos.distance(&subPath.back()) < sPlayerbotAIConfig->targetPosRecalcDistance)
break;
// Append the path excluding the start (this should be the same as the end of the startPath)
fullPath.insert(fullPath.end(), std::next(subPath.begin(), 1), subPath.end());
// Are we there yet?
if (isPathTo(subPath))
break;
// Continue pathfinding.
currentPos = subPath.back();
}
return fullPath;
}
bool WorldPosition::GetReachableRandomPointOnGround(Player* bot, float radius, bool randomRange)
{
radius *= randomRange ? rand_norm() : 1.f;
float angle = rand_norm() * static_cast<float>(2 * M_PI);
m_positionX += radius * cosf(angle);
m_positionY += radius * sinf(angle);
return getMap()->CanReachPositionAndGetValidCoords(bot, m_positionX, m_positionY, m_positionZ);
}
uint32 WorldPosition::getUnitsAggro(GuidVector& units, Player* bot)
{
units.erase(std::remove_if(units.begin(), units.end(),
[this, bot](ObjectGuid guid)
{
Creature* creature = ObjectAccessor::GetCreature(*bot, guid);
if (!creature)
return true;
return sqDistance(WorldPosition(creature)) >
creature->GetAttackDistance(bot) * creature->GetAttackDistance(bot);
}),
units.end());
return units.size();
}
void FindPointCreatureData::operator()(CreatureData const& creatureData)
{
if (!entry || creatureData.id1 == entry)
if ((!point || creatureData.mapid == point.getMapId()) &&
(!radius || point.sqDistance(WorldPosition(creatureData.mapid, creatureData.posX, creatureData.posY,
creatureData.posZ)) < radius * radius))
{
data.push_back(&creatureData);
}
}
void FindPointGameObjectData::operator()(GameObjectData const& gameobjectData)
{
if (!entry || gameobjectData.id == entry)
if ((!point || gameobjectData.mapid == point.getMapId()) &&
(!radius || point.sqDistance(WorldPosition(gameobjectData.mapid, gameobjectData.posX, gameobjectData.posY,
gameobjectData.posZ)) < radius * radius))
{
data.push_back(&gameobjectData);
}
}
std::vector<CreatureData const*> WorldPosition::getCreaturesNear(float radius, uint32 entry)
{
FindPointCreatureData worker(*this, radius, entry);
for (auto const& itr : sObjectMgr->GetAllCreatureData())
worker(itr.second);
return worker.GetResult();
}
std::vector<GameObjectData const*> WorldPosition::getGameObjectsNear(float radius, uint32 entry)
{
FindPointGameObjectData worker(*this, radius, entry);
for (auto const& itr : sObjectMgr->GetAllGOData())
worker(itr.second);
return worker.GetResult();
}
Creature* GuidPosition::GetCreature()
{
if (!*this)
return nullptr;
if (loadedFromDB)
{
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
if (creatureBounds.first != creatureBounds.second)
return creatureBounds.second->second;
return nullptr;
}
return getMap()->GetCreature(*this);
}
Unit* GuidPosition::GetUnit()
{
if (!*this)
return nullptr;
if (loadedFromDB)
{
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
if (creatureBounds.first != creatureBounds.second)
return creatureBounds.second->second;
return nullptr;
}
if (IsPlayer())
return ObjectAccessor::FindPlayer(*this);
if (IsPet())
return getMap()->GetPet(*this);
return GetCreature();
}
GameObject* GuidPosition::GetGameObject()
{
if (!*this)
return nullptr;
if (loadedFromDB)
{
auto gameobjectBounds = getMap()->GetGameObjectBySpawnIdStore().equal_range(GetCounter());
if (gameobjectBounds.first != gameobjectBounds.second)
return gameobjectBounds.second->second;
return nullptr;
}
return getMap()->GetGameObject(*this);
}
Player* GuidPosition::GetPlayer()
{
if (!*this)
return nullptr;
if (IsPlayer())
return ObjectAccessor::FindPlayer(*this);
return nullptr;
}
bool GuidPosition::isDead()
{
if (!getMap())
return false;
if (!getMap()->IsGridLoaded(getX(), getY()))
return false;
if (IsUnit() && GetUnit() && GetUnit()->IsInWorld() && GetUnit()->IsAlive())
return false;
if (IsGameObject() && GetGameObject() && GetGameObject()->IsInWorld())
return false;
return true;
}
GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {}
GuidPosition::GuidPosition(CreatureData const& creData)
: ObjectGuid(HighGuid::Unit, creData.id1, creData.spawnId),
WorldPosition(creData.mapid, creData.posX, creData.posY, creData.posZ, creData.orientation)
{
loadedFromDB = true;
}
GuidPosition::GuidPosition(GameObjectData const& goData)
: ObjectGuid(HighGuid::GameObject, goData.id),
WorldPosition(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)
{
loadedFromDB = true;
}
CreatureTemplate const* GuidPosition::GetCreatureTemplate()
{
return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr;
}
GameObjectTemplate const* GuidPosition::GetGameObjectTemplate()
{
return IsGameObject() ? sObjectMgr->GetGameObjectTemplate(GetEntry()) : nullptr;
}
WorldObject* GuidPosition::GetWorldObject()
{
if (!*this)
return nullptr;
switch (GetHigh())
{
case HighGuid::Player:
return ObjectAccessor::FindPlayer(*this);
case HighGuid::Transport:
case HighGuid::Mo_Transport:
case HighGuid::GameObject:
return GetGameObject();
case HighGuid::Vehicle:
case HighGuid::Unit:
return GetCreature();
case HighGuid::Pet:
return getMap()->GetPet(*this);
case HighGuid::DynamicObject:
return getMap()->GetDynamicObject(*this);
case HighGuid::Corpse:
return getMap()->GetCorpse(*this);
default:
return nullptr;
}
return nullptr;
}
bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; }
std::vector<WorldPosition*> TravelDestination::getPoints(bool ignoreFull)
{
if (ignoreFull)
return points;
uint32 max = maxVisitorsPerPoint;
if (!max)
return points;
std::vector<WorldPosition*> retVec;
std::copy_if(points.begin(), points.end(), std::back_inserter(retVec),
[max](WorldPosition* p) { return p->getVisitors() < max; });
return retVec;
}
WorldPosition* TravelDestination::nearestPoint(WorldPosition* pos)
{
return *std::min_element(points.begin(), points.end(),
[pos](WorldPosition* i, WorldPosition* j) { return i->distance(pos) < j->distance(pos); });
}
std::vector<WorldPosition*> TravelDestination::touchingPoints(WorldPosition* pos)
{
std::vector<WorldPosition*> ret_points;
for (auto& point : points)
{
float dist = pos->distance(point);
if (!dist)
continue;
if (dist > radiusMax * 2)
continue;
ret_points.push_back(point);
}
return ret_points;
};
std::vector<WorldPosition*> TravelDestination::sortedPoints(WorldPosition* pos)
{
std::vector<WorldPosition*> ret_points = points;
std::sort(ret_points.begin(), ret_points.end(),
[pos](WorldPosition* i, WorldPosition* j) { return i->distance(pos) < j->distance(pos); });
return ret_points;
};
std::vector<WorldPosition*> TravelDestination::nextPoint(WorldPosition* pos, bool ignoreFull)
{
return sTravelMgr->getNextPoint(pos, ignoreFull ? points : getPoints());
}
bool TravelDestination::isFull(bool ignoreFull)
{
if (!ignoreFull && maxVisitors > 0 && visitors >= maxVisitors)
return true;
if (maxVisitorsPerPoint > 0)
if (getPoints(ignoreFull).empty())
return true;
return false;
}
std::string const QuestTravelDestination::getTitle() { return ChatHelper::FormatQuest(questTemplate); }
bool QuestRelationTravelDestination::isActive(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
if (botAI && !botAI->HasStrategy("rpg quest", BOT_STATE_NON_COMBAT))
return false;
if (relation == 0)
{
if ((int32)questTemplate->GetQuestLevel() >= (int32)bot->GetLevel() + (int32)5)
return false;
// skip for now this quest
if (getPoints().front()->GetMapId() != bot->GetMapId())
return false;
if (!bot->GetMap()->GetEntry()->IsWorldMap() || !bot->CanTakeQuest(questTemplate, false))
return false;
uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate);
if (AI_VALUE(bool, "can fight equal"))
{
if (AI_VALUE(uint8, "free quest log slots") < 5)
return false;
//None has yellow exclamation mark.
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest npc::" + std::to_string(entry)))
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest low level npc::" + std::to_string(entry) + "need quest objective::" + std::to_string(questId))) //Noone can do this quest for a usefull reward.
return false;
}
else
{
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest low level npc::" + std::to_string(entry))) //Noone can pick up this quest for money.
return false;
if (AI_VALUE(uint8, "free quest log slots") < 10)
return false;
}
// Do not try to pick up dungeon/elite quests in instances without a group.
if ((questTemplate->GetType() == QUEST_TYPE_ELITE || questTemplate->GetType() == QUEST_TYPE_DUNGEON) &&
!AI_VALUE(bool, "can fight boss"))
return false;
}
else
{
if (!AI_VALUE2(bool, "group or", "following party,near leader,can turn in quest npc::" + std::to_string(entry)))
return false;
//Do not try to hand-in dungeon/elite quests in instances without a group.
if ((questTemplate->GetType() == QUEST_TYPE_ELITE || questTemplate->GetType() == QUEST_TYPE_DUNGEON) && !AI_VALUE(bool, "can fight boss"))
{
WorldPosition pos(bot);
if (!this->nearestPoint(&pos)->isOverworld())
return false;
}
}
return true;
}
std::string const QuestRelationTravelDestination::getTitle()
{
std::ostringstream out;
if (relation == 0)
out << "questgiver";
else
out << "questtaker";
out << " " << ChatHelper::FormatWorldEntry(entry);
return out.str();
}
bool QuestObjectiveTravelDestination::isActive(Player* bot)
{
if (questTemplate->GetQuestLevel() > bot->GetLevel() + 1)
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
if (questTemplate->GetQuestLevel() + 5 > bot->GetLevel() && !AI_VALUE(bool, "can fight equal"))
return false;
// Check mob level
if (getEntry() > 0)
{
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(getEntry());
if (cInfo && (int)cInfo->maxlevel - (int)bot->GetLevel() > 4)
return false;
// Do not try to hand-in dungeon/elite quests in instances without a group.
if (cInfo->rank > CREATURE_ELITE_NORMAL)
{
WorldPosition pos(bot);
if (!this->nearestPoint(const_cast<WorldPosition*>(&pos))->isOverworld() &&
!AI_VALUE(bool, "can fight boss"))
return false;
if (!AI_VALUE(bool, "can fight elite"))
return false;
}
}
if (questTemplate->GetType() == QUEST_TYPE_ELITE && !AI_VALUE(bool, "can fight elite"))
return false;
if (!sTravelMgr->getObjectiveStatus(bot, questTemplate, objective))
return false;
WorldPosition botPos(bot);
if (getEntry() > 0 && !isOut(&botPos))
{
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
for (auto& target : targets)
if (target.GetEntry() == getEntry() && target.IsCreature() && botAI->GetCreature(target) &&
botAI->GetCreature(target)->IsAlive())
return true;
return false;
}
return true;
}
std::string const QuestObjectiveTravelDestination::getTitle()
{
std::ostringstream out;
out << "objective " << objective;
if (itemId)
out << " loot " << ChatHelper::FormatItem(sObjectMgr->GetItemTemplate(itemId), 0, 0) << " from";
else
out << " to kill";
out << " " << ChatHelper::FormatWorldEntry(entry);
return out.str();
}
bool RpgTravelDestination::isActive(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
CreatureTemplate const* cInfo = GetCreatureTemplate();
if (!cInfo)
return false;
bool isUsefull = false;
if (cInfo->npcflag & UNIT_NPC_FLAG_VENDOR)
if (AI_VALUE2_LAZY(bool, "group or", "should sell,can sell,following party,near leader"))
isUsefull = true;
if (cInfo->npcflag & UNIT_NPC_FLAG_REPAIR)
if (AI_VALUE2_LAZY(bool, "group or", "should repair,can repair,following party,near leader"))
isUsefull = true;
if (!isUsefull)
return false;
// Once the target rpged with it is added to the ignore list. We can now move on.
GuidSet& ignoreList = GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<GuidSet&>("ignore rpg target")->Get();
for (ObjectGuid const guid : ignoreList)
{
if (guid.GetEntry() == getEntry())
{
return false;
}
}
FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction);
ReputationRank reaction = bot->GetReputationRank(factionEntry->faction);
return reaction > REP_NEUTRAL;
}
CreatureTemplate const* RpgTravelDestination::GetCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); }
std::string const RpgTravelDestination::getTitle()
{
std::ostringstream out;
if(entry > 0)
out << "rpg npc ";
out << " " << ChatHelper::FormatWorldEntry(entry);
return out.str();
}
bool ExploreTravelDestination::isActive(Player* bot)
{
AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId);
if (area->area_level && (uint32)area->area_level > bot->GetLevel() && bot->GetLevel() < DEFAULT_MAX_LEVEL)
return false;
if (area->exploreFlag == 0xffff)
return false;
int offset = area->exploreFlag / 32;
uint32 val = (uint32)(1 << (area->exploreFlag % 32));
uint32 currFields = bot->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
return !(currFields & val);
}
// std::string const ExploreTravelDestination::getTitle()
//{
// return points[0]->getAreaName();
// };
bool GrindTravelDestination::isActive(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
if (!AI_VALUE(bool, "should get money"))
return false;
if (AI_VALUE(bool, "should sell"))
return false;
CreatureTemplate const* cInfo = GetCreatureTemplate();
int32 botLevel = bot->GetLevel();
uint8 botPowerLevel = AI_VALUE(uint8, "durability");
float levelMod = botPowerLevel / 500.0f; //(0-0.2f)
float levelBoost = botPowerLevel / 50.0f; //(0-2.0f)
int32 maxLevel = std::max(botLevel * (0.5f + levelMod), botLevel - 5.0f + levelBoost);
if ((int32)cInfo->maxlevel > maxLevel) //@lvl5 max = 3, @lvl60 max = 57
return false;
int32 minLevel = std::max(botLevel * (0.4f + levelMod), botLevel - 12.0f + levelBoost);
if ((int32)cInfo->maxlevel < minLevel) //@lvl5 min = 3, @lvl60 max = 50
return false;
if (!cInfo->mingold)
return false;
if (cInfo->rank > CREATURE_ELITE_NORMAL && !AI_VALUE(bool, "can fight elite"))
return false;
FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction);
ReputationRank reaction = bot->GetReputationRank(factionEntry->faction);
return reaction < REP_NEUTRAL;
}
CreatureTemplate const* GrindTravelDestination::GetCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); }
std::string const GrindTravelDestination::getTitle()
{
std::ostringstream out;
out << "grind mob ";
out << " " << ChatHelper::FormatWorldEntry(entry);
return out.str();
}
bool BossTravelDestination::isActive(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
if (!AI_VALUE(bool, "can fight boss"))
return false;
CreatureTemplate const* cInfo = getCreatureTemplate();
/*
int32 botLevel = bot->GetLevel();
uint8 botPowerLevel = AI_VALUE(uint8, "durability");
float levelMod = botPowerLevel / 500.0f; //(0-0.2f)
float levelBoost = botPowerLevel / 50.0f; //(0-2.0f)
int32 maxLevel = botLevel + 3.0;
if ((int32)cInfo->MaxLevel > maxLevel) //@lvl5 max = 3, @lvl60 max = 57
return false;
int32 minLevel = botLevel - 10;
if ((int32)cInfo->MaxLevel < minLevel) //@lvl5 min = 3, @lvl60 max = 50
return false;
*/
if ((int32)cInfo->maxlevel > bot->GetLevel() + 3)
return false;
FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction);
ReputationRank reaction = Unit::GetFactionReactionTo(bot->GetFactionTemplateEntry(), factionEntry);
if (reaction >= REP_NEUTRAL)
return false;
WorldPosition botPos(bot);
if (!isOut(&botPos))
{
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
for (auto& target : targets)
if (target.GetEntry() == getEntry() && target.IsCreature() && botAI->GetCreature(target) &&
botAI->GetCreature(target)->IsAlive())
return true;
return false;
}
if (!AI_VALUE2(bool, "has upgrade", getEntry()))
return false;
return true;
}
CreatureTemplate const* BossTravelDestination::getCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); }
std::string const BossTravelDestination::getTitle()
{
std::ostringstream out;
out << "boss mob ";
out << " " << ChatHelper::FormatWorldEntry(entry);
return out.str();
}
TravelTarget::~TravelTarget()
{
if (!tDestination)
return;
releaseVisitors();
// sTravelMgr->botTargets.erase(std::remove(sTravelMgr->botTargets.begin(), sTravelMgr->botTargets.end(), this),
// sTravelMgr->botTargets.end());
}
void TravelTarget::setTarget(TravelDestination* tDestination1, WorldPosition* wPosition1, bool groupCopy1)
{
releaseVisitors();
wPosition = wPosition1;
tDestination = tDestination1;
groupCopy = groupCopy1;
forced = false;
radius = 0;
addVisitors();
setStatus(TRAVEL_STATUS_TRAVEL);
}
void TravelTarget::copyTarget(TravelTarget* target)
{
setTarget(target->tDestination, target->wPosition);
groupCopy = target->isGroupCopy();
forced = target->forced;
extendRetryCount = target->extendRetryCount;
}
void TravelTarget::addVisitors()
{
if (!visitor)
{
wPosition->addVisitor();
tDestination->addVisitor();
}
visitor = true;
}
void TravelTarget::releaseVisitors()
{
if (visitor)
{
if (tDestination)
tDestination->remVisitor();
if (wPosition)
wPosition->remVisitor();
}
visitor = false;
}
float TravelTarget::distance(Player* bot)
{
WorldPosition pos(bot);
return wPosition->distance(&pos);
}
WorldPosition* TravelTarget::getPosition() { return wPosition; }
TravelDestination* TravelTarget::getDestination() { return tDestination; }
void TravelTarget::setStatus(TravelStatus status)
{
m_status = status;
startTime = getMSTime();
switch (m_status)
{
case TRAVEL_STATUS_NONE:
case TRAVEL_STATUS_PREPARE:
case TRAVEL_STATUS_EXPIRED:
statusTime = 1;
break;
case TRAVEL_STATUS_TRAVEL:
statusTime = getMaxTravelTime() * 2 + sPlayerbotAIConfig->maxWaitForMove;
break;
case TRAVEL_STATUS_WORK:
statusTime = tDestination->getExpireDelay();
break;
case TRAVEL_STATUS_COOLDOWN:
statusTime = tDestination->getCooldownDelay();
default:
break;
}
}
bool TravelTarget::isActive()
{
if (m_status == TRAVEL_STATUS_NONE || m_status == TRAVEL_STATUS_EXPIRED || m_status == TRAVEL_STATUS_PREPARE)
return false;
if (forced && isTraveling())
return true;
if ((statusTime > 0 && startTime + statusTime < getMSTime()))
{
setStatus(TRAVEL_STATUS_EXPIRED);
return false;
}
if (m_status == TRAVEL_STATUS_COOLDOWN)
return true;
if (isTraveling())
return true;
if (isWorking())
return true;
if (!tDestination->isActive(bot)) // Target has become invalid. Stop.
{
setStatus(TRAVEL_STATUS_COOLDOWN);
return true;
}
return true;
};
uint32 TravelTarget::getMaxTravelTime() { return (1000.0 * distance(bot)) / bot->GetSpeed(MOVE_RUN); }
bool TravelTarget::isTraveling()
{
if (m_status != TRAVEL_STATUS_TRAVEL)
return false;
if (!tDestination->isActive(bot) && !forced) // Target has become invalid. Stop.
{
setStatus(TRAVEL_STATUS_COOLDOWN);
return false;
}
WorldPosition pos(bot);
bool HasArrived = tDestination->isIn(&pos, radius);
if (HasArrived)
{
setStatus(TRAVEL_STATUS_WORK);
return false;
}
if (!botAI->HasStrategy("travel", BOT_STATE_NON_COMBAT))
{
setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true);
return false;
}
return true;
}
bool TravelTarget::isWorking()
{
if (m_status != TRAVEL_STATUS_WORK)
return false;
if (!tDestination->isActive(bot)) // Target has become invalid. Stop.
{
setStatus(TRAVEL_STATUS_COOLDOWN);
return false;
}
WorldPosition pos(bot);
/*
bool HasLeft = tDestination->isOut(&pos);
if (HasLeft)
{
setStatus(TRAVEL_STATUS_TRAVEL);
return false;
}
*/
if (!botAI->HasStrategy("travel", BOT_STATE_NON_COMBAT))
{
setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true);
return false;
}
return true;
}
bool TravelTarget::isPreparing()
{
if (m_status != TRAVEL_STATUS_PREPARE)
return false;
return true;
}
TravelState TravelTarget::getTravelState()
{
if (!tDestination || tDestination->getName() == "NullTravelDestination")
return TRAVEL_STATE_IDLE;
if (tDestination->getName() == "QuestRelationTravelDestination")
{
if (((QuestRelationTravelDestination*)tDestination)->getRelation() == 0)
{
if (isTraveling() || isPreparing())
return TRAVEL_STATE_TRAVEL_PICK_UP_QUEST;
if (isWorking())
return TRAVEL_STATE_WORK_PICK_UP_QUEST;
}
else
{
if (isTraveling() || isPreparing())
return TRAVEL_STATE_TRAVEL_HAND_IN_QUEST;
if (isWorking())
return TRAVEL_STATE_WORK_HAND_IN_QUEST;
}
}
else if (tDestination->getName() == "QuestObjectiveTravelDestination")
{
if (isTraveling() || isPreparing())
return TRAVEL_STATE_TRAVEL_DO_QUEST;
if (isWorking())
return TRAVEL_STATE_WORK_DO_QUEST;
}
else if (tDestination->getName() == "RpgTravelDestination")
{
return TRAVEL_STATE_TRAVEL_RPG;
}
else if (tDestination->getName() == "ExploreTravelDestination")
{
return TRAVEL_STATE_TRAVEL_EXPLORE;
}
return TRAVEL_STATE_IDLE;
}
void TravelMgr::Clear()
{
std::shared_lock<std::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
HashMapHolder<Player>::MapType const& m = ObjectAccessor::GetPlayers();
for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
TravelMgr::setNullTravelTarget(itr->second);
for (auto& quest : quests)
{
for (auto& dest : quest.second->questGivers)
{
delete dest;
}
for (auto& dest : quest.second->questTakers)
{
delete dest;
}
for (auto& dest : quest.second->questObjectives)
{
delete dest;
}
}
questGivers.clear();
quests.clear();
}
void TravelMgr::logQuestError(uint32 errorNr, Quest* quest, uint32 objective, uint32 unitId, uint32 itemId)
{
bool logQuestErrors = false; // For debugging.
if (!logQuestErrors)
return;
std::string unitName = "<unknown>";
CreatureTemplate const* cInfo = nullptr;
GameObjectTemplate const* gInfo = nullptr;
if (unitId > 0)
cInfo = sObjectMgr->GetCreatureTemplate(unitId);
else
gInfo = sObjectMgr->GetGameObjectTemplate(unitId * -1);
if (cInfo)
unitName = cInfo->Name;
else if (gInfo)
unitName = gInfo->name;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (errorNr == 1)
{
LOG_ERROR("playerbots", "Quest {} [{}] has {} {} [{}] but none is found in the world.",
quest->GetTitle().c_str(), quest->GetQuestId(), objective == 0 ? "quest giver" : "quest taker",
unitName.c_str(), unitId);
}
else if (errorNr == 2)
{
LOG_ERROR("playerbots", "Quest {} [{}] needs {} [{}] for objective {} but none is found in the world.",
quest->GetTitle().c_str(), quest->GetQuestId(), unitName.c_str(), unitId, objective);
}
else if (errorNr == 3)
{
LOG_ERROR("playerbots", "Quest {} [{}] needs itemId {} but no such item exists.", quest->GetTitle().c_str(),
quest->GetQuestId(), itemId);
}
else if (errorNr == 4)
{
LOG_ERROR(
"playerbots",
"Quest {} [{}] needs {} [{}] for loot of item {} [{}] for objective {} but none is found in the world.",
quest->GetTitle().c_str(), quest->GetQuestId(), unitName.c_str(), unitId, proto->Name1.c_str(), itemId,
objective);
}
else if (errorNr == 5)
{
LOG_ERROR("playerbots", "Quest {} [{}] needs item {} [{}] for objective {} but none is found in the world.",
quest->GetTitle().c_str(), quest->GetQuestId(), proto->Name1.c_str(), itemId, objective);
}
else if (errorNr == 6)
{
LOG_ERROR("playerbots", "Quest {} [{}] has no quest giver.", quest->GetTitle().c_str(), quest->GetQuestId());
}
else if (errorNr == 7)
{
LOG_ERROR("playerbots", "Quest {} [{}] has no quest taker.", quest->GetTitle().c_str(), quest->GetQuestId());
}
else if (errorNr == 8)
{
LOG_ERROR("playerbots", "Quest {} [{}] has no quest viable quest objective.", quest->GetTitle().c_str(),
quest->GetQuestId());
}
}
void TravelMgr::LoadQuestTravelTable()
{
if (!sTravelMgr->quests.empty())
return;
// Clearing store (for reloading case)
Clear();
/* remove this
questGuidMap cQuestMap = GAI_VALUE(questGuidMap,"quest objects");
for (auto cQuest : cQuestMap)
{
LOG_INFO("playerbots", "[Quest id: {}]", cQuest.first);
for (auto cObj : cQuest.second)
{
LOG_INFO("playerbots", " [Objective type: {}]", cObj.first);
for (auto cCre : cObj.second)
{
LOG_INFO("playerbots", " {} {}", cCre.GetTypeName().c_str(), cCre.GetEntry());
}
}
}
*/
struct unit
{
uint32 type;
uint32 entry;
uint32 map;
float x;
float y;
float z;
float o;
uint32 c;
} t_unit;
std::vector<unit> units;
/*struct relation
{
uint32 type;
uint32 role;
uint32 entry;
uint32 questId;
} t_rel;
std::vector<relation> relations;
struct loot
{
uint32 type;
uint32 entry;
uint32 item;
} t_loot;
std::vector<loot> loots;*/
ObjectMgr::QuestMap const& questMap = sObjectMgr->GetQuestTemplates();
std::vector<uint32> questIds;
std::unordered_map<uint32, uint32> entryCount;
for (auto& quest : questMap)
questIds.push_back(quest.first);
sort(questIds.begin(), questIds.end());
LOG_INFO("playerbots", "Loading units locations.");
for (auto& creatureData : WorldPosition().getCreaturesNear())
{
t_unit.type = 0;
t_unit.entry = creatureData->id1;
t_unit.map = creatureData->mapid;
t_unit.x = creatureData->posX;
t_unit.y = creatureData->posY;
t_unit.z = creatureData->posZ;
t_unit.o = creatureData->orientation;
entryCount[creatureData->id1]++;
units.push_back(t_unit);
}
for (auto& unit : units)
{
unit.c = entryCount[unit.entry];
}
LOG_INFO("playerbots", "Loading game object locations.");
for (auto& gameobjectData : WorldPosition().getGameObjectsNear())
{
t_unit.type = 1;
t_unit.entry = gameobjectData->id;
t_unit.map = gameobjectData->mapid;
t_unit.x = gameobjectData->posX;
t_unit.y = gameobjectData->posY;
t_unit.z = gameobjectData->posZ;
t_unit.o = gameobjectData->orientation;
t_unit.c = 1;
units.push_back(t_unit);
}
/*
// 0 1 2 3 4 5 6 7 8
std::string const query = "SELECT 0,guid,id,map,position_x,position_y,position_z,orientation, (SELECT COUNT(*) FROM
creature k WHERE c.id1 = k.id1) FROM creature c UNION ALL SELECT
1,guid,id,map,position_x,position_y,position_z,orientation, (SELECT COUNT(*) FROM gameobject h WHERE h.id = g.id)
FROM gameobject g";
QueryResult result = WorldDatabase.Query(query.c_str());
if (result)
{
do
{
Field* fields = result->Fetch();
t_unit.type = fields[0].Get<uint32>();
t_unit.guid = fields[1].Get<uint32>();
t_unit.entry = fields[2].Get<uint32>();
t_unit.map = fields[3].Get<uint32>();
t_unit.x = fields[4].Get<float>();
t_unit.y = fields[5].Get<float>();
t_unit.z = fields[6].Get<float>();
t_unit.o = fields[7].Get<float>();
t_unit.c = uint32(fields[8].Get<uint64>());
units.push_back(t_unit);
} while (result->NextRow());
LOG_INFO("playerbots", ">> Loaded {} units locations.", units.size());
}
else
{
LOG_ERROR("playerbots", ">> Error loading units locations.");
}
query = "SELECT 0, 0, id, quest FROM creature_queststarter UNION ALL SELECT 0, 1, id, quest FROM creature_questender
UNION ALL SELECT 1, 0, id, quest FROM gameobject_queststarter UNION ALL SELECT 1, 1, id, quest FROM
gameobject_questender"; result = WorldDatabase.Query(query.c_str());
if (result)
{
do
{
Field* fields = result->Fetch();
t_rel.type = fields[0].Get<uint32>();
t_rel.role = fields[1].Get<uint32>();
t_rel.entry = fields[2].Get<uint32>();
t_rel.questId = fields[3].Get<uint32>();
relations.push_back(t_rel);
} while (result->NextRow());
LOG_INFO("playerbots", ">> Loaded {} relations.", relations.size());
}
else
{
LOG_ERROR("playerbots", ">> Error loading relations.");
}
query = "SELECT 0, ct.entry, item FROM creature_template ct JOIN creature_loot_template clt ON (ct.lootid =
clt.entry) UNION ALL SELECT 0, entry, item FROM npc_vendor UNION ALL SELECT 1, gt.entry, item FROM
gameobject_template gt JOIN gameobject_loot_template glt ON (gt.TYPE = 3 AND gt.DATA1 = glt.entry)"; result =
WorldDatabase.Query(query.c_str());
if (result)
{
do
{
Field* fields = result->Fetch();
t_loot.type = fields[0].Get<uint32>();
t_loot.entry = fields[1].Get<uint32>();
t_loot.item = fields[2].Get<uint32>();
loots.push_back(t_loot);
} while (result->NextRow());
LOG_INFO("playerbots", ">> Loaded {} loot lists.", loots.size());
}
else
{
LOG_ERROR("playerbots", ">> Error loading loot lists.");
}
*/
LOG_INFO("playerbots", "Loading quest data.");
bool loadQuestData = true;
if (loadQuestData)
{
questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map");
for (auto& q : questMap)
{
uint32 questId = q.first;
QuestContainer* container = new QuestContainer;
for (auto& r : q.second)
{
uint32 flag = r.first;
for (auto& e : r.second)
{
int32 entry = e.first;
QuestTravelDestination* loc;
std::vector<QuestTravelDestination*> locs;
if (flag & (uint32)QuestRelationFlag::questGiver)
{
loc = new QuestRelationTravelDestination(
questId, entry, 0, sPlayerbotAIConfig->tooCloseDistance, sPlayerbotAIConfig->sightDistance);
loc->setExpireDelay(5 * 60 * 1000);
loc->setMaxVisitors(15, 0);
container->questGivers.push_back(loc);
locs.push_back(loc);
}
if (flag & (uint32)QuestRelationFlag::questTaker)
{
loc = new QuestRelationTravelDestination(
questId, entry, 1, sPlayerbotAIConfig->tooCloseDistance, sPlayerbotAIConfig->sightDistance);
loc->setExpireDelay(5 * 60 * 1000);
loc->setMaxVisitors(15, 0);
container->questTakers.push_back(loc);
locs.push_back(loc);
}
else
{
uint32 objective = 0;
if (flag & (uint32)QuestRelationFlag::objective1)
objective = 0;
else if (flag & (uint32)QuestRelationFlag::objective2)
objective = 1;
else if (flag & (uint32)QuestRelationFlag::objective3)
objective = 2;
else if (flag & (uint32)QuestRelationFlag::objective4)
objective = 3;
loc = new QuestObjectiveTravelDestination(questId, entry, objective,
sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance);
loc->setExpireDelay(1 * 60 * 1000);
loc->setMaxVisitors(100, 1);
container->questObjectives.push_back(loc);
locs.push_back(loc);
}
for (auto& guidP : e.second)
{
WorldPosition point = guidP;
for (auto tLoc : locs)
{
tLoc->addPoint(&point);
}
}
}
}
if (!container->questTakers.empty())
{
quests.insert(std::make_pair(questId, container));
for (auto loc : container->questGivers)
questGivers.push_back(loc);
}
}
}
/*
if (loadQuestData && false)
{
for (auto& questId : questIds)
{
Quest* quest = questMap.find(questId)->second;
QuestContainer* container = new QuestContainer;
QuestTravelDestination* loc = nullptr;
WorldPosition point;
bool hasError = false;
//Relations
for (auto& r : relations)
{
if (questId != r.questId)
continue;
int32 entry = r.type == 0 ? r.entry : r.entry * -1;
loc = new QuestRelationTravelDestination(r.questId, entry, r.role, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance); loc->setExpireDelay(5 * 60 * 1000); loc->setMaxVisitors(15, 0);
for (auto& u : units)
{
if (r.type != u.type || r.entry != u.entry)
continue;
int32 guid = u.type == 0 ? u.guid : u.guid * -1;
point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
loc->addPoint(&point);
}
if (loc->getPoints(0).empty())
{
logQuestError(1, quest, r.role, entry);
delete loc;
continue;
}
if (r.role == 0)
{
container->questGivers.push_back(loc);
}
else
container->questTakers.push_back(loc);
}
//Mobs
for (uint32 i = 0; i < 4; i++)
{
if (quest->RequiredNpcOrGoCount[i] == 0)
continue;
uint32 reqEntry = quest->RequiredNpcOrGo[i];
loc = new QuestObjectiveTravelDestination(questId, reqEntry, i, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1);
for (auto& u : units)
{
int32 entry = u.type == 0 ? u.entry : u.entry * -1;
if (entry != reqEntry)
continue;
int32 guid = u.type == 0 ? u.guid : u.guid * -1;
point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
loc->addPoint(&point);
}
if (loc->getPoints(0).empty())
{
logQuestError(2, quest, i, reqEntry);
delete loc;
hasError = true;
continue;
}
container->questObjectives.push_back(loc);
}
//Loot
for (uint32 i = 0; i < 4; i++)
{
if (quest->RequiredItemCount[i] == 0)
continue;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]);
if (!proto)
{
logQuestError(3, quest, i, 0, quest->RequiredItemId[i]);
hasError = true;
continue;
}
uint32 foundLoot = 0;
for (auto& l : loots)
{
if (l.item != quest->RequiredItemId[i])
continue;
int32 entry = l.type == 0 ? l.entry : l.entry * -1;
loc = new QuestObjectiveTravelDestination(questId, entry, i, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance, l.item); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1);
for (auto& u : units)
{
if (l.type != u.type || l.entry != u.entry)
continue;
int32 guid = u.type == 0 ? u.guid : u.guid * -1;
point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
loc->addPoint(&point);
}
if (loc->getPoints(0).empty())
{
logQuestError(4, quest, i, entry, quest->RequiredItemId[i]);
delete loc;
continue;
}
container->questObjectives.push_back(loc);
foundLoot++;
}
if (foundLoot == 0)
{
hasError = true;
logQuestError(5, quest, i, 0, quest->RequiredItemId[i]);
}
}
if (container->questTakers.empty())
logQuestError(7, quest);
if (!container->questGivers.empty() || !container->questTakers.empty() || hasError)
{
quests.insert(std::make_pair(questId, container));
for (auto loc : container->questGivers)
questGivers.push_back(loc);
}
}
LOG_INFO("playerbots", ">> Loaded {} quest details.", questIds.size());
}
*/
WorldPosition point;
LOG_INFO("playerbots", "Loading Rpg, Grind and Boss locations.");
// Rpg locations
for (auto& u : units)
{
RpgTravelDestination* rLoc;
GrindTravelDestination* gLoc;
BossTravelDestination* bLoc;
if (u.type != 0)
continue;
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(u.entry);
if (!cInfo)
continue;
std::vector<uint32> allowedNpcFlags;
allowedNpcFlags.push_back(UNIT_NPC_FLAG_INNKEEPER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_GOSSIP);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_QUESTGIVER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_FLIGHTMASTER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_BANKER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_AUCTIONEER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_STABLEMASTER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_PETITIONER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TABARDDESIGNER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_TRAINER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_VENDOR);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_REPAIR);
point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
for (std::vector<uint32>::iterator i = allowedNpcFlags.begin(); i != allowedNpcFlags.end(); ++i)
{
if ((cInfo->npcflag & *i) != 0)
{
rLoc = new RpgTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance);
rLoc->setExpireDelay(5 * 60 * 1000);
rLoc->setMaxVisitors(15, 0);
rLoc->addPoint(&point);
rpgNpcs.push_back(rLoc);
break;
}
}
if (cInfo->mingold > 0)
{
gLoc = new GrindTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance);
gLoc->setExpireDelay(5 * 60 * 1000);
gLoc->setMaxVisitors(100, 0);
point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
gLoc->addPoint(&point);
grindMobs.push_back(gLoc);
}
if (cInfo->rank == 3 || (cInfo->rank == 1 && !point.isOverworld() && u.c == 1))
{
std::string const nodeName = cInfo->Name;
bLoc = new BossTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance);
bLoc->setExpireDelay(5 * 60 * 1000);
bLoc->setMaxVisitors(0, 0);
bLoc->addPoint(&point);
bossMobs.push_back(bLoc);
}
}
LOG_INFO("playerbots", "Loading Explore locations.");
// Explore points
for (auto& u : units)
{
ExploreTravelDestination* loc;
WorldPosition point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
AreaTableEntry const* area = point.getArea();
if (!area)
continue;
if (!area->exploreFlag)
continue;
if (u.type == 1)
continue;
auto iloc = exploreLocs.find(area->ID);
if (iloc == exploreLocs.end())
{
loc = new ExploreTravelDestination(area->ID, sPlayerbotAIConfig->tooCloseDistance,
sPlayerbotAIConfig->sightDistance);
loc->setMaxVisitors(1000, 0);
loc->setCooldownDelay(1000);
loc->setExpireDelay(1000);
loc->setTitle(area->area_name[0]);
exploreLocs.insert_or_assign(area->ID, loc);
}
else
{
loc = iloc->second;
}
loc->addPoint(&point);
}
// Clear these logs files
sPlayerbotAIConfig->openLog("zones.csv", "w");
sPlayerbotAIConfig->openLog("creatures.csv", "w");
sPlayerbotAIConfig->openLog("gos.csv", "w");
sPlayerbotAIConfig->openLog("bot_movement.csv", "w");
sPlayerbotAIConfig->openLog("bot_pathfinding.csv", "w");
sPlayerbotAIConfig->openLog("pathfind_attempt.csv", "w");
sPlayerbotAIConfig->openLog("pathfind_attempt_point.csv", "w");
sPlayerbotAIConfig->openLog("pathfind_result.csv", "w");
sPlayerbotAIConfig->openLog("load_map_grid.csv", "w");
sPlayerbotAIConfig->openLog("strategy.csv", "w");
sPlayerbotAIConfig->openLog("unload_grid.csv", "w");
sPlayerbotAIConfig->openLog("unload_obj.csv", "w");
sTravelNodeMap->loadNodeStore();
sTravelNodeMap->generateAll();
/*
bool fullNavPointReload = false;
bool storeNavPointReload = true;
if (!fullNavPointReload && true)
TravelNodeStore::loadNodes();
//sTravelNodeMap->loadNodeStore();
for (auto node : sTravelNodeMap->getNodes())
{
node->setLinked(true);
}
bool reloadNavigationPoints = false || fullNavPointReload || storeNavPointReload;
if (reloadNavigationPoints)
{
LOG_INFO("playerbots", "Loading navigation points");
//Npc nodes
WorldPosition pos;
for (auto& u : units)
{
if (u.type != 0)
continue;
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(u.entry);
if (!cInfo)
continue;
std::vector<uint32> allowedNpcFlags;
allowedNpcFlags.push_back(UNIT_NPC_FLAG_INNKEEPER);
allowedNpcFlags.push_back(UNIT_NPC_FLAG_FLIGHTMASTER);
//allowedNpcFlags.push_back(UNIT_NPC_FLAG_QUESTGIVER);
for (std::vector<uint32>::iterator i = allowedNpcFlags.begin(); i != allowedNpcFlags.end(); ++i)
{
if ((cInfo->npcflag & *i) != 0)
{
pos = WorldPosition(u.map, u.x, u.y, u.z, u.o);
std::string const nodeName = pos.getAreaName(false);
if ((cInfo->npcflag & UNIT_NPC_FLAG_INNKEEPER) != 0)
nodeName += " innkeeper";
else
nodeName += " flightMaster";
sTravelNodeMap->addNode(&pos, nodeName, true, true);
break;
}
}
}
//Build flight paths
for (uint32 i = 0; i < sTaxiPathStore.GetNumRows(); ++i)
{
TaxiPathEntry const* taxiPath = sTaxiPathStore.LookupEntry(i);
if (!taxiPath)
continue;
TaxiNodesEntry const* startTaxiNode = sTaxiNodesStore.LookupEntry(taxiPath->from);
if (!startTaxiNode)
continue;
TaxiNodesEntry const* endTaxiNode = sTaxiNodesStore.LookupEntry(taxiPath->to);
if (!endTaxiNode)
continue;
TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[taxiPath->ID];
if (nodes.empty())
continue;
WorldPosition startPos(startTaxiNode->map_id, startTaxiNode->x, startTaxiNode->y, startTaxiNode->z);
WorldPosition endPos(endTaxiNode->map_id, endTaxiNode->x, endTaxiNode->y, endTaxiNode->z);
TravelNode* startNode = sTravelNodeMap->getNode(&startPos, nullptr, 15.0f);
TravelNode* endNode = sTravelNodeMap->getNode(&endPos, nullptr, 15.0f);
if (!startNode || !endNode)
continue;
std::vector<WorldPosition> ppath;
for (auto& n : nodes)
ppath.push_back(WorldPosition(n->mapid, n->x, n->y, n->z, 0.0));
float totalTime = startPos.getPathLength(ppath) / (450 * 8.0f);
TravelNodePath travelPath(0.1f, totalTime, (uint8) TravelNodePathType::flightPath, i, true);
travelPath.setPath(ppath);
startNode->setPathTo(endNode, travelPath);
}
//Unique bosses
for (auto& u : units)
{
if (u.type != 0)
continue;
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(u.entry);
if (!cInfo)
continue;
pos = WorldPosition(u.map, u.x, u.y, u.z, u.o);
if (cInfo->rank == 3 || (cInfo->rank == 1 && !pos.isOverworld() && u.c == 1))
{
std::string const nodeName = cInfo->Name;
sTravelNodeMap->addNode(&pos, nodeName, true, true);
}
}
std::map<uint8, std::string> startNames;
startNames[RACE_HUMAN] = "Human";
startNames[RACE_ORC] = "Orc and Troll";
startNames[RACE_DWARF] = "Dwarf and Gnome";
startNames[RACE_NIGHTELF] = "Night Elf";
startNames[RACE_UNDEAD_PLAYER] = "Undead";
startNames[RACE_TAUREN] = "Tauren";
startNames[RACE_GNOME] = "Dwarf and Gnome";
startNames[RACE_TROLL] = "Orc and Troll";
startNames[RACE_DRAENEI] = "Draenei";
startNames[RACE_BLOODELF] = "Blood Elf";
for (uint32 i = 0; i < MAX_RACES; i++)
{
for (uint32 j = 0; j < MAX_CLASSES; j++)
{
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(i, j);
if (!info)
continue;
pos = WorldPosition(info->mapId, info->positionX, info->positionY, info->positionZ, info->orientation);
std::string const nodeName = startNames[i] + " start";
sTravelNodeMap->addNode(&pos, nodeName, true, true);
}
}
//Transports
GameObjectTemplateContainer const* goTemplates = sObjectMgr->GetGameObjectTemplates();
for (auto const& iter : *goTemplates)
{
GameObjectTemplate const* data = &iter.second;
if (data && (data->type == GAMEOBJECT_TYPE_TRANSPORT || data->type == GAMEOBJECT_TYPE_MO_TRANSPORT))
{
TransportAnimation const* animation = sTransportMgr->GetTransportAnimInfo(iter.first);
uint32 pathId = data->moTransport.taxiPathId;
float moveSpeed = data->moTransport.moveSpeed;
if (pathId >= sTaxiPathNodesByPath.size())
continue;
TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
std::vector<WorldPosition> ppath;
TravelNode* prevNode = nullptr;
//Elevators/Trams
if (path.empty())
{
if (animation)
{
TransportPathContainer aPath = animation->Path;
float timeStart;
for (auto& u : units)
{
if (u.type != 1)
continue;
if (u.entry != iter.first)
continue;
prevNode = nullptr;
WorldPosition lPos = WorldPosition(u.map, 0, 0, 0, 0);
for (auto& p : aPath)
{
float dx = cos(u.o) * p.second->X - sin(u.o) * p.second->Y;
float dy = sin(u.o) * p.second->X + cos(u.o) * p.second->Y;
WorldPosition pos = WorldPosition(u.map, u.x + dx, u.y + dy, u.z + p.second->Z, u.o);
if (prevNode)
{
ppath.push_back(pos);
}
if (pos.distance(&lPos) == 0)
{
TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true,
iter.first);
if (!prevNode)
{
ppath.push_back(pos);
timeStart = p.second->TimeSeg;
}
else
{
float totalTime = (p.second->TimeSeg - timeStart) / 1000.0f;
TravelNodePath travelPath(0.1f, totalTime, (uint8)
TravelNodePathType::transport, entry, true); node->setPathTo(prevNode, travelPath); ppath.clear();
ppath.push_back(pos);
timeStart = p.second->TimeSeg;
}
prevNode = node;
}
lPos = pos;
}
if (prevNode)
{
for (auto& p : aPath)
{
float dx = cos(u.o) * p.second->X - sin(u.o) * p.second->Y;
float dy = sin(u.o) * p.second->X + cos(u.o) * p.second->Y;
WorldPosition pos = WorldPosition(u.map, u.x + dx, u.y + dy, u.z + p.second->Z,
u.o);
ppath.push_back(pos);
if (pos.distance(&lPos) == 0)
{
TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true,
iter.first); if (node != prevNode)
{
float totalTime = (p.second->TimeSeg - timeStart) / 1000.0f;
TravelNodePath travelPath(0.1f, totalTime, (uint8)
TravelNodePathType::transport, entry, true); travelPath.setPath(ppath); node->setPathTo(prevNode, travelPath);
ppath.clear();
ppath.push_back(pos);
timeStart = p.second->TimeSeg;
}
}
lPos = pos;
}
}
ppath.clear();
}
}
}
else //Boats/Zepelins
{
//Loop over the path and connect stop locations.
for (auto& p : path)
{
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
//if (data->displayId == 3015)
// pos.setZ(pos.getZ() + 6.0f);
//else if(data->displayId == 3031)
// pos.setZ(pos.getZ() - 17.0f);
if (prevNode)
{
ppath.push_back(pos);
}
if (p->delay > 0)
{
TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true, iter.first);
if (!prevNode)
{
ppath.push_back(pos);
}
else
{
TravelNodePath travelPath(0.1f, 0.0, (uint8) TravelNodePathType::transport, entry,
true); travelPath.setPathAndCost(ppath, moveSpeed); node->setPathTo(prevNode, travelPath); ppath.clear();
ppath.push_back(pos);
}
prevNode = node;
}
}
if (prevNode)
{
//Continue from start until first stop and connect to end.
for (auto& p : path)
{
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
//if (data->displayId == 3015)
// pos.setZ(pos.getZ() + 6.0f);
//else if (data->displayId == 3031)
// pos.setZ(pos.getZ() - 17.0f);
ppath.push_back(pos);
if (p->delay > 0)
{
TravelNode* node = sTravelNodeMap->getNode(&pos, nullptr, 5.0f);
if (node != prevNode)
{
TravelNodePath travelPath(0.1f, 0.0, (uint8) TravelNodePathType::transport, entry,
true); travelPath.setPathAndCost(ppath, moveSpeed); node->setPathTo(prevNode, travelPath);
}
}
}
}
ppath.clear();
}
}
}
//Zone means
for (auto& loc : exploreLocs)
{
std::vector<WorldPosition*> points;
for (auto p : loc.second->getPoints(true))
if (!p->isUnderWater())
points.push_back(p);
if (points.empty())
points = loc.second->getPoints(true);
WorldPosition pos = WorldPosition(points, WP_MEAN_CENTROID);
TravelNode* node = sTravelNodeMap->addNode(&pos, pos.getAreaName(), true, true, false);
}
LOG_INFO("playerbots", ">> Loaded {} navigation points.", sTravelNodeMap->getNodes().size());
}
sTravelNodeMap->calcMapOffset();
loadMapTransfers();
*/
/*
bool preloadNodePaths = false || fullNavPointReload || storeNavPointReload; //Calculate paths using
PathGenerator. bool preloadReLinkFullyLinked = false || fullNavPointReload || storeNavPointReload; //Retry
nodes that are fully linked. bool preloadUnlinkedPaths = false || fullNavPointReload; //Try to connect points
currently unlinked. bool preloadWorldPaths = true; //Try to load paths in overworld. bool
preloadInstancePaths = true; //Try to load paths in instances. bool preloadSubPrint = false; //Print output
every 2%.
if (preloadNodePaths)
{
std::unordered_map<uint32, Map*> instances;
//PathGenerator
std::vector<WorldPosition> ppath;
uint32 cur = 0, max = sTravelNodeMap->getNodes().size();
for (auto& startNode : sTravelNodeMap->getNodes())
{
if (!preloadReLinkFullyLinked && startNode->isLinked())
continue;
for (auto& endNode : sTravelNodeMap->getNodes())
{
if (startNode == endNode)
continue;
if (startNode->getPosition()->isOverworld() && !preloadWorldPaths)
continue;
if (!startNode->getPosition()->isOverworld() && !preloadInstancePaths)
continue;
if (startNode->hasCompletePathTo(endNode))
continue;
if (!preloadUnlinkedPaths && !startNode->hasLinkTo(endNode))
continue;
if (startNode->getMapId() != endNode->getMapId())
continue;
//if (preloadUnlinkedPaths && !startNode->hasLinkTo(endNode) && startNode->isUselessLink(endNode))
// continue;
startNode->buildPath(endNode, nullptr, false);
//if (startNode->hasLinkTo(endNode) && !startNode->getPathTo(endNode)->getComplete())
//startNode->removeLinkTo(endNode);
}
startNode->setLinked(true);
cur++;
if (preloadSubPrint && (cur * 50) / max > ((cur - 1) * 50) / max)
{
sTravelNodeMap->printMap();
sTravelNodeMap->printNodeStore();
}
}
if (!preloadSubPrint)
{
sTravelNodeMap->printNodeStore();
sTravelNodeMap->printMap();
}
LOG_INFO("playerbots", ">> Loaded paths for {} nodes.", sTravelNodeMap->getNodes().size());
}
bool removeLowLinkNodes = false || fullNavPointReload || storeNavPointReload;
if (removeLowLinkNodes)
{
std::vector<TravelNode*> goodNodes;
std::vector<TravelNode*> remNodes;
for (auto& node : sTravelNodeMap->getNodes())
{
if (!node->getPosition()->isOverworld())
continue;
if (std::find(goodNodes.begin(), goodNodes.end(), node) != goodNodes.end())
continue;
if (std::find(remNodes.begin(), remNodes.end(), node) != remNodes.end())
continue;
std::vector<TravelNode*> nodes = node->getNodeMap(true);
if (nodes.size() < 5)
remNodes.insert(remNodes.end(), nodes.begin(), nodes.end());
else
goodNodes.insert(goodNodes.end(), nodes.begin(), nodes.end());
}
for (auto& node : remNodes)
sTravelNodeMap->removeNode(node);
LOG_INFO("playerbots", ">> Checked {} nodes.", sTravelNodeMap->getNodes().size());
}
bool cleanUpNodeLinks = false || fullNavPointReload || storeNavPointReload;
bool cleanUpSubPrint = false; //Print output every 2%.
if (cleanUpNodeLinks)
{
//Routes
uint32 cur = 0;
uint32 max = sTravelNodeMap->getNodes().size();
//Clean up node links
for (auto& startNode : sTravelNodeMap->getNodes())
{
startNode->cropUselessLinks();
cur++;
if (cleanUpSubPrint && (cur * 10) / max > ((cur - 1) * 10) / max)
{
sTravelNodeMap->printMap();
sTravelNodeMap->printNodeStore();
}
}
LOG_INFO("playerbots", ">> Cleaned paths for {} nodes.", sTravelNodeMap->getNodes().size());
}
bool reCalculateCost = false || fullNavPointReload || storeNavPointReload;
bool forceReCalculate = false;
if (reCalculateCost)
{
for (auto& startNode : sTravelNodeMap->getNodes())
{
for (auto& path : *startNode->getLinks())
{
TravelNodePath* nodePath = path.second;
if (path.second->getPathType() != TravelNodePathType::walk)
continue;
if (nodePath->getCalculated() && !forceReCalculate)
continue;
nodePath->calculateCost();
}
}
LOG_INFO("playerbots", ">> Calculated pathcost for {} nodes.", sTravelNodeMap->getNodes().size());
}
bool mirrorMissingPaths = true || fullNavPointReload || storeNavPointReload;
if (mirrorMissingPaths)
{
for (auto& startNode : sTravelNodeMap->getNodes())
{
for (auto& path : *startNode->getLinks())
{
TravelNode* endNode = path.first;
if (endNode->hasLinkTo(startNode))
continue;
if (path.second->getPathType() != TravelNodePathType::walk)
continue;
TravelNodePath nodePath = *path.second;
std::vector<WorldPosition> pPath = nodePath.getPath();
std::reverse(pPath.begin(), pPath.end());
nodePath.setPath(pPath);
endNode->setPathTo(startNode, nodePath, true);
}
}
LOG_INFO("playerbots", ">> Reversed missing paths for {} nodes.", sTravelNodeMap->getNodes().size());
}
*/
sTravelNodeMap->printMap();
sTravelNodeMap->printNodeStore();
sTravelNodeMap->saveNodeStore();
// Creature/gos/zone export.
if (sPlayerbotAIConfig->hasLog("creatures.csv"))
{
for (CreatureData const* cData : WorldPosition().getCreaturesNear())
{
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(cData->id1);
if (!cInfo)
continue;
WorldPosition point =
WorldPosition(cData->mapid, cData->posX, cData->posY, cData->posZ, cData->orientation);
std::string name = cInfo->Name;
name.erase(remove(name.begin(), name.end(), ','), name.end());
name.erase(remove(name.begin(), name.end(), '\"'), name.end());
std::ostringstream out;
out << name << ",";
point.printWKT(out);
out << cInfo->maxlevel << ",";
out << cInfo->rank << ",";
out << cInfo->faction << ",";
out << cInfo->npcflag << ",";
out << point.getAreaName() << ",";
out << std::fixed;
sPlayerbotAIConfig->log("creatures.csv", out.str().c_str());
}
}
if (sPlayerbotAIConfig->hasLog("vmangoslines.csv"))
{
uint32 mapId = 0;
std::vector<WorldPosition> pos;
static float const topNorthSouthLimit[] = {
2032.048340f, -6927.750000f, 1634.863403f, -6157.505371f, 1109.519775f, -5181.036133f, 1315.204712f,
-4096.020508f, 1073.089233f, -3372.571533f, 825.8331910f, -3125.778809f, 657.3439940f, -2314.813232f,
424.7361450f, -1888.283691f, 744.3958130f, -1647.935425f, 1424.160645f, -654.9481810f, 1447.065308f,
-169.7513580f, 1208.715454f, 189.74870300f, 1596.240356f, 998.61669900f, 1577.923706f, 1293.4199220f,
1458.520264f, 1727.3732910f, 1591.916138f, 3728.1394040f};
pos.clear();
#define my_sizeof(type) ((char*)(&type + 1) - (char*)(&type))
uint32 size = my_sizeof(topNorthSouthLimit) / my_sizeof(topNorthSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (topNorthSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, topNorthSouthLimit[i], topNorthSouthLimit[i + 1], 0));
}
std::ostringstream out;
out << "topNorthSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const ironforgeAreaSouthLimit[] = {
-7491.33f, 3093.740f, -7472.04f, -391.880f, -6366.68f, -730.100f, -6063.96f, -1411.76f,
-6087.62f, -2190.21f, -6349.54f, -2533.66f, -6308.63f, -3049.32f, -6107.82f, -3345.30f,
-6008.49f, -3590.52f, -5989.37f, -4312.29f, -5806.26f, -5864.11f};
pos.clear();
size = my_sizeof(ironforgeAreaSouthLimit) / my_sizeof(ironforgeAreaSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (ironforgeAreaSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, ironforgeAreaSouthLimit[i], ironforgeAreaSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "ironforgeAreaSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const stormwindAreaNorthLimit[] = {
-8004.250f, 3714.110f, -8075.000f, -179.000f, -8638.000f, 169.0000f, -9044.000f, 35.00000f,
-9068.000f, -125.000f, -9094.000f, -147.000f, -9206.000f, -290.000f, -9097.000f, -510.000f,
-8739.000f, -501.000f, -8725.500f, -1618.45f, -9810.400f, -1698.41f, -10049.60f, -1740.40f,
-10670.61f, -1692.51f, -10908.48f, -1563.87f, -13006.40f, -1622.80f, -12863.23f, -4798.42f};
pos.clear();
size = my_sizeof(stormwindAreaNorthLimit) / my_sizeof(stormwindAreaNorthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (stormwindAreaNorthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, stormwindAreaNorthLimit[i], stormwindAreaNorthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "stormwindAreaNorthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const stormwindAreaSouthLimit[] = {
-8725.3378910f, 3535.62402300f, -9525.6992190f, 910.13256800f, -9796.9531250f, 839.06958000f,
-9946.3417970f, 743.10284400f, -10287.361328f, 760.07647700f, -10083.828125f, 380.38989300f,
-10148.072266f, 80.056450000f, -10014.583984f, -161.6385190f, -9978.1464840f, -361.6380310f,
-9877.4892580f, -563.3048710f, -9980.9677730f, -1128.510498f, -9991.7177730f, -1428.793213f,
-9887.5791020f, -1618.514038f, -10169.600586f, -1801.582031f, -9966.2744140f, -2227.197754f,
-9861.3095700f, -2989.841064f, -9944.0263670f, -3205.886963f, -9610.2099610f, -3648.369385f,
-7949.3295900f, -4081.389404f, -7910.8593750f, -5855.578125f};
pos.clear();
size = my_sizeof(stormwindAreaSouthLimit) / my_sizeof(stormwindAreaSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (stormwindAreaSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, stormwindAreaSouthLimit[i], stormwindAreaSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "stormwindAreaSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
mapId = 1;
static float const northMiddleLimit[] = {
-2280.00f, 4054.000f, -2401.00f, 2365.000f, -2432.00f, 1338.000f, -2286.00f, 769.0000f, -2137.00f,
662.0000f, -2044.54f, 489.8600f, -1808.52f, 436.3900f, -1754.85f, 504.5500f, -1094.55f, 651.7500f,
-747.460f, 647.7300f, -685.550f, 408.4300f, -311.380f, 114.4300f, -358.400f, -587.420f, -377.920f,
-748.700f, -512.570f, -919.490f, -280.650f, -1008.87f, -81.2900f, -930.890f, 284.3100f, -1105.39f,
568.8600f, -892.280f, 1211.090f, -1135.55f, 879.6000f, -2110.18f, 788.9600f, -2276.02f, 899.6800f,
-2625.56f, 1281.540f, -2689.42f, 1521.820f, -3047.85f, 1424.220f, -3365.69f, 1694.110f, -3615.20f,
2373.780f, -4019.96f, 2388.130f, -5124.35f, 2193.790f, -5484.38f, 1703.570f, -5510.53f, 1497.590f,
-6376.56f, 1368.000f, -8530.00f};
pos.clear();
size = my_sizeof(northMiddleLimit) / my_sizeof(northMiddleLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (northMiddleLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, northMiddleLimit[i], northMiddleLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "northMiddleLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const durotarSouthLimit[] = {
2755.0f, -3766.f, 2225.0f, -3596.f, 1762.0f, -3746.f, 1564.0f, -3943.f, 1184.0f, -3915.f, 737.00f,
-3782.f, -75.00f, -3742.f, -263.0f, -3836.f, -173.0f, -4064.f, -81.00f, -4091.f, -49.00f, -4089.f,
-16.00f, -4187.f, -5.000f, -4192.f, -14.00f, -4551.f, -397.0f, -4601.f, -522.0f, -4583.f, -668.0f,
-4539.f, -790.0f, -4502.f, -1176.f, -4213.f, -1387.f, -4674.f, -2243.f, -6046.f};
pos.clear();
size = my_sizeof(durotarSouthLimit) / my_sizeof(durotarSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (durotarSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, durotarSouthLimit[i], durotarSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "durotarSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const valleyoftrialsSouthLimit[] = {-324.f, -3869.f, -774.f, -3992.f, -965.f, -4290.f, -932.f,
-4349.f, -828.f, -4414.f, -661.f, -4541.f, -521.f, -4582.f};
pos.clear();
size = my_sizeof(valleyoftrialsSouthLimit) / my_sizeof(valleyoftrialsSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (valleyoftrialsSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, valleyoftrialsSouthLimit[i], valleyoftrialsSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "valleyoftrialsSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const middleToSouthLimit[] = {
-2402.010000f, 4255.7000000f, -2475.933105f, 3199.5683590f, // Desolace
-2344.124023f, 1756.1643070f, -2826.438965f, 403.82473800f, // Mulgore
-3472.819580f, 182.52247600f, // Feralas
-4365.006836f, -1602.575439f, // the Barrens
-4515.219727f, -1681.356079f, -4543.093750f, -1882.869385f, // Thousand Needles
-4824.160000f, -2310.110000f, -5102.913574f, -2647.062744f, -5248.286621f,
-3034.536377f, -5246.920898f, -3339.139893f, -5459.449707f, -4920.155273f, // Tanaris
-5437.000000f, -5863.000000f};
pos.clear();
size = my_sizeof(middleToSouthLimit) / my_sizeof(middleToSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (middleToSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, middleToSouthLimit[i], middleToSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "middleToSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const orgrimmarSouthLimit[] = {
2132.5076f, -3912.2478f, 1944.4298f, -3855.2583f, 1735.6906f, -3834.2417f, 1654.3671f, -3380.9902f,
1593.9861f, -3975.5413f, 1439.2548f, -4249.6923f, 1436.3106f, -4007.8950f, 1393.3199f, -4196.0625f,
1445.2428f, -4373.9052f, 1407.2349f, -4429.4145f, 1464.7142f, -4545.2875f, 1584.1331f, -4596.8764f,
1716.8065f, -4601.1323f, 1875.8312f, -4788.7187f, 1979.7647f, -4883.4585f, 2219.1562f, -4854.3330f};
pos.clear();
size = my_sizeof(orgrimmarSouthLimit) / my_sizeof(orgrimmarSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (orgrimmarSouthLimit[i] == 0)
break;
pos.push_back(WorldPosition(mapId, orgrimmarSouthLimit[i], orgrimmarSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "orgrimmarSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
static float const feralasThousandNeedlesSouthLimit[] = {
-6495.4995f, -4711.9810f, -6674.9995f, -4515.0019f, -6769.5717f, -4122.4272f, -6838.2651f, -3874.2792f,
-6851.1314f, -3659.1179f, -6624.6845f, -3063.3843f, -6416.9067f, -2570.1301f, -5959.8466f, -2287.2634f,
-5947.9135f, -1866.5028f, -5947.9135f, -820.48810f, -5876.7114f, -3.5138000f, -5876.7114f, 917.640700f,
-6099.3603f, 1153.28840f, -6021.8989f, 1638.18090f, -6091.6176f, 2335.88920f, -6744.9946f, 2393.48550f,
-6973.8608f, 3077.02810f, -7068.7241f, 4376.23040f, -7142.1211f, 4808.43310f};
pos.clear();
size = my_sizeof(feralasThousandNeedlesSouthLimit) / my_sizeof(feralasThousandNeedlesSouthLimit[0]);
for (uint32 i = 0; i < size - 1; i = i + 2)
{
if (feralasThousandNeedlesSouthLimit[i] == 0)
break;
pos.push_back(
WorldPosition(mapId, feralasThousandNeedlesSouthLimit[i], feralasThousandNeedlesSouthLimit[i + 1], 0));
}
out.str("");
out.clear();
out << "feralasThousandNeedlesSouthLimit"
<< ",";
WorldPosition().printWKT(pos, out, 1);
out << std::fixed;
sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str());
}
if (sPlayerbotAIConfig->hasLog("gos.csv"))
{
for (GameObjectData const* gData : WorldPosition().getGameObjectsNear())
{
GameObjectTemplate const* data = sObjectMgr->GetGameObjectTemplate(gData->id);
if (!data)
continue;
WorldPosition point =
WorldPosition(gData->mapid, gData->posX, gData->posY, gData->posZ, gData->orientation);
std::string name = data->name;
name.erase(remove(name.begin(), name.end(), ','), name.end());
name.erase(remove(name.begin(), name.end(), '\"'), name.end());
std::ostringstream out;
out << name << ",";
point.printWKT(out);
out << data->type << ",";
out << point.getAreaName() << ",";
out << std::fixed;
sPlayerbotAIConfig->log("gos.csv", out.str().c_str());
}
}
if (sPlayerbotAIConfig->hasLog("zones.csv"))
{
std::unordered_map<std::string, std::vector<WorldPosition>> zoneLocs;
std::vector<WorldPosition> Locs;
for (auto& u : units)
{
WorldPosition point = WorldPosition(u.map, u.x, u.y, u.z, u.o);
std::string const name = std::to_string(u.map) + point.getAreaName();
if (zoneLocs.find(name) == zoneLocs.end())
zoneLocs.insert_or_assign(name, Locs);
zoneLocs.find(name)->second.push_back(point);
}
for (auto& loc : zoneLocs)
{
if (loc.second.empty())
continue;
if (!sTravelNodeMap->getMapOffset(loc.second.front().getMapId()) && loc.second.front().getMapId() != 0)
continue;
std::vector<WorldPosition> points = loc.second;
;
std::ostringstream out;
WorldPosition pos = WorldPosition(points, WP_MEAN_CENTROID);
out << "\"center\""
<< ",";
out << points.begin()->getMapId() << ",";
out << points.begin()->getAreaName() << ",";
out << points.begin()->getAreaName(true, true) << ",";
pos.printWKT(out);
out << "\n";
out << "\"area\""
<< ",";
out << points.begin()->getMapId() << ",";
out << points.begin()->getAreaName() << ",";
out << points.begin()->getAreaName(true, true) << ",";
point.printWKT(points, out, 0);
sPlayerbotAIConfig->log("zones.csv", out.str().c_str());
}
}
bool printStrategyMap = false;
if (printStrategyMap && sPlayerbotAIConfig->hasLog("strategy.csv"))
{
static std::map<uint8, std::string> classes;
static std::map<uint8, std::map<uint8, std::string>> specs;
classes[CLASS_DRUID] = "druid";
specs[CLASS_DRUID][0] = "balance";
specs[CLASS_DRUID][1] = "feral combat";
specs[CLASS_DRUID][2] = "restoration";
classes[CLASS_HUNTER] = "hunter";
specs[CLASS_HUNTER][0] = "beast mastery";
specs[CLASS_HUNTER][1] = "marksmanship";
specs[CLASS_HUNTER][2] = "survival";
classes[CLASS_MAGE] = "mage";
specs[CLASS_MAGE][0] = "arcane";
specs[CLASS_MAGE][1] = "fire";
specs[CLASS_MAGE][2] = "frost";
classes[CLASS_PALADIN] = "paladin";
specs[CLASS_PALADIN][0] = "holy";
specs[CLASS_PALADIN][1] = "protection";
specs[CLASS_PALADIN][2] = "retribution";
classes[CLASS_PRIEST] = "priest";
specs[CLASS_PRIEST][0] = "discipline";
specs[CLASS_PRIEST][1] = "holy";
specs[CLASS_PRIEST][2] = "shadow";
classes[CLASS_ROGUE] = "rogue";
specs[CLASS_ROGUE][0] = "assasination";
specs[CLASS_ROGUE][1] = "combat";
specs[CLASS_ROGUE][2] = "subtlety";
classes[CLASS_SHAMAN] = "shaman";
specs[CLASS_SHAMAN][0] = "elemental";
specs[CLASS_SHAMAN][1] = "enhancement";
specs[CLASS_SHAMAN][2] = "restoration";
classes[CLASS_WARLOCK] = "warlock";
specs[CLASS_WARLOCK][0] = "affliction";
specs[CLASS_WARLOCK][1] = "demonology";
specs[CLASS_WARLOCK][2] = "destruction";
classes[CLASS_WARRIOR] = "warrior";
specs[CLASS_WARRIOR][0] = "arms";
specs[CLASS_WARRIOR][1] = "fury";
specs[CLASS_WARRIOR][2] = "protection";
classes[CLASS_DEATH_KNIGHT] = "dk";
specs[CLASS_DEATH_KNIGHT][0] = "blood";
specs[CLASS_DEATH_KNIGHT][1] = "frost";
specs[CLASS_DEATH_KNIGHT][2] = "unholy";
// Use randombot 0.
std::ostringstream cout;
cout << sPlayerbotAIConfig->randomBotAccountPrefix << 0;
std::string const accountName = cout.str();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->SetData(0, accountName);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (result)
{
Field* fields = result->Fetch();
uint32 accountId = fields[0].Get<uint32>();
WorldSession* session =
new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
LOCALE_enUS, 0, false, false, 0, true);
std::vector<std::pair<std::pair<uint32, uint32>, uint32>> classSpecLevel;
std::unordered_map<std::string, std::vector<std::pair<std::pair<uint32, uint32>, uint32>>> actions;
std::ostringstream out;
for (uint8 race = RACE_HUMAN; race < MAX_RACES; race++)
{
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
{
if (cls != 10)
{
std::unique_ptr<CharacterCreateInfo> characterInfo =
std::make_unique<CharacterCreateInfo>("dummy", race, cls, 1, 1, 1, 1, 1, 1);
Player* player = new Player(session);
if (player->Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(),
characterInfo.get()))
{
for (uint8 tab = 0; tab < 3; tab++)
{
TalentSpec newSpec;
if (tab == 0)
newSpec = TalentSpec(player, "1-0-0");
else if (tab == 1)
newSpec = TalentSpec(player, "0-1-0");
else
newSpec = TalentSpec(player, "0-0-1");
for (uint32 lvl = 1; lvl < MAX_LEVEL; lvl++)
{
player->SetLevel(lvl);
std::ostringstream tout;
newSpec.ApplyTalents(player, &tout);
PlayerbotAI* botAI = new PlayerbotAI(player);
botAI->ResetStrategies(false);
AiObjectContext* con = botAI->GetAiObjectContext();
std::vector<std::string> tstrats;
std::set<std::string> strategies;
std::set<std::string> sstrats;
tstrats = botAI->GetStrategies(BOT_STATE_COMBAT);
sstrats = con->GetSupportedStrategies();
if (!sstrats.empty())
strategies.insert(tstrats.begin(), tstrats.end());
tstrats = botAI->GetStrategies(BOT_STATE_NON_COMBAT);
if (!tstrats.empty())
strategies.insert(tstrats.begin(), tstrats.end());
tstrats = botAI->GetStrategies(BOT_STATE_DEAD);
if (!tstrats.empty())
strategies.insert(tstrats.begin(), tstrats.end());
sstrats = con->GetSupportedStrategies();
if (!sstrats.empty())
strategies.insert(sstrats.begin(), sstrats.end());
for (auto& stratName : strategies)
{
Strategy* strat = con->GetStrategy(stratName);
if (strat->getDefaultActions())
for (uint32 i = 0; i < NextAction::size(strat->getDefaultActions()); i++)
{
NextAction* nextAction = strat->getDefaultActions()[i];
std::ostringstream aout;
aout << nextAction->getRelevance() << "," << nextAction->getName()
<< ",,S:" << stratName;
if (actions.find(aout.str().c_str()) != actions.end())
classSpecLevel = actions.find(aout.str().c_str())->second;
else
classSpecLevel.clear();
classSpecLevel.push_back(std::make_pair(std::make_pair(cls, tab), lvl));
actions.insert_or_assign(aout.str().c_str(), classSpecLevel);
}
std::vector<TriggerNode*> triggers;
strat->InitTriggers(triggers);
for (auto& triggerNode : triggers)
{
// out << " TN:" << triggerNode->getName();
if (Trigger* trigger = con->GetTrigger(triggerNode->getName()))
{
triggerNode->setTrigger(trigger);
NextAction** nextActions = triggerNode->getHandlers();
for (uint32 i = 0; i < NextAction::size(nextActions); i++)
{
NextAction* nextAction = nextActions[i];
// out << " A:" << nextAction->getName() << "(" <<
// nextAction->getRelevance() << ")";
std::ostringstream aout;
aout << nextAction->getRelevance() << "," << nextAction->getName()
<< "," << triggerNode->getName() << "," << stratName;
if (actions.find(aout.str().c_str()) != actions.end())
classSpecLevel = actions.find(aout.str().c_str())->second;
else
classSpecLevel.clear();
classSpecLevel.push_back(
std::make_pair(std::make_pair(cls, tab), lvl));
actions.insert_or_assign(aout.str().c_str(), classSpecLevel);
}
}
}
}
delete botAI;
}
}
}
delete player;
}
}
}
std::vector<std::string> actionKeys;
for (auto& action : actions)
actionKeys.push_back(action.first);
std::sort(actionKeys.begin(), actionKeys.end(),
[](std::string const i, std::string const j)
{
std::stringstream is(i);
std::stringstream js(j);
float iref, jref;
std::string iact, jact, itrig, jtrig, istrat, jstrat;
is >> iref >> iact >> itrig >> istrat;
js >> jref >> jact >> jtrig >> jstrat;
if (iref > jref)
return true;
if (iref == jref && istrat < jstrat)
return true;
if (iref == jref && !(istrat > jstrat) && iact < jact)
return true;
if (iref == jref && !(istrat > jstrat) && !(iact > jact) && itrig < jtrig)
return true;
return false;
});
sPlayerbotAIConfig->log("strategy.csv", "relevance, action, trigger, strategy, classes");
for (auto& actionkey : actionKeys)
{
if (actions.find(actionkey)->second.size() != (MAX_LEVEL - 1) * (MAX_CLASSES - 1))
{
classSpecLevel = actions.find(actionkey)->second;
std::vector<std::pair<std::pair<uint32, uint32>, std::pair<uint32, uint32>>> classs;
for (auto cl : classSpecLevel)
{
uint32 minLevel = MAX_LEVEL;
uint32 maxLevel = 0;
uint32 cls = cl.first.first;
uint32 tb = cl.first.second;
if (std::find_if(classs.begin(), classs.end(),
[cls, tb](std::pair<std::pair<uint32, uint32>, std::pair<uint32, uint32>> i)
{ return i.first.first == cls && i.first.second == tb; }) == classs.end())
{
for (auto cll : classSpecLevel)
{
if (cll.first.first == cl.first.first && cll.first.second == cl.first.second)
{
minLevel = std::min(minLevel, cll.second);
maxLevel = std::max(maxLevel, cll.second);
}
}
classs.push_back(std::make_pair(cl.first, std::make_pair(minLevel, maxLevel)));
}
}
out << actionkey;
if (classs.size() != 9 * 3)
{
out << ",";
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
{
bool a[3] = {false, false, false};
uint32 min[3] = {0, 0, 0};
uint32 max[3] = {0, 0, 0};
if (std::find_if(classs.begin(), classs.end(),
[cls](std::pair<std::pair<uint32, uint32>, std::pair<uint32, uint32>> i)
{ return i.first.first == cls; }) == classs.end())
continue;
for (uint32 tb = 0; tb < 3; tb++)
{
auto tcl = std::find_if(
classs.begin(), classs.end(),
[cls, tb](std::pair<std::pair<uint32, uint32>, std::pair<uint32, uint32>> i)
{ return i.first.first == cls && i.first.second == tb; });
if (tcl == classs.end())
continue;
a[tb] = true;
min[tb] = tcl->second.first;
max[tb] = tcl->second.second;
}
if (a[0] && a[1] && a[2] && min[0] == min[1] == min[2] && max[0] == max[1] == max[2])
{
if (min[0] != 1 || max[0] != MAX_LEVEL - 1)
out << classes[cls] << "(" << min[0] << "-" << max[0] << ")";
else
out << classes[cls];
if (cls != classs.back().first.first)
out << ";";
}
else
{
for (uint32 tb = 0; tb < 3; tb++)
{
if (!a[tb])
continue;
if (min[tb] != 1 || max[tb] != MAX_LEVEL - 1)
out << specs[cls][tb] << " " << classes[cls] << "(" << min[tb] << "-" << max[tb]
<< ")";
else
out << specs[cls][tb] << " " << classes[cls];
if (cls != classs.back().first.first || tb != classs.back().first.second)
out << ";";
}
}
}
}
else
out << "all";
out << "\n";
}
else
out << actionkey << "\n";
}
sPlayerbotAIConfig->log("strategy.csv", out.str().c_str());
}
}
/*
sPlayerbotAIConfig->openLog(7, "w");
//Zone area map REMOVE!
uint32 k = 0;
for (auto& node : sTravelNodeMap->getNodes())
{
WorldPosition* pos = node->getPosition();
//map area
for (uint32 x = 0; x < 2000; x++)
{
for (uint32 y = 0; y < 2000; y++)
{
if (!pos->getMap())
continue;
float nx = pos->getX() + (x*5)-5000.0f;
float ny = pos->getY() + (y*5)-5000.0f;
float nz = pos->getZ() + 100.0f;
//pos->getMap()->GetHitPosition(nx, ny, nz + 200.0f, nx, ny, nz, -0.5f);
if (!pos->getMap()->GetHeightInRange(nx, ny, nz, 5000.0f)) // GetHeight can fail
continue;
WorldPosition npos = WorldPosition(pos->getMapId(), nx, ny, nz, 0.0);
uint32 area = path.getArea(npos.getMapId(), npos.getX(), npos.getY(), npos.getZ());
std::ostringstream out;
out << std::fixed << area << "," << npos.getDisplayX() << "," << npos.getDisplayY();
sPlayerbotAIConfig->log(7, out.str().c_str());
}
}
k++;
if (k > 0)
break;
}
//Explore map output (REMOVE!)
sPlayerbotAIConfig->openLog(5, "w");
for (auto i : exploreLocs)
{
for (auto j : i.second->getPoints())
{
std::ostringstream out;
std::string const name = i.second->getTitle();
name.erase(remove(name.begin(), name.end(), '\"'), name.end());
out << std::fixed << std::setprecision(2) << name.c_str() << "," << i.first << "," << j->getDisplayX() <<
"," << j->getDisplayY() << "," << j->getX() << "," << j->getY() << "," << j->getZ(); sPlayerbotAIConfig->log(5,
out.str().c_str());
}
}
*/
}
uint32 TravelMgr::getDialogStatus(Player* pPlayer, int32 questgiver, Quest const* pQuest)
{
uint32 dialogStatus = DIALOG_STATUS_NONE;
QuestRelationBounds rbounds; // QuestRelations (quest-giver)
QuestRelationBounds irbounds; // InvolvedRelations (quest-finisher)
uint32 questId = pQuest->GetQuestId();
if (questgiver > 0)
{
rbounds = sObjectMgr->GetCreatureQuestRelationBounds(questgiver);
irbounds = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver);
}
else
{
rbounds = sObjectMgr->GetGOQuestRelationBounds(questgiver * -1);
irbounds = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver * -1);
}
// Check markings for quest-finisher
for (QuestRelations::const_iterator itr = irbounds.first; itr != irbounds.second; ++itr)
{
if (itr->second != questId)
continue;
uint32 dialogStatusNew = DIALOG_STATUS_NONE;
if (!pQuest)
{
continue;
}
QuestStatus status = pPlayer->GetQuestStatus(questId);
if ((status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(questId)) ||
(pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false)))
{
if (pQuest->IsAutoComplete() && pQuest->IsRepeatable())
{
dialogStatusNew = DIALOG_STATUS_REWARD_REP;
}
else
{
dialogStatusNew = DIALOG_STATUS_REWARD2;
}
}
else if (status == QUEST_STATUS_INCOMPLETE)
{
dialogStatusNew = DIALOG_STATUS_INCOMPLETE;
}
if (dialogStatusNew > dialogStatus)
{
dialogStatus = dialogStatusNew;
}
}
// check markings for quest-giver
for (QuestRelations::const_iterator itr = rbounds.first; itr != rbounds.second; ++itr)
{
if (itr->second != questId)
continue;
uint32 dialogStatusNew = DIALOG_STATUS_NONE;
if (!pQuest)
{
continue;
}
QuestStatus status = pPlayer->GetQuestStatus(questId);
if (status == QUEST_STATUS_NONE) // For all other cases the mark is handled either at some place else, or with
// involved-relations already
{
if (pPlayer->CanSeeStartQuest(pQuest))
{
if (pPlayer->SatisfyQuestLevel(pQuest, false))
{
int32 lowLevelDiff = sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF);
if (pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->IsQuestRewarded(questId)))
{
dialogStatusNew = DIALOG_STATUS_REWARD_REP;
}
else if (lowLevelDiff < 0 ||
pPlayer->GetLevel() <= pPlayer->GetQuestLevel(pQuest) + uint32(lowLevelDiff))
{
dialogStatusNew = DIALOG_STATUS_AVAILABLE;
}
else
{
dialogStatusNew = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
}
}
else
{
dialogStatusNew = DIALOG_STATUS_UNAVAILABLE;
}
}
}
if (dialogStatusNew > dialogStatus)
{
dialogStatus = dialogStatusNew;
}
}
return dialogStatus;
}
// Selects a random WorldPosition from a list. Use a distance weighted distribution.
std::vector<WorldPosition*> TravelMgr::getNextPoint(WorldPosition* center, std::vector<WorldPosition*> points,
uint32 amount)
{
std::vector<WorldPosition*> retVec;
if (points.size() < 2)
{
if (points.size() == 1)
retVec.push_back(points[0]);
return retVec;
}
retVec = points;
std::vector<uint32> weights;
// List of weights based on distance (Gausian curve that starts at 100 and lower to 1 at 1000 distance)
// std::transform(retVec.begin(), retVec.end(), std::back_inserter(weights), [center](WorldPosition point) { return
// 1 + 1000 * exp(-1 * pow(point.distance(center) / 400.0, 2)); });
// List of weights based on distance (Twice the distance = half the weight). Caps out at 200.0000 range.
std::transform(retVec.begin(), retVec.end(), std::back_inserter(weights),
[center](WorldPosition* point)
{ return static_cast<uint32>(200000.f / (1.f + point->distance(center))); });
Acore::Containers::RandomShuffle(retVec);
std::vector<float> dists;
// Total sum of all those weights.
/*
uint32 sum = std::accumulate(weights.begin(), weights.end(), 0);
//Pick a random point based on weights.
for (uint32 nr = 0; nr < amount; nr++)
{
//Pick a random number in that range.
uint32 rnd = urand(0, sum);
for (unsigned i = 0; i < points.size(); ++i)
if (rnd < weights[i] && (retVec.empty() || std::find(retVec.begin(), retVec.end(), points[i]) ==
retVec.end()))
{
retVec.push_back(points[i]);
break;
}
else
rnd -= weights[i];
}*/
return retVec;
}
std::vector<WorldPosition> TravelMgr::getNextPoint(WorldPosition center, std::vector<WorldPosition> points,
uint32 amount)
{
std::vector<WorldPosition> retVec;
if (points.size() == 1)
{
retVec.push_back(points[0]);
return retVec;
}
// List of weights based on distance (Gausian curve that starts at 100 and lower to 1 at 1000 distance)
std::vector<uint32> weights;
std::transform(points.begin(), points.end(), std::back_inserter(weights),
[center](WorldPosition point)
{ return 1 + 1000 * static_cast<uint32>(exp(-1.f * pow(point.distance(center) / 400.f, 2.f))); });
// Total sum of all those weights.
uint32 sum = std::accumulate(weights.begin(), weights.end(), 0);
// Pick a random number in that range.
uint32 rnd = urand(0, sum);
// Pick a random point based on weights.
for (uint32 nr = 0; nr < amount; nr++)
{
for (unsigned i = 0; i < points.size(); ++i)
if (rnd < weights[i] &&
(retVec.empty() || std::find(retVec.begin(), retVec.end(), points[i]) == retVec.end()))
{
retVec.push_back(points[i]);
break;
}
else
rnd -= weights[i];
}
// Peiru: Crash failsafe - if the retVec is still empty but points exist, return first point
if (retVec.empty() && points.size() > 0)
retVec.push_back(points[0]);
if (!retVec.empty())
return retVec;
assert(!"No valid point found.");
return retVec;
}
QuestStatusData* TravelMgr::getQuestStatus(Player* bot, uint32 questId) { return &bot->getQuestStatusMap()[questId]; }
bool TravelMgr::getObjectiveStatus(Player* bot, Quest const* pQuest, uint32 objective)
{
uint32 questId = pQuest->GetQuestId();
if (!bot->IsActiveQuest(questId))
return false;
if (bot->GetQuestStatus(questId) != QUEST_STATUS_INCOMPLETE)
return false;
QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId);
uint32 reqCount = pQuest->RequiredItemCount[objective];
uint32 hasCount = questStatus->ItemCount[objective];
if (reqCount && hasCount < reqCount)
return true;
reqCount = pQuest->RequiredNpcOrGoCount[objective];
hasCount = questStatus->CreatureOrGOCount[objective];
if (reqCount && hasCount < reqCount)
return true;
return false;
}
std::vector<TravelDestination*> TravelMgr::getQuestTravelDestinations(Player* bot, int32 questId, bool ignoreFull,
bool ignoreInactive, float maxDistance,
bool ignoreObjectives)
{
WorldPosition botLocation(bot);
std::vector<TravelDestination*> retTravelLocations;
if (!questId)
{
for (auto& dest : questGivers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
for (auto& quest : quests)
{
for (auto& dest : quest.second->questTakers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
if (!ignoreObjectives)
for (auto& dest : quest.second->questObjectives)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
}
}
else if (questId == -1)
{
for (auto& dest : questGivers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
}
else
{
auto i = quests.find(questId);
if (i != quests.end())
{
for (auto& dest : i->second->questTakers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
if (!ignoreObjectives)
for (auto& dest : i->second->questObjectives)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
}
}
return retTravelLocations;
}
std::vector<TravelDestination*> TravelMgr::getRpgTravelDestinations(Player* bot, bool ignoreFull, bool ignoreInactive,
float maxDistance)
{
WorldPosition botLocation(bot);
std::vector<TravelDestination*> retTravelLocations;
for (auto& dest : rpgNpcs)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
return std::move(retTravelLocations);
}
std::vector<TravelDestination*> TravelMgr::getExploreTravelDestinations(Player* bot, bool ignoreFull,
bool ignoreInactive)
{
WorldPosition botLocation(bot);
std::vector<TravelDestination*> retTravelLocations;
for (auto& dest : exploreLocs)
{
if (!ignoreInactive && !dest.second->isActive(bot))
continue;
if (dest.second->isFull(ignoreFull))
continue;
retTravelLocations.push_back(dest.second);
}
return retTravelLocations;
}
std::vector<TravelDestination*> TravelMgr::getGrindTravelDestinations(Player* bot, bool ignoreFull, bool ignoreInactive,
float maxDistance)
{
WorldPosition botLocation(bot);
std::vector<TravelDestination*> retTravelLocations;
for (auto& dest : grindMobs)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (dest->isFull(ignoreFull))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
return retTravelLocations;
}
void TravelMgr::setNullTravelTarget(Player* player)
{
if (!player)
return;
PlayerbotAI* playerBotAI = GET_PLAYERBOT_AI(player);
if (!playerBotAI)
return;
TravelTarget* target = playerBotAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
if (target)
target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true);
}
void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance, bool makeShortcuts)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
if (sMap == eMap)
return;
// Calculate shortcuts.
if (makeShortcuts)
{
for (auto& mapTransfers : mapTransfersMap)
{
uint32 sMapt = mapTransfers.first.first;
uint32 eMapt = mapTransfers.first.second;
for (auto& mapTransfer : mapTransfers.second)
{
if (eMapt == sMap && sMapt != eMap) // [S1 >MT> E1 -> S2] >THIS> E2
{
float newDistToEnd = mapTransDistance(*mapTransfer.getPointFrom(), start) + portalDistance;
if (mapTransDistance(*mapTransfer.getPointFrom(), end) > newDistToEnd)
addMapTransfer(*mapTransfer.getPointFrom(), end, newDistToEnd, false);
}
if (sMapt == eMap && eMapt != sMap) // S1 >THIS> [E1 -> S2 >MT> E2]
{
float newDistToEnd = portalDistance + mapTransDistance(end, *mapTransfer.getPointTo());
if (mapTransDistance(start, *mapTransfer.getPointTo()) > newDistToEnd)
addMapTransfer(start, *mapTransfer.getPointTo(), newDistToEnd, false);
}
}
}
}
// Add actual transfer.
auto mapTransfers = mapTransfersMap.find(std::make_pair(start.getMapId(), end.getMapId()));
if (mapTransfers == mapTransfersMap.end())
mapTransfersMap.insert({{sMap, eMap}, {mapTransfer(start, end, portalDistance)}});
else
mapTransfers->second.push_back(mapTransfer(start, end, portalDistance));
};
void TravelMgr::loadMapTransfers()
{
for (auto& node : sTravelNodeMap->getNodes())
{
for (auto& link : *node->getLinks())
{
addMapTransfer(*node->getPosition(), *link.first->getPosition(), link.second->getDistance());
}
}
}
float TravelMgr::mapTransDistance(WorldPosition start, WorldPosition end)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
if (sMap == eMap)
return start.distance(end);
float minDist = 200000;
auto mapTransfers = mapTransfersMap.find({sMap, eMap});
if (mapTransfers == mapTransfersMap.end())
return minDist;
for (auto& mapTrans : mapTransfers->second)
{
float dist = mapTrans.distance(start, end);
if (dist < minDist)
minDist = dist;
}
return minDist;
}
float TravelMgr::fastMapTransDistance(WorldPosition start, WorldPosition end)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
if (sMap == eMap)
return start.fDist(end);
float minDist = 200000;
auto mapTransfers = mapTransfersMap.find({sMap, eMap});
if (mapTransfers == mapTransfersMap.end())
return minDist;
for (auto& mapTrans : mapTransfers->second)
{
float dist = mapTrans.fDist(start, end);
if (dist < minDist)
minDist = dist;
}
return minDist;
}
QuestTravelDestination::QuestTravelDestination(uint32 questId1, float radiusMin1, float radiusMax1)
: TravelDestination(radiusMin1, radiusMax1)
{
questId = questId1;
questTemplate = sObjectMgr->GetQuestTemplate(questId);
}
bool QuestTravelDestination::isActive(Player* bot) { return bot->IsActiveQuest(questId); }
bool QuestObjectiveTravelDestination::isCreature() { return GetQuestTemplate()->RequiredNpcOrGo[objective] > 0; }
uint32 QuestObjectiveTravelDestination::ReqCreature()
{
return isCreature() ? GetQuestTemplate()->RequiredNpcOrGo[objective] : 0;
}
uint32 QuestObjectiveTravelDestination::ReqGOId()
{
return !isCreature() ? abs(GetQuestTemplate()->RequiredNpcOrGo[objective]) : 0;
}
uint32 QuestObjectiveTravelDestination::ReqCount() { return GetQuestTemplate()->RequiredNpcOrGoCount[objective]; }
void TravelMgr::printGrid(uint32 mapId, int x, int y, std::string const type)
{
std::string const fileName = "unload_grid.csv";
if (sPlayerbotAIConfig->hasLog(fileName))
{
WorldPosition p = WorldPosition(mapId, 0, 0, 0, 0);
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00, " << 0 << 0 << x << "," << y << ", " << type << ",";
p.printWKT(p.fromGridCoord(GridCoord(x, y)), out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
void TravelMgr::printObj(WorldObject* obj, std::string const type)
{
std::string fileName = "unload_grid.csv";
if (sPlayerbotAIConfig->hasLog(fileName))
{
WorldPosition p = WorldPosition(obj);
Cell cell(obj->GetPositionX(), obj->GetPositionY());
std::vector<WorldPosition> vcell, vgrid;
vcell = p.fromCellCoord(p.getCellCoord());
vgrid = p.gridFromCellCoord(p.getCellCoord());
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX()
<< "," << cell.GridY() << ", " << type << ",";
p.printWKT(vcell, out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX()
<< "," << cell.GridY() << ", " << type << ",";
p.printWKT(vgrid, out, 1, true);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
fileName = "unload_obj.csv";
if (sPlayerbotAIConfig->hasLog(fileName))
{
WorldPosition p = WorldPosition(obj);
Cell cell(obj->GetPositionX(), obj->GetPositionY());
{
std::ostringstream out;
out << sPlayerbotAIConfig->GetTimestampStr();
out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX()
<< "," << cell.GridY() << ", " << type << ",";
p.printWKT({p}, out, 0);
sPlayerbotAIConfig->log(fileName, out.str().c_str());
}
}
}