void HostileRefManager::addTempThreat(float threat, bool apply) { HostileReference* ref = getFirst(); while (ref) { if (apply) { if (ref->getTempThreatModifier() == 0.0f) ref->addTempThreat(threat); } else ref->resetTempThreat(); ref = ref->next(); } }
void ThreatManager::_addThreat(Unit* victim, float threat) { HostileReference* ref = iThreatContainer.addThreat(victim, threat); // Ref is not in the online refs, search the offline refs next if (!ref) ref = iThreatOfflineContainer.addThreat(victim, threat); if (!ref) // there was no ref => create a new one { // threat has to be 0 here HostileReference* hostileRef = new HostileReference(victim, this, 0); iThreatContainer.addReference(hostileRef); hostileRef->addThreat(threat); // now we add the real threat if (victim->GetTypeId() == TYPEID_PLAYER && victim->ToPlayer()->isGameMaster()) hostileRef->setOnlineOfflineState(false); // GM is always offline } }
void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) { threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager HostileReference* 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 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 HostileRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) { float redirectedMod = pVictim->getHostileRefManager().GetThreatRedirectionMod(); Unit* redirectedTarget = redirectedMod ? pVictim->getHostileRefManager().GetThreatRedirectionTarget() : NULL; uint32 size = pSingleTarget ? 1 : getSize(); // if pSingleTarget do not devide threat HostileReference* ref = getFirst(); while(ref != NULL) { float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, false, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell); if (threat > 0.0f) { if (redirectedTarget && redirectedTarget != ref->getTarget() && redirectedTarget->isAlive()) { float redirectedThreat = threat * redirectedMod; threat -= redirectedThreat; if(redirectedTarget == getOwner()) // It is faster to modify the threat durectly if possible ref->addThreat(float (threat) / size); else ref->getSource()->addThreat(redirectedTarget, redirectedThreat); } } if (pVictim == getOwner()) ref->addThreat(float (threat) / size); // It is faster to modify the threat durectly if possible else ref->getSource()->addThreat(pVictim, float (threat) / size); ref = ref->next(); } }
void HostileRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) { if (iOwner->hasUnitState(UNIT_STAT_IGNORE_ATTACKERS)) return; HostileReference* 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(); } }
void ThreatManager::addThreat(Unit* pVictim, float pThreat, bool crit, SpellSchoolMask schoolMask, 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.) // not to self if (pVictim == getOwner()) return; // not to GM if (!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) ) return; // not to dead and not for dead if (!pVictim->isAlive() || !getOwner()->isAlive() ) return; ASSERT(getOwner()->GetTypeId()== TYPEID_UNIT); float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, crit, schoolMask, pThreatSpell); HostileReference* ref = iThreatContainer.addThreat(pVictim, threat); // Ref is online if (ref) iUpdateNeed = true; // Ref is not in the online refs, search the offline refs next else ref = iThreatOfflineContainer.addThreat(pVictim, threat); if (!ref) // there was no ref => create a new one { // threat has to be 0 here HostileReference* hostileReference = new HostileReference(pVictim, this, 0); iThreatContainer.addReference(hostileReference); hostileReference->addThreat(threat); // now we add the real threat iUpdateNeed = true; if (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) hostileReference->setOnlineOfflineState(false); // GM is always offline } }
void ThreatManager::addThreatDirectly(Unit* pVictim, float threat) { HostileReference* ref = iThreatContainer.addThreat(pVictim, threat); // Ref is online if (ref) iUpdateNeed = true; // Ref is not in the online refs, search the offline refs next else ref = iThreatOfflineContainer.addThreat(pVictim, threat); if (!ref) // there was no ref => create a new one { // threat has to be 0 here HostileReference* hostileReference = new HostileReference(pVictim, this, 0); iThreatContainer.addReference(hostileReference); hostileReference->addThreat(threat); // now we add the real threat iUpdateNeed = true; if (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) hostileReference->setOnlineOfflineState(false); // GM is always offline } }
Unit* GetHatefullStrikeTarget() { // Get all Targets in Meleerange const std::list<HostileReference *> &threatlist = me->getThreatManager().getThreatList(); std::list<Unit*> targetList; for (std::list<HostileReference*>::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) { HostileReference* ref = (*itr); if (ref->getTarget() && me->IsWithinMeleeRange(ref->getTarget())) targetList.push_back(ref->getTarget()); } // Get Target with most HP and not getVictim() uint32 MostHP = 0; Unit* pMostHPTarget = NULL; uint32 counter = 0; for(std::list<Unit*>::const_iterator itr = targetList.begin(); itr != targetList.end(); ++itr) { counter++; //Only first 3 Targets in Threadlist if(counter > 3) break; Unit *pTarget = (*itr); if (pTarget->isAlive() && pTarget->GetHealth() > MostHP) { MostHP = pTarget->GetHealth(); pMostHPTarget = pTarget; } } if(pMostHPTarget) return pMostHPTarget; else return me->getVictim(); }
void RandomPlayerbotMgr::Refresh(Player* bot) { if (bot->isDead()) { bot->ResurrectPlayer(1.0f); bot->SpawnCorpseBones(); bot->SaveToDB(); bot->GetPlayerbotAI()->ResetStrategies(); } bot->GetPlayerbotAI()->Reset(); HostileReference *ref = bot->getHostileRefManager().getFirst(); while( ref ) { ThreatManager *threatManager = ref->GetSource(); Unit *unit = threatManager->GetOwner(); float threat = ref->getThreat(); unit->RemoveAllAttackers(); unit->ClearInCombat(); ref = ref->next(); } bot->RemoveAllAttackers(); bot->ClearInCombat(); bot->DurabilityRepairAll(false, 1.0f, false); bot->SetFullHealth(); bot->SetPvP(true); if (bot->GetMaxPower(POWER_MANA) > 0) bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA)); if (bot->GetMaxPower(POWER_ENERGY) > 0) bot->SetPower(POWER_ENERGY, bot->GetMaxPower(POWER_ENERGY)); }
void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) { threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager HostileReference* hostileReference = threatRefStatusChangeEvent->getReference(); switch(threatRefStatusChangeEvent->getType()) { case UEV_THREAT_REF_THREAT_CHANGE: if((getCurrentVictim() == hostileReference && threatRefStatusChangeEvent->getFValue()<0.0f) || (getCurrentVictim() != hostileReference && threatRefStatusChangeEvent->getFValue()>0.0f)) setDirty(true); // the order in the threat list might have changed break; case UEV_THREAT_REF_ONLINE_STATUS: if(!hostileReference->isOnline()) { if (hostileReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } if (getOwner() && getOwner()->IsInWorld()) if (Unit* target = ObjectAccessor::GetUnit(*getOwner(), hostileReference->getUnitGuid())) if (getOwner()->IsInMap(target)) getOwner()->SendThreatRemove(hostileReference); iThreatContainer.remove(hostileReference); iUpdateNeed = true; iThreatOfflineContainer.addReference(hostileReference); } else { if(getCurrentVictim() && hostileReference->getThreat() > (1.1f * getCurrentVictim()->getThreat())) setDirty(true); iThreatContainer.addReference(hostileReference); iUpdateNeed = true; iThreatOfflineContainer.remove(hostileReference); } break; case UEV_THREAT_REF_REMOVE_FROM_LIST: if (hostileReference == getCurrentVictim()) { setCurrentVictim(NULL); setDirty(true); } if(hostileReference->isOnline()) { if (getOwner() && getOwner()->IsInWorld()) if (Unit* target = ObjectAccessor::GetUnit(*getOwner(), hostileReference->getUnitGuid())) if (getOwner()->IsInMap(target)) getOwner()->SendThreatRemove(hostileReference); iThreatContainer.remove(hostileReference); iUpdateNeed = true; } else iThreatOfflineContainer.remove(hostileReference); break; } }
void RandomPlayerbotMgr::Refresh(Player* bot) { if (bot->IsDead()) { PlayerbotChatHandler ch(bot); ch.revive(*bot); bot->GetPlayerbotAI()->ResetStrategies(); } bot->GetPlayerbotAI()->Reset(); HostileReference *ref = bot->GetHostileRefManager().getFirst(); while( ref ) { ThreatManager *threatManager = ref->getSource(); Unit *unit = threatManager->getOwner(); float threat = ref->getThreat(); unit->RemoveAllAttackers(); unit->ClearInCombat(); ref = ref->next(); } bot->RemoveAllAttackers(); bot->ClearInCombat(); bot->DurabilityRepairAll(false, 1.0f); bot->SetHealthPercent(100); bot->SetPvP(true); if (bot->GetMaxPower(POWER_MANA) > 0) bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA)); if (bot->GetMaxPower(POWER_ENERGY) > 0) bot->SetPower(POWER_ENERGY, bot->GetMaxPower(POWER_ENERGY)); }
void HostileRefManager::UpdateVisibility(bool checkThreat) { HostileReference* ref = getFirst(); while (ref) { HostileReference* nextRef = ref->next(); if ((!checkThreat || ref->GetSource()->getThreatList().size() <= 1) && !ref->GetSource()->GetOwner()->CanSeeOrDetect(GetOwner())) { nextRef = ref->next(); ref->removeReference(); delete ref; } ref = nextRef; } }
HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim) { HostileReference* pCurrentRef = nullptr; bool found = false; bool onlySecondChoiceTargetsFound = false; bool checkedCurrentVictim = false; ThreatList::const_iterator lastRef = iThreatList.end(); --lastRef; for (ThreatList::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) { pCurrentRef = (*iter); Unit* pTarget = pCurrentRef->getTarget(); MANGOS_ASSERT(pTarget); // if the ref has status online the target must be there! // some units are prefered in comparison to others // if (checkThreatArea) consider IsOutOfThreatArea - expected to be only set for pCurrentVictim // This prevents dropping valid targets due to 1.1 or 1.3 threat rule vs invalid current target if (!onlySecondChoiceTargetsFound && pAttacker->IsSecondChoiceTarget(pTarget, pCurrentRef == pCurrentVictim)) { if (iter != lastRef) ++iter; 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. onlySecondChoiceTargetsFound = true; iter = iThreatList.begin(); } // current victim is a second choice target, so don't compare threat with it below if (pCurrentRef == pCurrentVictim) pCurrentVictim = nullptr; // second choice targets are only handled threat dependend if we have only have second choice targets continue; } if (!pAttacker->IsOutOfThreatArea(pTarget)) // skip non attackable currently targets { if (pCurrentVictim) // select 1.3/1.1 better target in comparison current target { // normal case: pCurrentRef is still valid and most hated if (pCurrentVictim == pCurrentRef) { found = true; break; } // we found a valid target, but only compare its threat if the currect victim is also a valid target // Additional check to prevent unneeded comparision in case of valid current victim if (!checkedCurrentVictim) { Unit* pCurrentTarget = pCurrentVictim->getTarget(); MANGOS_ASSERT(pCurrentTarget); if (pAttacker->IsSecondChoiceTarget(pCurrentTarget, true)) { // CurrentVictim is invalid, so return CurrentRef found = true; break; } checkedCurrentVictim = true; } // list sorted and and we check current target, then this is best case if (pCurrentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat()) { pCurrentRef = pCurrentVictim; found = true; break; } if (pCurrentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || (pCurrentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->CanReachWithMeleeAttack(pTarget))) { // 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) pCurrentRef = nullptr; return pCurrentRef; }
void ThreatManager::tauntFadeOut(Unit* taunter) { HostileReference* ref = iThreatContainer.getReferenceByTarget(taunter); if (ref) ref->resetTempThreat(); }
HostileReference* ThreatContainer::selectNextVictim(Creature* attacker, HostileReference* currentVictim) { HostileReference* currentRef = NULL; bool found = false; bool noPriorityTargetFound = false; std::list<HostileReference*>::const_iterator lastRef = iThreatList.end(); --lastRef; for (std::list<HostileReference*>::const_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(attacker->GetMeleeDamageSchoolMask()) || target->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE))) { if (iter != lastRef) { // current victim is a second choice target, so don't compare threat with it below if (currentRef == currentVictim) currentVictim = 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 (attacker->canCreatureAttack(target)) // skip non attackable currently targets { if (currentVictim) // 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 (currentVictim == currentRef || currentRef->getThreat() <= 1.1f * currentVictim->getThreat()) { if (currentVictim != currentRef && attacker->canCreatureAttack(currentVictim->getTarget())) currentRef = currentVictim; // for second case, if currentvictim is attackable found = true; break; } if (currentRef->getThreat() > 1.3f * currentVictim->getThreat() || (currentRef->getThreat() > 1.1f * currentVictim->getThreat() && attacker->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; } } ++iter; } if (!found) currentRef = NULL; return currentRef; }
void InitializeAI() { CasterAI::InitializeAI(); Unit* owner = me->GetOwner(); if (!owner) return; // Clone Me! owner->CastSpell(me, SPELL_MAGE_CLONE_ME, true); // xinef: Glyph of Mirror Image (4th copy) float angle = 0.0f; switch (me->GetUInt32Value(UNIT_CREATED_BY_SPELL)) { case SPELL_SUMMON_MIRROR_IMAGE1: angle = 0.5f * M_PI; break; case SPELL_SUMMON_MIRROR_IMAGE2: angle = M_PI; break; case SPELL_SUMMON_MIRROR_IMAGE3: angle = 1.5f * M_PI; break; } ((Minion*)me)->SetFollowAngle(angle); if (owner->IsInCombat()) me->NearTeleportTo(me->GetPositionX() + cos(angle)*dist, me->GetPositionY() + sin(angle)*dist, me->GetPositionZ(), me->GetOrientation(), false, false, false, false); else me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); me->SetReactState(REACT_DEFENSIVE); // Xinef: Inherit Master's Threat List (not yet implemented) //owner->CastSpell((Unit*)NULL, SPELL_MAGE_MASTERS_THREAT_LIST, true); HostileReference* ref = owner->getHostileRefManager().getFirst(); while (ref) { if (Unit* unit = ref->GetSource()->GetOwner()) unit->AddThreat(me, ref->getThreat() - ref->getTempThreatModifier()); ref = ref->next(); } _ebonGargoyleGUID = 0; // Xinef: copy caster auras Unit::VisibleAuraMap const* visibleAuraMap = owner->GetVisibleAuras(); for (Unit::VisibleAuraMap::const_iterator itr = visibleAuraMap->begin(); itr != visibleAuraMap->end(); ++itr) if (Aura* visAura = itr->second->GetBase()) { // Ebon Gargoyle if (visAura->GetId() == 49206 && me->GetUInt32Value(UNIT_CREATED_BY_SPELL) == SPELL_SUMMON_MIRROR_IMAGE1) { if (Unit* gargoyle = visAura->GetCaster()) _ebonGargoyleGUID = gargoyle->GetGUID(); continue; } SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(visAura->GetId()); if (bounds.first != bounds.second) continue; std::vector<int32> const* spellTriggered = sSpellMgr->GetSpellLinked(visAura->GetId() + SPELL_LINK_AURA); if (!spellTriggered || !spellTriggered->empty()) continue; if (Aura* newAura = me->AddAura(visAura->GetId(), me)) newAura->SetDuration(visAura->GetDuration()); } me->m_Events.AddEvent(new DeathEvent(*me), me->m_Events.CalculateTime(29500)); }
void HostileRefManager::updateThreatTables() { for (HostileReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->updateOnlineStatus(); }
void HostileRefManager::setOnlineOfflineState(bool pIsOnline) { for (HostileReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->setOnlineOfflineState(pIsOnline); }
void HostileRefManager::addThreatPercent(int32 pValue) { for (HostileReference* ref = getFirst(); ref != NULL; ref = ref->next()) ref->addThreatPercent(pValue); }
HostileReference* ThreatContainer::selectNextVictim(Creature* attacker, HostileReference* currentVictim) const { // pussywizard: pretty much remade this whole function HostileReference* currentRef = NULL; bool found = false; bool noPriorityTargetFound = false; uint32 currTime = sWorld->GetGameTime(); // pussywizard: currentVictim is needed to compare if threat was exceeded by 10%/30% for melee/range targets (only then switching current target) if (currentVictim) { Unit* cvUnit = currentVictim->getTarget(); if (!attacker->_CanDetectFeignDeathOf(cvUnit) || !attacker->CanCreatureAttack(cvUnit) || attacker->isTargetNotAcceptableByMMaps(cvUnit->GetGUID(), currTime, cvUnit)) // pussywizard: if currentVictim is not valid => don't compare the threat with it, just take the highest threat valid target currentVictim = NULL; else if (cvUnit->IsImmunedToDamageOrSchool(attacker->GetMeleeDamageSchoolMask()) || cvUnit->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)) // pussywizard: no 10%/30% if currentVictim is immune to damage or has auras breakable by damage currentVictim = NULL; } ThreatContainer::StorageType::const_iterator lastRef = iThreatList.end(); --lastRef; // pussywizard: iterate from highest to lowest threat for (ThreatContainer::StorageType::const_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 ! // pussywizard: don't go to threat comparison if this ref is immune to damage or has aura breakable on damage (second choice target) // pussywizard: if this is the last entry on the threat list, then all targets are second choice, set bool to true and loop threat list again, ignoring this section if (!noPriorityTargetFound && (target->IsImmunedToDamageOrSchool(attacker->GetMeleeDamageSchoolMask()) || target->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE) || target->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, attacker->GetGUID()))) { if (iter != lastRef) { ++iter; continue; } else { noPriorityTargetFound = true; iter = iThreatList.begin(); continue; } } // pussywizard: skip not valid targets if (attacker->_CanDetectFeignDeathOf(target) && attacker->CanCreatureAttack(target) && !attacker->isTargetNotAcceptableByMMaps(target->GetGUID(), currTime, target)) { if (currentVictim) // pussywizard: if not NULL then target must have 10%/30% more threat { if (currentVictim == currentRef) // pussywizard: nothing found previously was good and enough, currentRef passed all necessary tests, so end now { found = true; break; } // pussywizard: implement 110% threat rule for targets in melee range and 130% rule for targets in ranged distances if (currentRef->getThreat() > 1.3f * currentVictim->getThreat()) // pussywizard: enough in all cases, end { found = true; break; } else if (currentRef->getThreat() > 1.1f * currentVictim->getThreat()) // pussywizard: enought only if target in melee range { if (attacker->IsWithinMeleeRange(target)) { found = true; break; } } else // pussywizard: nothing found previously was good and enough, this and next entries on the list have less than 110% threat, and currentVictim is present and valid as checked before the loop (otherwise it's NULL), so end now { currentRef = currentVictim; found = true; break; } } else // pussywizard: no currentVictim, first passing all checks is chosen (highest threat, list is sorted) { found = true; break; } } ++iter; } if (!found) currentRef = NULL; return currentRef; }
HostileReference* ThreatContainer::selectNextVictim(Creature* attacker, HostileReference* currentVictim) { HostileReference* currentRef = NULL; bool found = false; bool noPriorityTargetFound = false; std::list<HostileReference*>::const_iterator lastRef = iThreatList.end(); lastRef--; for(std::list<HostileReference*>::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) { currentRef = (*iter); Unit* pTarget = currentRef->getTarget(); ASSERT(pTarget); // if the ref has status online the target must be there ! if(!noPriorityTargetFound && (pTarget->IsImmunedToDamage(attacker->GetMeleeDamageSchoolMask()) || pTarget->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE))) { if(iter != lastRef) { if(currentRef == currentVictim) currentVictim = NULL; ++iter; continue; } else { noPriorityTargetFound = true; iter = iThreatList.begin(); continue; } } if(attacker->canCreatureAttack(pTarget)) // skip non attackable currently targets { if(currentVictim && currentRef) // select 1.3/1.1 better target in comparison current target { if(currentVictim == currentRef || currentRef->getThreat() <= 1.1f * currentVictim->getThreat()) { if(currentVictim != currentRef && attacker->canCreatureAttack(currentVictim->getTarget())) currentRef = currentVictim; // for second case found = true; break; } if(currentRef->getThreat() > 1.3f * currentVictim->getThreat() || (currentRef->getThreat() > 1.1f * currentVictim->getThreat() && attacker->IsWithinMeleeRange(pTarget))) { //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 { found = true; break; } } ++iter; } if(!found) currentRef = NULL; return currentRef; }