fix(Core/Pets): Pet follow and stay behavior when casting spells out of range (#26661)

This commit is contained in:
Saqra1
2026-09-07 20:25:24 +02:00
committed by GitHub
parent 992d8c566e
commit fea7a17e0f
3 changed files with 163 additions and 124 deletions
+145 -122
View File
@@ -715,128 +715,7 @@ void Pet::Update(uint32 diff)
}
}
if (m_tempspell)
{
Unit* tempspellTarget = nullptr;
if (!m_tempspellTarget.IsEmpty())
tempspellTarget = ObjectAccessor::GetUnit(*this, m_tempspellTarget);
Unit* tempoldTarget = nullptr;
if (!m_tempoldTarget.IsEmpty())
tempoldTarget = ObjectAccessor::GetUnit(*this, m_tempoldTarget);
bool tempspellIsPositive = m_tempspellIsPositive;
uint32 tempspell = m_tempspell;
Unit* charmer = GetCharmerOrOwner();
if (!charmer)
return;
if (!GetCharmInfo())
return;
if (tempspellTarget && tempspellTarget->IsAlive())
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tempspell);
if (!spellInfo)
return;
float max_range = GetSpellMaxRangeForTarget(tempspellTarget, spellInfo);
if (spellInfo->RangeEntry->Flags == SPELL_RANGE_MELEE)
max_range -= 2 * MIN_MELEE_REACH;
if (IsWithinLOSInMap(tempspellTarget) && GetDistance(tempspellTarget) < max_range)
{
if (!GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo) && !HasSpellCooldown(tempspell))
{
StopMoving();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
GetCharmInfo()->SetIsAtStay(true);
GetCharmInfo()->SetIsCommandFollow(false);
GetCharmInfo()->SetIsFollowing(false);
GetCharmInfo()->SetIsReturning(false);
GetCharmInfo()->SaveStayPosition(true);
AddSpellCooldown(tempspell, 0, spellInfo->IsCooldownStartedOnEvent() ? infinityCooldownDelay : 0);
CastSpell(tempspellTarget, tempspell, false);
m_tempspell = 0;
m_tempspellTarget = ObjectGuid::Empty;
if (tempspellIsPositive)
{
if (tempoldTarget && tempoldTarget->IsAlive())
{
GetCharmInfo()->SetIsCommandAttack(true);
GetCharmInfo()->SetIsAtStay(false);
GetCharmInfo()->SetIsFollowing(false);
GetCharmInfo()->SetIsCommandFollow(false);
GetCharmInfo()->SetIsReturning(false);
if (ToCreature() && ToCreature()->IsAIEnabled)
ToCreature()->AI()->AttackStart(tempoldTarget);
}
else
{
if (IsAIEnabled)
AI()->PetStopAttack();
else
{
GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
GetCharmInfo()->SetIsCommandAttack(false);
GetCharmInfo()->SetIsAtStay(false);
GetCharmInfo()->SetIsReturning(true);
GetCharmInfo()->SetIsCommandFollow(true);
GetCharmInfo()->SetIsFollowing(false);
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle());
}
}
m_tempoldTarget = ObjectGuid::Empty;
m_tempspellIsPositive = false;
}
}
}
}
else
{
m_tempspell = 0;
m_tempspellTarget = ObjectGuid::Empty;
m_tempoldTarget = ObjectGuid::Empty;
m_tempspellIsPositive = false;
Unit* victim = charmer->GetVictim();
if (victim && victim->IsAlive())
{
StopMoving();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
GetCharmInfo()->SetIsCommandAttack(true);
GetCharmInfo()->SetIsAtStay(false);
GetCharmInfo()->SetIsFollowing(false);
GetCharmInfo()->SetIsCommandFollow(false);
GetCharmInfo()->SetIsReturning(false);
if (ToCreature() && ToCreature()->IsAIEnabled)
ToCreature()->AI()->AttackStart(victim);
}
else
{
StopMoving();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
GetCharmInfo()->SetIsCommandAttack(false);
GetCharmInfo()->SetIsAtStay(false);
GetCharmInfo()->SetIsReturning(true);
GetCharmInfo()->SetIsCommandFollow(true);
GetCharmInfo()->SetIsFollowing(false);
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle());
}
}
}
CastPendingSpell();
if (getPetType() == HUNTER_PET)
{
@@ -857,6 +736,150 @@ void Pet::Update(uint32 diff)
Creature::Update(diff);
}
void Pet::CastPendingSpell()
{
if (!m_tempspell)
return;
Unit* tempSpellTarget = nullptr;
if (!m_tempspellTarget.IsEmpty())
tempSpellTarget = ObjectAccessor::GetUnit(*this, m_tempspellTarget);
Unit* tempOldTarget = nullptr;
if (!m_tempoldTarget.IsEmpty())
tempOldTarget = ObjectAccessor::GetUnit(*this, m_tempoldTarget);
bool tempSpellIsPositive = m_tempspellIsPositive;
uint32 tempSpell = m_tempspell;
Unit* charmer = GetCharmerOrOwner();
if (!charmer)
return;
CharmInfo* charmInfo = GetCharmInfo();
if (!charmInfo)
return;
if (!tempSpellTarget || !tempSpellTarget->IsAlive())
{
m_tempspell = 0;
m_tempspellTarget = ObjectGuid::Empty;
m_tempoldTarget = ObjectGuid::Empty;
m_tempspellIsPositive = false;
Unit* victim = charmer->GetVictim();
StopMoving();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
if (victim && victim->IsAlive())
{
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsReturning(false);
if (ToCreature() && ToCreature()->IsAIEnabled)
ToCreature()->AI()->AttackStart(victim);
}
else
{
charmInfo->SetCommandState(COMMAND_FOLLOW);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsCommandFollow(true);
charmInfo->SetIsReturning(true);
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle());
}
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tempSpell);
if (!spellInfo)
return;
float maxRange = GetSpellMaxRangeForTarget(tempSpellTarget, spellInfo);
if (spellInfo->RangeEntry->Flags == SPELL_RANGE_MELEE)
maxRange -= 2 * MIN_MELEE_REACH;
if (!IsWithinLOSInMap(tempSpellTarget) || GetDistance(tempSpellTarget) >= maxRange)
return;
if (charmInfo->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo) || HasSpellCooldown(tempSpell))
return;
StopMoving();
GetMotionMaster()->Clear(false);
GetMotionMaster()->MoveIdle();
charmInfo->SetIsAtStay(true);
charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
charmInfo->SaveStayPosition(true);
AddSpellCooldown(tempSpell, 0, spellInfo->IsCooldownStartedOnEvent() ? infinityCooldownDelay : 0);
CastSpell(tempSpellTarget, tempSpell, false);
m_tempspell = 0;
m_tempspellTarget = ObjectGuid::Empty;
if (tempSpellIsPositive)
{
if (tempOldTarget && tempOldTarget->IsAlive())
{
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsReturning(false);
if (ToCreature() && ToCreature()->IsAIEnabled)
ToCreature()->AI()->AttackStart(tempOldTarget);
}
else
{
if (IsAIEnabled)
{
// Do not use PetStopAttack here: it interrupts the spell just started above.
AttackStop();
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
}
else
{
charmInfo->SetCommandState(COMMAND_FOLLOW);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsCommandFollow(true);
charmInfo->SetIsReturning(true);
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle());
}
}
m_tempoldTarget = ObjectGuid::Empty;
m_tempspellIsPositive = false;
}
else if (HasReactState(REACT_PASSIVE) && charmInfo->HasCommandState(COMMAND_FOLLOW))
{
// Do not use PetStopAttack here: it interrupts the spell just started above.
// Once the cast finishes, PetAI will see no victim and resume following the owner.
AttackStop();
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
}
}
void Pet::LoseHappiness()
{
uint32 curValue = GetPower(POWER_HAPPINESS);
+1
View File
@@ -172,5 +172,6 @@ private:
{
ABORT();
}
void CastPendingSpell();
};
#endif
+17 -2
View File
@@ -462,6 +462,8 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
if (pet->isPossessed() || pet->IsVehicle())
Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
else if (charmInfo->HasCommandState(COMMAND_STAY))
spell->SendPetCastResult(result);
else if (GetPlayer()->IsFriendlyTo(unit_target) && !haspositiveeffect)
spell->SendPetCastResult(SPELL_FAILED_TARGET_FRIENDLY);
else
@@ -477,6 +479,13 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
if (_player->HasPacifyAura())
return;
if (charmInfo->HasCommandState(COMMAND_STAY))
{
charmInfo->SetForcedSpell(0);
charmInfo->SetForcedTargetGUID();
return;
}
bool tempspellIsPositive = false;
if (!GetPlayer()->IsFriendlyTo(unit_target))
@@ -574,7 +583,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
else
{
// dont spam alerts
if (!charmInfo->GetForcedSpell())
if (charmInfo->HasCommandState(COMMAND_STAY) || !charmInfo->GetForcedSpell())
{
if (pet->isPossessed() || pet->IsVehicle())
Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
@@ -589,7 +598,13 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
delete spell;
// reset specific flags in case of spell fail. AI will reset other flags
pet->PetSpellFail(spellInfo, unit_target, result);
if (charmInfo->HasCommandState(COMMAND_STAY))
{
charmInfo->SetForcedSpell(0);
charmInfo->SetForcedTargetGUID();
}
else
pet->PetSpellFail(spellInfo, unit_target, result);
}
break;
}