void HandleEffectDummy(SpellEffIndex /*effIndex*/) { Unit* caster = GetCaster(); Position destPos; GetHitDest()->GetPosition(&destPos); // Body and soul if (AuraEffect* bodyAndSoul = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 2218, EFFECT_0)) { switch (bodyAndSoul->GetAmount()) { case 30: caster->CastSpell(GetHitUnit(), 64128, true); break; case 60: caster->CastSpell(GetHitUnit(), 65081, true); break; } } SpellCastTargets targets; targets.SetDst(destPos); targets.SetUnitTarget(caster); GetHitUnit()->CastSpell(targets, sSpellMgr->GetSpellInfo(GetEffectValue()), NULL); }
void HandleEffectDummy(SpellEffIndex /*effIndex*/) { Position destPos = GetHitDest()->GetPosition(); SpellCastTargets targets; targets.SetDst(destPos); targets.SetUnitTarget(GetCaster()); GetHitUnit()->CastSpell(targets, sSpellMgr->GetSpellInfo(GetEffectValue()), NULL); }
void PetAI::UpdateAI(const uint32 diff) { if (!me->isAlive() || !me->GetCharmInfo()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (me->getVictim() && me->getVictim()->isAlive()) { // is only necessary to stop casting, the pet must not exit combat if (me->getVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { sLog->outDebug(LOG_FILTER_GENERAL, "Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->getVictim()))) DoMeleeAttackIfReady(); } else DoMeleeAttackIfReady(); } else { if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay()) { // Every update we need to check targets only in certain cases // Aggressive - Allow auto select if owner or pet don't have a target // Stay - Only pick from pet or owner targets / attackers so targets won't run by // while chasing our owner. Don't do auto select. // All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // check spell cooldown if (me->HasSpellCooldown(spellInfo->Id)) continue; // Check if we're in combat or commanded to attack if (!me->isInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->getAttackerForHelper(); if (!target && owner) target = owner->getAttackerForHelper(); if (target) { if (CanAttack(target) && spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair(target, spell)); spellUsed = true; } } if (spellInfo->HasEffect(SPELL_EFFECT_JUMP_DEST)) continue; // Pets must only jump to target // No enemy, check friendly if (!spellUsed) { for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* ally = ObjectAccessor::GetUnit(*me, *tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) continue; if (spell->CanAutoCast(ally)) { targetSpellStore.push_back(std::make_pair(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) delete spell; } else if (me->getVictim() && CanAttack(me->getVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->getVictim())) targetSpellStore.push_back(std::make_pair(me->getVictim(), spell)); else delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.SetUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Update speed as needed to prevent dropping too far behind and despawning me->UpdateSpeed(MOVE_RUN, true); me->UpdateSpeed(MOVE_WALK, true); me->UpdateSpeed(MOVE_FLIGHT, true); }
void PetAI::UpdateAI(const uint32 diff) { if (!me->isAlive()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. // Must also check if victim is alive if (me->getVictim() && me->getVictim()->isAlive()) { // is only necessary to stop casting, the pet must not exit combat if (me->getVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { sLog->outDebug(LOG_FILTER_GENERAL, "Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } DoMeleeAttackIfReady(); } else if (owner && me->GetCharmInfo()) //no victim { // Only aggressive pets do target search every update. // Defensive pets do target search only in these cases: // * Owner attacks something - handled by OwnerAttacked() // * Owner receives damage - handled by OwnerDamagedBy() // * Pet is in combat and current target dies - handled by KilledUnit() if (me->HasReactState(REACT_AGGRESSIVE)) { Unit* nextTarget = SelectNextTarget(); if (nextTarget) AttackStart(nextTarget); else { me->GetCharmInfo()->SetIsCommandAttack(false); HandleReturnMovement(); } } else { me->GetCharmInfo()->SetIsCommandAttack(false); HandleReturnMovement(); } } else if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW)) // no charm info and no victim HandleReturnMovement(); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // check spell cooldown if (me->HasSpellCooldown(spellInfo->Id)) continue; // Check if we're in combat or commanded to attack if (!me->isInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->getAttackerForHelper(); if (!target && owner) target = owner->getAttackerForHelper(); if (target) { if (CanAttack(target) && spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair(target, spell)); spellUsed = true; } } // No enemy, check friendly if (!spellUsed) { for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* ally = ObjectAccessor::GetUnit(*me, *tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) continue; if (spell->CanAutoCast(ally)) { targetSpellStore.push_back(std::make_pair(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) delete spell; } else if (me->getVictim() && CanAttack(me->getVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->getVictim())) targetSpellStore.push_back(std::make_pair(me->getVictim(), spell)); else delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.SetUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(const uint32 diff) { if (!me->isAlive()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. if (me->getVictim()) { if (_needToStop()) { sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } targetHasCC = _CheckTargetCC(me->getVictim()); DoMeleeAttackIfReady(); } else if (owner && me->GetCharmInfo()) //no victim { Unit* nextTarget = SelectNextTarget(); if (me->HasReactState(REACT_PASSIVE)) _stopAttack(); else if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else if (owner && !me->HasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STAT_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; if (spellInfo->IsPositive()) { // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (spellInfo->CanBeUsedInCombat()) { // allow only spell without spell cost or with spell cost but not duration limit int32 duration = spellInfo->GetDuration(); if ((spellInfo->ManaCost || spellInfo->ManaCostPercentage || spellInfo->ManaPerSecond) && duration > 0) continue; // allow only spell without cooldown > duration int32 cooldown = spellInfo->GetRecoveryTime(); if (cooldown >= 0 && duration >= 0 && cooldown > duration) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* target = ObjectAccessor::GetUnit(*me, *tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!target) continue; if (spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } else if (me->getVictim() && CanAttack(me->getVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->getVictim())) targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(me->getVictim(), spell)); else delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.SetUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(const uint32 diff) { if (!me->isAlive()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. if (me->getVictim()) { // is only necessary to stop casting, the pet must not exit combat if (me->getVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } DoMeleeAttackIfReady(); } else if (owner && me->GetCharmInfo()) //no victim { Unit* nextTarget = SelectNextTarget(); if (me->HasReactState(REACT_PASSIVE)) _stopAttack(); else if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW)) // no charm info and no victim me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; // Check global cooldown if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // Check spell cooldown if (me->HasSpellCooldown(spellInfo->Id)) continue; // Check if pet is in combat and if spell can be cast if (me->isInCombat() && !spellInfo->CanBeUsedInCombat()) continue; // Prevent spells like Furious Howl from constantly casting out of // combat when the cooldown is up if (!me->isInCombat() && !spellInfo->NeedsToBeTriggeredByCaster()) continue; // We have a spell we can cast, let's pick a target if (spellInfo->IsPositive()) { // These would be buff spells like Furious Howl, Consume Shadows, etc. Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* target = ObjectAccessor::GetUnit(*me, *tar); if (!target) continue; if (spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } else if (me->getVictim() && CanAttack(me->getVictim())) { // These would be offensive spells like Claw, Bite, Torment, Fireball, etc. Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->getVictim())) targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(me->getVictim(), spell)); else delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.SetUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(uint32 diff) { if (!me->IsAlive() || !me->GetCharmInfo()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (me->GetVictim() && me->GetVictim()->IsAlive()) { // is only necessary to stop casting, the pet must not exit combat if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { ;//sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim()))) _doMeleeAttack(); } else _doMeleeAttack(); } else if (!me->GetCharmInfo() || (!me->GetCharmInfo()->GetForcedSpell() && !me->HasUnitState(UNIT_STATE_CASTING))) { if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay()) { // Every update we need to check targets only in certain cases // Aggressive - Allow auto select if owner or pet don't have a target // Stay - Only pick from pet or owner targets / attackers so targets won't run by // while chasing our owner. Don't do auto select. // All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // xinef: charm info must be always available if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { if (owner && owner->GetTypeId() == TYPEID_PLAYER && me->GetCharmInfo()->GetForcedSpell() && me->GetCharmInfo()->GetForcedTarget()) { owner->ToPlayer()->GetSession()->HandlePetActionHelper(me, me->GetGUID(), abs(me->GetCharmInfo()->GetForcedSpell()), ACT_ENABLED, me->GetCharmInfo()->GetForcedTarget()); // xinef: if spell was casted properly and we are in passive mode, handle return if (!me->GetCharmInfo()->GetForcedSpell() && me->HasReactState(REACT_PASSIVE)) { if (me->HasUnitState(UNIT_STATE_CASTING)) { me->GetMotionMaster()->Clear(false); me->StopMoving(); } else _stopAttack(); } return; } // xinef: dont allow ghouls to cast spells below 75 energy if (me->IsPet() && me->ToPet()->IsPetGhoul() && me->GetPower(POWER_ENERGY) < 75) return; typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // check spell cooldown, this should be checked in CheckCast... if (me->HasSpellCooldown(spellInfo->Id)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // Check if we're in combat or commanded to attack if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); spell->LoadScripts(); // xinef: load for CanAutoCast (calling CheckPetCast) bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->getAttackerForHelper(); if (!target && owner) target = owner->getAttackerForHelper(); if (target) { if (CanAttack(target) && spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair(target, spell)); spellUsed = true; } } // No enemy, check friendly if (!spellUsed) { for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* ally = ObjectAccessor::GetUnit(*me, *tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) continue; if (spell->CanAutoCast(ally)) { targetSpellStore.push_back(std::make_pair(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) delete spell; } else if (me->GetVictim() && CanAttack(me->GetVictim(), spellInfo) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->GetVictim())) targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell)); else delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.SetUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->AddSpellCooldown(spell->m_spellInfo->Id, 0, 0); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(uint32 diff) { if (!me->IsAlive() || !me->GetCharmInfo()) return; Unit* owner = me->GetCharmerOrOwner(); if (_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else _updateAlliesTimer -= diff; if (me->GetVictim() && me->EnsureVictim()->IsAlive()) { // is only necessary to stop casting, the pet must not exit combat if (!me->GetCurrentSpell(CURRENT_CHANNELED_SPELL) && // ignore channeled spells (Pin, Seduction) me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (NeedToStop()) { TC_LOG_TRACE("scripts.ai.petai", "PetAI::UpdateAI: AI stopped attacking %s", me->GetGUID().ToString().c_str()); StopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim()))) DoMeleeAttackIfReady(); } else DoMeleeAttackIfReady(); } else { if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay()) { // Every update we need to check targets only in certain cases // Aggressive - Allow auto select if owner or pet don't have a target // Stay - Only pick from pet or owner targets / attackers so targets won't run by // while chasing our owner. Don't do auto select. // All other cases (ie: defensive) - Targets are assigned by DamageTaken(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (cast only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo)) continue; // check spell cooldown if (!me->GetSpellHistory()->IsReady(spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // Check if we're in combat or commanded to attack if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->getAttackerForHelper(); if (!target && owner) target = owner->getAttackerForHelper(); if (target) { if (CanAttack(target) && spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair(target, spell)); spellUsed = true; } } if (spellInfo->HasEffect(SPELL_EFFECT_JUMP_DEST)) { if (!spellUsed) delete spell; continue; // Pets must only jump to target } // No enemy, check friendly if (!spellUsed) { for (GuidSet::const_iterator tar = _allySet.begin(); tar != _allySet.end(); ++tar) { Unit* ally = ObjectAccessor::GetUnit(*me, *tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) continue; if (spell->CanAutoCast(ally)) { targetSpellStore.push_back(std::make_pair(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) delete spell; } else if (me->GetVictim() && CanAttack(me->GetVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); if (spell->CanAutoCast(me->GetVictim())) targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell)); else delete spell; } } // found units to cast on to if (!targetSpellStore.empty()) { TargetSpellList::iterator it = targetSpellStore.begin(); std::advance(it, urand(0, targetSpellStore.size() - 1)); Spell* spell = (*it).second; Unit* target = (*it).first; targetSpellStore.erase(it); SpellCastTargets targets; targets.SetUnitTarget(target); spell->prepare(targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Update speed as needed to prevent dropping too far behind and despawning me->UpdateSpeed(MOVE_RUN); me->UpdateSpeed(MOVE_WALK); me->UpdateSpeed(MOVE_FLIGHT); }