mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
feat(Core/Travel): Port TravelPath::ClipPath; call from MoveFarTo, drop inline clip
This commit is contained in:
parent
8a3a91070b
commit
a24e1b033c
@ -125,6 +125,12 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||
if (onTransport)
|
||||
return false;
|
||||
|
||||
// ClipPath — truncate at first hostile creature in range / non-walkable
|
||||
// hop / drifted past reactDistance / > 125 sqDist jump.
|
||||
path.ClipPath(botAI, bot, false);
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
// Walk dispatch.
|
||||
std::vector<WorldPosition> const& pts = path.getPointPath();
|
||||
Movement::PointsArray points;
|
||||
@ -164,42 +170,8 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
||||
for (auto& pt : points)
|
||||
bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z);
|
||||
|
||||
// ClipPath — truncate path at first hostile creature within its
|
||||
// own attack range. Skipped while in combat or dead.
|
||||
if (botAI->GetState() != BOT_STATE_COMBAT && bot->IsAlive())
|
||||
{
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
if (!targets.empty())
|
||||
{
|
||||
size_t clipAt = points.size();
|
||||
for (size_t i = 0; i < points.size() && clipAt == points.size(); ++i)
|
||||
{
|
||||
for (ObjectGuid const& guid : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
Creature* cre = unit->ToCreature();
|
||||
if (!cre)
|
||||
continue;
|
||||
if (unit->GetLevel() > bot->GetLevel() + 5)
|
||||
continue;
|
||||
float range = cre->GetAttackDistance(bot);
|
||||
float dx = unit->GetPositionX() - points[i].x;
|
||||
float dy = unit->GetPositionY() - points[i].y;
|
||||
float dz = unit->GetPositionZ() - points[i].z;
|
||||
if (dx * dx + dy * dy + dz * dz > range * range)
|
||||
continue;
|
||||
if (!unit->IsWithinLOSInMap(bot))
|
||||
continue;
|
||||
clipAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clipAt < points.size() && clipAt + 1 < points.size())
|
||||
points.erase(points.begin() + clipAt + 1, points.end());
|
||||
}
|
||||
}
|
||||
// ClipPath now runs at MoveFarTo level on the TravelPath before the
|
||||
// points array is built. No per-dispatch clip here.
|
||||
|
||||
if (points.size() < 2)
|
||||
return false;
|
||||
|
||||
@ -956,6 +956,75 @@ bool TravelPath::UpcommingSpecialMovement(WorldPosition startPos,
|
||||
return false;
|
||||
}
|
||||
|
||||
void TravelPath::ClipPath(PlayerbotAI* ai, Unit* mover, bool ignoreEnemyTargets)
|
||||
{
|
||||
auto startP = getNextPoint(WorldPosition(mover), 0.0f, false);
|
||||
cutTo(*startP, false);
|
||||
|
||||
if (startP == fullPath.end())
|
||||
return;
|
||||
|
||||
GuidVector targets;
|
||||
Player* bot = ai ? ai->GetBot() : nullptr;
|
||||
if (bot && ai->GetState() != BOT_STATE_COMBAT && !bot->isDead() && !ignoreEnemyTargets)
|
||||
targets = AI_VALUE(GuidVector, "possible targets");
|
||||
|
||||
auto endP = fullPath.end();
|
||||
auto prevP = fullPath.begin();
|
||||
float const reactSq = sPlayerbotAIConfig.reactDistance * sPlayerbotAIConfig.reactDistance;
|
||||
|
||||
for (auto p = fullPath.begin(); p != fullPath.end(); ++p)
|
||||
{
|
||||
// Hostile-target check: stop before walking into a mob that
|
||||
// would aggro. Level-capped (mover->level + 5) so over-level
|
||||
// mobs we'd avoid anyway are ignored.
|
||||
for (ObjectGuid const& targetGuid : targets)
|
||||
{
|
||||
if (!targetGuid.IsCreature())
|
||||
continue;
|
||||
Unit* unit = ai->GetUnit(targetGuid);
|
||||
if (!unit || unit->isDead())
|
||||
continue;
|
||||
if (unit->GetLevel() > mover->GetLevel() + 5)
|
||||
continue;
|
||||
Creature* cre = unit->ToCreature();
|
||||
if (!cre)
|
||||
continue;
|
||||
float const range = cre->GetAttackDistance(mover);
|
||||
if (WorldPosition(unit).sqDistance(p->point) > range * range)
|
||||
continue;
|
||||
if (!unit->IsHostileTo(mover) || !unit->IsWithinLOSInMap(mover))
|
||||
continue;
|
||||
|
||||
endP = p;
|
||||
break;
|
||||
}
|
||||
if (endP != fullPath.end())
|
||||
break;
|
||||
|
||||
// Reject paths that drift past reactDistance from the start —
|
||||
// a sign the path looped or wandered.
|
||||
if (p->point.sqDistance(fullPath.begin()->point) > reactSq)
|
||||
endP = p;
|
||||
// Non-walkable hop in the middle (portal/transport/etc.) terminates.
|
||||
else if (!p->isWalkable())
|
||||
endP = p;
|
||||
// Gap between adjacent points > ~11y (sqDist 125) — likely bad data.
|
||||
else if (p->point.sqDistance(prevP->point) > 125.0f)
|
||||
endP = prevP;
|
||||
|
||||
if (endP != fullPath.end())
|
||||
break;
|
||||
|
||||
prevP = p;
|
||||
}
|
||||
|
||||
if (endP == fullPath.end())
|
||||
return;
|
||||
|
||||
fullPath.erase(std::next(endP), fullPath.end());
|
||||
}
|
||||
|
||||
bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist, Unit* bot)
|
||||
{
|
||||
if (GetPath().empty())
|
||||
|
||||
@ -507,6 +507,13 @@ public:
|
||||
// dispatches the matching special-movement handler on the new head.
|
||||
bool UpcommingSpecialMovement(WorldPosition startPos, float maxDist, bool onTransport);
|
||||
|
||||
// Truncate the path at the first waypoint that would put the bot in
|
||||
// range of a hostile creature (within attack range, in LOS, level-cap
|
||||
// sane), at a non-walkable hop, after drifting beyond reactDistance
|
||||
// from the start, or across a > 125-sqDist jump. Set ignoreEnemyTargets
|
||||
// to suppress the hostile-target check (used by combat repositioning).
|
||||
void ClipPath(PlayerbotAI* ai, Unit* mover, bool ignoreEnemyTargets = false);
|
||||
|
||||
// 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.
|
||||
static bool IsPathCheating(std::vector<WorldPosition> const& path,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user