diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 46ce5b842..e310de645 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -306,7 +306,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); me->SetLastDamagedTime(0); - me->SetCannotReachTarget(false); + me->SetCannotReachTarget(); if (me->IsInEvadeMode()) { diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index a85638975..6db1fa404 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -88,6 +88,7 @@ public: EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty 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_NO_PATH, // the creature was unable to reach its target for over 5 seconds EVADE_REASON_OTHER }; @@ -210,6 +211,8 @@ public: virtual void CalculateThreat(Unit* /*hatedUnit*/, float& /*threat*/, SpellInfo const* /*threatSpell*/) { } + virtual bool OnTeleportUnreacheablePlayer(Player* /*player*/) { return false; } + protected: virtual void MoveInLineOfSight(Unit* /*who*/); diff --git a/src/server/game/Combat/ThreatMgr.cpp b/src/server/game/Combat/ThreatMgr.cpp index 87af1c4d4..7cbbcd94d 100644 --- a/src/server/game/Combat/ThreatMgr.cpp +++ b/src/server/game/Combat/ThreatMgr.cpp @@ -261,12 +261,18 @@ HostileReference* ThreatContainer::getReferenceByTarget(Unit* victim) const if (!victim) 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) { HostileReference* ref = (*i); if (ref && ref->getUnitGuid() == guid) + { return ref; + } } return nullptr; diff --git a/src/server/game/Combat/ThreatMgr.h b/src/server/game/Combat/ThreatMgr.h index 5f87b094c..6844b08d1 100644 --- a/src/server/game/Combat/ThreatMgr.h +++ b/src/server/game/Combat/ThreatMgr.h @@ -164,6 +164,7 @@ public: } HostileReference* getReferenceByTarget(Unit* victim) const; + HostileReference* getReferenceByTarget(ObjectGuid const& guid) const; [[nodiscard]] StorageType const& getThreatList() const { return iThreatList; } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index ecacbf3b3..772d74cc9 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -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_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_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) { 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()); break; 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)) { - - 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. - m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay; - break; - } - - bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning - if (!allowed) // Will be rechecked on next Update call - break; - - ObjectGuid dbtableHighGuid = ObjectGuid::Create(GetEntry(), m_spawnId); - time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); - if (!linkedRespawntime) // Can respawn - Respawn(); - else // the master is dead - { - ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); - 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 - } + // Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn. + m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay; + break; + } + + bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning + if (!allowed) // Will be rechecked on next Update call + break; + + ObjectGuid dbtableHighGuid = ObjectGuid::Create(GetEntry(), m_spawnId); + time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); + if (!linkedRespawntime) // Can respawn + Respawn(); + else // the master is dead + { + ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); + 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: - { - Unit::Update(diff); - // deathstate changed on spells update, prevent problems - 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)); - } + { + Unit::Update(diff); + // deathstate changed on spells update, prevent problems + if (m_deathState != CORPSE) break; - } - case ALIVE: + + if (m_groupLootTimer && lootingGroupLowGUID) { - Unit::Update(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) + if (m_groupLootTimer <= diff) { - UpdateCharmAI(); - NeedChangeAI = false; - IsAIEnabled = true; + 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; + } + case ALIVE: + { + Unit::Update(diff); - // xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode - SelectVictim(); + // 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(); + 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 - if (IsAIEnabled && !IsInEvadeMode() && IsEngaged()) + // Circling the target + if (diff >= m_moveCircleMovementTime) { - if (diff >= m_boundaryCheckTime) - { - AI()->CheckInRoom(); - m_boundaryCheckTime = 2500; - } else - m_boundaryCheckTime -= diff; + AI()->MoveCircleChecks(); + m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2); } - - Unit* owner = GetCharmerOrOwner(); - if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false)) + else { - 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 (diff >= m_moveBackwardsMovementTime) + if (CanPeriodicallyCallForAssistance()) { - 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; - } - - // 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; + SetNoCallAssistance(false); + CallAssistance(); } + m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD); } - - // Call for assistance if not disabled - if (m_assistanceTimer) + else { - 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(); - 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. - // 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()) + if (CreatureAI* ai = AI()) { - RegenerateHealth(); - LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo()); + 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 - 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: break; } @@ -1926,7 +1957,7 @@ void Creature::setDeathState(DeathState s, bool despawn) SetFullHealth(); SetLootRecipient(nullptr); ResetPlayerDamageReq(); - SetCannotReachTarget(false); + SetCannotReachTarget(); CreatureTemplate const* cinfo = GetCreatureTemplate(); // Xinef: npc run by default //SetWalk(true); @@ -3457,15 +3488,35 @@ bool Creature::IsMovementPreventedByCasting() const return false; } -void Creature::SetCannotReachTarget(bool cannotReach) +void Creature::SetCannotReachTarget(ObjectGuid const& cannotReach) { if (cannotReach == m_cannotReachTarget) + { return; + } + m_cannotReachTarget = cannotReach; m_cannotReachTimer = 0; if (cannotReach) + { 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 diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index b1fd4eea4..d97d6ef17 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -309,10 +309,9 @@ public: return m_charmInfo->GetCharmSpell(pos)->GetAction(); } - void SetCannotReachTarget(bool cannotReach); - [[nodiscard]] bool CanNotReachTarget() const { return m_cannotReachTarget; } - [[nodiscard]] bool IsNotReachable() const { return (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS)) && m_cannotReachTarget; } - [[nodiscard]] bool IsNotReachableAndNeedRegen() const { return (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID) * IN_MILLISECONDS)) && m_cannotReachTarget; } + void SetCannotReachTarget(ObjectGuid const& target = ObjectGuid::Empty); + [[nodiscard]] bool CanNotReachTarget() const; + [[nodiscard]] bool IsNotReachableAndNeedRegen() const; void SetPosition(float x, float y, float z, float o); void SetPosition(const Position& pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } @@ -456,7 +455,7 @@ private: mutable std::shared_ptr _lastDamagedTime; // Part of Evade mechanics - bool m_cannotReachTarget; + ObjectGuid m_cannotReachTarget; uint32 m_cannotReachTimer; Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 7d454e8e6..09751eac6 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -80,8 +80,7 @@ enum CreatureFlagsExtra : uint32 CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000, // Masks - 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 = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_26 | 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 }; diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 128a3b25f..30b5c8dcf 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -63,7 +63,7 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) _lastTargetPosition.reset(); if (Creature* cOwner2 = owner->ToCreature()) { - cOwner2->SetCannotReachTarget(false); + cOwner2->SetCannotReachTarget(); } return true; @@ -91,13 +91,14 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional(), angle)) { - i_recalculateTravel = false; - i_path = nullptr; 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->SetInFront(target); MovementInform(owner); @@ -107,14 +108,24 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized()) { - i_recalculateTravel = false; - i_path = nullptr; owner->ClearUnitState(UNIT_STATE_CHASE_MOVE); owner->SetInFront(target); MovementInform(owner); if (owner->IsWithinMeleeRange(this->i_target.getTarget())) + { 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) @@ -139,7 +150,7 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) // can we get to the target? if (cOwner && !target->isInAccessiblePlaceFor(cOwner)) { - cOwner->SetCannotReachTarget(true); + cOwner->SetCannotReachTarget(target->GetGUID()); cOwner->StopMoving(); i_path = nullptr; return true; @@ -176,7 +187,10 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) if (!success || i_path->GetPathType() & PATHFIND_NOPATH) { if (cOwner) - cOwner->SetCannotReachTarget(true); + { + cOwner->SetCannotReachTarget(target->GetGUID()); + } + return true; } @@ -184,7 +198,9 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget); if (cOwner) - cOwner->SetCannotReachTarget(false); + { + cOwner->SetCannotReachTarget(); + } bool walk = false; if (cOwner && !cOwner->IsPet()) @@ -239,7 +255,9 @@ void ChaseMovementGenerator::DoFinalize(T* owner) { owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE); if (Creature* cOwner = owner->ToCreature()) - cOwner->SetCannotReachTarget(false); + { + cOwner->SetCannotReachTarget(); + } } template diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp index ccf460440..cf9d16c47 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp @@ -313,6 +313,12 @@ public: } } + bool OnTeleportUnreacheablePlayer(Player* player) override + { + DoCast(player, SPELL_SUMMON_PLAYER, true); + return true; + } + void DoMeleeAttackIfReady(bool ignoreCasting) { if (!ignoreCasting && me->HasUnitState(UNIT_STATE_CASTING))