void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchools pSchool, SpellEntry const *pThreatSpell) { //function deals with adding threat and adding players and pets into ThreatList //mobs, NPCs, guards have ThreatList and HateOfflineList //players and pets have only InHateListOf //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.) if (pVictim == getOwner()) // only for same creatures :) return; if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) ) return; assert(getOwner()->GetTypeId()== TYPEID_UNIT); float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, pSchool, pThreatSpell); HostilReference* ref = iThreatContainer.addThreat(pVictim, threat); // Ref is not in the online refs, search the offline refs next if(!ref) ref = iThreatOfflineContainer.addThreat(pVictim, threat); if(!ref) // there was no ref => create a new one { // threat has to be 0 here HostilReference* hostilReference = new HostilReference(pVictim, this, 0); iThreatContainer.addReference(hostilReference); hostilReference->addThreat(threat); // now we add the real threat if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) hostilReference->setOnlineOfflineState(false); // GM is always offline } }
HostilReference* ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent) { HostilReference* ref = getReferenceByTarget(pVictim); assert(ref); // for a modification the reference must be there. (or not?) ref->addThreatPercent(pPercent); return ref; }
void TeleportToInnerVeil(Player* plr) { if (plr->HasAura(SPELL_SPECTRAL_EXHAUSTION, 0)) { plr->GetSession()->SendNotification(NOTIFY_SPECTRALLY_EXHAUSTED); return; } ScriptedInstance* pInstance = ((ScriptedInstance*)plr->GetInstanceData()); if (!pInstance) { error_log(ERROR_INST_DATA); plr->GetSession()->SendNotification(ERROR_INST_DATA_PLR); return; } pInstance->SetData64(DATA_PLAYER_SPECTRAL_REALM, plr->GetGUID()); // Remove the player from Kalecgos' Threat List Creature* Kalecgos = ((Creature*)Unit::GetUnit(*plr, pInstance->GetData64(DATA_KALECGOS_DRAGON))); if (Kalecgos) { HostilReference* ref = Kalecgos->getThreatManager().getOnlineContainer().getReferenceByTarget(plr); if (ref) ref->removeReference(); } // Add the player to Sathrovarr's Threat List Creature* Sathrovarr = ((Creature*)Unit::GetUnit(*plr, pInstance->GetData64(DATA_SATHROVARR))); if (Sathrovarr) Sathrovarr->AddThreat(plr, 1.0f); // Make them able to see Sathrovarr (he's invisible for some reason). Also, when this buff wears off, they get teleported back to Normal Realm (this is handled by Instance Script) plr->CastSpell(plr, SPELL_SPECTRAL_REALM, true); plr->CastSpell(plr, SPELL_TELEPORT_SPECTRAL_REALM, true); }
HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat) { HostilReference* ref = getReferenceByTarget(pVictim); if(ref) ref->addThreat(pThreat); return ref; }
void EjectPlayer(Player* plr) { debug_log("SD2: INST: Ejecting Player %s from Spectral Realm", plr->GetName()); // Remove player from Sathrovarr's threat list Creature* Sath = ((Creature*)Unit::GetUnit(*plr, Sathrovarr)); if (Sath && Sath->isAlive()) { HostilReference* ref = Sath->getThreatManager().getOnlineContainer().getReferenceByTarget(plr); if (ref) { ref->removeReference(); debug_log("SD2: INST: Deleting %s from Sathrovarr's threatlist", plr->GetName()); } } // Put player back in Kalecgos(Dragon)'s threat list Creature* Kalecgos = ((Creature*)Unit::GetUnit(*plr, Kalecgos_Dragon)); if (Kalecgos && Kalecgos->isAlive()) { debug_log("SD2: INST: Putting %s in Kalecgos' threatlist", plr->GetName()); Kalecgos->AddThreat(plr, 1.0f); } plr->CastSpell(plr, SPELL_TELEPORT_NORMAL_REALM, true); plr->CastSpell(plr, SPELL_SPECTRAL_EXHAUSTION, true); }
void ThreatManager::tauntApply(Unit* pTaunter) { HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat())) { if(ref->getTempThreatModifier() == 0.0f) // Ok, temp threat is unused ref->setTempThreat(getCurrentVictim()->getThreat()); } }
void HostilRefManager::updateThreatTables() { HostilReference* ref = getFirst(); while(ref) { ref->updateOnlineStatus(); ref = ref->next(); } }
HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim) { HostilReference* currentRef = NULL; bool found = false; std::list<HostilReference*>::iterator lastRef = iThreatList.end(); lastRef--; for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter) { currentRef = (*iter); Unit* target = currentRef->getTarget(); assert(target); // if the ref has status online the target must be there ! // some units are preferred in comparison to others if(iter != lastRef && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask(), false) || target->hasUnitState(UNIT_STAT_CONFUSED) ) ) { // current victim is a second choice target, so don't compare threat with it below if(currentRef == pCurrentVictim) pCurrentVictim = NULL; continue; } if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets { if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target { // list sorted and and we check current target, then this is best case if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() ) { currentRef = pCurrentVictim; // for second case found = true; break; } if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinMeleeRange(target) ) { //implement 110% threat rule for targets in melee range found = true; //and 130% rule for targets in ranged distances break; //for selecting alive targets } } else // select any { found = true; break; } } } if(!found) currentRef = NULL; return currentRef; }
float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList) { float threat = 0.0f; HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim); if(!ref && pAlsoSearchOfflineList) ref = iThreatOfflineContainer.getReferenceByTarget(pVictim); if(ref) threat = ref->getThreat(); return threat; }
void HostilRefManager::deleteReferences() { HostilReference* ref = getFirst(); while(ref) { HostilReference* nextRef = ref->next(); ref->removeReference(); delete ref; ref = nextRef; } }
void HostilRefManager::addThreatPercent(int32 iPercent) { HostilReference* ref; ref = getFirst(); while (ref != NULL) { ref->addThreatPercent(iPercent); ref = ref->next(); } }
void HostilRefManager::setOnlineOfflineState(bool bIsOnline) { HostilReference* ref; ref = getFirst(); while(ref != NULL) { ref->setOnlineOfflineState(bIsOnline); ref = ref->next(); } }
bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent) { bool consumed = false; ThreatRefStatusChangeEvent* threatRefStatusChangeEvent; HostilReference* hostilReference; threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent; threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager hostilReference = threatRefStatusChangeEvent->getReference(); switch(pUnitBaseEvent->getType()) { case UEV_THREAT_REF_THREAT_CHANGE: if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) || (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f)) setDirty(true); // the order in the threat list might have changed break; case UEV_THREAT_REF_ONLINE_STATUS: if(!hostilReference->isOnline()) { if (hostilReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } iThreatContainer.remove(hostilReference); iThreatOfflineContainer.addReference(hostilReference); } else { if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat())) setDirty(true); iThreatContainer.addReference(hostilReference); iThreatOfflineContainer.remove(hostilReference); } break; case UEV_THREAT_REF_REMOVE_FROM_LIST: if (hostilReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } if(hostilReference->isOnline()) iThreatContainer.remove(hostilReference); else iThreatOfflineContainer.remove(hostilReference); break; } return consumed; }
void HostilRefManager::setOnlineOfflineState(Unit *pCreature, bool bIsOnline) { HostilReference* ref = getFirst(); while(ref) { HostilReference* nextRef = ref->next(); if(ref->getSource()->getOwner() == pCreature) { ref->setOnlineOfflineState(bIsOnline); break; } ref = nextRef; } }
void HostilRefManager::deleteReference(Unit *pCreature) { HostilReference* ref = getFirst(); while(ref) { HostilReference* nextRef = ref->next(); if(ref->getSource()->getOwner() == pCreature) { ref->removeReference(); delete ref; break; } ref = nextRef; } }
void HostilRefManager::threatAssist(Unit *pVictim, float fThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) { HostilReference* ref; float size = pSingleTarget ? 1.0f : getSize(); // if pSingleTarget do not divide threat ref = getFirst(); while(ref != NULL) { float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, fThreat, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell); if(pVictim == getOwner()) ref->addThreat(threat / size); // It is faster to modify the threat durectly if possible else ref->getSource()->addThreat(pVictim, threat / size); ref = ref->next(); } }
void ThreatManager::_addThreat(Unit *pVictim, float threat) { HostilReference* ref = iThreatContainer.addThreat(pVictim, threat); // Ref is not in the online refs, search the offline refs next if(!ref) ref = iThreatOfflineContainer.addThreat(pVictim, threat); if(!ref) // there was no ref => create a new one { // threat has to be 0 here HostilReference* hostilReference = new HostilReference(pVictim, this, 0); iThreatContainer.addReference(hostilReference); hostilReference->addThreat(threat); // now we add the real threat if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) hostilReference->setOnlineOfflineState(false); // GM is always offline } }
void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) { threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager HostilReference* hostilReference = threatRefStatusChangeEvent->getReference(); switch(threatRefStatusChangeEvent->getType()) { case UEV_THREAT_REF_THREAT_CHANGE: if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) || (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f)) setDirty(true); // the order in the threat list might have changed break; case UEV_THREAT_REF_ONLINE_STATUS: if(!hostilReference->isOnline()) { if (hostilReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } iThreatContainer.remove(hostilReference); iThreatOfflineContainer.addReference(hostilReference); } else { if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat())) setDirty(true); iThreatContainer.addReference(hostilReference); iThreatOfflineContainer.remove(hostilReference); } break; case UEV_THREAT_REF_REMOVE_FROM_LIST: if (hostilReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } iOwner->SendRemoveFromThreatListOpcode(hostilReference); if(hostilReference->isOnline()) iThreatContainer.remove(hostilReference); else iThreatOfflineContainer.remove(hostilReference); break; } }
void HostilRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) { if (iOwner->hasUnitState(UNIT_STAT_IGNORE_ATTACKERS)) return; HostilReference* ref; uint32 size = pSingleTarget ? 1 : getSize(); // if pSingleTarget do not divide threat ref = getFirst(); while (ref != NULL) { float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, (pThreatSpell ? SpellMgr::GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell); if (pVictim == getOwner()) ref->addThreat(float (threat) / size); // It is faster to modify the threat directly if possible else ref->getSource()->addThreat(pVictim, float (threat) / size); ref = ref->next(); } }
HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim) { HostilReference* currentRef = NULL; bool found = false; for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter) { currentRef = (*iter); Unit* target = currentRef->getTarget(); assert(target); // if the ref has status online the target must be there ! if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets { if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target { // list sorted and and we check current target, then this is best case if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() ) { currentRef = pCurrentVictim; // for second case found = true; break; } if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) ) { //implement 110% threat rule for targets in melee range found = true; //and 130% rule for targets in ranged distances break; //for selecting alive targets } } else // select any { found = true; break; } } } if(!found) currentRef = NULL; return currentRef; }
bool ChatHandler::HandleDebugHostilRefList(const char * /*args*/) { Unit* target = getSelectedUnit(); if(!target) target = m_session->GetPlayer(); HostilReference* ref = target->getHostilRefManager().getFirst(); uint32 cnt = 0; PSendSysMessage("Hostil reference list of %s (guid %u)",target->GetName(), target->GetGUIDLow()); while(ref) { if(Unit * unit = ref->getSource()->getOwner()) { ++cnt; PSendSysMessage(" %u. %s (guid %u) - threat %f",cnt,unit->GetName(), unit->GetGUIDLow(), ref->getThreat()); } ref = ref->next(); } SendSysMessage("End of hostil reference list."); return true; }
void ThreatManager::tauntFadeOut(Unit *pTaunter) { HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); if(ref) ref->resetTempThreat(); }
HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim) { HostilReference* currentRef = NULL; bool found = false; bool noPriorityTargetFound = false; std::list<HostilReference*>::iterator lastRef = iThreatList.end(); lastRef--; for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) { currentRef = (*iter); Unit* target = currentRef->getTarget(); assert(target); // if the ref has status online the target must be there ! // some units are prefered in comparison to others if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) ) { if(iter != lastRef) { // current victim is a second choice target, so don't compare threat with it below if(currentRef == pCurrentVictim) pCurrentVictim = NULL; ++iter; continue; } else { // if we reached to this point, everyone in the threatlist is a second choice target. In such a situation the target with the highest threat should be attacked. noPriorityTargetFound = true; iter = iThreatList.begin(); continue; } } if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets { if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target { // list sorted and and we check current target, then this is best case if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() ) { currentRef = pCurrentVictim; // for second case found = true; break; } if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) ) { //implement 110% threat rule for targets in melee range found = true; //and 130% rule for targets in ranged distances break; //for selecting alive targets } } else // select any { found = true; break; } } ++iter; } if(!found) currentRef = NULL; return currentRef; }
void HostilRefManager::updateThreatTables() { for (HostilReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->updateOnlineStatus(); }
void HostilRefManager::addThreatPercent(int32 pValue) { for (HostilReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->addThreatPercent(pValue); }
void HostilRefManager::setOnlineOfflineState(bool pIsOnline) { for (HostilReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->setOnlineOfflineState(pIsOnline); }
void UpdateAI(const uint32 uiDiff) { if (m_bIsActiveCheck) { if (!m_bIsActived && Active_Timer < uiDiff) { m_bIsActived = true; Active_Timer = 1000; }else Active_Timer -= uiDiff; } else { if (Active_Timer < uiDiff) { if(m_pInstance) { bool m_bIsAlive = false; Creature* pStalagg; Creature* pFeugen; if (pStalagg = ((Creature*)Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_STALAGG)))) if (pStalagg->isAlive()) m_bIsAlive = true; if (pFeugen = ((Creature*)Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_FEUGEN)))) if (pFeugen->isAlive()) m_bIsAlive = true; if (!m_bIsAlive) { m_bIsActiveCheck = true; Active_Timer = 15000; } else { if (pStalagg->isInCombat() && pFeugen->isInCombat()) { if (SwitchTarget_Timer < uiDiff) { Unit* pStalaggTarget; Unit* pFeugenTarget; float StalaggTargetThreat; float FeugenTargetThreat; // Get Stalagg's target threat if (pStalagg && pStalagg->isAlive()) { if (pStalaggTarget = pStalagg->getVictim()) StalaggTargetThreat = m_creature->getThreatManager().getThreat(pStalaggTarget); } // Get Feugen's target threat if (pFeugen && pFeugen->isAlive()) { if (pFeugenTarget = pFeugen->getVictim()) FeugenTargetThreat = m_creature->getThreatManager().getThreat(pFeugenTarget); } // Switch Feugen's target from Stalagg if (pStalagg && pStalagg->isAlive()) { if (pFeugen && pFeugen->isAlive()) { HostilReference* ref = pFeugen->getThreatManager().getOnlineContainer().getReferenceByTarget(pFeugenTarget); if (ref) { pStalagg->CastSpell(pFeugenTarget, 54517, true); ((Player*)pFeugenTarget)->TeleportTo(pFeugenTarget->GetMapId(), pStalagg->GetPositionX(), pStalagg->GetPositionY(), pStalagg->GetPositionZ(), 0, TELE_TO_NOT_LEAVE_COMBAT); ref->removeReference(); pStalagg->AddThreat(pFeugenTarget, FeugenTargetThreat); pStalagg->AI()->AttackStart(pFeugenTarget); } } } // Switch Stalagg's target from Feugen if (pFeugen && pFeugen->isAlive()) { if (pStalagg && pStalagg->isAlive()) { HostilReference* ref = pStalagg->getThreatManager().getOnlineContainer().getReferenceByTarget(pStalaggTarget); if (ref) { pFeugen->CastSpell(pStalaggTarget, 54517, true); ((Player*)pStalaggTarget)->TeleportTo(pStalaggTarget->GetMapId(), pFeugen->GetPositionX(), pFeugen->GetPositionY(), pFeugen->GetPositionZ(), 0, TELE_TO_NOT_LEAVE_COMBAT); ref->removeReference(); pFeugen->AddThreat(pStalaggTarget, StalaggTargetThreat); pFeugen->AI()->AttackStart(pStalaggTarget); } } } SwitchTarget_Timer = 20000; }else SwitchTarget_Timer -= uiDiff; } else if (pStalagg->isInCombat() || pFeugen->isInCombat()) { if (m_pInstance) m_pInstance->SetData(TYPE_THADDIUS, IN_PROGRESS); } else if (!pStalagg->isInCombat() && !pFeugen->isInCombat()) { if (m_pInstance) m_pInstance->SetData(TYPE_THADDIUS, NOT_STARTED); } Active_Timer = 1000; } } }else Active_Timer -= uiDiff; } if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return; if (ChainLightning_Timer < uiDiff) { DoCast(m_creature, m_bIsHeroicMode ? H_SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING); ChainLightning_Timer = 15000; }else ChainLightning_Timer -= uiDiff; if(m_bIsPolarityShift) { // workaround for POLARITY_SHIFT if (PolarityShift_Timer < uiDiff) { Map *map = m_creature->GetMap(); if (map->IsDungeon()) { Map::PlayerList const &PlayerList = map->GetPlayers(); if (PlayerList.isEmpty()) return; for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) if (i->getSource()->isAlive() && i->getSource()->isTargetableForAttack()) { switch(rand()%2) { case 0: if (i->getSource()->HasAura(SPELL_CHARGE_NEGATIVE_NEARDMG)) i->getSource()->RemoveAurasDueToSpell(SPELL_CHARGE_NEGATIVE_NEARDMG); i->getSource()->CastSpell(i->getSource(), SPELL_CHARGE_POSITIVE_NEARDMG, true); break; case 1: if (i->getSource()->HasAura(SPELL_CHARGE_POSITIVE_NEARDMG)) i->getSource()->RemoveAurasDueToSpell(SPELL_CHARGE_POSITIVE_NEARDMG); i->getSource()->CastSpell(i->getSource(), SPELL_CHARGE_NEGATIVE_NEARDMG, true); break; } } } m_bIsPolarityShift = false; PolarityShift_Timer = 27000; }else PolarityShift_Timer -= uiDiff; } else { if(PolarityShift_Timer < uiDiff) { DoCast(m_creature, SPELL_POLARITY_SHIFT); // need core support m_bIsPolarityShift = true; PolarityShift_Timer = 3000; }else PolarityShift_Timer -= uiDiff; } if (Enrage_Timer < uiDiff) { DoCast(m_creature, SPELL_BESERK); Enrage_Timer = 300000; }else Enrage_Timer -= uiDiff; if (Scream_Timer < uiDiff) { switch(rand()%4) { case 0: DoScriptText(SAY_SCREAM1, m_creature);break; case 1: DoScriptText(SAY_SCREAM2, m_creature);break; case 2: DoScriptText(SAY_SCREAM3, m_creature);break; case 3: DoScriptText(SAY_SCREAM4, m_creature);break; } Scream_Timer = 60000+rand()%30000; }else Scream_Timer -= uiDiff; if (RangeCheck_Timer < uiDiff) { m_bInMeleeRange = false; std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList(); for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) { Unit* pTarget = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); //if in melee range if (pTarget && pTarget->IsWithinDistInMap(m_creature, ATTACK_DISTANCE)) { m_bInMeleeRange = true; break; } } if (!m_bInMeleeRange) DoCast(SelectUnit(SELECT_TARGET_TOPAGGRO,0), SPELL_BALL_LIGHTNING); RangeCheck_Timer = 2000; }else RangeCheck_Timer -= uiDiff; //if nobody is in melee range if (m_bInMeleeRange) DoMeleeAttackIfReady(); }