mirror of
https://github.com/mangosfour/server.git
synced 2025-12-15 01:37:00 +00:00
[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 <schmoozerd@scriptdev2.com>
This commit is contained in:
parent
b35dea1703
commit
f4d862ac0a
4 changed files with 86 additions and 52 deletions
|
|
@ -281,58 +281,82 @@ void ThreatContainer::update()
|
||||||
|
|
||||||
HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim)
|
HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim)
|
||||||
{
|
{
|
||||||
HostileReference* currentRef = NULL;
|
HostileReference* pCurrentRef = NULL;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
bool noPriorityTargetFound = false;
|
bool onlySecondChoiceTargetsFound = false;
|
||||||
|
bool checkedCurrentVictim = false;
|
||||||
|
|
||||||
ThreatList::const_iterator lastRef = iThreatList.end();
|
ThreatList::const_iterator lastRef = iThreatList.end();
|
||||||
lastRef--;
|
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();
|
Unit* pTarget = pCurrentRef->getTarget();
|
||||||
MANGOS_ASSERT(target); // if the ref has status online the target must be there !
|
MANGOS_ASSERT(pTarget); // if the ref has status online the target must be there!
|
||||||
|
|
||||||
// some units are prefered in comparison to others
|
// 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)
|
if (iter != lastRef)
|
||||||
{
|
|
||||||
// current victim is a second choice target, so don't compare threat with it below
|
|
||||||
if(currentRef == pCurrentVictim)
|
|
||||||
pCurrentVictim = NULL;
|
|
||||||
++iter;
|
++iter;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
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.
|
// 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();
|
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
|
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
|
// normal case: pCurrentRef is still valid and most hated
|
||||||
if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
|
if (pCurrentVictim == pCurrentRef)
|
||||||
{
|
{
|
||||||
currentRef = pCurrentVictim; // for second case
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
|
// we found a valid target, but only compare its threat if the currect victim is also a valid target
|
||||||
(currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() &&
|
// Additional check to prevent unneeded comparision in case of valid current victim
|
||||||
pAttacker->CanReachWithMeleeAttack(target)) )
|
if (!checkedCurrentVictim)
|
||||||
{ //implement 110% threat rule for targets in melee range
|
{
|
||||||
found = true; //and 130% rule for targets in ranged distances
|
Unit* pCurrentTarget = pCurrentVictim->getTarget();
|
||||||
break; //for selecting alive targets
|
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
|
else // select any
|
||||||
|
|
@ -343,10 +367,10 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, Hostile
|
||||||
}
|
}
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
if(!found)
|
if (!found)
|
||||||
currentRef = NULL;
|
pCurrentRef = NULL;
|
||||||
|
|
||||||
return currentRef;
|
return pCurrentRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================
|
//============================================================
|
||||||
|
|
|
||||||
|
|
@ -8539,10 +8539,14 @@ void Unit::TauntApply(Unit* taunter)
|
||||||
if (target && target == taunter)
|
if (target && target == taunter)
|
||||||
return;
|
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())
|
if (((Creature*)this)->AI())
|
||||||
((Creature*)this)->AI()->AttackStart(taunter);
|
((Creature*)this)->AI()->AttackStart(taunter);
|
||||||
|
}
|
||||||
|
|
||||||
m_ThreatManager.tauntApply(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()
|
bool Unit::SelectHostileTarget()
|
||||||
{
|
{
|
||||||
//function provides main threat functionality
|
//function provides main threat functionality
|
||||||
|
|
@ -8608,6 +8624,7 @@ bool Unit::SelectHostileTarget()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* target = NULL;
|
Unit* target = NULL;
|
||||||
|
Unit* oldTarget = getVictim();
|
||||||
|
|
||||||
// First checking if we have some taunt on us
|
// First checking if we have some taunt on us
|
||||||
const AuraList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT);
|
const AuraList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT);
|
||||||
|
|
@ -8615,26 +8632,17 @@ bool Unit::SelectHostileTarget()
|
||||||
{
|
{
|
||||||
Unit* caster;
|
Unit* caster;
|
||||||
|
|
||||||
// The last taunt aura caster is alive an we are happy to attack him
|
// Find first available taunter target
|
||||||
if ((caster = tauntAuras.back()->GetCaster()) && caster->isAlive())
|
// Auras are pushed_back, last caster will be on the end
|
||||||
return true;
|
for (AuraList::const_reverse_iterator aura = tauntAuras.rbegin(); aura != tauntAuras.rend(); ++aura)
|
||||||
else if (tauntAuras.size() > 1)
|
|
||||||
{
|
{
|
||||||
// We do not have last taunt aura caster but we have more taunt auras,
|
if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) &&
|
||||||
// so find first available target
|
caster->isTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this) &&
|
||||||
|
!IsSecondChoiceTarget(caster, true))
|
||||||
// Auras are pushed_back, last caster will be on the end
|
|
||||||
AuraList::const_iterator aura = --tauntAuras.end();
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
--aura;
|
target = caster;
|
||||||
if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) &&
|
break;
|
||||||
caster->isTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this))
|
}
|
||||||
{
|
|
||||||
target = caster;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}while (aura != tauntAuras.begin());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8647,7 +8655,8 @@ bool Unit::SelectHostileTarget()
|
||||||
if (!hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED))
|
if (!hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED))
|
||||||
{
|
{
|
||||||
SetInFront(target);
|
SetInFront(target);
|
||||||
((Creature*)this)->AI()->AttackStart(target);
|
if (oldTarget != target)
|
||||||
|
((Creature*)this)->AI()->AttackStart(target);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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);
|
float ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
|
||||||
void DeleteThreatList();
|
void DeleteThreatList();
|
||||||
|
bool IsSecondChoiceTarget(Unit* pTarget, bool checkThreatArea);
|
||||||
bool SelectHostileTarget();
|
bool SelectHostileTarget();
|
||||||
void TauntApply(Unit* pVictim);
|
void TauntApply(Unit* pVictim);
|
||||||
void TauntFadeOut(Unit *taunter);
|
void TauntFadeOut(Unit *taunter);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef __REVISION_NR_H__
|
#ifndef __REVISION_NR_H__
|
||||||
#define __REVISION_NR_H__
|
#define __REVISION_NR_H__
|
||||||
#define REVISION_NR "11845"
|
#define REVISION_NR "11846"
|
||||||
#endif // __REVISION_NR_H__
|
#endif // __REVISION_NR_H__
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue