Created
July 21, 2018 07:46
-
-
Save Langerz82/1dbe4acec7fb6bbedf61fb4a44aeece3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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