mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-20 09:17:36 +01:00
Core/Movement: inital work on chase movement rewrite
* chase movement will now launch within a sniffed time interval * the movement will now use a predicting method to get a estimated chase destination to avoid falling behind the chase target * ChaseRange has been dropped and stepping backward has now a own implementation within the movement generator. Todo: analyzing sniffs for the correct time interval
This commit is contained in:
@@ -198,7 +198,7 @@ void ArcherAI::AttackStart(Unit* who)
|
||||
if (me->IsWithinCombatRange(who, m_minRange))
|
||||
{
|
||||
if (me->Attack(who, true) && !who->IsFlying())
|
||||
me->GetMotionMaster()->MoveChase(who);
|
||||
me->GetMotionMaster()->MoveChase(who, 0.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -491,7 +491,7 @@ void PetAI::DoAttack(Unit* target, bool chase)
|
||||
|
||||
// Pets with ranged attacks should not care about the chase angle at all.
|
||||
ChaseAngle angle = ChaseAngle(chaseDistance == 0.f ? float(M_PI) : 0.f, chaseDistance == 0.f ? float(M_PI_4) : float(M_PI * 2));
|
||||
me->GetMotionMaster()->MoveChase(target, ChaseRange(0.f, chaseDistance), angle);
|
||||
me->GetMotionMaster()->MoveChase(target, chaseDistance, angle);
|
||||
}
|
||||
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
|
||||
{
|
||||
|
||||
@@ -263,7 +263,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, ChaseAngle angle, bool a
|
||||
Mutate(new FollowMovementGenerator(target, dist, angle, allignToTargetSpeed), slot);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveChase(Unit* target, Optional<ChaseRange> dist, Optional<ChaseAngle> angle)
|
||||
void MotionMaster::MoveChase(Unit* target, float dist, Optional<ChaseAngle> angle)
|
||||
{
|
||||
// ignore movement request if target not exist
|
||||
if (!target || target == _owner)
|
||||
|
||||
@@ -89,19 +89,6 @@ enum RotateDirection
|
||||
ROTATE_DIRECTION_RIGHT
|
||||
};
|
||||
|
||||
struct ChaseRange
|
||||
{
|
||||
ChaseRange(float range) : MinRange(range > CONTACT_DISTANCE ? 0 : range - CONTACT_DISTANCE), MinTolerance(range), MaxRange(range + CONTACT_DISTANCE), MaxTolerance(range) {}
|
||||
ChaseRange(float min, float max) : MinRange(min), MinTolerance(std::min(min + CONTACT_DISTANCE, (min + max) / 2)), MaxRange(max), MaxTolerance(std::max(max - CONTACT_DISTANCE, MinTolerance)) {}
|
||||
ChaseRange(float min, float tMin, float tMax, float max) : MinRange(min), MinTolerance(tMin), MaxRange(max), MaxTolerance(tMax) {}
|
||||
|
||||
// this contains info that informs how we should path!
|
||||
float MinRange; // we have to move if we are within this range... (min. attack range)
|
||||
float MinTolerance; // ...and if we are, we will move this far away
|
||||
float MaxRange; // we have to move if we are outside this range... (max. attack range)
|
||||
float MaxTolerance; // ...and if we are, we will move into this range
|
||||
};
|
||||
|
||||
struct ChaseAngle
|
||||
{
|
||||
ChaseAngle(float angle, float tol = M_PI_4) : RelativeAngle(Position::NormalizeOrientation(angle)), Tolerance(tol) {}
|
||||
@@ -161,9 +148,10 @@ class TC_GAME_API MotionMaster
|
||||
void MoveTargetedHome();
|
||||
void MoveRandom(float spawndist = 0.0f);
|
||||
void MoveFollow(Unit* target, float dist, ChaseAngle angle, bool allignToTargetSpeed = false, MovementSlot slot = MOTION_SLOT_ACTIVE);
|
||||
void MoveChase(Unit* target, Optional<ChaseRange> dist = {}, Optional<ChaseAngle> angle = {});
|
||||
void MoveChase(Unit* target, float dist, float angle) { MoveChase(target, ChaseRange(dist), ChaseAngle(angle)); }
|
||||
void MoveChase(Unit* target, float dist) { MoveChase(target, ChaseRange(dist)); }
|
||||
|
||||
void MoveChase(Unit* target, float dist = 0.f, Optional<ChaseAngle> angle = {});
|
||||
void MoveChase(Unit* target, float dist, float angle) { MoveChase(target, dist, ChaseAngle(angle)); }
|
||||
|
||||
void MoveConfused();
|
||||
void MoveFleeing(Unit* enemy, uint32 time = 0);
|
||||
void MovePoint(uint32 id, Position const& pos, bool generatePath = true)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "PathGenerator.h"
|
||||
#include "Unit.h"
|
||||
#include "Util.h"
|
||||
#include "Vehicle.h"
|
||||
|
||||
static bool IsMutualChase(Unit* owner, Unit* target)
|
||||
{
|
||||
@@ -41,22 +42,26 @@ static void DoMovementInform(Unit* owner, Unit* target)
|
||||
ai->MovementInform(CHASE_MOTION_TYPE, target->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
static bool PositionOkay(Unit* owner, Unit* target, Optional<float> minDistance, Optional<float> maxDistance, Optional<ChaseAngle> angle)
|
||||
static bool PositionOkay(Unit* owner, Unit* target, float distance, Optional<ChaseAngle> angle)
|
||||
{
|
||||
float const distSq = owner->GetExactDistSq(target);
|
||||
if (minDistance && distSq < square(*minDistance))
|
||||
return false;
|
||||
if (maxDistance && distSq > square(*maxDistance))
|
||||
float const dist = owner->GetExactDist2d(target);
|
||||
|
||||
// owner's distance to its chase target is outside of its range
|
||||
if (dist > distance)
|
||||
return false;
|
||||
|
||||
// owner's relative angle to its target is not within boundaries
|
||||
if (angle && !angle->IsAngleOkay(target->GetRelativeAngle(owner)))
|
||||
return false;
|
||||
|
||||
// owner cannot see its target
|
||||
if (!owner->IsWithinLOSInMap(target))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ChaseMovementGenerator::ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range, Optional<ChaseAngle> angle) : AbstractFollower(ASSERT_NOTNULL(target)), _range(range), _angle(angle) {}
|
||||
ChaseMovementGenerator::ChaseMovementGenerator(Unit* target, float range, Optional<ChaseAngle> angle) : AbstractFollower(ASSERT_NOTNULL(target)), _range(range), _angle(angle) { }
|
||||
ChaseMovementGenerator::~ChaseMovementGenerator() = default;
|
||||
|
||||
void ChaseMovementGenerator::Initialize(Unit* owner)
|
||||
@@ -64,6 +69,8 @@ void ChaseMovementGenerator::Initialize(Unit* owner)
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
owner->SetWalk(false);
|
||||
_lastTargetPosition.reset();
|
||||
_nextMovementTimer.Reset(0);
|
||||
_nextRepositioningTimer.Reset(0);
|
||||
}
|
||||
|
||||
bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
@@ -92,107 +99,81 @@ bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
}
|
||||
|
||||
bool const mutualChase = IsMutualChase(owner, target);
|
||||
float const hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
|
||||
float const minRange = _range ? _range->MinRange + hitboxSum : CONTACT_DISTANCE;
|
||||
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum;
|
||||
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes
|
||||
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum;
|
||||
Optional<ChaseAngle> angle = mutualChase ? Optional<ChaseAngle>() : _angle;
|
||||
//float const hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
|
||||
float const chaseRange = owner->GetCombatReach() + target->GetCombatReach();
|
||||
float const rangeTolerance = _range > 0.f ? _range : chaseRange;
|
||||
Optional<ChaseAngle> chaseAngle = mutualChase ? Optional<ChaseAngle>() : _angle;
|
||||
|
||||
// if we're already moving, periodically check if we're already in the expected range...
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
|
||||
{
|
||||
if (_rangeCheckTimer > diff)
|
||||
_rangeCheckTimer -= diff;
|
||||
else
|
||||
{
|
||||
_rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
if (PositionOkay(owner, target, _movingTowards ? Optional<float>() : minTarget, _movingTowards ? maxTarget : Optional<float>(), angle))
|
||||
{
|
||||
_path = nullptr;
|
||||
owner->StopMoving();
|
||||
owner->SetInFront(target);
|
||||
DoMovementInform(owner, target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're done moving, we want to clean up
|
||||
// We are done moving. Trigger movement inform hook and clear chase move state
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
_path = nullptr;
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
owner->SetInFront(target);
|
||||
DoMovementInform(owner, target);
|
||||
}
|
||||
|
||||
// if the target moved, we have to consider whether to adjust
|
||||
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.get() || mutualChase != _mutualChase)
|
||||
// Update Movement
|
||||
_nextMovementTimer.Update(diff);
|
||||
_nextRepositioningTimer.Update(diff);
|
||||
|
||||
// Handle repositioning and scattering arround target
|
||||
if (_nextRepositioningTimer.Passed())
|
||||
{
|
||||
_lastTargetPosition = target->GetPosition();
|
||||
_mutualChase = mutualChase;
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, minRange, maxRange, angle))
|
||||
_nextRepositioningTimer.Reset(REPOSITION_MOVEMENT_INTERVAL);
|
||||
|
||||
if (!owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->IsCreature())
|
||||
{
|
||||
Creature* const cOwner = owner->ToCreature();
|
||||
// can we get to the target?
|
||||
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
||||
// Owner is too close to its target. Step back.
|
||||
if (owner->GetExactDist2d(target) < owner->GetBoundaryRadius())
|
||||
{
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
cOwner->StopMoving();
|
||||
_path = nullptr;
|
||||
LaunchMovement(owner, target, chaseRange, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_nextMovementTimer.Passed())
|
||||
{
|
||||
_nextMovementTimer.Reset(CHASE_MOVEMENT_INTERVAL);
|
||||
|
||||
// Target has moved since we last checked its position. Handle new cases
|
||||
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.get())
|
||||
{
|
||||
// Create new snapshot of our target's position
|
||||
_lastTargetPosition = target->GetPosition();
|
||||
|
||||
Creature* creature = owner->ToCreature();
|
||||
// Owner cannot reach target (example: target is in water and owner cannot swim)
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && creature)
|
||||
{
|
||||
if (!target->isInAccessiblePlaceFor(creature))
|
||||
{
|
||||
creature->SetCannotReachTarget(true);
|
||||
creature->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (creature && !target->isInAccessiblePlaceFor(creature))
|
||||
{
|
||||
creature->SetCannotReachTarget(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// figure out which way we want to move
|
||||
bool const moveToward = !owner->IsInDist(target, maxRange);
|
||||
|
||||
// make a new path if we have to...
|
||||
if (!_path || moveToward != _movingTowards)
|
||||
_path = std::make_unique<PathGenerator>(owner);
|
||||
|
||||
float x, y, z;
|
||||
bool shortenPath;
|
||||
// if we want to move toward the target and there's no fixed angle...
|
||||
if (moveToward && !angle)
|
||||
bool isMoving = target->HasUnitMovementFlag(MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD) || !target->movespline->Finalized();
|
||||
if (PositionOkay(owner, target, rangeTolerance, chaseAngle) && !isMoving)
|
||||
{
|
||||
// ...we'll pathfind to the center, then shorten the path
|
||||
target->GetPosition(x, y, z);
|
||||
shortenPath = true;
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
|
||||
{
|
||||
// Just stopping our movement, movementinform and cleanups will be executed in the code above on the next update tick
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, we fall back to nearpoint finding
|
||||
target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, angle ? target->NormalizeOrientation(target->GetOrientation() - angle->RelativeAngle) : target->GetAngle(owner));
|
||||
shortenPath = false;
|
||||
}
|
||||
|
||||
if (owner->IsHovering())
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
|
||||
bool success = _path->CalculatePath(x, y, z, owner->CanFly());
|
||||
if (!success || (_path->GetPathType() & (PATHFIND_NOPATH /* | PATHFIND_INCOMPLETE*/)))
|
||||
{
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortenPath)
|
||||
_path->ShortenPathUntilDist(PositionToVector3(target), maxTarget);
|
||||
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_path->GetPath());
|
||||
init.SetWalk(false);
|
||||
init.SetFacing(target);
|
||||
init.Launch();
|
||||
else if (owner->GetExactDist2d(target) > rangeTolerance + 0.1f) // 0.1f here to avoid edge cases when the owner has stepped back before
|
||||
LaunchMovement(owner, target, chaseRange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,3 +187,63 @@ void ChaseMovementGenerator::Finalize(Unit* owner)
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
void ChaseMovementGenerator::LaunchMovement(Unit* owner, Unit* target, float chaseRange, bool backward /*= false*/)
|
||||
{
|
||||
// Owner may launch a new spline
|
||||
Position dest = target->GetVehicle() ? target->GetVehicle()->GetBase()->GetPosition() : target->GetPosition();
|
||||
|
||||
// Player chase target is currently moving
|
||||
bool predictDestination = target->HasUnitMovementFlag(MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
|
||||
|
||||
if (!backward && predictDestination)
|
||||
{
|
||||
UnitMoveType moveType = MOVE_RUN;
|
||||
if (target->CanFly())
|
||||
moveType = target->HasUnitMovementFlag(MOVEMENTFLAG_BACKWARD) ? MOVE_FLIGHT_BACK : MOVE_FLIGHT;
|
||||
else
|
||||
{
|
||||
if (target->IsWalking())
|
||||
moveType = MOVE_WALK;
|
||||
else
|
||||
moveType = target->HasUnitMovementFlag(MOVEMENTFLAG_BACKWARD) ? MOVE_RUN_BACK : MOVE_RUN;
|
||||
}
|
||||
|
||||
float additionalRange = target->GetSpeed(moveType) * 0.5f;
|
||||
|
||||
target->MovePositionToFirstCollision(dest, additionalRange, _angle ? _angle->RelativeAngle : target->GetRelativeAngle(owner) + float(M_PI));
|
||||
}
|
||||
else
|
||||
target->MovePositionToFirstCollision(dest, chaseRange, _angle ? _angle->RelativeAngle : target->GetRelativeAngle(owner));
|
||||
|
||||
owner->UpdateAllowedPositionZ(dest.GetPositionX(), dest.GetPositionY(), dest.m_positionZ);
|
||||
|
||||
Creature* creature = owner->ToCreature();
|
||||
|
||||
PathGenerator path(owner);
|
||||
bool success = path.CalculatePath(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), owner->CanFly());
|
||||
if (!success || (path.GetPathType() & (PATHFIND_NOPATH /*| PATHFIND_INCOMPLETE*/)))
|
||||
{
|
||||
if (creature)
|
||||
creature->SetCannotReachTarget(true);
|
||||
|
||||
owner->StopMoving();
|
||||
return;
|
||||
}
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(path.GetPath());
|
||||
init.SetWalk(false);
|
||||
if (backward)
|
||||
init.SetBackward();
|
||||
else
|
||||
init.SetFacing(target);
|
||||
|
||||
init.Launch();
|
||||
|
||||
if (!backward)
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
|
||||
if (creature)
|
||||
creature->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "AbstractFollower.h"
|
||||
#include "MovementGenerator.h"
|
||||
#include "Optional.h"
|
||||
#include "Timer.h"
|
||||
|
||||
class PathGenerator;
|
||||
class Unit;
|
||||
@@ -30,7 +31,7 @@ class ChaseMovementGenerator : public MovementGenerator, public AbstractFollower
|
||||
public:
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; }
|
||||
|
||||
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {});
|
||||
ChaseMovementGenerator(Unit* target, float range, Optional<ChaseAngle> angle = {});
|
||||
~ChaseMovementGenerator();
|
||||
|
||||
void Initialize(Unit* owner) override;
|
||||
@@ -41,16 +42,17 @@ class ChaseMovementGenerator : public MovementGenerator, public AbstractFollower
|
||||
void UnitSpeedChanged() override { _lastTargetPosition.reset(); }
|
||||
|
||||
private:
|
||||
static constexpr uint32 RANGE_CHECK_INTERVAL = 100; // time (ms) until we attempt to recalculate
|
||||
void LaunchMovement(Unit* owner, Unit* target, float chaseRange, bool backward = false);
|
||||
|
||||
Optional<ChaseRange> const _range;
|
||||
Optional<ChaseAngle> const _angle;
|
||||
static constexpr uint32 CHASE_MOVEMENT_INTERVAL = 400; // sniffed value (1 batch update cyclice)
|
||||
static constexpr uint32 REPOSITION_MOVEMENT_INTERVAL = 1200; // (3 batch update cycles) TODO: verify
|
||||
|
||||
TimeTrackerSmall _nextMovementTimer;
|
||||
TimeTrackerSmall _nextRepositioningTimer;
|
||||
|
||||
std::unique_ptr<PathGenerator> _path;
|
||||
Optional<Position> _lastTargetPosition;
|
||||
uint32 _rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
bool _movingTowards = true;
|
||||
bool _mutualChase = true;
|
||||
float const _range;
|
||||
Optional<ChaseAngle> const _angle;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user