From f4d862ac0acdf9b59b70c12c8ac88714431f42eb Mon Sep 17 00:00:00 2001 From: Schmoozerd Date: Sat, 12 Nov 2011 21:24:50 +0100 Subject: [PATCH] [11846] Changes to default target selection * Add a new function: Unit::IsSecondChoiceTarget to evaluate if a target will be selected * Cleanup a bit related code * SelectHostileTarget - Only call AI()->AttackStart for new targets - Remove exception for top-most taunter * Fix a few minor bugs related to selectNextVictim Signed-off-by: Schmoozerd --- src/game/ThreatManager.cpp | 82 ++++++++++++++++++++++++-------------- src/game/Unit.cpp | 53 ++++++++++++++---------- src/game/Unit.h | 1 + src/shared/revision_nr.h | 2 +- 4 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp index 71e1a56a8..7f52dc3b8 100644 --- a/src/game/ThreatManager.cpp +++ b/src/game/ThreatManager.cpp @@ -281,58 +281,82 @@ void ThreatContainer::update() HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim) { - HostileReference* currentRef = NULL; + HostileReference* pCurrentRef = NULL; bool found = false; - bool noPriorityTargetFound = 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;) + for (ThreatList::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) { - currentRef = (*iter); + pCurrentRef = (*iter); - Unit* target = currentRef->getTarget(); - MANGOS_ASSERT(target); // if the ref has status online the target must be there ! + 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(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) ) + // 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) - { - // current victim is a second choice target, so don't compare threat with it below - if(currentRef == pCurrentVictim) - pCurrentVictim = NULL; + if (iter != lastRef) ++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; + onlySecondChoiceTargetsFound = true; iter = iThreatList.begin(); - continue; } + + // current victim is a second choice target, so don't compare threat with it below + if (pCurrentRef == pCurrentVictim) + pCurrentVictim = NULL; + + // second choice targets are only handled threat dependend if we have only have second choice targets + continue; } - if (!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets + if (!pAttacker->IsOutOfThreatArea(pTarget)) // 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() ) + // normal case: pCurrentRef is still valid and most hated + if (pCurrentVictim == pCurrentRef) { - currentRef = pCurrentVictim; // for second case found = true; break; } - if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || - (currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && - pAttacker->CanReachWithMeleeAttack(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 + // 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 @@ -343,10 +367,10 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, Hostile } ++iter; } - if(!found) - currentRef = NULL; + if (!found) + pCurrentRef = NULL; - return currentRef; + return pCurrentRef; } //============================================================ diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 0076aa2f9..71e568525 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -8539,10 +8539,14 @@ void Unit::TauntApply(Unit* taunter) if (target && target == taunter) return; - SetInFront(taunter); + // Only attack taunter if this is a valid target + if (!hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED) && !IsSecondChoiceTarget(taunter, true)) + { + SetInFront(taunter); - if (((Creature*)this)->AI()) - ((Creature*)this)->AI()->AttackStart(taunter); + if (((Creature*)this)->AI()) + ((Creature*)this)->AI()->AttackStart(taunter); + } m_ThreatManager.tauntApply(taunter); } @@ -8592,6 +8596,18 @@ void Unit::TauntFadeOut(Unit *taunter) //====================================================================== +bool Unit::IsSecondChoiceTarget(Unit* pTarget, bool checkThreatArea) +{ + MANGOS_ASSERT(pTarget && GetTypeId() == TYPEID_UNIT); + + return + pTarget->IsImmunedToDamage(GetMeleeDamageSchoolMask()) || + pTarget->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE) || + checkThreatArea && ((Creature*)this)->IsOutOfThreatArea(pTarget); +} + +//====================================================================== + bool Unit::SelectHostileTarget() { //function provides main threat functionality @@ -8608,6 +8624,7 @@ bool Unit::SelectHostileTarget() return false; Unit* target = NULL; + Unit* oldTarget = getVictim(); // First checking if we have some taunt on us const AuraList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT); @@ -8615,26 +8632,17 @@ bool Unit::SelectHostileTarget() { Unit* caster; - // The last taunt aura caster is alive an we are happy to attack him - if ((caster = tauntAuras.back()->GetCaster()) && caster->isAlive()) - return true; - else if (tauntAuras.size() > 1) + // Find first available taunter target + // Auras are pushed_back, last caster will be on the end + for (AuraList::const_reverse_iterator aura = tauntAuras.rbegin(); aura != tauntAuras.rend(); ++aura) { - // We do not have last taunt aura caster but we have more taunt auras, - // so find first available target - - // Auras are pushed_back, last caster will be on the end - AuraList::const_iterator aura = --tauntAuras.end(); - do + if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) && + caster->isTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this) && + !IsSecondChoiceTarget(caster, true)) { - --aura; - if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) && - caster->isTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this)) - { - target = caster; - break; - } - }while (aura != tauntAuras.begin()); + target = caster; + break; + } } } @@ -8647,7 +8655,8 @@ bool Unit::SelectHostileTarget() if (!hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED)) { SetInFront(target); - ((Creature*)this)->AI()->AttackStart(target); + if (oldTarget != target) + ((Creature*)this)->AI()->AttackStart(target); } return true; } diff --git a/src/game/Unit.h b/src/game/Unit.h index 1459ece1b..7e742e8e0 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1669,6 +1669,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void AddThreat(Unit* pVictim, float threat = 0.0f, bool crit = false, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NONE, SpellEntry const *threatSpell = NULL); float ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL); void DeleteThreatList(); + bool IsSecondChoiceTarget(Unit* pTarget, bool checkThreatArea); bool SelectHostileTarget(); void TauntApply(Unit* pVictim); void TauntFadeOut(Unit *taunter); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 5dda3da6a..ec053c3d3 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11845" + #define REVISION_NR "11846" #endif // __REVISION_NR_H__