diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 54f6abdb0..34f5267d7 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -801,7 +801,7 @@ void Creature::Update(uint32 diff) // Periodically check if able to move, if not, extend leash timer if (diff >= m_extendLeashTime) { - if (!CanFreeMove()) + if (HasUnitState(UNIT_STATE_LOST_CONTROL)) UpdateLeashExtensionTime(); m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL; } @@ -2685,10 +2685,11 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS); - if (GetCharmerOrOwner()) + if (Unit* unit = GetCharmerOrOwner()) { - dist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f); - return IsWithinDist(victim, dist); + float visibilityDist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f); + if (!victim->IsWithinDist(unit, visibilityDist)) + return false; } if (!dist) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 318e50bf9..96a10b1c7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -4185,6 +4185,15 @@ void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withI InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, bySelf); } +Spell* Unit::GetFirstCurrentCastingSpell() const +{ + for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++) + if (m_currentSpells[i] && m_currentSpells[i]->GetCastTimeRemaining() > 0) + return m_currentSpells[i]; + + return nullptr; +} + Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const { for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++) @@ -10412,8 +10421,29 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) if (meleeAttack) AddUnitState(UNIT_STATE_MELEE_ATTACKING); + Unit* owner = GetCharmerOrOwner(); + Creature* ownerCreature = owner ? owner->ToCreature() : nullptr; + Creature* controlledCreatureWithSameVictim = nullptr; + if (creature && !m_Controlled.empty()) + { + for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + { + if ((*itr)->ToCreature() && (*itr)->GetVictim() == victim) + { + controlledCreatureWithSameVictim = (*itr)->ToCreature(); + break; + } + } + } + + // Share leash timer with controlled unit + if (controlledCreatureWithSameVictim) + creature->SetLastLeashExtensionTimePtr(controlledCreatureWithSameVictim->GetLastLeashExtensionTimePtr()); + // Share leash timer with owner + else if (creature && ownerCreature && ownerCreature->GetVictim() == victim) + creature->SetLastLeashExtensionTimePtr(ownerCreature->GetLastLeashExtensionTimePtr()); // Update leash timer when attacking creatures - if (victim->IsCreature()) + else if (victim->IsCreature()) victim->ToCreature()->UpdateLeashExtensionTime(); // set position before any AI calls/assistance diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 29344cb76..f83e2a623 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1494,6 +1494,7 @@ public: [[nodiscard]] Player* GetSpellModOwner() const; [[nodiscard]] Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } [[nodiscard]] Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; } + [[nodiscard]] Spell* GetFirstCurrentCastingSpell() const; [[nodiscard]] Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; [[nodiscard]] int32 GetCurrentSpellCastTime(uint32 spell_id) const; diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 7dec2d8ce..76fb0840a 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -72,15 +72,27 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) return false; Creature* cOwner = owner->ToCreature(); + bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting(); // the owner might be unable to move (rooted or casting), or we have lost the target, pause movement - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || (cOwner && cOwner->IsMovementPreventedByCasting())) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || isStoppedBecauseOfCasting) { owner->StopMoving(); _lastTargetPosition.reset(); if (cOwner) { - cOwner->UpdateLeashExtensionTime(); + if (isStoppedBecauseOfCasting) + { + // Don't reset leash timer if it's a spell like Shoot with a short cast time. + /// @todo: Research how it should actually work. + Spell *spell = cOwner->GetFirstCurrentCastingSpell(); + bool spellHasLongCast = spell && spell->GetCastTime() > 1 * SECOND * IN_MILLISECONDS; + if (spellHasLongCast) + cOwner->UpdateLeashExtensionTime(); + } + else + cOwner->UpdateLeashExtensionTime(); + cOwner->SetCannotReachTarget(); } return true;