Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const { // Provides next target selection after current target death. // This function should only be called internally by the AI // Targets are not evaluated here for being valid targets, that is done in _CanAttack() // The parameter: allowAutoSelect lets us disable aggressive pet auto targeting for certain situations // Passive pets don't do next target selection if (me->HasReactState(REACT_PASSIVE)) return NULL; // Check pet attackers first so we don't drag a bunch of targets to the owner if (Unit* myAttacker = me->getAttackerForHelper()) if (!myAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(myAttacker) && me->CanCreatureAttack(myAttacker) && !me->isTargetNotAcceptableByMMaps(myAttacker->GetGUID(), sWorld->GetGameTime(), myAttacker)) return myAttacker; // Check pet's attackers first to prevent dragging mobs back to owner if (me->HasAuraType(SPELL_AURA_MOD_TAUNT)) { const Unit::AuraEffectList& tauntAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT); if (!tauntAuras.empty()) for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr) if (Unit* caster = (*itr)->GetCaster()) if (me->_CanDetectFeignDeathOf(caster) && me->CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, me->GetGUID())) return caster; } // Not sure why we wouldn't have an owner but just in case... Unit* owner = me->GetCharmerOrOwner(); if (!owner) return NULL; // Check owner attackers if (Unit* ownerAttacker = owner->getAttackerForHelper()) if (!ownerAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(ownerAttacker) && me->CanCreatureAttack(ownerAttacker) && !me->isTargetNotAcceptableByMMaps(ownerAttacker->GetGUID(), sWorld->GetGameTime(), ownerAttacker)) return ownerAttacker; // Check owner victim // 3.0.2 - Pets now start attacking their owners victim in defensive mode as soon as the hunter does if (Unit* ownerVictim = owner->GetVictim()) if (me->_CanDetectFeignDeathOf(ownerVictim) && me->CanCreatureAttack(ownerVictim) && !me->isTargetNotAcceptableByMMaps(ownerVictim->GetGUID(), sWorld->GetGameTime(), ownerVictim)) return ownerVictim; // Neither pet or owner had a target and aggressive pets can pick any target // To prevent aggressive pets from chain selecting targets and running off, we // only select a random target if certain conditions are met. if (allowAutoSelect) if (!me->GetCharmInfo()->IsReturning() || me->GetCharmInfo()->IsFollowing() || me->GetCharmInfo()->IsAtStay()) if (Unit* nearTarget = me->ToCreature()->SelectNearestTargetInAttackDistance(MAX_AGGRO_RADIUS)) return nearTarget; // Default - no valid targets return NULL; }
void EnterCombat(Unit* who) { if (!ghost && instance) { Talk(YELL_SKARVALD_AGGRO); Unit* dalronn = Unit::GetUnit(*me, instance->GetData64(DATA_DALRONN)); if (dalronn && dalronn->IsAlive() && !dalronn->GetVictim()) dalronn->getThreatManager().addThreat(who, 0.0f); instance->SetData(DATA_SKARVALD_DALRONN_EVENT, IN_PROGRESS); } }
void EnterCombat(Unit* who) { if (!ghost && instance) { Unit* skarvald = Unit::GetUnit(*me, instance->GetData64(DATA_SKARVALD)); if (skarvald && skarvald->IsAlive() && !skarvald->GetVictim()) skarvald->getThreatManager().addThreat(who, 0.0f); AggroYell_Timer = 5000; if (instance) instance->SetData(DATA_SKARVALD_DALRONN_EVENT, IN_PROGRESS); } }
void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) { Unit* target = GetTarget(); Unit* victim = target->GetVictim(); if (victim && (target->GetHealthPct() > victim->GetHealthPct())) { if (!target->HasAura(SPELL_ROGUE_PREY_ON_THE_WEAK)) { int32 bp = GetSpellInfo()->Effects[EFFECT_0].CalcValue(); target->CastCustomSpell(target, SPELL_ROGUE_PREY_ON_THE_WEAK, &bp, nullptr, nullptr, true); } } else target->RemoveAurasDueToSpell(SPELL_ROGUE_PREY_ON_THE_WEAK); }
void AttackersValue::AddAttackersOf(Unit* unit, set<Unit*>& targets) { HostileRefManager& refManager = unit->getHostileRefManager(); HostileReference *ref = refManager.getFirst(); if (!ref) return; while( ref ) { ThreatManager *threatManager = ref->GetSource(); Unit *attacker = threatManager->GetOwner(); Unit *victim = attacker->GetVictim(); if (victim == unit) targets.insert(attacker); ref = ref->next(); } }
void EffectMovementGenerator::Finalize(Unit& unit) { if (unit.GetTypeId() != TYPEID_UNIT) return; // Need restore previous movement since we have no proper states system if (unit.IsAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED)) { if (Unit* victim = unit.GetVictim()) unit.GetMotionMaster()->MoveChase(victim); else unit.GetMotionMaster()->Initialize(); } else if (!unit.IsAlive()) { unit.GetMotionMaster()->MoveIdle(); } if (unit.ToCreature()->AI()) unit.ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id); }
void UpdateAI(uint32 diff) override { if (TalkTimer) { if (!TalkSequence) { me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); me->InterruptNonMeleeSpells(true); me->RemoveAllAuras(); me->DeleteThreatList(); me->CombatStop(); ++TalkSequence; } if (TalkTimer <= diff) { if (isFriendly) GoodEnding(); else BadEnding(); ++TalkSequence; } else TalkTimer -= diff; } else { if (bJustReset) { if (ResetTimer <= diff) { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); me->SetDisableGravity(false); me->SetVisible(true); me->SetStandState(UNIT_STAND_STATE_SLEEP); ResetTimer = 10000; bJustReset = false; } else ResetTimer -= diff; return; } if (!UpdateVictim()) return; if (CheckTimer <= diff) { if (me->GetDistance(CENTER_X, CENTER_Y, DRAGON_REALM_Z) >= 75) { EnterEvadeMode(); return; } if (HealthBelowPct(10) && !isEnraged) { if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) Sath->AI()->DoAction(DO_ENRAGE); DoAction(DO_ENRAGE); } if (!isBanished && HealthBelowPct(1)) { if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) { if (Sath->HasAura(SPELL_BANISH)) { Sath->DealDamage(Sath, Sath->GetHealth()); return; } else DoAction(DO_BANISH); } else { TC_LOG_ERROR("scripts", "Didn't find Shathrowar. Kalecgos event reseted."); EnterEvadeMode(); return; } } CheckTimer = 1000; } else CheckTimer -= diff; if (ArcaneBuffetTimer <= diff) { DoCastAOE(SPELL_ARCANE_BUFFET); ArcaneBuffetTimer = 8000; } else ArcaneBuffetTimer -= diff; if (FrostBreathTimer <= diff) { DoCastAOE(SPELL_FROST_BREATH); FrostBreathTimer = 15000; } else FrostBreathTimer -= diff; if (TailLashTimer <= diff) { DoCastAOE(SPELL_TAIL_LASH); TailLashTimer = 15000; } else TailLashTimer -= diff; if (WildMagicTimer <= diff) { DoCastAOE(WildMagic[rand32() % 6]); WildMagicTimer = 20000; } else WildMagicTimer -= diff; if (SpectralBlastTimer <= diff) { ThreatContainer::StorageType const& m_threatlist = me->getThreatManager().getThreatList(); std::list<Unit*> targetList; for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) { Unit* target = (*itr)->getTarget(); if (target && target->GetTypeId() == TYPEID_PLAYER && (!target->GetVictim() || target->GetGUID() != me->EnsureVictim()->GetGUID()) && target->GetPositionZ() > me->GetPositionZ() - 5 && !target->HasAura(AURA_SPECTRAL_EXHAUSTION)) { targetList.push_back(target); } } if (targetList.empty()) { SpectralBlastTimer = 1000; return; } std::list<Unit*>::const_iterator i = targetList.begin(); advance(i, rand32() % targetList.size()); if ((*i)) { (*i)->CastSpell((*i), SPELL_SPECTRAL_BLAST, true); SpectralBlastTimer = 20000 + rand32() % 5000; } else SpectralBlastTimer = 1000; } else SpectralBlastTimer -= diff; DoMeleeAttackIfReady(); } }
void DoNormalAttack(uint32 diff) { opponent = me->GetVictim(); if (opponent) { if (!IsCasting()) StartAttack(opponent); } else return; AttackerSet m_attackers = master->getAttackers(); AttackerSet b_attackers = me->getAttackers(); Unit* u = me->SelectNearestTarget(20); //ICE_BARRIER if (ICE_BARRIER && Ice_Barrier_cd <= diff && u && u->GetVictim() == me && u->GetDistance(me) < 8 && !me->HasAura(ICE_BARRIER)) { if (me->IsNonMeleeSpellCast(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, ICE_BARRIER)) { Ice_Barrier_cd = 25000; GC_Timer = 800; return; } } if ((!ICE_BARRIER || Ice_Barrier_cd > diff) && BLINK && Blink_cd < 3000 && u && u->GetVictim() == me && u->GetDistance(me) < 6 && !me->HasAura(ICE_BARRIER)) { if (me->IsNonMeleeSpellCast(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, BLINK)) { Blink_cd = 13000; GC_Timer = 800; return; } } if (me->HasAura(ICEBLOCK)) if (((GetManaPCT(me) > 45 && GetHealthPCT(me) > 80) || b_attackers.empty()) && Iceblock_cd <= 57000 && tank) me->RemoveAurasDueToSpell(ICEBLOCK); //ICEBLOCK if (ICEBLOCK && Rand() < 50 && !b_attackers.empty() && tank && Iceblock_cd <= diff && (GetManaPCT(me) < 15 || GetHealthPCT(me) < 45 || b_attackers.size() > 4) && !me->HasAura(ICEBLOCK)) { if (me->IsNonMeleeSpellCast(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, ICEBLOCK)) { Iceblock_cd = 60000; Nova_cd = 0; //Glyph of Iceblock return; } } if (IsCasting()) return; float dist = me->GetExactDist(opponent); BOLT = (CCed(opponent, true) || !FROSTBOLT) ? FIREBALL : FROSTBOLT; NOVA = BOLT == FROSTBOLT && BLASTWAVE && dist > 5 ? BLASTWAVE : FROSTNOVA ? FROSTNOVA : 0; ////Combustion doesn't work properly on 434 (14.08.13) //if (COMBUSTION && Combustion_cd <= diff && Rand() < 15 && dist < 40) //{ // if (opponent->HasAuraTypeWithCaster(SPELL_AURA_PERIODIC_DAMAGE, me->GetGUID())) // { // temptimer = GC_Timer; // if (doCast(me, COMBUSTION)) // { // Combustion_cd = 60000; // Nova_cd = 0; FireBlast_cd = 0; DragonBreath_cd = 0; // } // GC_Timer = temptimer; // } //} //DAMAGE //PYROBLAST if (PYROBLAST && Pyroblast_cd <= diff && GC_Timer <= diff && Rand() < 95 && (b_attackers.size() < 2 || (*b_attackers.begin()) == opponent) && dist < 40 && opponent->IsPolymorphed() && doCast(opponent, PYROBLAST)) { Pyroblast_cd = 7500; //debug DragonBreath_cd = std::max<uint32>(DragonBreath_cd, 450); Nova_cd = std::max<uint32>(Nova_cd, 450); return; } //nova //TODO: SEPARATE u = me->SelectNearestTarget(6.3f); if (NOVA && Nova_cd <= diff && u && Rand() < 75 && !CCed(u, true) && IsInBotParty(u->GetVictim())) { if (doCast(me, NOVA)) { Nova_cd = 15000; GetInPosition(true); return; } } //living bomb if (LIVINGBOMB && Living_Bomb_cd <= diff && GC_Timer <= diff && Rand() < 45 && dist < 40 && opponent->GetHealth() > me->GetHealth()/2 && !opponent->HasAura(LIVINGBOMB, me->GetGUID()) && doCast(opponent, LIVINGBOMB)) { Living_Bomb_cd = 6000; GC_Timer = 500; return; } //cone of cold if (CONEOFCOLD && ConeofCold_cd <= diff && GC_Timer <= diff && Rand() < 50 && dist < 7 && me->HasInArc(M_PI*0.75f, opponent) && doCast(opponent, CONEOFCOLD)) { ConeofCold_cd = 8000; GC_Timer = 500; GetInPosition(true); return; } //dragon's breath if (DRAGONBREATH && DragonBreath_cd <= diff && GC_Timer <= diff && !CCed(opponent, true) && ((me->HasInArc(M_PI*0.75f, opponent) && dist < 7) || (u && u != opponent && me->HasInArc(M_PI*0.75f, u) && IsInBotParty(u->GetVictim()))) && doCast(/*opponent*/me, DRAGONBREATH)) { DragonBreath_cd = 15000; GC_Timer = 800; return; } /*//blast wave //TODO Separate again u = me->SelectNearestTarget(8); if (BLASTWAVE != 0 && u && isTimerReady(BlastWave_cd) && !HasAuraName(u, FROSTNOVA) && !HasAuraName(u, DRAGONBREATH) && doCast(me, BLASTWAVE)) { BlastWave_cd = BLASTWAVE_CD; GC_Timer = 800; }*/ //fire blast if (FIREBLAST && FireBlast_cd <= diff && GC_Timer <= diff && dist < 40) { if (Rand() < 20 + 80*(!opponent->isFrozen() && !opponent->HasAuraType(SPELL_AURA_MOD_STUN) && me->HasAura(IMPACT_BUFF))) { if (doCast(opponent, FIREBLAST)) { FireBlast_cd = 6000; GC_Timer = 500; return; } } } //flamestrike - use Improved Flamestrike for instant cast if (FLAMESTRIKE && GC_Timer <= diff && me->getLevel() >= 50 && Rand() < 25) { Unit* FStarget = FindAOETarget(40, true, false); if (FStarget && doCast(FStarget, FLAMESTRIKE, true)) return; } //blizzard if (BLIZZARD && Blizzard_cd <= diff && !me->isMoving() && Rand() < 40) { Unit* blizztarget = FindAOETarget(35, true); if (blizztarget && doCast(blizztarget, BLIZZARD)) { Blizzard_cd = 5000; return; } Blizzard_cd = 1500; //fail } //Frost of Fire Bolt if (BOLT && Bolt_cd <= diff && Rand() < 75 && dist < 35 && doCast(opponent, BOLT)) { Bolt_cd = uint32(float(sSpellMgr->GetSpellInfo(BOLT)->CalcCastTime()/100) * me->GetFloatValue(UNIT_MOD_CAST_SPEED) + 200); //debug DragonBreath_cd = std::max<uint32>(DragonBreath_cd, 450); Nova_cd = std::max<uint32>(Nova_cd, 450); return; } ////Arcane Missiles //if (Rand() < 15 && GC_Timer <= diff && !me->isMoving() && dist < 40 && // doCast(opponent, ARCANEMISSILES)) // return; }
void DoNormalAttack(uint32 diff) { opponent = me->GetVictim(); if (opponent) { if (!IsCasting()) StartAttack(opponent); } else return; AttackerSet m_attackers = master->getAttackers(); AttackerSet b_attackers = me->getAttackers(); Unit* u = me->SelectNearestTarget(20); //ICE_BARRIER if (ICE_BARRIER && Ice_Barrier_cd <= diff && u && u->GetVictim() == me && u->GetDistance(me) < 8 && !me->HasAura(ICE_BARRIER)) { if (me->IsNonMeleeSpellCasted(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, ICE_BARRIER)) { Ice_Barrier_cd = 41000 - me->getLevel()*200;//down to 25 sec on 80 GC_Timer = 800; return; } } if ((!ICE_BARRIER || Ice_Barrier_cd > diff) && BLINK && Blink_cd < 3000 && u && u->GetVictim() == me && !me->HasAura(ICE_BARRIER) && u->GetDistance(me) < 6) { if (me->IsNonMeleeSpellCasted(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, BLINK)) { Blink_cd = 15000 - me->getLevel()/4 * 100; GC_Timer = 800; return; } } if (me->HasAura(ICEBLOCK)) if (((GetManaPCT(me) > 45 && GetHealthPCT(me) > 80) || b_attackers.empty()) && Iceblock_cd <= 57000 && tank) me->RemoveAurasDueToSpell(ICEBLOCK); //ICEBLOCK if (ICEBLOCK && Rand() < 50 && !b_attackers.empty() && tank && Iceblock_cd <= diff && (GetManaPCT(me) < 15 || GetHealthPCT(me) < 45 || b_attackers.size() > 4) && !me->HasAura(ICEBLOCK)) { if (me->IsNonMeleeSpellCasted(true)) me->InterruptNonMeleeSpells(true); if (doCast(me, ICEBLOCK)) { Iceblock_cd = 60000; return; } } if (IsCasting()) return; BOLT = (CCed(opponent, true) || (opponent->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) && me->HasAura(COMBUSTION))) ? FIREBALL : FROSTBOLT; NOVA = BOLT == FIREBALL && BLASTWAVE ? BLASTWAVE : FROSTNOVA ? FROSTNOVA : 0; float dist = me->GetExactDist(opponent); if (dist > 30) return; if (COMBUSTION && Rand() < 15 && (opponent->GetMaxHealth() > master->GetMaxHealth()*10 || m_attackers.size() > 1 || b_attackers.size() > 2)) { if (!HasAuraName(me, "Combustion") && Combustion_cd <= diff) { temptimer = GC_Timer; if (doCast(me, COMBUSTION)) { Combustion_cd = 60000; //Reset timers for fun Nova_cd = 0; FireBlast_cd = 0; DragonBreath_cd = 0; } GC_Timer = temptimer; } } //DAMAGE //PYROBLAST if (PYROBLAST && Rand() < 75 && Pyroblast_cd <= diff && GC_Timer <= diff && b_attackers.size() < 2 && dist < 30 && opponent->IsPolymorphed() && doCast(opponent, PYROBLAST)) Pyroblast_cd = 50; //nova //TODO: SEPARATE u = me->SelectNearestTarget(7); if (u && NOVA && Nova_cd <= diff && !CCed(u, true) && IsInBotParty(u->GetVictim())) { Unit* tar = u->GetVictim(); if (tar && IsInBotParty(tar) && doCast(me, NOVA)) { Nova_cd = 15000; return; } } //living bomb if (LIVINGBOMB && Rand() < 25 && Living_Bomb_cd <= diff && GC_Timer <= diff && dist < 30 && opponent->GetHealth() > me->GetHealth()/2 && !opponent->HasAura(LIVINGBOMB, me->GetGUID()) && doCast(opponent, LIVINGBOMB)) { Living_Bomb_cd = 6000; GC_Timer = 500; return; } //cone of cold if (CONEOFCOLD && ConeofCold_cd <= diff && GC_Timer <= diff && dist < 7 && me->HasInArc(M_PI, opponent) && doCast(opponent, CONEOFCOLD)) { ConeofCold_cd = 14000; GC_Timer = 500; return; } //dragon's breath u = me->SelectNearestTarget(7); if (DRAGONBREATH && u && DragonBreath_cd <= diff && GC_Timer <= diff && me->HasInArc(M_PI, opponent) && !HasAuraName(u, FROSTNOVA) && doCast(opponent, DRAGONBREATH)) { DragonBreath_cd = 25000; GC_Timer = 800; return; } /*//blast wave //TODO Separate again u = me->SelectNearestTarget(8); if (BLASTWAVE != 0 && u && isTimerReady(BlastWave_cd) && !HasAuraName(u, FROSTNOVA) && !HasAuraName(u, DRAGONBREATH) && doCast(me, BLASTWAVE)) { BlastWave_cd = BLASTWAVE_CD; GC_Timer = 800; }*/ //fire blast if (FireBlast_cd <= diff && GC_Timer <= diff && dist < 20 && Rand() < 20 + 80*(!opponent->HasAuraType(SPELL_AURA_MOD_STUN) && me->HasAura(IMPACT_BUFF)) && doCast(opponent, FIREBLAST)) { FireBlast_cd = 6000; GC_Timer = 500; return; } //flamestrike if (GC_Timer <= diff && Rand() < 60 && me->HasAura(FIRESTARTERBUFF)) { Unit* FStarget = FindAOETarget(30, true, false); if (FStarget && doCast(FStarget, FLAMESTRIKE, true)) { me->RemoveAurasDueToSpell(FIRESTARTERBUFF); GC_Timer = 0; return; } } //blizzard if (BLIZZARD && Rand() < 80 && Blizzard_cd <= diff) { Unit* blizztarget = FindAOETarget(30, true); if (blizztarget && doCast(blizztarget, BLIZZARD)) { Blizzard_cd = 5000; return; } Blizzard_cd = 2000;//fail } //Frost of Fire Bolt if (Rand() < 75 && Bolt_cd <= diff && dist < 30 && doCast(opponent, BOLT)) { Bolt_cd = uint32(float(sSpellMgr->GetSpellInfo(BOLT)->CalcCastTime()/100) * me->GetFloatValue(UNIT_MOD_CAST_SPEED) + 200); return; } //Arcane Missiles if (Rand() < 10 && GC_Timer <= diff && !me->isMoving() && dist < 20 && doCast(opponent, ARCANEMISSILES)) return; }