refactor(Core/UnitAI): Update target selector (#13169)
* refactor(Core/UnitAI): Update TargetSelector. Cherry-pick: *https://github.com/TrinityCore/TrinityCore/pull/19930 Co-authored-by: Treeston <treeston@users.noreply.github.com> * fix GGC/CLang errors in scripts * . * missing a file and fix a script error * . * fix path changes dude to a miss click in the second commit * . Co-authored-by: Maelthyrr <lynethris@protonmail.ch> Co-authored-by: Treeston <treeston@users.noreply.github.com>
This commit is contained in:
@@ -108,14 +108,14 @@ void UnitAI::DoSpellAttackToRandomTargetIfReady(uint32 spell, uint32 threatTable
|
||||
}
|
||||
}
|
||||
|
||||
Unit* UnitAI::SelectTarget(SelectTargetMethod targetType, uint32 position, float dist, bool playerOnly, int32 aura)
|
||||
Unit* UnitAI::SelectTarget(SelectTargetMethod targetType, uint32 position, float dist, bool playerOnly, bool withTank, int32 aura)
|
||||
{
|
||||
return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, aura));
|
||||
return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
|
||||
}
|
||||
|
||||
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, float dist, bool playerOnly, int32 aura)
|
||||
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 position, float dist, bool playerOnly, bool withTank, int32 aura)
|
||||
{
|
||||
SelectTargetList(targetList, DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
|
||||
SelectTargetList(targetList, num, targetType, position, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
|
||||
}
|
||||
|
||||
float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
|
||||
@@ -210,7 +210,7 @@ SpellCastResult UnitAI::DoCast(uint32 spellId)
|
||||
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER);
|
||||
float range = spellInfo->GetMaxRange(false);
|
||||
|
||||
DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId);
|
||||
DefaultTargetSelector targetSelector(me, range, playerOnly, true, -(int32)spellId);
|
||||
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
|
||||
&& targetSelector(me->GetVictim()))
|
||||
target = me->GetVictim();
|
||||
@@ -317,6 +317,16 @@ void UnitAI::FillAISpellInfo()
|
||||
}
|
||||
}
|
||||
|
||||
ThreatMgr& UnitAI::GetThreatMgr()
|
||||
{
|
||||
return me->GetThreatMgr();
|
||||
}
|
||||
|
||||
void UnitAI::SortByDistance(std::list<Unit*>& list, bool ascending)
|
||||
{
|
||||
list.sort(Acore::ObjectDistanceOrderPred(me, ascending));
|
||||
}
|
||||
|
||||
//Enable PlayerAI when charmed
|
||||
void PlayerAI::OnCharmed(bool apply)
|
||||
{
|
||||
@@ -397,5 +407,8 @@ bool NonTankTargetSelector::operator()(Unit const* target) const
|
||||
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
|
||||
return false;
|
||||
|
||||
if (Unit* currentVictim = _source->GetThreatMgr().GetCurrentVictim())
|
||||
return target != currentVictim;
|
||||
|
||||
return target != _source->GetVictim();
|
||||
}
|
||||
|
||||
@@ -43,14 +43,16 @@ struct DefaultTargetSelector : public Acore::unary_function<Unit*, bool>
|
||||
{
|
||||
Unit const* me;
|
||||
float m_dist;
|
||||
Unit const* except;
|
||||
bool m_playerOnly;
|
||||
int32 m_aura;
|
||||
|
||||
// unit: the reference unit
|
||||
// dist: if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit
|
||||
// playerOnly: self explaining
|
||||
// withMainTank: allow current tank to be selected
|
||||
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
|
||||
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, int32 aura) : me(unit), m_dist(dist), m_playerOnly(playerOnly), m_aura(aura) {}
|
||||
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withMainTank, int32 aura) : me(unit), m_dist(dist), except(withMainTank ? me->GetThreatMgr().GetCurrentVictim() : nullptr), m_playerOnly(playerOnly), m_aura(aura) {}
|
||||
|
||||
bool operator()(Unit const* target) const
|
||||
{
|
||||
@@ -60,6 +62,9 @@ struct DefaultTargetSelector : public Acore::unary_function<Unit*, bool>
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
if (target == except)
|
||||
return false;
|
||||
|
||||
if (m_playerOnly && (target->GetTypeId() != TYPEID_PLAYER))
|
||||
return false;
|
||||
|
||||
@@ -208,7 +213,7 @@ public:
|
||||
// - Not the current tank (if withTank = false)
|
||||
// - Has aura with ID <aura> (if aura > 0)
|
||||
// - Does not have aura with ID -<aura> (if aura < 0)
|
||||
Unit* SelectTarget(SelectTargetMethod targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
|
||||
Unit* SelectTarget(SelectTargetMethod targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
|
||||
|
||||
// Select the best target (in <targetType> order) satisfying <predicate> from the threat list.
|
||||
// If <offset> is nonzero, the first <offset> entries in <targetType> order (or SelectTargetMethod::MaxThreat
|
||||
@@ -216,48 +221,30 @@ public:
|
||||
template <class PREDICATE>
|
||||
Unit* SelectTarget(SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate)
|
||||
{
|
||||
ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList();
|
||||
if (position >= threatlist.size())
|
||||
ThreatMgr& mgr = GetThreatMgr();
|
||||
// shortcut: if we ignore the first <offset> elements, and there are at most <offset> elements, then we ignore ALL elements
|
||||
if (mgr.GetThreatListSize() <= position)
|
||||
return nullptr;
|
||||
|
||||
std::list<Unit*> targetList;
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
if (predicate((*itr)->getTarget()))
|
||||
targetList.push_back((*itr)->getTarget());
|
||||
SelectTargetList(targetList, mgr.GetThreatListSize(), targetType, position, predicate);
|
||||
|
||||
if (position >= targetList.size())
|
||||
// maybe nothing fulfills the predicate
|
||||
if (targetList.empty())
|
||||
return nullptr;
|
||||
|
||||
if (targetType == SelectTargetMethod::MaxDistance || targetType == SelectTargetMethod::MinDistance)
|
||||
targetList.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
|
||||
switch (targetType)
|
||||
{
|
||||
case SelectTargetMethod::MaxDistance:
|
||||
case SelectTargetMethod::MaxThreat:
|
||||
{
|
||||
std::list<Unit*>::iterator itr = targetList.begin();
|
||||
std::advance(itr, position);
|
||||
return *itr;
|
||||
}
|
||||
case SelectTargetMethod::MinDistance:
|
||||
case SelectTargetMethod::MinThreat:
|
||||
{
|
||||
std::list<Unit*>::reverse_iterator ritr = targetList.rbegin();
|
||||
std::advance(ritr, position);
|
||||
return *ritr;
|
||||
}
|
||||
case SelectTargetMethod::Random:
|
||||
{
|
||||
std::list<Unit*>::iterator itr = targetList.begin();
|
||||
std::advance(itr, urand(position, targetList.size() - 1));
|
||||
return *itr;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
case SelectTargetMethod::MaxThreat:
|
||||
case SelectTargetMethod::MinThreat:
|
||||
case SelectTargetMethod::MaxDistance:
|
||||
case SelectTargetMethod::MinDistance:
|
||||
return targetList.front();
|
||||
case SelectTargetMethod::Random:
|
||||
return Acore::Containers::SelectRandomContainerElement(targetList);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Select the best (up to) <num> targets (in <targetType> order) from the threat list that fulfill the following:
|
||||
@@ -270,35 +257,79 @@ public:
|
||||
// - Has aura with ID <aura> (if aura > 0)
|
||||
// - Does not have aura with ID -<aura> (if aura < 0)
|
||||
// The resulting targets are stored in <targetList> (which is cleared first).
|
||||
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
|
||||
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
|
||||
|
||||
// Select the best (up to) <num> targets (in <targetType> order) satisfying <predicate> from the threat list and stores them in <targetList> (which is cleared first).
|
||||
// If <offset> is nonzero, the first <offset> entries in <targetType> order (or SelectTargetMethod::MaxThreat
|
||||
// order, if <targetType> is SelectTargetMethod::Random) are skipped.
|
||||
template <class PREDICATE>
|
||||
void SelectTargetList(std::list<Unit*>& targetList, PREDICATE const& predicate, uint32 maxTargets, SelectTargetMethod targetType)
|
||||
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate)
|
||||
{
|
||||
ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList();
|
||||
if (threatlist.empty())
|
||||
return;
|
||||
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
if (predicate((*itr)->getTarget()))
|
||||
targetList.push_back((*itr)->getTarget());
|
||||
|
||||
if (targetList.size() < maxTargets)
|
||||
targetList.clear();
|
||||
ThreatMgr& mgr = GetThreatMgr();
|
||||
// shortcut: we're gonna ignore the first <offset> elements, and there's at most <offset> elements, so we ignore them all - nothing to do here
|
||||
if (mgr.GetThreatListSize() <= position)
|
||||
return;
|
||||
|
||||
if (targetType == SelectTargetMethod::MaxDistance || targetType == SelectTargetMethod::MinDistance)
|
||||
targetList.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
{
|
||||
for (ThreatReference const* ref : mgr.GetUnsortedThreatList())
|
||||
{
|
||||
if (ref->IsOffline())
|
||||
continue;
|
||||
|
||||
if (targetType == SelectTargetMethod::MinDistance || targetType == SelectTargetMethod::MinThreat)
|
||||
targetList.push_back(ref->GetVictim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Unit* currentVictim = mgr.GetCurrentVictim();
|
||||
if (currentVictim)
|
||||
targetList.push_back(currentVictim);
|
||||
|
||||
for (ThreatReference const* ref : mgr.GetSortedThreatList())
|
||||
{
|
||||
if (ref->IsOffline())
|
||||
continue;
|
||||
|
||||
Unit* thisTarget = ref->GetVictim();
|
||||
if (thisTarget != currentVictim)
|
||||
targetList.push_back(thisTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// shortcut: the list isn't gonna get any larger
|
||||
if (targetList.size() <= position)
|
||||
{
|
||||
targetList.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// right now, list is unsorted for DISTANCE types - re-sort by SelectTargetMethod::MaxDistance
|
||||
if (targetType == SelectTargetMethod::MaxDistance || targetType == SelectTargetMethod::MinDistance)
|
||||
SortByDistance(targetList, targetType == SelectTargetMethod::MinDistance);
|
||||
|
||||
// now the list is MAX sorted, reverse for MIN types
|
||||
if (targetType == SelectTargetMethod::MinThreat)
|
||||
targetList.reverse();
|
||||
|
||||
// ignore the first <offset> elements
|
||||
while (position)
|
||||
{
|
||||
targetList.pop_front();
|
||||
--position;
|
||||
}
|
||||
|
||||
// then finally filter by predicate
|
||||
targetList.remove_if([&predicate](Unit* target) { return !predicate(target); });
|
||||
|
||||
if (targetList.size() <= num)
|
||||
return;
|
||||
|
||||
if (targetType == SelectTargetMethod::Random)
|
||||
Acore::Containers::RandomResize(targetList, maxTargets);
|
||||
Acore::Containers::RandomResize(targetList, num);
|
||||
else
|
||||
targetList.resize(maxTargets);
|
||||
targetList.resize(num);
|
||||
}
|
||||
|
||||
// Called at any Damage to any victim (before damage apply)
|
||||
@@ -348,6 +379,10 @@ public:
|
||||
virtual void sOnGameEvent(bool /*start*/, uint16 /*eventId*/) {}
|
||||
|
||||
virtual std::string GetDebugInfo() const;
|
||||
|
||||
private:
|
||||
ThreatMgr& GetThreatMgr();
|
||||
void SortByDistance(std::list<Unit*>& list, bool ascending = true);
|
||||
};
|
||||
|
||||
class PlayerAI : public UnitAI
|
||||
|
||||
@@ -225,6 +225,9 @@ public:
|
||||
[[nodiscard]] bool isThreatListEmpty() const { return iThreatContainer.empty(); }
|
||||
[[nodiscard]] bool areThreatListsEmpty() const { return iThreatContainer.empty() && iThreatOfflineContainer.empty(); }
|
||||
|
||||
Acore::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.GetThreatList(); return { list.cbegin(), list.cend() }; }
|
||||
Acore::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); }
|
||||
|
||||
void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent);
|
||||
|
||||
bool isNeedUpdateToClient(uint32 time);
|
||||
|
||||
Reference in New Issue
Block a user