mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-21 16:09:26 +02:00
Compare commits
No commits in common. "6cf10c505775a3d56b9397bffde7a808e2ba6b00" and "ba1b429bc54fbc5d4a69d7430301ea94cee59b81" have entirely different histories.
6cf10c5057
...
ba1b429bc5
@ -3142,29 +3142,6 @@ bool MovementAction::LaunchWalkSpline(TravelPlan& state)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sparse-segment clip (cmangos parity): truncate the chain at the
|
|
||||||
// first segment longer than ~11.18y. Spline interpolation between
|
|
||||||
// sparse waypoints can cut corners through visual obstacles (trees,
|
|
||||||
// walls) the navmesh routed around. Bot re-plans from a closer
|
|
||||||
// position next tick where the resolved poly chain is denser.
|
|
||||||
{
|
|
||||||
constexpr float SPARSE_SEG_SQ = 125.0f; // sqrt(125) ≈ 11.18y
|
|
||||||
for (size_t i = 1; i < state.walkPoints.size(); ++i)
|
|
||||||
{
|
|
||||||
G3D::Vector3 d = state.walkPoints[i] - state.walkPoints[i - 1];
|
|
||||||
if (d.squaredLength() > SPARSE_SEG_SQ)
|
|
||||||
{
|
|
||||||
state.walkPoints.resize(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-clamp cached waypoints to current valid Z. Rows in
|
// Re-clamp cached waypoints to current valid Z. Rows in
|
||||||
// playerbots_travelnode_path store absolute coords baked at
|
// playerbots_travelnode_path store absolute coords baked at
|
||||||
// offline generation; if the live navmesh has shifted since
|
// offline generation; if the live navmesh has shifted since
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
#include "NewRpgBaseAction.h"
|
#include "NewRpgBaseAction.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "BroadcastHelper.h"
|
#include "BroadcastHelper.h"
|
||||||
@ -221,21 +220,6 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
stepDest.GetPositionY(), stepDest.GetPositionZ());
|
stepDest.GetPositionY(), stepDest.GetPositionZ());
|
||||||
if (endDistToDest + 5.0f < disToDest)
|
if (endDistToDest + 5.0f < disToDest)
|
||||||
{
|
{
|
||||||
// Z gap check: if the probe's last waypoint is well below
|
|
||||||
// the requested destination Z, the chain walked the ground
|
|
||||||
// polygon graph toward an elevated target it can't reach
|
|
||||||
// (quest giver on top of Aldrassil etc.). Refuse to dispatch
|
|
||||||
// — bot waits instead of tunneling into the visual model.
|
|
||||||
// 10y tolerates normal terrain variation (ramp ends, hill
|
|
||||||
// tops) while still catching clearly unreachable elevations.
|
|
||||||
if (std::fabs(stepDest.GetPositionZ() - dest.GetPositionZ()) > 10.0f)
|
|
||||||
{
|
|
||||||
EmitDebugMove("MoveFar", "z-mismatch",
|
|
||||||
dest.GetPositionX(), dest.GetPositionY(),
|
|
||||||
dest.GetPositionZ());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Movement::PointsArray points;
|
Movement::PointsArray points;
|
||||||
points.reserve(probe.size());
|
points.reserve(probe.size());
|
||||||
for (auto const& wp : probe)
|
for (auto const& wp : probe)
|
||||||
@ -294,57 +278,6 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
|||||||
if (points.size() < 2)
|
if (points.size() < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Prefix trim (cmangos parity: makeShortCut on every dispatch).
|
|
||||||
// Drop leading waypoints behind the bot's current position so the
|
|
||||||
// spline begins from where the bot actually is, not from a stale
|
|
||||||
// planner-start. Picks the waypoint closest to the bot in 3D and
|
|
||||||
// erases everything before it.
|
|
||||||
{
|
|
||||||
float const bx = bot->GetPositionX();
|
|
||||||
float const by = bot->GetPositionY();
|
|
||||||
float const bz = bot->GetPositionZ();
|
|
||||||
float minSq = std::numeric_limits<float>::max();
|
|
||||||
size_t closest = 0;
|
|
||||||
for (size_t i = 0; i < points.size(); ++i)
|
|
||||||
{
|
|
||||||
float dx = points[i].x - bx;
|
|
||||||
float dy = points[i].y - by;
|
|
||||||
float dz = points[i].z - bz;
|
|
||||||
float sq = dx * dx + dy * dy + dz * dz;
|
|
||||||
if (sq < minSq)
|
|
||||||
{
|
|
||||||
minSq = sq;
|
|
||||||
closest = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (closest > 0)
|
|
||||||
points.erase(points.begin(), points.begin() + closest);
|
|
||||||
if (points.size() < 2)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sparse-segment clip (cmangos parity): if any consecutive segment
|
|
||||||
// is longer than ~11.18y, truncate the path at that point. Short,
|
|
||||||
// dense waypoints reduce spline interpolation across visual
|
|
||||||
// obstacles between sparse points; bot re-plans from a closer
|
|
||||||
// position next tick.
|
|
||||||
{
|
|
||||||
constexpr float SPARSE_SEG_SQ = 125.0f; // sqrt(125) ≈ 11.18y
|
|
||||||
for (size_t i = 1; i < points.size(); ++i)
|
|
||||||
{
|
|
||||||
float dx = points[i].x - points[i - 1].x;
|
|
||||||
float dy = points[i].y - points[i - 1].y;
|
|
||||||
float dz = points[i].z - points[i - 1].z;
|
|
||||||
if (dx * dx + dy * dy + dz * dz > SPARSE_SEG_SQ)
|
|
||||||
{
|
|
||||||
points.resize(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (points.size() < 2)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOS gate: reject paths whose segments pass through visual
|
// LOS gate: reject paths whose segments pass through visual
|
||||||
// geometry. mmap is blind to M2 models (trees, decorative props)
|
// geometry. mmap is blind to M2 models (trees, decorative props)
|
||||||
// and will route through them; vmap LOS catches the cases that
|
// and will route through them; vmap LOS catches the cases that
|
||||||
@ -540,61 +473,32 @@ bool NewRpgBaseAction::MoveWorldObjectTo(ObjectGuid guid, float distance)
|
|||||||
if (!object)
|
if (!object)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Map* map = bot->GetMap();
|
float x = object->GetPositionX();
|
||||||
if (!map)
|
float y = object->GetPositionY();
|
||||||
return false;
|
|
||||||
|
|
||||||
// 8-angle deterministic iteration around the target. For each angle,
|
|
||||||
// validate the candidate against the navmesh with a strict ground-only
|
|
||||||
// filter (NAV_GROUND, exclude STEEP/WATER/MAGMA/SLIME). Reject if no
|
|
||||||
// valid poly within 5y XY+Z or if the snap drifts the Z by >10y.
|
|
||||||
// First angle that passes both LOS and navmesh-snap wins.
|
|
||||||
dtNavMeshQuery const* navMeshQuery =
|
|
||||||
map->GetMapCollisionData().GetMMapData().GetNavMeshQuery();
|
|
||||||
float const baseAngle = object->GetAngle(bot);
|
|
||||||
|
|
||||||
for (float step = 0.0f; step < 2.0f * static_cast<float>(M_PI);
|
|
||||||
step += static_cast<float>(M_PI) / 4.0f)
|
|
||||||
{
|
|
||||||
float const angle = baseAngle + step;
|
|
||||||
float x = object->GetPositionX() + std::cos(angle) * distance;
|
|
||||||
float y = object->GetPositionY() + std::sin(angle) * distance;
|
|
||||||
float z = object->GetPositionZ();
|
float z = object->GetPositionZ();
|
||||||
|
float angle = 0.f;
|
||||||
|
|
||||||
// LOS check at eye height.
|
if (!object->ToUnit() || !object->ToUnit()->isMoving())
|
||||||
if (!bot->IsWithinLOS(x, y, z + bot->GetCollisionHeight()))
|
angle = object->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target
|
||||||
continue;
|
else
|
||||||
|
angle = object->GetOrientation() +
|
||||||
|
(M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement)
|
||||||
|
|
||||||
// Strict navmesh-snap validation (cmangos ClosestCorrectPoint port).
|
// Bias toward the full radius so the bot stops next to the object,
|
||||||
if (navMeshQuery)
|
// not on top of it. Uniform rnd would put dest anywhere in [0, distance].
|
||||||
|
float rnd = 0.85f + 0.15f * rand_norm();
|
||||||
|
x += cos(angle) * distance * rnd;
|
||||||
|
y += sin(angle) * distance * rnd;
|
||||||
|
if (!object->GetMap()->CheckCollisionAndGetValidCoords(object, object->GetPositionX(), object->GetPositionY(),
|
||||||
|
object->GetPositionZ(), x, y, z))
|
||||||
{
|
{
|
||||||
dtQueryFilter filter;
|
x = object->GetPositionX();
|
||||||
filter.setIncludeFlags(NAV_GROUND);
|
y = object->GetPositionY();
|
||||||
filter.setExcludeFlags(NAV_GROUND_STEEP | NAV_WATER | NAV_MAGMA | NAV_SLIME);
|
z = object->GetPositionZ();
|
||||||
|
|
||||||
float const point[VERTEX_SIZE] = { y, z, x };
|
|
||||||
float const extents[VERTEX_SIZE] = { 5.0f, 5.0f, 5.0f };
|
|
||||||
float closest[VERTEX_SIZE] = { 0.0f, 0.0f, 0.0f };
|
|
||||||
dtPolyRef polyRef = INVALID_POLYREF;
|
|
||||||
|
|
||||||
if (!dtStatusSucceed(navMeshQuery->findNearestPoly(
|
|
||||||
point, extents, &filter, &polyRef, closest)) ||
|
|
||||||
polyRef == INVALID_POLYREF)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float const snappedZ = closest[1];
|
|
||||||
if (std::fabs(snappedZ - z) > 10.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
x = closest[2];
|
|
||||||
y = closest[0];
|
|
||||||
z = snappedZ;
|
|
||||||
}
|
}
|
||||||
|
// Route through MoveFarTo so every approach gets the full probe
|
||||||
|
// + travel-node fallback (and a precise debug label).
|
||||||
return MoveFarTo(WorldPosition(object->GetMapId(), x, y, z));
|
return MoveFarTo(WorldPosition(object->GetMapId(), x, y, z));
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority, WorldObject* center)
|
bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority, WorldObject* center)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user