fix(Core/Creatures): Added new AI function OnTeleportUnreacheablePlayer to teleport all unreachable players. (#12193)
* fix(Core/Creatures): Implemented CREATURE_FLAG_EXTRA_TELEPORT_UNREACHABLE_PLAYERS. Fixed #11750 * Update. * Update. * Update.
This commit is contained in:
@@ -306,7 +306,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
|||||||
me->SetLootRecipient(nullptr);
|
me->SetLootRecipient(nullptr);
|
||||||
me->ResetPlayerDamageReq();
|
me->ResetPlayerDamageReq();
|
||||||
me->SetLastDamagedTime(0);
|
me->SetLastDamagedTime(0);
|
||||||
me->SetCannotReachTarget(false);
|
me->SetCannotReachTarget();
|
||||||
|
|
||||||
if (me->IsInEvadeMode())
|
if (me->IsInEvadeMode())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ public:
|
|||||||
EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
|
EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
|
||||||
EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary
|
EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary
|
||||||
EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet
|
EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet
|
||||||
|
EVADE_REASON_NO_PATH, // the creature was unable to reach its target for over 5 seconds
|
||||||
EVADE_REASON_OTHER
|
EVADE_REASON_OTHER
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,6 +211,8 @@ public:
|
|||||||
|
|
||||||
virtual void CalculateThreat(Unit* /*hatedUnit*/, float& /*threat*/, SpellInfo const* /*threatSpell*/) { }
|
virtual void CalculateThreat(Unit* /*hatedUnit*/, float& /*threat*/, SpellInfo const* /*threatSpell*/) { }
|
||||||
|
|
||||||
|
virtual bool OnTeleportUnreacheablePlayer(Player* /*player*/) { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void MoveInLineOfSight(Unit* /*who*/);
|
virtual void MoveInLineOfSight(Unit* /*who*/);
|
||||||
|
|
||||||
|
|||||||
@@ -261,13 +261,19 @@ HostileReference* ThreatContainer::getReferenceByTarget(Unit* victim) const
|
|||||||
if (!victim)
|
if (!victim)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
ObjectGuid const guid = victim->GetGUID();
|
return getReferenceByTarget(victim->GetGUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
HostileReference* ThreatContainer::getReferenceByTarget(ObjectGuid const& guid) const
|
||||||
|
{
|
||||||
for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
|
for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
|
||||||
{
|
{
|
||||||
HostileReference* ref = (*i);
|
HostileReference* ref = (*i);
|
||||||
if (ref && ref->getUnitGuid() == guid)
|
if (ref && ref->getUnitGuid() == guid)
|
||||||
|
{
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
HostileReference* getReferenceByTarget(Unit* victim) const;
|
HostileReference* getReferenceByTarget(Unit* victim) const;
|
||||||
|
HostileReference* getReferenceByTarget(ObjectGuid const& guid) const;
|
||||||
|
|
||||||
[[nodiscard]] StorageType const& getThreatList() const { return iThreatList; }
|
[[nodiscard]] StorageType const& getThreatList() const { return iThreatList; }
|
||||||
|
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
|
|||||||
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
|
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
|
||||||
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
||||||
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
||||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _lastDamagedTime(nullptr), m_cannotReachTarget(false), m_cannotReachTimer(0),
|
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _lastDamagedTime(nullptr), m_cannotReachTimer(0),
|
||||||
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false)
|
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false)
|
||||||
{
|
{
|
||||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||||
@@ -718,7 +718,8 @@ void Creature::Update(uint32 diff)
|
|||||||
{
|
{
|
||||||
AI()->CheckInRoom();
|
AI()->CheckInRoom();
|
||||||
m_boundaryCheckTime = 2500;
|
m_boundaryCheckTime = 2500;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
m_boundaryCheckTime -= diff;
|
m_boundaryCheckTime -= diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,7 +729,7 @@ void Creature::Update(uint32 diff)
|
|||||||
RemoveCharmAuras();
|
RemoveCharmAuras();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Unit *victim = GetVictim())
|
if (Unit* victim = GetVictim())
|
||||||
{
|
{
|
||||||
// If we are closer than 50% of the combat reach we are going to reposition the victim
|
// If we are closer than 50% of the combat reach we are going to reposition the victim
|
||||||
if (diff >= m_moveBackwardsMovementTime)
|
if (diff >= m_moveBackwardsMovementTime)
|
||||||
@@ -818,15 +819,45 @@ void Creature::Update(uint32 diff)
|
|||||||
m_regenTimer += CREATURE_REGEN_INTERVAL;
|
m_regenTimer += CREATURE_REGEN_INTERVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
|
if (CanNotReachTarget() && !IsInEvadeMode())
|
||||||
{
|
{
|
||||||
m_cannotReachTimer += diff;
|
m_cannotReachTimer += diff;
|
||||||
if (IsNotReachable() && IsAIEnabled)
|
if (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS))
|
||||||
{
|
{
|
||||||
AI()->EnterEvadeMode();
|
Player* cannotReachPlayer = ObjectAccessor::GetPlayer(*this, m_cannotReachTarget);
|
||||||
|
if (cannotReachPlayer && IsEngagedBy(cannotReachPlayer) && IsAIEnabled && AI()->OnTeleportUnreacheablePlayer(cannotReachPlayer))
|
||||||
|
{
|
||||||
|
SetCannotReachTarget();
|
||||||
}
|
}
|
||||||
|
else if (!GetMap()->IsRaid())
|
||||||
|
{
|
||||||
|
auto EnterEvade = [&]()
|
||||||
|
{
|
||||||
|
if (CreatureAI* ai = AI())
|
||||||
|
{
|
||||||
|
ai->EnterEvadeMode(CreatureAI::EvadeReason::EVADE_REASON_NO_PATH);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GetThreatMgr().getThreatList().size() <= 1)
|
||||||
|
{
|
||||||
|
EnterEvade();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (HostileReference* ref = GetThreatMgr().getOnlineContainer().getReferenceByTarget(m_cannotReachTarget))
|
||||||
|
{
|
||||||
|
ref->removeReference();
|
||||||
|
SetCannotReachTarget();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnterEvade();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1926,7 +1957,7 @@ void Creature::setDeathState(DeathState s, bool despawn)
|
|||||||
SetFullHealth();
|
SetFullHealth();
|
||||||
SetLootRecipient(nullptr);
|
SetLootRecipient(nullptr);
|
||||||
ResetPlayerDamageReq();
|
ResetPlayerDamageReq();
|
||||||
SetCannotReachTarget(false);
|
SetCannotReachTarget();
|
||||||
CreatureTemplate const* cinfo = GetCreatureTemplate();
|
CreatureTemplate const* cinfo = GetCreatureTemplate();
|
||||||
// Xinef: npc run by default
|
// Xinef: npc run by default
|
||||||
//SetWalk(true);
|
//SetWalk(true);
|
||||||
@@ -3457,15 +3488,35 @@ bool Creature::IsMovementPreventedByCasting() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::SetCannotReachTarget(bool cannotReach)
|
void Creature::SetCannotReachTarget(ObjectGuid const& cannotReach)
|
||||||
{
|
{
|
||||||
if (cannotReach == m_cannotReachTarget)
|
if (cannotReach == m_cannotReachTarget)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_cannotReachTarget = cannotReach;
|
m_cannotReachTarget = cannotReach;
|
||||||
m_cannotReachTimer = 0;
|
m_cannotReachTimer = 0;
|
||||||
|
|
||||||
if (cannotReach)
|
if (cannotReach)
|
||||||
|
{
|
||||||
LOG_DEBUG("entities.unit", "Creature::SetCannotReachTarget() called with true. Details: {}", GetDebugInfo());
|
LOG_DEBUG("entities.unit", "Creature::SetCannotReachTarget() called with true. Details: {}", GetDebugInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Creature::CanNotReachTarget() const
|
||||||
|
{
|
||||||
|
return m_cannotReachTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Creature::IsNotReachableAndNeedRegen() const
|
||||||
|
{
|
||||||
|
if (CanNotReachTarget())
|
||||||
|
{
|
||||||
|
return m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID) * IN_MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t Creature::GetLastDamagedTime() const
|
time_t Creature::GetLastDamagedTime() const
|
||||||
|
|||||||
@@ -309,10 +309,9 @@ public:
|
|||||||
return m_charmInfo->GetCharmSpell(pos)->GetAction();
|
return m_charmInfo->GetCharmSpell(pos)->GetAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCannotReachTarget(bool cannotReach);
|
void SetCannotReachTarget(ObjectGuid const& target = ObjectGuid::Empty);
|
||||||
[[nodiscard]] bool CanNotReachTarget() const { return m_cannotReachTarget; }
|
[[nodiscard]] bool CanNotReachTarget() const;
|
||||||
[[nodiscard]] bool IsNotReachable() const { return (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS)) && m_cannotReachTarget; }
|
[[nodiscard]] bool IsNotReachableAndNeedRegen() const;
|
||||||
[[nodiscard]] bool IsNotReachableAndNeedRegen() const { return (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID) * IN_MILLISECONDS)) && m_cannotReachTarget; }
|
|
||||||
|
|
||||||
void SetPosition(float x, float y, float z, float o);
|
void SetPosition(float x, float y, float z, float o);
|
||||||
void SetPosition(const Position& pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); }
|
void SetPosition(const Position& pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); }
|
||||||
@@ -456,7 +455,7 @@ private:
|
|||||||
|
|
||||||
mutable std::shared_ptr<time_t> _lastDamagedTime; // Part of Evade mechanics
|
mutable std::shared_ptr<time_t> _lastDamagedTime; // Part of Evade mechanics
|
||||||
|
|
||||||
bool m_cannotReachTarget;
|
ObjectGuid m_cannotReachTarget;
|
||||||
uint32 m_cannotReachTimer;
|
uint32 m_cannotReachTimer;
|
||||||
|
|
||||||
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
|
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ enum CreatureFlagsExtra : uint32
|
|||||||
CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000,
|
CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000,
|
||||||
|
|
||||||
// Masks
|
// Masks
|
||||||
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_26 |
|
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_26 | CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28), // SKIP
|
||||||
CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28), // SKIP
|
|
||||||
|
|
||||||
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
|
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
_lastTargetPosition.reset();
|
_lastTargetPosition.reset();
|
||||||
if (Creature* cOwner2 = owner->ToCreature())
|
if (Creature* cOwner2 = owner->ToCreature())
|
||||||
{
|
{
|
||||||
cOwner2->SetCannotReachTarget(false);
|
cOwner2->SetCannotReachTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -91,13 +91,14 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
|
|
||||||
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
|
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
|
||||||
{
|
{
|
||||||
i_recalculateTravel = false;
|
|
||||||
i_path = nullptr;
|
|
||||||
if (Creature* cOwner2 = owner->ToCreature())
|
if (Creature* cOwner2 = owner->ToCreature())
|
||||||
{
|
{
|
||||||
cOwner2->SetCannotReachTarget(false);
|
cOwner2->SetCannotReachTarget(i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE ? target->GetGUID() : ObjectGuid::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i_recalculateTravel = false;
|
||||||
|
i_path = nullptr;
|
||||||
|
|
||||||
owner->StopMoving();
|
owner->StopMoving();
|
||||||
owner->SetInFront(target);
|
owner->SetInFront(target);
|
||||||
MovementInform(owner);
|
MovementInform(owner);
|
||||||
@@ -107,15 +108,25 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
|
|
||||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
||||||
{
|
{
|
||||||
i_recalculateTravel = false;
|
|
||||||
i_path = nullptr;
|
|
||||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||||
owner->SetInFront(target);
|
owner->SetInFront(target);
|
||||||
MovementInform(owner);
|
MovementInform(owner);
|
||||||
|
|
||||||
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
|
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
|
||||||
|
{
|
||||||
owner->Attack(this->i_target.getTarget(), true);
|
owner->Attack(this->i_target.getTarget(), true);
|
||||||
}
|
}
|
||||||
|
else if (i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE)
|
||||||
|
{
|
||||||
|
if (Creature* cOwner2 = owner->ToCreature())
|
||||||
|
{
|
||||||
|
cOwner2->SetCannotReachTarget(this->i_target.getTarget()->GetGUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i_recalculateTravel = false;
|
||||||
|
i_path = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (_lastTargetPosition && i_target->GetPosition() == _lastTargetPosition.value() && mutualChase == _mutualChase)
|
if (_lastTargetPosition && i_target->GetPosition() == _lastTargetPosition.value() && mutualChase == _mutualChase)
|
||||||
return true;
|
return true;
|
||||||
@@ -139,7 +150,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
// can we get to the target?
|
// can we get to the target?
|
||||||
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
||||||
{
|
{
|
||||||
cOwner->SetCannotReachTarget(true);
|
cOwner->SetCannotReachTarget(target->GetGUID());
|
||||||
cOwner->StopMoving();
|
cOwner->StopMoving();
|
||||||
i_path = nullptr;
|
i_path = nullptr;
|
||||||
return true;
|
return true;
|
||||||
@@ -176,7 +187,10 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||||
{
|
{
|
||||||
if (cOwner)
|
if (cOwner)
|
||||||
cOwner->SetCannotReachTarget(true);
|
{
|
||||||
|
cOwner->SetCannotReachTarget(target->GetGUID());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +198,9 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget);
|
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget);
|
||||||
|
|
||||||
if (cOwner)
|
if (cOwner)
|
||||||
cOwner->SetCannotReachTarget(false);
|
{
|
||||||
|
cOwner->SetCannotReachTarget();
|
||||||
|
}
|
||||||
|
|
||||||
bool walk = false;
|
bool walk = false;
|
||||||
if (cOwner && !cOwner->IsPet())
|
if (cOwner && !cOwner->IsPet())
|
||||||
@@ -239,7 +255,9 @@ void ChaseMovementGenerator<T>::DoFinalize(T* owner)
|
|||||||
{
|
{
|
||||||
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||||
if (Creature* cOwner = owner->ToCreature())
|
if (Creature* cOwner = owner->ToCreature())
|
||||||
cOwner->SetCannotReachTarget(false);
|
{
|
||||||
|
cOwner->SetCannotReachTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -313,6 +313,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OnTeleportUnreacheablePlayer(Player* player) override
|
||||||
|
{
|
||||||
|
DoCast(player, SPELL_SUMMON_PLAYER, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DoMeleeAttackIfReady(bool ignoreCasting)
|
void DoMeleeAttackIfReady(bool ignoreCasting)
|
||||||
{
|
{
|
||||||
if (!ignoreCasting && me->HasUnitState(UNIT_STATE_CASTING))
|
if (!ignoreCasting && me->HasUnitState(UNIT_STATE_CASTING))
|
||||||
|
|||||||
Reference in New Issue
Block a user