bool GuardianAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker, Creature* pAIEventSender /*=nullptr*/) { 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)) { if (!IsTimerBasedEvent(pHolder.Event.event_type)) DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: Event %u skipped because of phasemask %u. Current phase %u", pHolder.Event.event_id, pHolder.Event.event_inverse_phase_mask, m_Phase); return false; } if (!IsTimerBasedEvent(pHolder.Event.event_type)) LOG_PROCESS_EVENT; CreatureEventAI_Event const& event = pHolder.Event; // Check event conditions based on the event type, also reset events switch (event.event_type) { case EVENT_T_FRIENDLY_HP: { Unit* pUnit = DoSelectLowestHpFriendly((float)event.friendly_hp.radius, event.friendly_hp.hpDeficit, false); if (!pUnit) return false; pActionInvoker = pUnit; LOG_PROCESS_EVENT; // Repeat Timers pHolder.UpdateRepeatTimer(m_creature, event.friendly_hp.repeatMin, event.friendly_hp.repeatMax); break; } default: return CreatureEventAI::ProcessEvent(pHolder, pActionInvoker, pAIEventSender); } // 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, normal case if (!(pHolder.Event.event_flags & EFLAG_RANDOM_ACTION)) { for (uint32 j = 0; j < MAX_ACTIONS; ++j) ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker, pAIEventSender); } // Process actions, random case else { // amount of real actions uint32 count = 0; for (uint32 j = 0; j < MAX_ACTIONS; ++j) if (pHolder.Event.action[j].type != ACTION_T_NONE) ++count; if (count) { // select action number from found amount uint32 idx = rnd % count; // find selected action, skipping not used uint32 j = 0; for (; ; ++j) { if (pHolder.Event.action[j].type != ACTION_T_NONE) { if (!idx) break; --idx; } } ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker, pAIEventSender); } } return true; }
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 maxRange = 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 || !m_creature->CanAttack(victim) || !m_creature->IsWithinDistInMap(victim, maxRange) || m_creature->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(m_creature, m_creature, false)) { victim = nullptr; MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(m_creature, m_creature, maxRange); MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); Cell::VisitAllObjects(m_creature, checker, maxRange); } // 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(); }