Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save zabustak/cb2e26822a5908d8622277416ed10f96 to your computer and use it in GitHub Desktop.
Save zabustak/cb2e26822a5908d8622277416ed10f96 to your computer and use it in GitHub Desktop.
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index 1dd66d7699..8683884b25 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -350,6 +350,40 @@ void CreatureAI::SetBoundary(CreatureBoundary const* boundary, bool negateBounda
me->DoImmediateBoundaryCheck();
}
+bool CreatureAI::CheckMeleeRepositionRequirements()
+{
+ if (Unit* victim = me->GetVictim())
+ {
+ Position victimPos = victim->GetPosition();
+
+ // If we are closer than 50% of the combat reach we are going to reposition ourself
+ // Dont call if we have more than one attacker because Circling Target will take place.
+ float reach = CalculatePct(me->GetCombatReach() + victim->GetCombatReach(), 50);
+ float moveDist = CalculatePct(me->GetCombatReach() + victim->GetCombatReach(), 100);
+ if (me->IsFreeToMove() && victim->getAttackers().size() == 1 && me->GetDistance(victimPos) < reach)
+ {
+ me->GetMotionMaster()->MoveBackwards(victim, moveDist);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CreatureAI::CheckCircleRepositionRequirements()
+{
+ if (Unit* victim = me->GetVictim())
+ {
+ Position victimPos = victim->GetPosition();
+
+ if (me->IsFreeToMove() && victim->getAttackers().size() > 1)
+ {
+ me->GetMotionMaster()->MoveCircleTarget(victim);
+ return true;
+ }
+ }
+ return false;
+}
+
Creature* CreatureAI::DoSummon(uint32 entry, Position const& pos, uint32 despawnTime, TempSummonType summonType)
{
return me->SummonCreature(entry, pos, summonType, despawnTime);
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index d7cfec7b0b..25284740d3 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -226,6 +226,9 @@ class TC_GAME_API CreatureAI : public UnitAI
static bool IsInBounds(CreatureBoundary const& boundary, Position const* who);
bool IsInBoundary(Position const* who = nullptr) const;
+ bool CheckMeleeRepositionRequirements();
+ bool CheckCircleRepositionRequirements();
+
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 0ac2b3be02..6a0b813a15 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -779,6 +779,25 @@ void Creature::Update(uint32 diff)
m_boundaryCheckTime -= diff;
}
+ // Moving back from the target.
+ if (diff >= m_moveBackMovementTime)
+ {
+ AI()->CheckMeleeRepositionRequirements();
+ m_moveBackMovementTime = MOVE_BACK_CHECK_INTERVAL;
+ }
+ else
+ m_moveBackMovementTime -= diff;
+
+ // Circling the target.
+ if (diff >= m_moveCircleMovementTime)
+ {
+ AI()->CheckCircleRepositionRequirements();
+ m_moveCircleMovementTime = MOVE_CIRCLE_CHECK_INTERVAL;
+ }
+ else
+ m_moveCircleMovementTime -= diff;
+
+
// if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
if (m_combatPulseDelay > 0 && IsEngaged() && GetMap()->IsDungeon())
{
@@ -3272,3 +3291,15 @@ bool Creature::IsEscortNPC(bool onlyIfActive)
return AI()->IsEscortNPC(onlyIfActive);
}
+
+bool Creature::IsFreeToMove()
+{
+ uint32 moveFlags = m_movementInfo.GetMovementFlags();
+ // Do not reposition ourself when we are not allowed to move
+ if ((IsMovementPreventedByCasting() || isMoving() || !CanFreeMove()) &&
+ GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE ||
+ moveFlags & MOVEMENTFLAG_SPLINE_ENABLED)
+ return false;
+
+ return true;
+}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 23e567f733..ff1dde1039 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -348,6 +348,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void ReleaseFocus(Spell const* focusSpell = nullptr, bool withDelay = true);
bool IsMovementPreventedByCasting() const override;
+ bool IsFreeToMove();
// Part of Evade mechanics
time_t GetLastDamagedTime() const { return _lastDamagedTime; }
@@ -386,6 +387,11 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse
uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons)
+ static constexpr uint32 MOVE_BACK_CHECK_INTERVAL = 3000;
+ static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
+ uint32 m_moveBackMovementTime = MOVE_BACK_CHECK_INTERVAL;
+ uint32 m_moveCircleMovementTime = MOVE_CIRCLE_CHECK_INTERVAL;
+
ReactStates m_reactState; // for AI, not charmInfo
void RegenerateHealth();
void Regenerate(Powers power);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 09f5ef01a5..1d39fd9cdc 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -287,6 +287,7 @@ bool DispelableAura::RollDispel() const
return roll_chance_i(_chance);
}
+
Unit::Unit(bool isWorldObject) :
WorldObject(isWorldObject), m_playerMovingMe(nullptr), m_lastSanctuaryTime(0),
IsAIEnabled(false), NeedChangeAI(false), LastCharmerGUID(), m_ControlledByPlayer(false),
@@ -294,7 +295,8 @@ Unit::Unit(bool isWorldObject) :
m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0),
i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_vehicle(nullptr),
m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this),
- m_threatManager(this), m_comboTarget(nullptr), m_comboPoints(0), m_spellHistory(new SpellHistory(this))
+ m_threatManager(this), m_comboTarget(nullptr), m_comboPoints(0), m_spellHistory(new SpellHistory(this)),
+ m_attackPosition(NULL)
{
m_objectType |= TYPEMASK_UNIT;
m_objectTypeId = TYPEID_UNIT;
@@ -451,6 +453,16 @@ void Unit::Update(uint32 p_time)
ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, HealthAbovePct(75));
}
+ if (_attackPointCheckTimer > p_time)
+ {
+ _attackPointCheckTimer -= p_time;
+ }
+ else
+ {
+ _attackPointCheckTimer = ATTACK_POINT_CHECK_INTERVAL;
+ SetMeleeAttackPoints();
+ }
+
UpdateSplineMovement(p_time);
i_motionMaster->Update(p_time);
}
@@ -554,6 +566,20 @@ bool Unit::IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const
return distsq <= maxdist * maxdist;
}
+bool Unit::IsWithinRange(Unit const* obj, float dist) const
+{
+ if (!obj || !IsInMap(obj) || !InSamePhase(obj))
+ return false;
+
+ float dx = GetPositionX() - obj->GetPositionX();
+ float dy = GetPositionY() - obj->GetPositionY();
+ float dz = GetPositionZ() - obj->GetPositionZ();
+ float distsq = dx * dx + dy * dy + dz * dz;
+
+ return distsq <= dist * dist;
+}
+
+
float Unit::GetMeleeRange(Unit const* target) const
{
float range = GetCombatReach() + target->GetCombatReach() + 4.0f / 3.0f;
@@ -2005,7 +2031,7 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
if (GetTypeId() == TYPEID_UNIT && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISABLE_TURN))
SetFacingToObject(victim, false); // update client side facing to face the target (prevents visual glitches when casting untargeted spells)
-
+
// melee attack spell cast at main hand attack only - no normal melee dmg dealt
if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL] && !extra)
m_currentSpells[CURRENT_MELEE_SPELL]->cast();
@@ -13501,3 +13527,91 @@ float Unit::GetCollisionHeight() const
float const collisionHeight = scaleMod * modelData->CollisionHeight * modelData->Scale * displayInfo->scale;
return collisionHeight == 0.0f ? DEFAULT_COLLISION_HEIGHT : collisionHeight;
}
+
+
+struct AttackDistance {
+ AttackDistance(uint8 index, AttackPosition attackPos) : _index(index), _attackPos(attackPos) {}
+ uint8 _index;
+ AttackPosition _attackPos;
+};
+
+void Unit::SetMeleeAttackPoints()
+{
+ if (getAttackers().size() <= 1)
+ {
+ return;
+ }
+
+ // Delete the attack positions each iteration.
+ attackMeleePositions.clear();
+
+ // Calculate the attack positions.
+ // radius (Melee range unknown so use default).
+ float radius = MIN_MELEE_REACH;
+ uint8 attackPoints = getAttackers().size() * 3;
+ for (uint8 i = 0; i < attackPoints; ++i)
+ {
+ int8 anglePosition = ceil((i+1)/2) * ((i+1) % 2 ? -1 : 1);
+ float step = float(M_PI*2) / attackPoints;
+ Position const& pos = GetPosition();
+ float angle = GetOrientation() + float(M_PI * 2) + (step * anglePosition);
+ //TC_LOG_DEBUG("server", "AttackPosition (z,y,z)=(%4.1f,%4.1f,%4.1f)", pos.m_positionX + radius * cosf(angle), pos.m_positionY + radius * sinf(angle), pos.m_positionZ);
+ attackMeleePositions.push_back(AttackPosition(Position(pos.m_positionX + radius * cosf(angle), pos.m_positionY + radius * sinf(angle), pos.m_positionZ)));
+ }
+ m_previousAttackerCount = getAttackers().size();
+ m_previousPosition = GetPosition();
+}
+
+Position Unit::GetMeleeAttackPoint(Unit* attacker)
+{
+ if (getAttackers().size() <= 1)
+ return NULL;
+
+ // If already on an attack Position and close to Target abort.
+ if (attacker->GetDistance(GetPosition()) < GetCombatReach() && !(attacker->m_attackPosition == NULL) && attacker->GetDistance(attacker->m_attackPosition._pos) < 0.25f)
+ return NULL;
+ attacker->m_attackPosition = NULL;
+
+ // Get all the distances.
+ std::vector<AttackDistance> distances;
+ distances.reserve( attackMeleePositions.size());
+ for (uint8 i = 0; i < attackMeleePositions.size(); ++i)
+ {
+ // If the spot has been taken.
+ if (!attackMeleePositions[i]._taken)
+ distances.push_back(AttackDistance(i,attackMeleePositions[i]));
+ }
+
+ if (distances.size() == 0)
+ return NULL;
+
+ // Get the shortest point.
+ uint8 shortestIndex = 0;
+ float shortestLength = 100.0f;
+ for (uint8 i = 0; i < distances.size(); ++i)
+ {
+ float dist = attacker->GetDistance2d(distances[i]._attackPos._pos.m_positionX, distances[i]._attackPos._pos.m_positionY); // +GetDistance(distances[i]._attackPos._pos);
+ //TC_LOG_DEBUG("server", "dist=(%4.1f)", dist);
+ if (shortestLength > dist)
+ {
+ shortestLength = dist;
+ shortestIndex = i;
+ }
+ }
+ //TC_LOG_DEBUG("server", "shortestLength=(%4.1f)", shortestLength);
+
+ // Create closest Position.
+ Position closestPos(distances[shortestIndex]._attackPos._pos);
+
+ // Too close mark point taken and find another spot.
+ for (uint8 i = 0; i < attackMeleePositions.size(); ++i)
+ {
+ float dist = closestPos.GetExactDist(attackMeleePositions[i]._pos);
+ //TC_LOG_DEBUG("server", "points_dist=(%4.1f)", dist);
+ if (dist < GetCombatReach()/2)
+ attackMeleePositions[i]._taken = true;
+ }
+ attacker->m_attackPosition = distances[shortestIndex]._attackPos;
+
+ return closestPos;
+}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index dfd1c1e020..6bd4005951 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -702,6 +702,26 @@ struct TC_GAME_API CharmInfo
float _stayZ;
};
+struct AttackPosition {
+ AttackPosition(Position pos) : _pos(pos), _taken(false) {}
+ bool operator==(const int val)
+ {
+ return val == NULL;
+ };
+ int operator=(const int val)
+ {
+ if (val == NULL)
+ {
+ _pos = NULL;
+ _taken = false;
+ return NULL;
+ }
+ return NULL;
+ };
+ Position _pos;
+ bool _taken;
+};
+
// for clearing special attacks
#define REACTIVE_TIMER_START 4000
@@ -789,6 +809,7 @@ class TC_GAME_API Unit : public WorldObject
bool CanDualWield() const { return m_canDualWield; }
virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
+ bool IsWithinRange(Unit const* obj, float dist) const;
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
bool IsWithinMeleeRange(Unit const* obj) const { return IsWithinMeleeRangeAt(GetPosition(), obj); }
bool IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const;
@@ -805,6 +826,10 @@ class TC_GAME_API Unit : public WorldObject
bool AttackStop();
void RemoveAllAttackers();
AttackerSet const& getAttackers() const { return m_attackers; }
+
+ void Unit::SetMeleeAttackPoints();
+ Position GetMeleeAttackPoint(Unit* attacker);
+
bool isAttackingPlayer() const;
Unit* GetVictim() const { return m_attacking; }
// Use this only when 100% sure there is a victim
@@ -1676,6 +1701,9 @@ class TC_GAME_API Unit : public WorldObject
virtual void Whisper(uint32 textId, Player* target, bool isBossWhisper = false);
float GetCollisionHeight() const override;
+
+ AttackPosition getAttackPosition() { return m_attackPosition; }
+
protected:
explicit Unit (bool isWorldObject);
@@ -1756,6 +1784,10 @@ class TC_GAME_API Unit : public WorldObject
virtual void AtEnterCombat() { }
virtual void AtExitCombat();
+ std::vector<AttackPosition> attackMeleePositions;
+ Position m_previousPosition;
+ uint8 m_previousAttackerCount;
+ AttackPosition m_attackPosition;
private:
void UpdateSplineMovement(uint32 t_diff);
@@ -1799,6 +1831,9 @@ class TC_GAME_API Unit : public WorldObject
bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed?
SpellHistory* m_spellHistory;
+
+ static constexpr uint32 ATTACK_POINT_CHECK_INTERVAL = 200;
+ uint32 _attackPointCheckTimer = ATTACK_POINT_CHECK_INTERVAL;
};
namespace Trinity
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index ebccc6fa48..2712c85cf8 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -595,6 +595,54 @@ void MotionMaster::MoveChase(Unit* target, Optional<ChaseRange> dist, Optional<C
Add(new ChaseMovementGenerator(target, dist, angle));
}
+void MotionMaster::MoveCircleTarget(Unit* target)
+{
+ if (!target)
+ return;
+
+ //TC_LOG_DEBUG("server", "MoveCircleTarget");
+
+ Position point = target->GetMeleeAttackPoint(_owner);
+ if (point == NULL)
+ return;
+
+ if (_owner->IsFlying())
+ ; // Dont do anything yet might add later.
+ else
+ point.m_positionZ = _owner->GetMapHeight(point.m_positionX, point.m_positionY, point.m_positionZ);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(point.m_positionX, point.m_positionY, point.m_positionZ, true, true);
+ init.SetFacing(target);
+ init.Launch();
+}
+
+void MotionMaster::MoveBackwards(Unit* target, float dist)
+{
+ if (!target)
+ return;
+
+ //TC_LOG_DEBUG("server", "MoveBackwards");
+
+ Position const& pos = target->GetPosition();
+ float angle = pos.GetOrientation() + (M_PI*2);
+ G3D::Vector3 point;
+ point.x = pos.m_positionX + dist * cosf(angle);
+ point.y = pos.m_positionY + dist * sinf(angle);
+ point.z = pos.m_positionZ;
+
+ //if (_owner->IsFlying())
+ // point.z = pos.m_positionZ;
+ //else
+ // point.z = _owner->GetMapHeight(point.x, point.y, point.z);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(point.x, point.y, point.z);
+ init.SetFacing(target);
+ init.SetBackward();
+ init.Launch();
+}
+
void MotionMaster::MoveConfused()
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 3be8d735a2..592fec1c92 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -142,6 +142,8 @@ class TC_GAME_API MotionMaster
void MoveFollow(Unit* target, float dist, ChaseAngle angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
void MoveChase(Unit* target, Optional<ChaseRange> dist = {}, Optional<ChaseAngle> angle = {});
void MoveChase(Unit* target, float dist, float angle = 0.0f) { MoveChase(target, Optional<ChaseRange>(dist), Optional<ChaseAngle>(angle)); }
+ void MoveCircleTarget(Unit* target);
+ void MoveBackwards(Unit* target, float dist);
void MoveConfused();
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, Position const& pos, bool generatePath = true, Optional<float> finalOrient = {});
diff --git a/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.cpp
index f83c7e8b16..f6554e932a 100644
--- a/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.cpp
@@ -73,7 +73,7 @@ void ChaseMovementGenerator::Initialize(Unit* owner)
owner->SetWalk(false);
_path = nullptr;
- _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f);
+ //_lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f);
}
void ChaseMovementGenerator::Reset(Unit* owner)
@@ -121,7 +121,8 @@ bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
else
{
_rangeCheckTimer = RANGE_CHECK_INTERVAL;
- if (PositionOkay(owner, target, _movingTowards ? Optional<float>() : minTarget, _movingTowards ? maxTarget : Optional<float>(), angle))
+ if (PositionOkay(owner, target, _movingTowards ? Optional<float>() : minTarget, _movingTowards ? maxTarget : Optional<float>(), angle) &&
+ owner->getAttackPosition() == NULL)
{
_path = nullptr;
owner->StopMoving();
@@ -141,73 +142,100 @@ bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
DoMovementInform(owner, target);
}
- // if the target moved, we have to consider whether to adjust
- if (_lastTargetPosition != target->GetPosition() || mutualChase != _mutualChase)
+ //if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
+ //{
+ if (_pathCheckTimer > diff)
{
- _lastTargetPosition = target->GetPosition();
- _mutualChase = mutualChase;
- if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, minRange, maxRange, angle))
+ _pathCheckTimer -= diff;
+ return true;
+ }
+ else
+ {
+ _pathCheckTimer = PATH_CHECK_INTERVAL;
+ }
+ //}
+
+ if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, minRange, maxRange, angle))
+ {
+ Creature* const cOwner = owner->ToCreature();
+ // can we get to the target?
+ if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
{
- Creature* const cOwner = owner->ToCreature();
- // can we get to the target?
- if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
- {
- cOwner->SetCannotReachTarget(true);
- cOwner->StopMoving();
- _path = nullptr;
- return true;
- }
+ cOwner->SetCannotReachTarget(true);
+ cOwner->StopMoving();
+ _path = nullptr;
+ return true;
+ }
- // figure out which way we want to move
- bool const moveToward = !owner->IsInDist(target, maxRange);
+ // Fan Creatures around target if there are more than one.
+ Unit * target = GetTarget();
+ int chaserCount = target->getAttackers().size();
+ // Only start fanning if its within a certain distance.
+ if (chaserCount > 1)
+ {
+ owner->GetMotionMaster()->MoveCircleTarget(target);
+ return true;
+ }
- // make a new path if we have to...
- if (!_path || moveToward != _movingTowards)
- _path = std::make_unique<PathGenerator>(owner);
+ // figure out which way we want to move
+ bool const moveToward = !owner->IsInDist(target, maxRange);
- float x, y, z;
- bool shortenPath;
- // if we want to move toward the target and there's no fixed angle...
- if (moveToward && !angle)
- {
- // ...we'll pathfind to the center, then shorten the path
- target->GetPosition(x, y, z);
- shortenPath = true;
- }
- else
- {
- // otherwise, we fall back to nearpoint finding
- target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAbsoluteAngle(owner));
- shortenPath = false;
- }
+ // make a new path if we have to...
+ if (!_path || moveToward != _movingTowards)
+ _path = std::make_unique<PathGenerator>(owner);
- if (owner->IsHovering())
- owner->UpdateAllowedPositionZ(x, y, z);
+ float x, y, z;
+ bool shortenPath;
+ // if we want to move toward the target and there's no fixed angle...
+ if (moveToward && !angle)
+ {
+ // ...we'll pathfind to the center, then shorten the path
+ target->GetPosition(x, y, z);
+ shortenPath = true;
+ }
+ else
+ {
+ // otherwise, we fall back to nearpoint finding
+ target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAbsoluteAngle(owner));
+ shortenPath = false;
+ }
- bool success = _path->CalculatePath(x, y, z);
- if (!success || (_path->GetPathType() & PATHFIND_NOPATH))
- {
- if (cOwner)
- cOwner->SetCannotReachTarget(true);
- owner->StopMoving();
- return true;
- }
+ if (owner->IsHovering())
+ owner->UpdateAllowedPositionZ(x, y, z);
- if (shortenPath)
- _path->ShortenPathUntilDist(PositionToVector3(target), maxTarget);
+ bool success = _path->CalculatePath(x, y, z);
+ // Special case where Enemy cant get close to player to Melee because of height difference.
+ float floorZ;
+ bool canReach = true;
+ if (target && target->IsFlying() && !owner->IsFlying())
+ {
+ target->UpdateAllowedPositionZ(_path->GetEndPosition().x, _path->GetEndPosition().y, floorZ);
+ if (abs(floorZ - _path->GetStartPosition().z) > MELEE_RANGE)
+ canReach = false;
+ }
+
+ if ((!success || (!canReach && success)) || (_path->GetPathType() & PATHFIND_NOPATH))
+ {
if (cOwner)
- cOwner->SetCannotReachTarget(false);
+ cOwner->SetCannotReachTarget(true);
+ owner->StopMoving();
+ return true;
+ }
+
+ if (shortenPath)
+ _path->ShortenPathUntilDist(PositionToVector3(target), maxTarget);
- owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
+ if (cOwner)
+ cOwner->SetCannotReachTarget(false);
- Movement::MoveSplineInit init(owner);
- init.MovebyPath(_path->GetPath());
- init.SetWalk(false);
- init.SetFacing(target);
+ owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
- init.Launch();
- }
+ Movement::MoveSplineInit moveSplineInit(owner);
+ moveSplineInit.MovebyPath(_path->GetPath());
+ moveSplineInit.SetWalk(false);
+ moveSplineInit.SetFacing(target);
+ moveSplineInit.Launch();
}
// and then, finally, we're done for the tick
diff --git a/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.h
index 48d828b760..aef1a9c166 100644
--- a/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ChaseMovementGenerator.h
@@ -40,19 +40,21 @@ class ChaseMovementGenerator : public MovementGenerator, public AbstractFollower
void Finalize(Unit*, bool, bool) override;
MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; }
- void UnitSpeedChanged() override { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
+ //void UnitSpeedChanged() override { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
private:
- static constexpr uint32 RANGE_CHECK_INTERVAL = 100; // time (ms) until we attempt to recalculate
+ static constexpr uint32 RANGE_CHECK_INTERVAL = 500; // time (ms) until we attempt to recalculate
+ static constexpr uint32 PATH_CHECK_INTERVAL = 500;
Optional<ChaseRange> const _range;
Optional<ChaseAngle> const _angle;
std::unique_ptr<PathGenerator> _path;
- Position _lastTargetPosition;
+ //Position _lastTargetPosition;
uint32 _rangeCheckTimer = RANGE_CHECK_INTERVAL;
+ uint32 _pathCheckTimer = PATH_CHECK_INTERVAL;
bool _movingTowards = true;
- bool _mutualChase = true;
+ //bool _mutualChase = true;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment