Files
azerothcore-wotlk/src/server/game/Combat/ThreatMgr.h
Maelthyr 3024bee3ba 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>
2022-12-06 09:27:13 -03:00

309 lines
10 KiB
C++

/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _THREATMANAGER
#define _THREATMANAGER
#include "Common.h"
#include "IteratorPair.h"
#include "ObjectGuid.h"
#include "Reference.h"
#include "SharedDefines.h"
#include "UnitEvents.h"
#include <list>
//==============================================================
class Unit;
class Creature;
class ThreatMgr;
class SpellInfo;
#define THREAT_UPDATE_INTERVAL 2 * IN_MILLISECONDS // Server should send threat update to client periodically each second
//==============================================================
// Class to calculate the real threat based
struct ThreatCalcHelper
{
static float calcThreat(Unit* hatedUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = nullptr);
};
//==============================================================
class HostileReference : public Reference<Unit, ThreatMgr>
{
public:
HostileReference(Unit* refUnit, ThreatMgr* threatMgr, float threat);
Unit* GetOwner() const;
Unit* GetVictim() const { return getTarget(); }
//=================================================
void AddThreat(float modThreat);
void SetThreat(float threat) { AddThreat(threat - GetThreat()); }
void addThreatPercent(int32 percent);
[[nodiscard]] float GetThreat() const { return iThreat; }
void ClearThreat() { removeReference(); }
[[nodiscard]] bool IsOnline() const { return iOnline; }
[[nodiscard]] bool IsAvailable() const { return iOnline; } // unused for now
[[nodiscard]] bool IsOffline() const { return !iOnline; } // unused for now
// used for temporary setting a threat and reducting it later again.
// the threat modification is stored
void setTempThreat(float threat)
{
addTempThreat(threat - GetThreat());
}
void addTempThreat(float threat)
{
iTempThreatModifier = threat;
if (iTempThreatModifier != 0.0f)
AddThreat(iTempThreatModifier);
}
void resetTempThreat()
{
if (iTempThreatModifier != 0.0f)
{
AddThreat(-iTempThreatModifier);
iTempThreatModifier = 0.0f;
}
}
float getTempThreatModifier() { return iTempThreatModifier; }
//=================================================
// check, if source can reach target and set the status
void updateOnlineStatus();
void setOnlineOfflineState(bool isOnline);
//=================================================
bool operator == (const HostileReference& hostileRef) const { return hostileRef.getUnitGuid() == getUnitGuid(); }
//=================================================
[[nodiscard]] ObjectGuid getUnitGuid() const { return iUnitGuid; }
//=================================================
// reference is not needed anymore. realy delete it !
void removeReference();
//=================================================
HostileReference* next() { return ((HostileReference*) Reference<Unit, ThreatMgr>::next()); }
//=================================================
// Tell our refTo (target) object that we have a link
void targetObjectBuildLink() override;
// Tell our refTo (taget) object, that the link is cut
void targetObjectDestroyLink() override;
// Tell our refFrom (source) object, that the link is cut (Target destroyed)
void sourceObjectDestroyLink() override;
private:
// Inform the source, that the status of that reference was changed
void fireStatusChanged(ThreatRefStatusChangeEvent& threatRefStatusChangeEvent);
Unit* GetSourceUnit();
private:
float iThreat;
float iTempThreatModifier; // used for taunt
ObjectGuid iUnitGuid;
bool iOnline;
};
//==============================================================
class ThreatMgr;
class ThreatContainer
{
friend class ThreatMgr;
public:
typedef std::list<HostileReference*> StorageType;
ThreatContainer() = default;
~ThreatContainer() { clearReferences(); }
HostileReference* AddThreat(Unit* victim, float threat);
void ModifyThreatByPercent(Unit* victim, int32 percent);
HostileReference* SelectNextVictim(Creature* attacker, HostileReference* currentVictim) const;
void setDirty(bool isDirty) { iDirty = isDirty; }
[[nodiscard]] bool isDirty() const { return iDirty; }
[[nodiscard]] bool empty() const
{
return iThreatList.empty();
}
[[nodiscard]] HostileReference* getMostHated() const
{
return iThreatList.empty() ? nullptr : iThreatList.front();
}
HostileReference* getReferenceByTarget(Unit const* victim) const;
HostileReference* getReferenceByTarget(ObjectGuid const& guid) const;
[[nodiscard]] StorageType const& GetThreatList() const { return iThreatList; }
private:
void remove(HostileReference* hostileRef)
{
iThreatList.remove(hostileRef);
}
void addReference(HostileReference* hostileRef)
{
iThreatList.push_back(hostileRef);
}
void clearReferences();
// Sort the list if necessary
void update();
StorageType iThreatList;
bool iDirty{false};
};
//=================================================
typedef HostileReference ThreatReference;
class ThreatMgr
{
public:
friend class HostileReference;
explicit ThreatMgr(Unit* owner);
~ThreatMgr() { clearReferences(); }
Unit* SelectVictim() { return getHostileTarget(); }
Unit* GetCurrentVictim() const { if (ThreatReference* ref = getCurrentVictim()) return ref->GetVictim(); else return nullptr; }
Unit* GetAnyTarget() const { auto const& list = GetThreatList(); if (!list.empty()) return list.front()->getTarget(); return nullptr; }
void clearReferences();
void AddThreat(Unit* victim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
void DoAddThreat(Unit* victim, float threat);
void ModifyThreatByPercent(Unit* victim, int32 percent);
float GetThreat(Unit* victim, bool alsoSearchOfflineList = false);
float GetThreatListSize() const { return GetThreatList().size(); }
float getThreatWithoutTemp(Unit* victim, bool alsoSearchOfflineList = false);
[[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);
[[nodiscard]] HostileReference* getCurrentVictim() const { return iCurrentVictim; }
[[nodiscard]] Unit* GetOwner() const { return iOwner; }
Unit* getHostileTarget();
void tauntApply(Unit* taunter);
void tauntFadeOut(Unit* taunter);
void setCurrentVictim(HostileReference* hostileRef);
void setDirty(bool isDirty) { iThreatContainer.setDirty(isDirty); }
// Reset all aggro without modifying the threadlist.
void ResetThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->SetThreat(0.0f); }
void ResetAllThreat();
void ClearThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->removeReference(); }
void ClearAllThreat();
// Reset all aggro of unit in threadlist satisfying the predicate.
template<class PREDICATE> void resetAggro(PREDICATE predicate)
{
ThreatContainer::StorageType& threatList = iThreatContainer.iThreatList;
if (threatList.empty())
return;
for (auto ref : threatList)
{
if (predicate(ref->getTarget()))
{
ref->SetThreat(0);
setDirty(true);
}
}
}
// methods to access the lists from the outside to do some dirty manipulation (scriping and such)
// I hope they are used as little as possible.
[[nodiscard]] ThreatContainer::StorageType const& GetThreatList() const { return iThreatContainer.GetThreatList(); }
[[nodiscard]] ThreatContainer::StorageType const& GetOfflineThreatList() const { return iThreatOfflineContainer.GetThreatList(); }
ThreatContainer& GetOnlineContainer() { return iThreatContainer; }
ThreatContainer& GetOfflineContainer() { return iThreatOfflineContainer; }
private:
HostileReference* FindReference(Unit const* who, bool includeOffline) const { if (auto* ref = iThreatContainer.getReferenceByTarget(who)) return ref; if (includeOffline) if (auto* ref = iThreatOfflineContainer.getReferenceByTarget(who)) return ref; return nullptr; }
void _addThreat(Unit* victim, float threat);
HostileReference* iCurrentVictim;
Unit* iOwner;
uint32 iUpdateTimer;
ThreatContainer iThreatContainer;
ThreatContainer iThreatOfflineContainer;
};
//=================================================
namespace Acore
{
// Binary predicate for sorting HostileReferences based on threat value
class ThreatOrderPred
{
public:
ThreatOrderPred(bool ascending = false) : m_ascending(ascending) {}
bool operator() (HostileReference const* a, HostileReference const* b) const
{
return m_ascending ? a->GetThreat() < b->GetThreat() : a->GetThreat() > b->GetThreat();
}
private:
const bool m_ascending;
};
}
#endif