mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
feat(Core/Travel): Port UpcommingSpecialMovement + getNextPoint helpers
This commit is contained in:
parent
78ae50d3ba
commit
dcecb6844f
@ -718,6 +718,244 @@ bool TravelPath::cutTo(PathNodePoint point, bool including)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Inlined zone-test: cylinder (radius>0) or rotated AABB.
|
||||||
|
bool IsPointInAreaTrigger(AreaTrigger const* at, uint32 mapId,
|
||||||
|
float x, float y, float z, float delta)
|
||||||
|
{
|
||||||
|
if (mapId != at->map)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (at->radius > 0)
|
||||||
|
{
|
||||||
|
float dx = x - at->x;
|
||||||
|
float dy = y - at->y;
|
||||||
|
float dz = z - at->z;
|
||||||
|
float distSq = dx * dx + dy * dy + dz * dz;
|
||||||
|
float r = at->radius + delta;
|
||||||
|
return distSq <= r * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box: rotate the test point back to AT-local axes, then check
|
||||||
|
// axis-aligned half-extents (length=X, width=Y, height=Z).
|
||||||
|
double rot = 2.0 * M_PI - at->orientation;
|
||||||
|
double sv = std::sin(rot);
|
||||||
|
double cv = std::cos(rot);
|
||||||
|
|
||||||
|
float lx = x - at->x;
|
||||||
|
float ly = y - at->y;
|
||||||
|
float rx = float(at->x + lx * cv - ly * sv) - at->x;
|
||||||
|
float ry = float(at->y + ly * cv + lx * sv) - at->y;
|
||||||
|
float rz = z - at->z;
|
||||||
|
|
||||||
|
return std::fabs(rx) <= at->length / 2 + delta &&
|
||||||
|
std::fabs(ry) <= at->width / 2 + delta &&
|
||||||
|
std::fabs(rz) <= at->height / 2 + delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TravelPath::shouldMoveToNextPoint(WorldPosition startPos,
|
||||||
|
std::vector<PathNodePoint>::iterator beg,
|
||||||
|
std::vector<PathNodePoint>::iterator ed,
|
||||||
|
std::vector<PathNodePoint>::iterator p,
|
||||||
|
float& moveDist, float maxDist)
|
||||||
|
{
|
||||||
|
if (p == ed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto nextP = std::next(p);
|
||||||
|
if (nextP == ed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Stop at adjacent area-trigger pair sharing entry — second is the
|
||||||
|
// teleport-out point we want to land on, not skip past.
|
||||||
|
if (p->type == PathNodeType::NODE_AREA_TRIGGER &&
|
||||||
|
nextP->type == PathNodeType::NODE_AREA_TRIGGER &&
|
||||||
|
p->entry == nextP->entry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Same idea for static-portal pair.
|
||||||
|
if (p->type == PathNodeType::NODE_STATIC_PORTAL &&
|
||||||
|
nextP->type == PathNodeType::NODE_STATIC_PORTAL &&
|
||||||
|
p->entry == nextP->entry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Approaching a transport boarding node — stop before it.
|
||||||
|
if (nextP->type == PathNodeType::NODE_TRANSPORT && nextP->entry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Mid-transport: traverse to the disembark side.
|
||||||
|
if (p->type == PathNodeType::NODE_TRANSPORT && p->entry)
|
||||||
|
{
|
||||||
|
// Off-transport detour around a transport segment (rare): skip.
|
||||||
|
if (nextP->type != PathNodeType::NODE_TRANSPORT && p != beg &&
|
||||||
|
std::prev(p)->type != PathNodeType::NODE_TRANSPORT)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop within a flightpath run.
|
||||||
|
if (p->type == PathNodeType::NODE_FLIGHTPATH &&
|
||||||
|
nextP->type == PathNodeType::NODE_FLIGHTPATH)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float nextMove = p->point.distance(nextP->point);
|
||||||
|
|
||||||
|
if (p->point.GetMapId() != startPos.GetMapId() ||
|
||||||
|
((moveDist + nextMove > maxDist ||
|
||||||
|
startPos.distance(nextP->point) > maxDist) && moveDist > 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
moveDist += nextMove;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PathNodePoint>::iterator
|
||||||
|
TravelPath::getNextPoint(WorldPosition startPos, float maxDist, bool onTransport)
|
||||||
|
{
|
||||||
|
float minDist = FLT_MAX;
|
||||||
|
auto startP = fullPath.begin();
|
||||||
|
|
||||||
|
if (!onTransport)
|
||||||
|
{
|
||||||
|
// Closest walkable point on the path (same map as the bot).
|
||||||
|
for (auto p = fullPath.begin(); p != fullPath.end(); ++p)
|
||||||
|
{
|
||||||
|
if (p->point.GetMapId() != startPos.GetMapId())
|
||||||
|
continue;
|
||||||
|
if (!p->isWalkable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float curDist = p->point.distance(startPos);
|
||||||
|
if (curDist <= minDist)
|
||||||
|
{
|
||||||
|
minDist = curDist;
|
||||||
|
startP = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startP == fullPath.end())
|
||||||
|
return startP;
|
||||||
|
|
||||||
|
float moveDist = startP->point.distance(startPos);
|
||||||
|
|
||||||
|
for (auto p = startP; p != fullPath.end(); ++p)
|
||||||
|
{
|
||||||
|
if (shouldMoveToNextPoint(startPos, fullPath.begin(), fullPath.end(),
|
||||||
|
p, moveDist, maxDist))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
startP = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startP == fullPath.end() || !startP->isWalkable())
|
||||||
|
return startP;
|
||||||
|
|
||||||
|
auto nextP = std::next(startP);
|
||||||
|
if (nextP == fullPath.end())
|
||||||
|
return startP;
|
||||||
|
|
||||||
|
// If startPos is between startP and nextP, skip ahead to nextP.
|
||||||
|
float project = startPos.projectOnSegment(startP->point, nextP->point);
|
||||||
|
if (project > 0.0f && project < 1.0f)
|
||||||
|
return nextP;
|
||||||
|
|
||||||
|
return startP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TravelPath::UpcommingSpecialMovement(WorldPosition startPos,
|
||||||
|
float maxDist, bool onTransport)
|
||||||
|
{
|
||||||
|
if (fullPath.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto startP = getNextPoint(startPos, maxDist, onTransport);
|
||||||
|
if (startP == fullPath.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto prevP = startP, nextP = startP;
|
||||||
|
if (startP != fullPath.begin())
|
||||||
|
prevP = std::prev(prevP);
|
||||||
|
if (std::next(nextP) != fullPath.end())
|
||||||
|
nextP = std::next(nextP);
|
||||||
|
|
||||||
|
// Area trigger: zone-gated. With entry, must be inside the trigger
|
||||||
|
// zone; without entry, fire as soon as we reach it.
|
||||||
|
if (startP->type == PathNodeType::NODE_AREA_TRIGGER)
|
||||||
|
{
|
||||||
|
if (startP->entry)
|
||||||
|
{
|
||||||
|
AreaTrigger const* at = sObjectMgr->GetAreaTrigger(startP->entry);
|
||||||
|
if (!at)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsPointInAreaTrigger(at, startPos.GetMapId(),
|
||||||
|
startPos.GetPositionX(),
|
||||||
|
startPos.GetPositionY(),
|
||||||
|
startPos.GetPositionZ(), 0.5f))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cutTo(*startP, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static portal (game-object spellcaster): interact when in range.
|
||||||
|
if (startP->type == PathNodeType::NODE_STATIC_PORTAL &&
|
||||||
|
startPos.distance(startP->point) < INTERACTION_DISTANCE)
|
||||||
|
{
|
||||||
|
cutTo(*startP, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teleport spell (hearthstone et al.): fire on the next-step marker.
|
||||||
|
if (nextP->type == PathNodeType::NODE_TELEPORT)
|
||||||
|
{
|
||||||
|
cutTo(*nextP, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flight path: interact with flight master when in range.
|
||||||
|
if (startP->type == PathNodeType::NODE_FLIGHTPATH &&
|
||||||
|
startPos.distance(startP->point) < INTERACTION_DISTANCE)
|
||||||
|
{
|
||||||
|
cutTo(*startP, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport boarding/disembark. We don't expose a teleport-vs-walk
|
||||||
|
// toggle yet, so always take the walk-on-board path: cut to dock if
|
||||||
|
// off-transport, traverse to disembark if on-transport.
|
||||||
|
if (startP->type == PathNodeType::NODE_TRANSPORT)
|
||||||
|
{
|
||||||
|
uint32 entry = nextP->entry;
|
||||||
|
|
||||||
|
if (!onTransport)
|
||||||
|
{
|
||||||
|
// prevP = dock, startP = where transport will stop.
|
||||||
|
cutTo(*prevP, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On transport: walk to disembark.
|
||||||
|
for (auto p = startP; p != fullPath.end(); ++p)
|
||||||
|
{
|
||||||
|
if (p->type != PathNodeType::NODE_TRANSPORT ||
|
||||||
|
(p->entry && p->entry != entry))
|
||||||
|
{
|
||||||
|
cutTo(*p, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
prevP = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist, Unit* bot)
|
bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist, Unit* bot)
|
||||||
{
|
{
|
||||||
if (GetPath().empty())
|
if (GetPath().empty())
|
||||||
|
|||||||
@ -498,6 +498,13 @@ public:
|
|||||||
// area-trigger node once the bot reaches it.
|
// area-trigger node once the bot reaches it.
|
||||||
bool cutTo(PathNodePoint point, bool including);
|
bool cutTo(PathNodePoint point, bool including);
|
||||||
|
|
||||||
|
// Returns true if the next reachable segment is a special-handling
|
||||||
|
// node (portal / area-trigger / transport / flightpath / teleport)
|
||||||
|
// and the bot is close enough / positioned right to handle it now.
|
||||||
|
// Trims the path up to that segment as a side effect. Caller then
|
||||||
|
// dispatches the matching special-movement handler on the new head.
|
||||||
|
bool UpcommingSpecialMovement(WorldPosition startPos, float maxDist, bool onTransport);
|
||||||
|
|
||||||
// Reject paths the navmesh accepts but a player can't walk:
|
// Reject paths the navmesh accepts but a player can't walk:
|
||||||
// 2-point shortcut over 5y, or > 10y vertical drop with slope steeper than 2:1.
|
// 2-point shortcut over 5y, or > 10y vertical drop with slope steeper than 2:1.
|
||||||
static bool IsPathCheating(std::vector<WorldPosition> const& path,
|
static bool IsPathCheating(std::vector<WorldPosition> const& path,
|
||||||
@ -506,6 +513,24 @@ public:
|
|||||||
std::ostringstream const print();
|
std::ostringstream const print();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Returns the next-best-point iterator within maxDist from startPos:
|
||||||
|
// skips waypoints behind the bot, advances while shouldMoveToNextPoint
|
||||||
|
// allows, projects onto current segment to decide if the bot has
|
||||||
|
// already passed it.
|
||||||
|
std::vector<PathNodePoint>::iterator getNextPoint(WorldPosition startPos,
|
||||||
|
float maxDist,
|
||||||
|
bool onTransport);
|
||||||
|
|
||||||
|
// Heuristic for getNextPoint: decides whether the iterator should
|
||||||
|
// step forward to nextP. Stops at special nodes (area triggers,
|
||||||
|
// portals, transports, flight paths), at map boundaries, and when
|
||||||
|
// accumulated distance exceeds maxDist.
|
||||||
|
bool shouldMoveToNextPoint(WorldPosition startPos,
|
||||||
|
std::vector<PathNodePoint>::iterator beg,
|
||||||
|
std::vector<PathNodePoint>::iterator ed,
|
||||||
|
std::vector<PathNodePoint>::iterator p,
|
||||||
|
float& moveDist, float maxDist);
|
||||||
|
|
||||||
std::vector<PathNodePoint> fullPath;
|
std::vector<PathNodePoint> fullPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user