void TotemAI::UpdateAI(const uint32 /*diff*/) { if (i_totem.GetTotemType() != TOTEM_ACTIVE) return; if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell()); if (!spellInfo) return; // Get spell rangy SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); float max_range = GetSpellMaxRange(srange); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(i_totem, i_victimGuid) : NULL; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) || i_totem.IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(&i_totem,false)) { CellPair p(BlizzLike::ComputeCellPair(i_totem.GetPositionX(),i_totem.GetPositionY())); Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; victim = NULL; BlizzLike::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range); BlizzLike::UnitLastSearcher<BlizzLike::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); TypeContainerVisitor<BlizzLike::UnitLastSearcher<BlizzLike::NearestAttackableUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker); TypeContainerVisitor<BlizzLike::UnitLastSearcher<BlizzLike::NearestAttackableUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); //TODO: Backport BLizzLike Add to CreatureAI field pointing to creature itself //cell.Visit(p, grid_object_checker, *m_creature.GetMap(), *m_creature, max_range); //cell.Visit(p, world_object_checker, *m_creature.GetMap(), *m_creature, max_range); cell.Visit(p, grid_object_checker, *i_totem.GetMap()); cell.Visit(p, world_object_checker, *i_totem.GetMap()); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack i_totem.SetInFront(victim); // client change orientation by self i_totem.CastSpell(victim, i_totem.GetSpell(), false); } else i_victimGuid = 0; }
bool PetAI::_needToStop() const { Unit* pVictim = m_creature->getVictim(); if (!pVictim) return true; // This is needed for charmed creatures, as once their target was reset other effects can trigger threat if (m_creature->isCharmed() && m_creature->GetCharmer() == pVictim) return true; Unit* owner = m_creature->GetCharmerOrOwner(); if (owner == pVictim) return true; if (!pVictim->isVisibleForOrDetect(m_creature, m_creature, false)) return true; if (owner && !pVictim->isVisibleForOrDetect(owner, owner, true)) return true; if (m_primaryTargetGuid && m_primaryTargetGuid != pVictim->GetObjectGuid()) return true; return !pVictim->isTargetableForAttack(); }
void TotemAI::UpdateAI(const uint32 /*diff*/) { if (i_totem.GetTotemType() != TOTEM_ACTIVE) return; i_totem.SetSelection(0); if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell()); if (!spellInfo) return; // Get spell rangy SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); float max_range = SpellMgr::GetSpellMaxRange(srange); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any Unit* victim = i_victimGuid ? i_totem.GetMap()->GetUnit(i_victimGuid) : NULL; if (!max_range) victim = &i_totem; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || (!SpellMgr::SpellIgnoreLOS(spellInfo, 0) && !i_totem.IsWithinLOSInMap(victim)) || !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) || (i_totem.IsFriendlyTo(victim) && victim != &i_totem) || !victim->isVisibleForOrDetect(&i_totem, &i_totem, false)) { victim = NULL; Looking4group::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range); Looking4group::UnitLastSearcher<Looking4group::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); Cell::VisitAllObjects(m_creature, checker, max_range); } // If have target if (victim) { //this should prevent target-type totems from attacking from unattackable zones and attacking while being unattackable if ((i_totem.isInSanctuary() || victim->isInSanctuary()) && victim->GetCharmerOrOwnerPlayerOrPlayerItself()) return; // remember i_victimGuid = victim->GetGUID(); // attack i_totem.CastSpell(victim, i_totem.GetSpell(), false, NULL, NULL, i_totem.GetOwner()->GetGUID()); } else i_victimGuid = 0; //i_totem.SetFacingToObject(&i_totem); }
void TotemAI::UpdateAI(const uint32 diff) { if (i_totem.GetTotemType() != TOTEM_ACTIVE) return; if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell()); if (!spellInfo) return; // Get spell rangy SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); float max_range = GetMaxRange(srange); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems Unit* victim = NULL; // pointer to appropriate target if found any // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if( !i_victimGuid || !(victim = ObjectAccessor::GetUnit(i_totem, i_victimGuid)) || !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) || i_totem.IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(&i_totem,false) ) { CellPair p(MaNGOS::ComputeCellPair(i_totem.GetPositionX(),i_totem.GetPositionY())); Cell cell = RedZone::GetZone(p); cell.data.Part.reserved = ALL_DISTRICT; victim = NULL; MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range); MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker); TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack i_totem.SetInFront(victim); // client change orientation by self i_totem.CastSpell(victim, i_totem.GetSpell(), false); } else i_victimGuid = 0; }
void TotemAI::UpdateAI(const uint32 /*diff*/) { if (getTotem().GetTotemType() != TOTEM_ACTIVE) return; if (!m_creature->isAlive() || m_creature->IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const *spellInfo = sSpellStore.LookupEntry(getTotem().GetSpell()); if (!spellInfo) return; SpellMiscEntry const* spellMisc = sSpellMiscStore.LookupEntry(spellInfo->Id); // Get spell rangy SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellMisc->rangeIndex); float max_range = GetSpellMaxRange(srange); // SPELLMOD_RANGE not applied in this place just because nonexistent range mods for attacking totems // pointer to appropriate target if found any Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid); // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if( !victim || !victim->isTargetableForAttack() || !m_creature->IsWithinDistInMap(victim, max_range) || m_creature->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(m_creature,m_creature,false) ) { victim = NULL; Strawberry::NearestAttackableUnitInObjectRangeCheck u_check(m_creature, m_creature, max_range); Strawberry::UnitLastSearcher<Strawberry::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); Cell::VisitAllObjects(m_creature, checker, max_range); } // If have target if (victim) { // remember i_victimGuid = victim->GetObjectGuid(); // attack m_creature->SetInFront(victim); // client change orientation by self m_creature->CastSpell(victim, getTotem().GetSpell(), false); } else i_victimGuid.Clear(); }
Unit* PetAI::GetPrimaryTarget() { if (m_primaryTargetGuid.IsEmpty() || !m_primaryTargetGuid.IsUnit()) return NULL; Unit* target = m_creature->GetMap()->GetUnit(m_primaryTargetGuid); if (!target || !target->isAlive() || !target->isTargetableForAttack() || !target->isInAccessablePlaceFor(m_creature)) { m_primaryTargetGuid.Clear(); return NULL; } return target; }
void TotemAI::UpdateAI(const uint32 /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->isAlive() || me->IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const *spellInfo = sSpellStore.LookupEntry( me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry( spellInfo->rangeIndex); float max_range = GetSpellMaxRangeForHostile(srange); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->canSeeOrDetect(victim)) { victim = NULL; Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitLastSearcher< Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); me->VisitNearbyObject(max_range, checker); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false); } else i_victimGuid = 0; }
void TotemAI::UpdateAI(uint32 /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->IsAlive() || me->IsNonMeleeSpellCast(false)) return; // Search spell SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range float max_range = spellInfo->GetMaxRange(false); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any Unit* victim = !i_victimGuid.IsEmpty() ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim)) { victim = NULL; Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me->GetCharmerOrOwnerOrSelf(), max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); Cell::VisitAllObjects(me, checker, max_range); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false); } else i_victimGuid.Clear(); }
void TotemAI::UpdateAI(uint32 const /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->IsAlive()) return; // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; if (me->IsNonMeleeSpellCasted(false)) { if (victim && victim->HasCrowdControlAura()) victim = NULL; else return; } // Search spell SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range float max_range = spellInfo->GetMaxRange(false); // Apply SPELLMOD_RANGE from owner (required by Elemental Reach talent at least) if (Player * const modOwner = me->GetSpellModOwner()) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RANGE, max_range); // Searing Totem prioritize Flame Shock or Stormstrike targets if (spellInfo->Id == 3606) { if (Unit * caster = me->GetOwner()) { std::list<Unit*> targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check); Trinity::VisitNearbyObject(caster, max_range, searcher); // Find Stormstrike and Flame Shock -> Stormstrike -> Flame Shock const uint64 guid = caster->GetGUID(); auto itr = std::find_if(targets.begin(), targets.end(), [guid](Unit *u) { return u->HasAura(17364, guid) && u->HasAura(8050, guid); }); if (itr == targets.end()) itr = std::find_if(targets.begin(), targets.end(), [guid](Unit *u) { return u->HasAura(17364, guid); }); if (itr == targets.end()) itr = std::find_if(targets.begin(), targets.end(), [guid](Unit *u) { return u->HasAura(8050, guid); }); if (itr != targets.end()) victim = *itr; } if (!victim) { victim = me->GetOwner()->GetVictim(); if (!victim) return; } } // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->canSeeOrDetect(victim) || victim->HasCrowdControlAura()) { victim = NULL; Trinity::NearestAttackableNoCCUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableNoCCUnitInObjectRangeCheck> checker(me, victim, u_check); Trinity::VisitNearbyObject(me, max_range, checker); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false); } else i_victimGuid = 0; }
void TotemAI::UpdateAI(uint32 /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->isAlive() || me->IsNonMeleeSpellCasted(false)) return; // Search spell SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range float max_range = spellInfo->GetMaxRange(false); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // Nel caso del searing totem, si cerca prima un target che abbia il dot di flame shock // in caso contrario, si cerca il target più vicino if (me->GetEntry() == 2523) { std::list<Unit*> targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check); me->VisitNearbyObject(max_range, searcher); for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) { if ((*iter) && (*iter)->isTargetableForAttack() && me->IsWithinDistInMap((*iter), max_range) && !me->IsFriendlyTo((*iter)) && me->canSeeOrDetect((*iter)) && ((*iter)->HasAura(8050)|| (*iter)->HasAura(17364))) { i_victimGuid = (*iter)->GetGUID(); break; } else if (!(*iter)->HasAura(8050) && !(*iter)->HasAura(17364)) { Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); me->VisitNearbyObject(max_range, checker); if (victim) i_victimGuid = victim->GetGUID(); } } Unit* memtarget = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; if (memtarget) { me->CastSpell(memtarget, me->ToTotem()->GetSpell(), false); } } else { // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->canSeeOrDetect(victim)) { victim = NULL; Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); me->VisitNearbyObject(max_range, checker); } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false); } else i_victimGuid = 0; } }
void PetAI::UpdateAI(const uint32 diff) { if (!m_unit->isAlive()) return; Creature* creature = (m_unit->GetTypeId() == TYPEID_UNIT) ? static_cast<Creature*>(m_unit) : nullptr; Pet* pet = (creature && creature->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr; Unit* owner = m_unit->GetMaster(); if (!owner) return; Unit* victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && !victim) { m_unit->AttackStop(true, true); inCombat = false; } CharmInfo* charminfo = m_unit->GetCharmInfo(); MANGOS_ASSERT(charminfo); if (charminfo->GetIsRetreating()) { if (!owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) { if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW)) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); return; } else charminfo->SetIsRetreating(); } else if (charminfo->GetSpellOpener() != 0) // have opener stored { uint32 minRange = charminfo->GetSpellOpenerMinRange(); if (!(victim = m_unit->getVictim()) || (minRange != 0 && m_unit->IsWithinDistInMap(victim, minRange))) charminfo->SetSpellOpener(); else if (m_unit->IsWithinDistInMap(victim, charminfo->GetSpellOpenerMaxRange()) && m_unit->IsWithinLOSInMap(victim)) { // stop moving m_unit->clearUnitState(UNIT_STAT_MOVING); // auto turn to target m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); uint32 spell_id = charminfo->GetSpellOpener(); SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spell_id); Spell* spell = new Spell(m_unit, spellInfo, false); SpellCastResult result = spell->CheckPetCast(victim); if (result == SPELL_CAST_OK) spell->SpellStart(&(spell->m_targets)); else delete spell; charminfo->SetSpellOpener(); } else return; } // Auto cast (casted only in combat or persistent spells in any state) else if (!m_unit->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; if (pet) { for (uint8 i = 0; i < pet->GetPetAutoSpellSize(); ++i) { uint32 spellID = pet->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellID); if (!spellInfo) continue; if (!m_unit->IsSpellReady(*spellInfo)) continue; // ignore some combinations of combat state and combat/non combat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this requirements: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // allow only spell not on cooldown if (cooldown != 0 && duration < cooldown) continue; // not allow instant kill auto casts as full health cost if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL)) continue; } } // just ignore non-combat spells else if (IsNonCombatSpell(spellInfo)) continue; Spell* spell = new Spell(m_unit, spellInfo, false); if (inCombat && !m_unit->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(victim)) { targetSpellStore.push_back(TargetSpellList::value_type(victim, spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_unit->GetMap()->GetUnit(*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(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) 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 (!m_unit->HasInArc(M_PI_F, target)) { m_unit->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } spell->SpellStart(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Stop here if casting spell (No melee and no movement) if (m_unit->IsNonMeleeSpellCasted(false)) return; // we may get our actions disabled during spell casting, so do entire recheck for victim victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (victim) { // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. // This is needed for charmed creatures, as once their target was reset other effects can trigger threat if (!victim->isTargetableForAttack()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_unit->GetGUIDLow()); m_unit->CombatStop(); inCombat = false; return; } // if pet misses its target, it will also be the first in threat list if ((!creature || !(creature->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_MELEE)) && m_unit->CanReachWithMeleeAttack(victim)) { if (!m_unit->HasInArc(2 * M_PI_F / 3, victim)) { m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } DoMeleeAttackIfReady(); } else if (!m_unit->hasUnitState(UNIT_STAT_MOVING)) AttackStart(victim); } else if (owner) { CharmInfo* charmInfo = m_unit->GetCharmInfo(); if (owner->isInCombat() && !(charmInfo && charmInfo->HasReactState(REACT_PASSIVE))) AttackStart(owner->getAttackerForHelper()); else { if (charmInfo && charmInfo->HasCommandState(COMMAND_STAY)) { //if stay command is set but we don't have stay pos set then we need to establish current pos as stay position if (!charminfo->IsStayPosSet()) charminfo->SetStayPosition(true); float stayPosX = charminfo->GetStayPosX(); float stayPosY = charminfo->GetStayPosY(); float stayPosZ = charminfo->GetStayPosZ(); if (m_unit->GetPositionX() == stayPosX && m_unit->GetPositionY() == stayPosY && m_unit->GetPositionZ() == stayPosZ) { float StayPosO = charminfo->GetStayPosO(); if (m_unit->hasUnitState(UNIT_STAT_MOVING)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } else if (m_unit->GetOrientation() != StayPosO) m_unit->SetOrientation(StayPosO); } else m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false); } else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW)) { if (owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } } else if (charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW) && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } }
void TotemAI::UpdateAI(const uint32 /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->isAlive() || me->IsNonMeleeSpellCasted(false)) return; // Search spell SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range float max_range = spellInfo->GetMaxRange(false); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; Unit* tmpvictim = 0; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || (!victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->canSeeOrDetect(victim))) { Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); me->VisitNearbyObject(max_range, checker); tmpvictim = victim; } else if(!(victim->HasAura(FLAME_SHOCK, me->GetOwnerGUID()) || victim->HasAura(STORMSTRIKE, me->GetOwnerGUID()))) tmpvictim = victim; Unit* owner = me->GetOwner(); // looking for attacker with specific auras, do only if new victim found in radius or current victim has no aura if (owner && tmpvictim) { if (Unit* ownervictim = owner->getVictim()) { //Owner's target is priority if (me->IsWithinDistInMap(ownervictim, max_range) && (ownervictim->HasAura(FLAME_SHOCK, owner->GetGUID()) || ownervictim->HasAura(STORMSTRIKE, owner->GetGUID()))) victim = ownervictim; else { Unit::AttackerSet attackers = owner->getAttackers(); for (Unit::AttackerSet::iterator itr = attackers.begin(); itr != attackers.end(); ++itr) { tmpvictim = *itr; if (me->IsWithinDistInMap(tmpvictim, max_range) && (tmpvictim->HasAura(FLAME_SHOCK, owner->GetGUID()) || tmpvictim->HasAura(STORMSTRIKE, owner->GetGUID()))) victim = tmpvictim; } } } } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false, NULL, NULL, me->GetOwnerGUID()); } else i_victimGuid = 0; }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive() || !m_creature->GetCharmInfo()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // First checking if we have some taunt on us Unit* tauntTarget = NULL; const Unit::AuraList& tauntAuras = m_creature->GetAurasByType(SPELL_AURA_MOD_TAUNT); if (!tauntAuras.empty()) { Unit* caster = NULL; // Auras are pushed_back, last caster will be on the end Unit::AuraList::const_iterator aura = tauntAuras.end(); while (aura != tauntAuras.begin()) { --aura; caster = (*aura)->GetCaster(); if (caster && caster->isTargetableForAttack()) { tauntTarget = caster; break; } } if (tauntTarget) DoAttack(tauntTarget, true); } if (m_creature->getVictim() && m_creature->getVictim()->isAlive()) { if (_needToStop()) { _stopAttack(); return; } if (hasMelee) { // Check before attacking to prevent pets from leaving stay position bool attacked = false; if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (m_creature->GetCharmInfo()->IsCommandAttack() || (m_creature->GetCharmInfo()->IsAtStay() && m_creature->CanReachWithMeleeAttack(m_creature->getVictim()))) attacked = DoMeleeAttackIfReady(); } else attacked = DoMeleeAttackIfReady(); if (attacked && owner) if (Unit* v = m_creature->getVictim()) // Victim may have died between owner->SetInCombatWith(v); } } else { if (m_creature->HasReactState(REACT_AGGRESSIVE) || m_creature->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(m_creature->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (casted only in combat or persistent spells in any state) if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellID); if (!spellInfo) continue; if (m_creature->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // check spell cooldown if (m_creature->HasSpellCooldown(spellInfo->Id)) continue; if (IsPositiveSpell(spellInfo->Id)) { if (!IsNonCombatSpell(spellInfo)) // Can be used in combat. { /* Spells handled here: Dash (1850), Dive (23145), Furious Howl (24604), Tainted Blood (19478) Blood Pact (6307), Fire Shield (11771), Sacrifice ... Consume Shadows (17767) */ // Warlock Sacrifice: do not auto cast if not in combat bool castOnlyInCombat = IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL); if (!castOnlyInCombat) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // Keep this spell for when we will be in combat. if (cooldown >= 0 && duration >= 0 && cooldown > duration) castOnlyInCombat = true; } // 19478 - Tainted Blood, rank 1 enUS if (spellInfo->SpellIconID == 153) castOnlyInCombat = true; // 2947 - Fire Shield, rank 1 enUS // When set to auto-cast, the Imp will cast this on any party members within 30 yds if they receive a melee attack. if (spellInfo->IsFitToFamily<SPELLFAMILY_WARLOCK, CF_WARLOCK_IMP_BUFFS>() && spellInfo->SpellVisual == 289) castOnlyInCombat = false; // Furious Howl: in combat only if (IsSpellHaveAura(spellInfo, SPELL_AURA_MOD_DAMAGE_DONE)) castOnlyInCombat = true; if (castOnlyInCombat && !m_creature->getVictim()) continue; } Spell *spell = new Spell(m_creature, spellInfo, false); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = m_creature->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 = m_creature->GetMap()->GetUnit(*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) spell->Delete(); } else if (m_creature->getVictim() && CanAttack(m_creature->getVictim()) && !IsNonCombatSpell(spellInfo)) { Spell *spell = new Spell(m_creature, spellInfo, false); if (spell->CanAutoCast(m_creature->getVictim())) targetSpellStore.push_back(std::make_pair(m_creature->getVictim(), spell)); else spell->Delete(); } } //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 (!m_creature->HasInArc(M_PI_F, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); } if (((Creature*)m_creature)->IsPet()) ((Pet*)m_creature)->CheckLearning(spell->m_spellInfo->Id); // 10% chance to play special pet attack talk, else growl // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell if (((Creature*)m_creature)->IsPet() && (((Pet*)m_creature)->getPetType() == SUMMON_PET) && (m_creature != target) && (urand(0, 100) < 10)) m_creature->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else m_creature->SendPetAIReaction(); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) itr->second->Delete(); } // Update speed as needed to prevent dropping too far behind and despawning m_creature->UpdateSpeed(MOVE_RUN, true); m_creature->UpdateSpeed(MOVE_WALK, true); }
void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; float x, y, z; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; recv_data >> x >> y >> z; uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1 DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str()); // used also for charmed creature/player Unit* pet = _player->GetMap()->GetUnit(petGuid); if (!pet) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (_player->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && pet->GetCharmer()->GetTypeId() == TYPEID_PLAYER) { // controller player cannot use controlled player's spells if (flag != (ACT_COMMAND || ACT_REACTION)) return; } else if (((Creature*)pet)->IsPet()) { // pet can have action bar disabled if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; } CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } switch (flag) { case ACT_COMMAND: // 0x07 switch (spellid) { case COMMAND_STAY: // flat=1792 // STAY { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(true); ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(); ((Pet*)pet)->SetIsRetreating(true); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != pet && targetUnit->isTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet)) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != targetUnit) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(targetUnit); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(targetUnit, true); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) { Creature* petC = (Creature*)pet; if (petC->IsPet()) { Pet* p = (Pet*)petC; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->SetDeathState(CORPSE); } else // charmed _player->Uncharm(); if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed { petC->ForcedDespawn(); return; } ((Pet*)pet)->SetStayPosition(); break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } break; case ACT_REACTION: // 0x6 switch (spellid) { case REACT_PASSIVE: // passive { pet->AttackStop(true, true); ((Pet*)pet)->SetSpellOpener(); } case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete { charmInfo->SetReactState(ReactStates(spellid)); break; } } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); if (!spellEffect) continue; if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); SpellCastResult result = spell->CheckPetCast(unit_target); const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); if (unit_target && !(pet->IsWithinDistInMap(unit_target, sRange->maxRange) && pet->IsWithinLOSInMap(unit_target)) && !(GetPlayer()->IsFriendlyTo(unit_target) || pet->HasAuraType(SPELL_AURA_MOD_POSSESS))) { ((Pet*)pet)->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; pet->AttackStop(); pet->GetMotionMaster()->Clear(); ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); return; } // auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); _player->SetInCombatState(true, unit_target); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(unit_target, true); } } ((Pet*)pet)->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); } if (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); ((Pet*)pet)->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void UpdateAI(const uint32 diff) { if (!TankGUID) { if (!UpdateVictim()) return; if (me->GetHealth() < health_20 * (4 - Phase)) EnterPhase(Phase + 1); } if (Berserk_Timer <= diff) { DoCast(me, SPELL_BERSERK, true); me->MonsterYell(YELL_BERSERK, LANG_UNIVERSAL, NULL); DoPlaySoundToSet(me, SOUND_BERSERK); Berserk_Timer = 60000; } else Berserk_Timer -= diff; switch (Phase) { case 0: if (Intro_Timer) { if (Intro_Timer <= diff) { me->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL); DoPlaySoundToSet(me, SOUND_AGGRO); Intro_Timer = 0; } else Intro_Timer -= diff; } if (Whirlwind_Timer <= diff) { DoCast(me, SPELL_WHIRLWIND); Whirlwind_Timer = 15000 + rand()%5000; } else Whirlwind_Timer -= diff; if (Grievous_Throw_Timer <= diff) { if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(pTarget, SPELL_GRIEVOUS_THROW, false); Grievous_Throw_Timer = 10000; } else Grievous_Throw_Timer -= diff; break; case 1: if (Creeping_Paralysis_Timer <= diff) { DoCast(me, SPELL_CREEPING_PARALYSIS); Creeping_Paralysis_Timer = 20000; } else Creeping_Paralysis_Timer -= diff; if (Overpower_Timer <= diff) { // implemented in DoMeleeAttackIfReady() Overpower_Timer = 0; } else Overpower_Timer -= diff; break; case 2: return; case 3: if (Claw_Rage_Timer <= diff) { if (!TankGUID) { if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0)) { TankGUID = me->getVictim()->GetGUID(); me->SetSpeed(MOVE_RUN, 5.0f); AttackStart(pTarget); // change victim Claw_Rage_Timer = 0; Claw_Loop_Timer = 500; Claw_Counter = 0; } } else if (!Claw_Rage_Timer) // do not do this when Lynx_Rush { if (Claw_Loop_Timer <= diff) { Unit *pTarget = me->getVictim(); if (!pTarget || !pTarget->isTargetableForAttack()) pTarget = Unit::GetUnit(*me, TankGUID); if (!pTarget || !pTarget->isTargetableForAttack()) pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0); if (pTarget) { AttackStart(pTarget); if (me->IsWithinMeleeRange(pTarget)) { DoCast(pTarget, SPELL_CLAW_RAGE_DAMAGE, true); ++Claw_Counter; if (Claw_Counter == 12) { Claw_Rage_Timer = 15000 + rand()%5000; me->SetSpeed(MOVE_RUN, 1.2f); AttackStart(Unit::GetUnit(*me, TankGUID)); TankGUID = 0; return; } else Claw_Loop_Timer = 500; } } else { EnterEvadeMode(); // if (pTarget) return; } } else Claw_Loop_Timer -= diff; } //if (TankGUID) } else Claw_Rage_Timer -= diff; if (Lynx_Rush_Timer <= diff) { if (!TankGUID) { if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0)) { TankGUID = me->getVictim()->GetGUID(); me->SetSpeed(MOVE_RUN, 5.0f); AttackStart(pTarget); // change victim Lynx_Rush_Timer = 0; Claw_Counter = 0; } } else if (!Lynx_Rush_Timer) { Unit *pTarget = me->getVictim(); if (!pTarget || !pTarget->isTargetableForAttack()) { pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0); AttackStart(pTarget); } if (pTarget) { if (me->IsWithinMeleeRange(pTarget)) { DoCast(pTarget, SPELL_LYNX_RUSH_DAMAGE, true); ++Claw_Counter; if (Claw_Counter == 9) { Lynx_Rush_Timer = 15000 + rand()%5000; me->SetSpeed(MOVE_RUN, 1.2f); AttackStart(Unit::GetUnit(*me, TankGUID)); TankGUID = 0; } else AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0)); } } else { EnterEvadeMode(); // if (pTarget) return; } } //if (TankGUID) } else Lynx_Rush_Timer -= diff; break; case 4: if (Flame_Whirl_Timer <= diff) { DoCast(me, SPELL_FLAME_WHIRL); Flame_Whirl_Timer = 12000; }Flame_Whirl_Timer -= diff; if (Pillar_Of_Fire_Timer <= diff) { if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0)) DoCast(pTarget, SPELL_SUMMON_PILLAR); Pillar_Of_Fire_Timer = 10000; } else Pillar_Of_Fire_Timer -= diff; if (Flame_Breath_Timer <= diff) { if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0)) me->SetInFront(pTarget); DoCast(me, SPELL_FLAME_BREATH); Flame_Breath_Timer = 10000; } else Flame_Breath_Timer -= diff; break; default: break; } if (!TankGUID) DoMeleeAttackIfReady(); }
void TotemAI::UpdateAI(const uint32 diff) { // Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events if (m_EventUpdateTime < diff) { m_EventDiff += diff; // Check for time based events for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { // Decrement Timers if (i->Time) { // Do not decrement timers if event cannot trigger in this phase if (!(i->Event.event_inverse_phase_mask & (1 << m_Phase))) { if (i->Time > m_EventDiff) i->Time -= m_EventDiff; else i->Time = 0; } } // Skip processing of events that have time remaining or are disabled if (!(i->Enabled) || i->Time) continue; if (IsTimerBasedEvent(i->Event.event_type)) ProcessEvent(*i); } m_EventDiff = 0; m_EventUpdateTime = EVENT_UPDATE_TIME; } else { m_EventDiff += diff; m_EventUpdateTime -= diff; } if (getTotem().GetTotemType() != TOTEM_ACTIVE) return; if (!m_creature->isAlive() || m_creature->IsNonMeleeSpellCasted(false)) return; // Search spell SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(getTotem().GetSpell()); if (!spellInfo) return; // Get spell rangy SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); float max_range = GetSpellMaxRange(srange); // SPELLMOD_RANGE not applied in this place just because nonexistent range mods for attacking totems // pointer to appropriate target if found any Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid); // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !m_creature->IsWithinDistInMap(victim, max_range) || m_creature->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(m_creature, m_creature, false)) { victim = nullptr; MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(m_creature, m_creature, max_range); MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); Cell::VisitAllObjects(m_creature, checker, max_range); } // If have target if (victim) { // remember i_victimGuid = victim->GetObjectGuid(); // attack m_creature->SetInFront(victim); // client change orientation by self m_creature->CastSpell(victim, getTotem().GetSpell(), TRIGGERED_NONE); } else i_victimGuid.Clear(); }
void TotemAI::UpdateAI(uint32 const /*diff*/) { if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) return; if (!me->isAlive()) return; // pointer to appropriate target if found any Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; if (me->IsNonMeleeSpellCasted(false)) { if (victim && victim->HasCrowdControlAura()) victim = NULL; else return; } // Search spell SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell()); if (!spellInfo) return; // Get spell range float max_range = spellInfo->GetMaxRange(false); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->canSeeOrDetect(victim) || victim->HasCrowdControlAura()) { victim = NULL; MoPCore::NearestAttackableNoCCUnitInObjectRangeCheck u_check(me, me, max_range); MoPCore::UnitLastSearcher<MoPCore::NearestAttackableNoCCUnitInObjectRangeCheck> checker(me, victim, u_check); me->VisitNearbyObject(max_range, checker); } if (me->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 3599) // Searing Totem { if (me->GetOwner() && me->GetOwner()->getVictim()) { i_victimGuid = me->GetOwner()->getVictim()->GetGUID(); victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; if (victim) { me->SetTarget(i_victimGuid); me->SetInCombatWith(victim); } } } // If have target if (victim) { // remember i_victimGuid = victim->GetGUID(); // attack me->SetInFront(victim); // client change orientation by self me->CastSpell(victim, me->ToTotem()->GetSpell(), false); } else i_victimGuid = 0; }