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:
UltraNix
2022-07-24 18:10:41 +02:00
committed by GitHub
parent aa39e6ab86
commit 801e68b1dd
9 changed files with 274 additions and 191 deletions
+1 -1
View File
@@ -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())
{ {
+3
View File
@@ -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*/);
+7 -1
View File
@@ -261,12 +261,18 @@ 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;
+1
View File
@@ -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; }
+223 -172
View File
@@ -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;
@@ -628,207 +628,238 @@ void Creature::Update(uint32 diff)
LOG_ERROR("entities.unit", "Creature ({}) in wrong state: JUST_DEAD (1)", GetGUID().ToString()); LOG_ERROR("entities.unit", "Creature ({}) in wrong state: JUST_DEAD (1)", GetGUID().ToString());
break; break;
case DEAD: case DEAD:
{
time_t now = GameTime::GetGameTime().count();
if (m_respawnTime <= now)
{ {
time_t now = GameTime::GetGameTime().count();
if (m_respawnTime <= now) ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
{ {
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry()); m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
break;
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions)) }
{
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn. bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay; if (!allowed) // Will be rechecked on next Update call
break; break;
}
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(GetEntry(), m_spawnId);
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
if (!allowed) // Will be rechecked on next Update call if (!linkedRespawntime) // Can respawn
break; Respawn();
else // the master is dead
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(GetEntry(), m_spawnId); {
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
if (!linkedRespawntime) // Can respawn if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
Respawn(); SetRespawnTime(DAY);
else // the master is dead else
{ m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); SaveRespawnTime(); // also save to DB immediately
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
SetRespawnTime(DAY);
else
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
SaveRespawnTime(); // also save to DB immediately
}
} }
break;
} }
break;
}
case CORPSE: case CORPSE:
{ {
Unit::Update(diff); Unit::Update(diff);
// deathstate changed on spells update, prevent problems // deathstate changed on spells update, prevent problems
if (m_deathState != CORPSE) if (m_deathState != CORPSE)
break;
if (m_groupLootTimer && lootingGroupLowGUID)
{
if (m_groupLootTimer <= diff)
{
Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
if (group)
group->EndRoll(&loot, GetMap());
m_groupLootTimer = 0;
lootingGroupLowGUID = 0;
}
else
{
m_groupLootTimer -= diff;
}
}
else if (m_corpseRemoveTime <= GameTime::GetGameTime().count())
{
RemoveCorpse(false);
LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetUInt32Value(OBJECT_FIELD_ENTRY));
}
break; break;
}
case ALIVE: if (m_groupLootTimer && lootingGroupLowGUID)
{ {
Unit::Update(diff); if (m_groupLootTimer <= diff)
// creature can be dead after Unit::Update call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if (!IsAlive())
break;
// if creature is charmed, switch to charmed AI
if (NeedChangeAI)
{ {
UpdateCharmAI(); Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
NeedChangeAI = false; if (group)
IsAIEnabled = true; group->EndRoll(&loot, GetMap());
m_groupLootTimer = 0;
lootingGroupLowGUID = 0;
}
else
{
m_groupLootTimer -= diff;
}
}
else if (m_corpseRemoveTime <= GameTime::GetGameTime().count())
{
RemoveCorpse(false);
LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetUInt32Value(OBJECT_FIELD_ENTRY));
}
break;
}
case ALIVE:
{
Unit::Update(diff);
// xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode // creature can be dead after Unit::Update call
SelectVictim(); // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if (!IsAlive())
break;
// if creature is charmed, switch to charmed AI
if (NeedChangeAI)
{
UpdateCharmAI();
NeedChangeAI = false;
IsAIEnabled = true;
// xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode
SelectVictim();
}
// periodic check to see if the creature has passed an evade boundary
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
{
if (diff >= m_boundaryCheckTime)
{
AI()->CheckInRoom();
m_boundaryCheckTime = 2500;
}
else
m_boundaryCheckTime -= diff;
}
Unit* owner = GetCharmerOrOwner();
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
{
RemoveCharmAuras();
}
if (Unit* victim = GetVictim())
{
// If we are closer than 50% of the combat reach we are going to reposition the victim
if (diff >= m_moveBackwardsMovementTime)
{
float MaxRange = GetCollisionRadius() + GetVictim()->GetCollisionRadius();
if (IsInDist(victim, MaxRange))
AI()->MoveBackwardsChecks();
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
}
else
{
m_moveBackwardsMovementTime -= diff;
} }
// periodic check to see if the creature has passed an evade boundary // Circling the target
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged()) if (diff >= m_moveCircleMovementTime)
{ {
if (diff >= m_boundaryCheckTime) AI()->MoveCircleChecks();
{ m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
AI()->CheckInRoom();
m_boundaryCheckTime = 2500;
} else
m_boundaryCheckTime -= diff;
} }
else
Unit* owner = GetCharmerOrOwner();
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
{ {
RemoveCharmAuras(); m_moveCircleMovementTime -= diff;
} }
}
if (Unit *victim = GetVictim()) // Call for assistance if not disabled
if (m_assistanceTimer)
{
if (m_assistanceTimer <= diff)
{ {
// If we are closer than 50% of the combat reach we are going to reposition the victim if (CanPeriodicallyCallForAssistance())
if (diff >= m_moveBackwardsMovementTime)
{ {
float MaxRange = GetCollisionRadius() + GetVictim()->GetCollisionRadius(); SetNoCallAssistance(false);
CallAssistance();
if (IsInDist(victim, MaxRange))
AI()->MoveBackwardsChecks();
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
}
else
{
m_moveBackwardsMovementTime -= diff;
}
// Circling the target
if (diff >= m_moveCircleMovementTime)
{
AI()->MoveCircleChecks();
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
}
else
{
m_moveCircleMovementTime -= diff;
} }
m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD);
} }
else
// Call for assistance if not disabled
if (m_assistanceTimer)
{ {
if (m_assistanceTimer <= diff) m_assistanceTimer -= diff;
}
}
if (!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
m_AI_locked = true;
i_AI->UpdateAI(diff);
m_AI_locked = false;
}
// creature can be dead after UpdateAI call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if (!IsAlive())
break;
m_regenTimer -= diff;
if (m_regenTimer <= 0)
{
if (!IsInEvadeMode())
{
// regenerate health if not in combat or if polymorphed)
if (!IsInCombat() || IsPolymorphed())
RegenerateHealth();
else if (IsNotReachableAndNeedRegen())
{ {
if (CanPeriodicallyCallForAssistance()) // regenerate health if cannot reach the target and the setting is set to do so.
// this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
{ {
SetNoCallAssistance(false);
CallAssistance();
}
m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD);
}
else
{
m_assistanceTimer -= diff;
}
}
if (!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
m_AI_locked = true;
i_AI->UpdateAI(diff);
m_AI_locked = false;
}
// creature can be dead after UpdateAI call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if (!IsAlive())
break;
m_regenTimer -= diff;
if (m_regenTimer <= 0)
{
if (!IsInEvadeMode())
{
// regenerate health if not in combat or if polymorphed)
if (!IsInCombat() || IsPolymorphed())
RegenerateHealth(); RegenerateHealth();
else if (IsNotReachableAndNeedRegen()) LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
}
else
LOG_DEBUG("entities.unit", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
}
}
if (getPowerType() == POWER_ENERGY)
Regenerate(POWER_ENERGY);
else
Regenerate(POWER_MANA);
m_regenTimer += CREATURE_REGEN_INTERVAL;
}
if (CanNotReachTarget() && !IsInEvadeMode())
{
m_cannotReachTimer += diff;
if (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS))
{
Player* cannotReachPlayer = ObjectAccessor::GetPlayer(*this, m_cannotReachTarget);
if (cannotReachPlayer && IsEngagedBy(cannotReachPlayer) && IsAIEnabled && AI()->OnTeleportUnreacheablePlayer(cannotReachPlayer))
{
SetCannotReachTarget();
}
else if (!GetMap()->IsRaid())
{
auto EnterEvade = [&]()
{ {
// regenerate health if cannot reach the target and the setting is set to do so. if (CreatureAI* ai = AI())
// this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
{ {
RegenerateHealth(); ai->EnterEvadeMode(CreatureAI::EvadeReason::EVADE_REASON_NO_PATH);
LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo()); }
};
if (GetThreatMgr().getThreatList().size() <= 1)
{
EnterEvade();
}
else
{
if (HostileReference* ref = GetThreatMgr().getOnlineContainer().getReferenceByTarget(m_cannotReachTarget))
{
ref->removeReference();
SetCannotReachTarget();
} }
else else
LOG_DEBUG("entities.unit", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo()); {
EnterEvade();
}
} }
} }
if (getPowerType() == POWER_ENERGY)
Regenerate(POWER_ENERGY);
else
Regenerate(POWER_MANA);
m_regenTimer += CREATURE_REGEN_INTERVAL;
} }
if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
{
m_cannotReachTimer += diff;
if (IsNotReachable() && IsAIEnabled)
{
AI()->EnterEvadeMode();
}
}
break;
} }
break;
}
default: default:
break; break;
} }
@@ -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
+4 -5
View File
@@ -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,14 +108,24 @@ 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)
@@ -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))