void UpdateAI(const uint32 uiDiff) { if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; if (m_uiHealTimer < uiDiff) { if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) { if (DoCastSpellIfCan(pTarget, SPELL_FLASH_HEAL) == CAST_OK) m_uiHealTimer = urand(15000, 20000); } } else m_uiHealTimer -= uiDiff; if (m_uiRenewTimer < uiDiff) { if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) { if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_RENEW : SPELL_RENEW_H) == CAST_OK) m_uiRenewTimer = urand(5000, 10000); } } else m_uiRenewTimer -= uiDiff; if (m_uiShieldTimer < uiDiff) { if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHIELD : SPELL_SHIELD_H) == CAST_OK) m_uiShieldTimer = urand(30000, 35000); } else m_uiShieldTimer -= uiDiff; if (m_uiDispelTimer < uiDiff) { Unit* pTarget = NULL; std::list<Creature*> lTempList = DoFindFriendlyCC(50.0f); if (!lTempList.empty()) pTarget = *(lTempList.begin()); else pTarget = DoSelectLowestHpFriendly(50.0f); if (pTarget) { if (DoCastSpellIfCan(pTarget, SPELL_DISPEL_MAGIC) == CAST_OK) m_uiDispelTimer = urand(12000, 15000); } } else m_uiDispelTimer -= uiDiff; // Use the Medallion if CC - only on heroic. Not sure how many times they are allowed to use it. if (!m_bIsRegularMode && m_uiMedallionTimer) { if (m_creature->isFrozen() || m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) { if (m_uiMedallionTimer <= uiDiff) { if (DoCastSpellIfCan(m_creature, SPELL_MEDALLION, CAST_TRIGGERED) == CAST_OK) m_uiMedallionTimer = 0; } else m_uiMedallionTimer -= uiDiff; } } if (m_uiSWPainTimer < uiDiff) { if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) { if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_WORD_PAIN : SPELL_SHADOW_WORD_PAIN_H) == CAST_OK) m_uiSWPainTimer = 10000; } } else m_uiSWPainTimer -= uiDiff; if (m_uiScreamTimer < uiDiff) { if (DoCastSpellIfCan(m_creature, SPELL_SCREAM) == CAST_OK) m_uiScreamTimer = urand(15000, 20000); } else m_uiScreamTimer -= uiDiff; DoMeleeAttackIfReady(); }
bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker /*=NULL*/) { if (!pHolder.Enabled || pHolder.Time) return false; //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask) if (pHolder.Event.event_inverse_phase_mask & (1 << m_Phase)) return false; CreatureEventAI_Event const& event = pHolder.Event; //Check event conditions based on the event type, also reset events switch (event.event_type) { case EVENT_T_TIMER: if (!me->isInCombat()) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.timer.repeatMin, event.timer.repeatMax); break; case EVENT_T_TIMER_OOC: if (me->isInCombat()) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.timer.repeatMin, event.timer.repeatMax); break; case EVENT_T_HP: { if (!me->isInCombat() || !me->GetMaxHealth()) return false; uint32 perc = uint32(me->GetHealthPct()); if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.percent_range.repeatMin, event.percent_range.repeatMax); break; } case EVENT_T_MANA: { if (!me->isInCombat() || !me->GetMaxPower(POWER_MANA)) return false; uint32 perc = (me->GetPower(POWER_MANA) * 100) / me->GetMaxPower(POWER_MANA); if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.percent_range.repeatMin, event.percent_range.repeatMax); break; } case EVENT_T_AGGRO: break; case EVENT_T_KILL: //Repeat Timers pHolder.UpdateRepeatTimer(me, event.kill.repeatMin, event.kill.repeatMax); break; case EVENT_T_DEATH: case EVENT_T_EVADE: break; case EVENT_T_SPELLHIT: //Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit //Repeat Timers pHolder.UpdateRepeatTimer(me, event.spell_hit.repeatMin, event.spell_hit.repeatMax); break; case EVENT_T_RANGE: //Repeat Timers pHolder.UpdateRepeatTimer(me, event.range.repeatMin, event.range.repeatMax); break; case EVENT_T_OOC_LOS: //Repeat Timers pHolder.UpdateRepeatTimer(me, event.ooc_los.repeatMin, event.ooc_los.repeatMax); break; case EVENT_T_RESET: case EVENT_T_SPAWNED: break; case EVENT_T_TARGET_HP: { if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxHealth()) return false; uint32 perc = uint32(me->getVictim()->GetHealthPct()); if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.percent_range.repeatMin, event.percent_range.repeatMax); break; } case EVENT_T_TARGET_CASTING: if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true)) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.target_casting.repeatMin, event.target_casting.repeatMax); break; case EVENT_T_FRIENDLY_HP: { if (!me->isInCombat()) return false; Unit* pUnit = DoSelectLowestHpFriendly((float) event.friendly_hp.radius, event.friendly_hp.hpDeficit); if (!pUnit) return false; pActionInvoker = pUnit; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.friendly_hp.repeatMin, event.friendly_hp.repeatMax); break; } case EVENT_T_FRIENDLY_IS_CC: { if (!me->isInCombat()) return false; std::list<Creature*> pList; DoFindFriendlyCC(pList, (float) event.friendly_is_cc.radius); //List is empty if (pList.empty()) return false; //We don't really care about the whole list, just return first available pActionInvoker = *(pList.begin()); //Repeat Timers pHolder.UpdateRepeatTimer(me, event.friendly_is_cc.repeatMin, event.friendly_is_cc.repeatMax); break; } case EVENT_T_FRIENDLY_MISSING_BUFF: { std::list<Creature*> pList; DoFindFriendlyMissingBuff(pList, (float) event.friendly_buff.radius, event.friendly_buff.spellId); //List is empty if (pList.empty()) return false; //We don't really care about the whole list, just return first available pActionInvoker = *(pList.begin()); //Repeat Timers pHolder.UpdateRepeatTimer(me, event.friendly_buff.repeatMin, event.friendly_buff.repeatMax); break; } case EVENT_T_SUMMONED_UNIT: { //Prevent event from occuring on no unit or non creatures if (!pActionInvoker || pActionInvoker->GetTypeId() != TYPEID_UNIT) return false; //Creature id doesn't match up if (pActionInvoker->ToCreature()->GetEntry() != event.summon_unit.creatureId) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.summon_unit.repeatMin, event.summon_unit.repeatMax); break; } case EVENT_T_TARGET_MANA: { if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxPower(POWER_MANA)) return false; uint32 perc = (me->getVictim()->GetPower(POWER_MANA) * 100) / me->getVictim()->GetMaxPower(POWER_MANA); if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.percent_range.repeatMin, event.percent_range.repeatMax); break; } case EVENT_T_REACHED_HOME: case EVENT_T_RECEIVE_EMOTE: break; case EVENT_T_BUFFED: { //Note: checked only aura for effect 0, if need check aura for effect 1/2 then // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) Aura const * aura = me->GetAura(event.buffed.spellId); if (!aura || aura->GetStackAmount() < event.buffed.amount) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.buffed.repeatMin, event.buffed.repeatMax); break; } case EVENT_T_TARGET_BUFFED: { //Prevent event from occuring on no unit if (!pActionInvoker) return false; //Note: checked only aura for effect 0, if need check aura for effect 1/2 then // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) Aura const * aura = pActionInvoker->GetAura(event.buffed.spellId); if (!aura || aura->GetStackAmount() < event.buffed.amount) return false; //Repeat Timers pHolder.UpdateRepeatTimer(me, event.buffed.repeatMin, event.buffed.repeatMax); break; } default: sLog->outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", me->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); break; } //Disable non-repeatable events if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) pHolder.Enabled = false; //Store random here so that all random actions match up uint32 rnd = rand(); //Return if chance for event is not met if (pHolder.Event.event_chance <= rnd % 100) return false; //Process actions for (uint8 j = 0; j < MAX_ACTIONS; ++j) ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker); return true; }
void UpdateAI(const uint32 uiDiff) override { if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; switch (m_uiPhase) { case PHASE_FAKE_DEATH: if (m_uiResurrectTimer < uiDiff) { if (!m_pInstance) return; if (m_pInstance->GetData(TYPE_THEKAL) != SPECIAL || m_pInstance->GetData(TYPE_ZATH) != SPECIAL) { DoCastSpellIfCan(m_creature, SPELL_RESURRECT); m_pInstance->SetData(TYPE_LORKHAN, IN_PROGRESS); } m_uiPhase = PHASE_WAITING; } else m_uiResurrectTimer -= uiDiff; // no break needed here case PHASE_WAITING: return; case PHASE_NORMAL: if (m_uiDispelTimer < uiDiff) { std::list<Creature*> pList = DoFindFriendlyCC(30.0f); Creature* dispelTarget = nullptr; if (!pList.empty()) for (std::list<Creature*>::iterator itr = pList.begin(); itr != pList.end(); ++itr) { dispelTarget = (*itr); break; } if (!dispelTarget && (m_creature->isInRoots() || m_creature->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED))) dispelTarget = m_creature; if (dispelTarget && (DoCastSpellIfCan(dispelTarget, SPELL_DISPEL_MAGIC) == CAST_OK)) m_uiDispelTimer = urand(15000, 20000); } else m_uiDispelTimer -= uiDiff; // Lightning_Shield_Timer if (m_uiLightningShieldTimer < uiDiff) { if (DoCastSpellIfCan(m_creature, SPELL_LIGHTNING_SHIELD) == CAST_OK) m_uiLightningShieldTimer = 61000; } else m_uiLightningShieldTimer -= uiDiff; // Casting Greatheal to Thekal or Zath if they are in meele range. // TODO - why this range check? if (m_uiGreatHealTimer < uiDiff) { if (m_pInstance) { Creature* pThekal = m_pInstance->GetSingleCreatureFromStorage(NPC_THEKAL); Creature* pZath = m_pInstance->GetSingleCreatureFromStorage(NPC_ZATH); switch (urand(0, 1)) { case 0: if (pThekal && m_creature->IsWithinDistInMap(pThekal, 3 * ATTACK_DISTANCE)) DoCastSpellIfCan(pThekal, SPELL_GREAT_HEAL); break; case 1: if (pZath && m_creature->IsWithinDistInMap(pZath, 3 * ATTACK_DISTANCE)) DoCastSpellIfCan(pZath, SPELL_GREAT_HEAL); break; } } m_uiGreatHealTimer = urand(15000, 20000); } else m_uiGreatHealTimer -= uiDiff; // Disarm_Timer if (m_uiDisarmTimer < uiDiff) { if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DISARM) == CAST_OK) m_uiDisarmTimer = urand(15000, 25000); } else m_uiDisarmTimer -= uiDiff; break; } DoMeleeAttackIfReady(); }
void UpdateAI(const uint32 uiDiff) { if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; if (m_uiArcaneTorrentTimer < uiDiff) { if (IsEnemyPlayerInRangeForSpell(SPELL_ARCANE_TORRENT)) { DoCastSpellIfCan(m_creature, SPELL_ARCANE_TORRENT); m_uiArcaneTorrentTimer = 60000; } else m_uiArcaneTorrentTimer = 1000; } else m_uiArcaneTorrentTimer -= uiDiff; if (m_uiFlashHealTimer < uiDiff) { //this will fail if we previously was following target and pTarget is now different than before if (Unit* pTarget = DoSelectLowestHpFriendly(RANGE_FRIENDLY_TARGET*2, 30000)) { if (pTarget->IsWithinDistInMap(m_creature, RANGE_FRIENDLY_TARGET)) { DoCastSpellIfCan(pTarget, SPELL_FLASH_HEAL); //if not already chasing, start chase if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), 20.0f); } else { //if chasing, start follow target instead if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) { m_creature->GetMotionMaster()->MovementExpired(); m_creature->GetMotionMaster()->MoveFollow(pTarget, 20.0f, 0.0f); } } } m_uiFlashHealTimer = 2500; } else m_uiFlashHealTimer -= uiDiff; if (m_uiDispelMagicTimer < uiDiff) { Unit* pTarget = NULL; std::list<Creature*> lTempList = DoFindFriendlyCC(RANGE_FRIENDLY_TARGET); if (!lTempList.empty()) pTarget = *(lTempList.begin()); else pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); if (pTarget) DoCastSpellIfCan(pTarget, SPELL_DISPEL_MAGIC); m_uiDispelMagicTimer = 12000; } else m_uiDispelMagicTimer -= uiDiff; DoMeleeAttackIfReady(); }
bool UpdateCompanionAI(const uint32 uiDiff) { if (m_uiGoblinDragonGunTimer < uiDiff) { if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_GOBLIN_DRAGON_GUN : SPELL_GOBLIN_DRAGON_GUN_H) == CAST_OK) m_uiGoblinDragonGunTimer = urand(10000, 20000); } else m_uiGoblinDragonGunTimer -= uiDiff; if (m_uiRocketLaunchTimer < uiDiff) { if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) { if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ROCKET_LAUNCH : SPELL_ROCKET_LAUNCH_H) == CAST_OK) m_uiRocketLaunchTimer = 9000; } } else m_uiRocketLaunchTimer -= uiDiff; if (m_uiFelIronBombTimer < uiDiff) { if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) { if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FEL_IRON_BOMB : SPELL_FEL_IRON_BOMB_H) == CAST_OK) m_uiFelIronBombTimer = 15000; } } else m_uiFelIronBombTimer -= uiDiff; if (m_uiRecombobulateTimer < uiDiff) { // Note: this should be casted only on Polyformed targets Unit* pTarget = NULL; std::list<Creature*> lTempList = DoFindFriendlyCC(50.0f); if (!lTempList.empty()) pTarget = *(lTempList.begin()); else pTarget = DoSelectLowestHpFriendly(50.0f); if (pTarget) { if (DoCastSpellIfCan(pTarget, SPELL_RECOMBOBULATE) == CAST_OK) m_uiRecombobulateTimer = 2000; } } else m_uiRecombobulateTimer -= uiDiff; if (m_uiHighExplosiveSheepTimer < uiDiff) { if (DoCastSpellIfCan(m_creature, SPELL_HIGH_EXPLOSIVE_SHEEP) == CAST_OK) m_uiHighExplosiveSheepTimer = 65000; } else m_uiHighExplosiveSheepTimer -= uiDiff; return true; }