aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Combat
diff options
context:
space:
mode:
authorRat <none@none>2010-06-05 23:40:08 +0200
committerRat <none@none>2010-06-05 23:40:08 +0200
commit75b80d9f5b02a643c983b2fb1ededed79fd5d133 (patch)
treeebd1c2cc12a2715909dd04c1ed147a260c6ceb14 /src/server/game/Combat
parent6a9357b13d7ea6bd7d77dbfc6587af9028caa401 (diff)
rearranged core files
--HG-- branch : trunk
Diffstat (limited to 'src/server/game/Combat')
-rw-r--r--src/server/game/Combat/CombatHandler.cpp91
-rw-r--r--src/server/game/Combat/HostileRefManager.cpp189
-rw-r--r--src/server/game/Combat/HostileRefManager.h74
-rw-r--r--src/server/game/Combat/ThreatManager.cpp553
-rw-r--r--src/server/game/Combat/ThreatManager.h279
5 files changed, 1186 insertions, 0 deletions
diff --git a/src/server/game/Combat/CombatHandler.cpp b/src/server/game/Combat/CombatHandler.cpp
new file mode 100644
index 00000000000..404b70c1c84
--- /dev/null
+++ b/src/server/game/Combat/CombatHandler.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "Log.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "ObjectDefines.h"
+
+void WorldSession::HandleAttackSwingOpcode(WorldPacket & recv_data)
+{
+ uint64 guid;
+ recv_data >> guid;
+
+ DEBUG_LOG("WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid));
+
+ Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid);
+
+ if (!pEnemy)
+ {
+ if (!IS_UNIT_GUID(guid))
+ sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
+ else
+ sLog.outError("WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid));
+
+ // stop attack state at client
+ SendAttackStop(NULL);
+ return;
+ }
+
+ if (!_player->canAttack(pEnemy))
+ {
+ sLog.outError("WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid));
+
+ // stop attack state at client
+ SendAttackStop(pEnemy);
+ return;
+ }
+
+ _player->Attack(pEnemy,true);
+}
+
+void WorldSession::HandleAttackStopOpcode(WorldPacket & /*recv_data*/)
+{
+ GetPlayer()->AttackStop();
+}
+
+void WorldSession::HandleSetSheathedOpcode(WorldPacket & recv_data)
+{
+ uint32 sheathed;
+ recv_data >> sheathed;
+
+ //sLog.outDebug("WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed);
+
+ if (sheathed >= MAX_SHEATH_STATE)
+ {
+ sLog.outError("Unknown sheath state %u ??",sheathed);
+ return;
+ }
+
+ GetPlayer()->SetSheath(SheathState(sheathed));
+}
+
+void WorldSession::SendAttackStop(Unit const* enemy)
+{
+ WorldPacket data(SMSG_ATTACKSTOP, (4+20)); // we guess size
+ data.append(GetPlayer()->GetPackGUID());
+ data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid
+ data << uint32(0); // unk, can be 1 also
+ SendPacket(&data);
+}
+
diff --git a/src/server/game/Combat/HostileRefManager.cpp b/src/server/game/Combat/HostileRefManager.cpp
new file mode 100644
index 00000000000..64fc19c78d4
--- /dev/null
+++ b/src/server/game/Combat/HostileRefManager.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "HostileRefManager.h"
+#include "ThreatManager.h"
+#include "Unit.h"
+#include "DBCStructure.h"
+#include "SpellMgr.h"
+
+HostileRefManager::~HostileRefManager()
+{
+ deleteReferences();
+}
+
+//=================================================
+// send threat to all my hateres for the pVictim
+// The pVictim is hated than by them as well
+// use for buffs and healing threat functionality
+
+void HostileRefManager::threatAssist(Unit *pVictim, float fThreat, SpellEntry const *pThreatSpell, bool pSingleTarget)
+{
+ HostileReference* ref;
+
+ float size = pSingleTarget ? 1.0f : getSize(); // if pSingleTarget do not divide threat
+ ref = getFirst();
+ while (ref != NULL)
+ {
+ float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, fThreat, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell);
+ if (pVictim == getOwner())
+ ref->addThreat(threat / size); // It is faster to modify the threat durectly if possible
+ else
+ ref->getSource()->addThreat(pVictim, threat / size);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+
+void HostileRefManager::addTempThreat(float fThreat, bool apply)
+{
+ HostileReference* ref = getFirst();
+
+ while (ref != NULL)
+ {
+ if (apply)
+ {
+ if (ref->getTempThreatModifier() == 0.0f)
+ ref->addTempThreat(fThreat);
+ }
+ else
+ ref->resetTempThreat();
+
+ ref = ref->next();
+ }
+}
+
+
+//=================================================
+
+void HostileRefManager::addThreatPercent(int32 iPercent)
+{
+ HostileReference* ref;
+
+ ref = getFirst();
+ while (ref != NULL)
+ {
+ ref->addThreatPercent(iPercent);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The online / offline status is given to the method. The calculation has to be done before
+
+void HostileRefManager::setOnlineOfflineState(bool bIsOnline)
+{
+ HostileReference* ref;
+
+ ref = getFirst();
+ while (ref != NULL)
+ {
+ ref->setOnlineOfflineState(bIsOnline);
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The online / offline status is calculated and set
+
+void HostileRefManager::updateThreatTables()
+{
+ HostileReference* ref = getFirst();
+ while (ref)
+ {
+ ref->updateOnlineStatus();
+ ref = ref->next();
+ }
+}
+
+//=================================================
+// The references are not needed anymore
+// tell the source to remove them from the list and free the mem
+
+void HostileRefManager::deleteReferences()
+{
+ HostileReference* ref = getFirst();
+ while (ref)
+ {
+ HostileReference* nextRef = ref->next();
+ ref->removeReference();
+ delete ref;
+ ref = nextRef;
+ }
+}
+
+//=================================================
+// delete one reference, defined by faction
+
+void HostileRefManager::deleteReferencesForFaction(uint32 faction)
+{
+ HostileReference* ref = getFirst();
+ while (ref)
+ {
+ HostileReference* nextRef = ref->next();
+ if (ref->getSource()->getOwner()->getFactionTemplateEntry()->faction == faction)
+ {
+ ref->removeReference();
+ delete ref;
+ }
+ ref = nextRef;
+ }
+}
+
+//=================================================
+// delete one reference, defined by Unit
+
+void HostileRefManager::deleteReference(Unit *pCreature)
+{
+ HostileReference* ref = getFirst();
+ while (ref)
+ {
+ HostileReference* nextRef = ref->next();
+ if (ref->getSource()->getOwner() == pCreature)
+ {
+ ref->removeReference();
+ delete ref;
+ break;
+ }
+ ref = nextRef;
+ }
+}
+
+//=================================================
+// set state for one reference, defined by Unit
+
+void HostileRefManager::setOnlineOfflineState(Unit *pCreature, bool bIsOnline)
+{
+ HostileReference* ref = getFirst();
+ while (ref)
+ {
+ HostileReference* nextRef = ref->next();
+ if (ref->getSource()->getOwner() == pCreature)
+ {
+ ref->setOnlineOfflineState(bIsOnline);
+ break;
+ }
+ ref = nextRef;
+ }
+}
+
+//=================================================
+
diff --git a/src/server/game/Combat/HostileRefManager.h b/src/server/game/Combat/HostileRefManager.h
new file mode 100644
index 00000000000..80b676312a1
--- /dev/null
+++ b/src/server/game/Combat/HostileRefManager.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _HOSTILEREFMANAGER
+#define _HOSTILEREFMANAGER
+
+#include "Common.h"
+#include "Utilities/LinkedReference/RefManager.h"
+
+class Unit;
+class ThreatManager;
+class HostileReference;
+struct SpellEntry;
+
+//=================================================
+
+class HostileRefManager : public RefManager<Unit, ThreatManager>
+{
+ private:
+ Unit *iOwner;
+ public:
+ explicit HostileRefManager(Unit *pOwner) { iOwner = pOwner; }
+ ~HostileRefManager();
+
+ Unit* getOwner() { return iOwner; }
+
+ // send threat to all my hateres for the pVictim
+ // The pVictim is hated than by them as well
+ // use for buffs and healing threat functionality
+ void threatAssist(Unit *pVictim, float fThreat, SpellEntry const *threatSpell = 0, bool pSingleTarget = false);
+
+ void addTempThreat(float fThreat, bool apply);
+
+ void addThreatPercent(int32 iPercent);
+
+ // The references are not needed anymore
+ // tell the source to remove them from the list and free the mem
+ void deleteReferences();
+
+ // Remove specific faction references
+ void deleteReferencesForFaction(uint32 faction);
+
+ HostileReference* getFirst() { return ((HostileReference*) RefManager<Unit, ThreatManager>::getFirst()); }
+
+ void updateThreatTables();
+
+ void setOnlineOfflineState(bool bIsOnline);
+
+ // set state for one reference, defined by Unit
+ void setOnlineOfflineState(Unit *pCreature, bool bIsOnline);
+
+ // delete one reference, defined by Unit
+ void deleteReference(Unit *pCreature);
+};
+//=================================================
+#endif
+
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp
new file mode 100644
index 00000000000..52f02f0f66d
--- /dev/null
+++ b/src/server/game/Combat/ThreatManager.cpp
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ThreatManager.h"
+#include "Unit.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Map.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "UnitEvents.h"
+#include "SpellAuras.h"
+
+//==============================================================
+//================= ThreatCalcHelper ===========================
+//==============================================================
+
+// The pHatingUnit is not used yet
+float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* /*pHatingUnit*/, float fThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
+{
+ if (pThreatSpell)
+ if (Player* modOwner = pHatedUnit->GetSpellModOwner())
+ modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, fThreat);
+
+ return pHatedUnit->ApplyTotalThreatModifier(fThreat, schoolMask);
+}
+
+//============================================================
+//================= HostileReference ==========================
+//============================================================
+
+HostileReference::HostileReference(Unit* pUnit, ThreatManager *pThreatManager, float fThreat)
+{
+ iThreat = fThreat;
+ iTempThreatModifier = 0.0f;
+ link(pUnit, pThreatManager);
+ iUnitGuid = pUnit->GetGUID();
+ iOnline = true;
+ iAccessible = true;
+}
+
+//============================================================
+// Tell our refTo (target) object that we have a link
+void HostileReference::targetObjectBuildLink()
+{
+ getTarget()->addHatedBy(this);
+}
+
+//============================================================
+// Tell our refTo (taget) object, that the link is cut
+void HostileReference::targetObjectDestroyLink()
+{
+ getTarget()->removeHatedBy(this);
+}
+
+//============================================================
+// Tell our refFrom (source) object, that the link is cut (Target destroyed)
+
+void HostileReference::sourceObjectDestroyLink()
+{
+ setOnlineOfflineState(false);
+}
+
+//============================================================
+// Inform the source, that the status of the reference changed
+
+void HostileReference::fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
+{
+ if (getSource())
+ getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
+}
+
+//============================================================
+
+void HostileReference::addThreat(float fModThreat)
+{
+ iThreat += fModThreat;
+ // the threat is changed. Source and target unit have to be available
+ // if the link was cut before relink it again
+ if (!isOnline())
+ updateOnlineStatus();
+ if (fModThreat != 0.0f)
+ {
+ ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, fModThreat);
+ fireStatusChanged(event);
+ }
+
+ if (isValid() && fModThreat >= 0.0f)
+ {
+ Unit* victim_owner = getTarget()->GetCharmerOrOwner();
+ if (victim_owner && victim_owner->isAlive())
+ getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
+ }
+}
+
+//============================================================
+// check, if source can reach target and set the status
+
+void HostileReference::updateOnlineStatus()
+{
+ bool online = false;
+ bool accessible = false;
+
+ if (!isValid())
+ if (Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid()))
+ link(target, getSource());
+
+ // only check for online status if
+ // ref is valid
+ // target is no player or not gamemaster
+ // target is not in flight
+ if (isValid() &&
+ ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
+ !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
+ {
+ Creature* creature = getSourceUnit()->ToCreature();
+ online = getTarget()->isInAccessiblePlaceFor(creature);
+ if (!online)
+ {
+ if (creature->IsWithinCombatRange(getTarget(), creature->m_CombatDistance))
+ online = true; // not accessible but stays online
+ }
+ else
+ accessible = true;
+
+ }
+ setAccessibleState(accessible);
+ setOnlineOfflineState(online);
+}
+
+//============================================================
+// set the status and fire the event on status change
+
+void HostileReference::setOnlineOfflineState(bool pIsOnline)
+{
+ if (iOnline != pIsOnline)
+ {
+ iOnline = pIsOnline;
+ if (!iOnline)
+ setAccessibleState(false); // if not online that not accessable as well
+
+ ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this);
+ fireStatusChanged(event);
+ }
+}
+
+//============================================================
+
+void HostileReference::setAccessibleState(bool pIsAccessible)
+{
+ if (iAccessible != pIsAccessible)
+ {
+ iAccessible = pIsAccessible;
+
+ ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this);
+ fireStatusChanged(event);
+ }
+}
+
+//============================================================
+// prepare the reference for deleting
+// this is called be the target
+
+void HostileReference::removeReference()
+{
+ invalidate();
+
+ ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this);
+ fireStatusChanged(event);
+}
+
+//============================================================
+
+Unit* HostileReference::getSourceUnit()
+{
+ return (getSource()->getOwner());
+}
+
+//============================================================
+//================ ThreatContainer ===========================
+//============================================================
+
+void ThreatContainer::clearReferences()
+{
+ for (std::list<HostileReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
+ {
+ (*i)->unlink();
+ delete (*i);
+ }
+ iThreatList.clear();
+}
+
+//============================================================
+// Return the HostileReference of NULL, if not found
+HostileReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
+{
+ HostileReference* result = NULL;
+
+ uint64 guid = pVictim->GetGUID();
+ for (std::list<HostileReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
+ {
+ if ((*i)->getUnitGuid() == guid)
+ {
+ result = (*i);
+ break;
+ }
+ }
+
+ return result;
+}
+
+//============================================================
+// Add the threat, if we find the reference
+
+HostileReference* ThreatContainer::addThreat(Unit* pVictim, float fThreat)
+{
+ HostileReference* ref = getReferenceByTarget(pVictim);
+ if (ref)
+ ref->addThreat(fThreat);
+ return ref;
+}
+
+//============================================================
+
+void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 iPercent)
+{
+ if (HostileReference* ref = getReferenceByTarget(pVictim))
+ ref->addThreatPercent(iPercent);
+}
+
+//============================================================
+// Check if the list is dirty and sort if necessary
+
+void ThreatContainer::update()
+{
+ if (iDirty && iThreatList.size() >1)
+ {
+ iThreatList.sort(Trinity::ThreatOrderPred());
+ }
+ iDirty = false;
+}
+
+//============================================================
+// return the next best victim
+// could be the current victim
+
+HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim)
+{
+ 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(pAttacker->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 == pCurrentVictim)
+ pCurrentVictim = 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 (pAttacker->canCreatureAttack(target)) // 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())
+ {
+ currentRef = pCurrentVictim; // for second case
+ found = true;
+ break;
+ }
+
+ if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
+ currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() &&
+ pAttacker->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;
+}
+
+//============================================================
+//=================== ThreatManager ==========================
+//============================================================
+
+ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL)
+{
+}
+
+//============================================================
+
+void ThreatManager::clearReferences()
+{
+ iThreatContainer.clearReferences();
+ iThreatOfflineContainer.clearReferences();
+ iCurrentVictim = NULL;
+ iUpdateTimer = THREAT_UPDATE_INTERVAL;
+}
+
+//============================================================
+
+void ThreatManager::addThreat(Unit* pVictim, float fThreat, 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 && pVictim->ToPlayer()->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, fThreat, schoolMask, pThreatSpell);
+
+ // must check > 0.0f, otherwise dead loop
+ if (threat > 0.0f && pVictim->GetReducedThreatPercent())
+ {
+ uint32 reducedThreadPercent = pVictim->GetReducedThreatPercent();
+
+ Unit *unit = pVictim->GetMisdirectionTarget();
+ if (unit)
+ if (Aura* pAura = unit->GetAura(63326)) // Glyph of Vigilance
+ reducedThreadPercent += pAura->GetSpellProto()->EffectBasePoints[0];
+
+ float reducedThreat = threat * reducedThreadPercent / 100;
+ threat -= reducedThreat;
+ if (unit)
+ _addThreat(unit, reducedThreat);
+ }
+
+ _addThreat(pVictim, threat);
+}
+
+void ThreatManager::_addThreat(Unit *pVictim, float fThreat)
+{
+ HostileReference* ref = iThreatContainer.addThreat(pVictim, fThreat);
+ // Ref is not in the online refs, search the offline refs next
+ if (!ref)
+ ref = iThreatOfflineContainer.addThreat(pVictim, fThreat);
+
+ if (!ref) // there was no ref => create a new one
+ {
+ // threat has to be 0 here
+ HostileReference* hostilReference = new HostileReference(pVictim, this, 0);
+ iThreatContainer.addReference(hostilReference);
+ hostilReference->addThreat(fThreat); // now we add the real threat
+ if (pVictim->GetTypeId() == TYPEID_PLAYER && pVictim->ToPlayer()->isGameMaster())
+ hostilReference->setOnlineOfflineState(false); // GM is always offline
+ }
+}
+
+//============================================================
+
+void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 iPercent)
+{
+ iThreatContainer.modifyThreatPercent(pVictim, iPercent);
+}
+
+//============================================================
+
+Unit* ThreatManager::getHostilTarget()
+{
+ iThreatContainer.update();
+ HostileReference* nextVictim = iThreatContainer.selectNextVictim(getOwner()->ToCreature(), getCurrentVictim());
+ setCurrentVictim(nextVictim);
+ return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
+}
+
+//============================================================
+
+float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
+{
+ float threat = 0.0f;
+ HostileReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
+ if (!ref && pAlsoSearchOfflineList)
+ ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
+ if (ref)
+ threat = ref->getThreat();
+ return threat;
+}
+
+//============================================================
+
+void ThreatManager::tauntApply(Unit* pTaunter)
+{
+ HostileReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
+ if (getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
+ {
+ if (ref->getTempThreatModifier() == 0.0f) // Ok, temp threat is unused
+ ref->setTempThreat(getCurrentVictim()->getThreat());
+ }
+}
+
+//============================================================
+
+void ThreatManager::tauntFadeOut(Unit *pTaunter)
+{
+ HostileReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
+ if (ref)
+ ref->resetTempThreat();
+}
+
+//============================================================
+
+void ThreatManager::setCurrentVictim(HostileReference* pHostileReference)
+{
+ if (pHostileReference && pHostileReference != iCurrentVictim)
+ {
+ iOwner->SendChangeCurrentVictimOpcode(pHostileReference);
+ }
+ iCurrentVictim = pHostileReference;
+}
+
+//============================================================
+// The hated unit is gone, dead or deleted
+// return true, if the event is consumed
+
+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;
+ }
+}
+
+bool ThreatManager::isNeedUpdateToClient(uint32 time)
+{
+ if (isThreatListEmpty())
+ return false;
+ if (time >= iUpdateTimer)
+ {
+ iUpdateTimer = THREAT_UPDATE_INTERVAL;
+ return true;
+ }
+ iUpdateTimer -= time;
+ return false;
+}
+
+// Reset all aggro without modifying the threadlist.
+void ThreatManager::resetAllAggro()
+{
+ std::list<HostileReference*> &threatlist = getThreatList();
+ if (threatlist.empty())
+ return;
+
+ for (std::list<HostileReference*>::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
+ {
+ (*itr)->setThreat(0);
+ }
+
+ setDirty(true);
+}
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
new file mode 100644
index 00000000000..723a553e9d7
--- /dev/null
+++ b/src/server/game/Combat/ThreatManager.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _THREATMANAGER
+#define _THREATMANAGER
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Utilities/LinkedReference/Reference.h"
+#include "UnitEvents.h"
+
+#include <list>
+
+//==============================================================
+
+class Unit;
+class Creature;
+class ThreatManager;
+struct SpellEntry;
+
+#define THREAT_UPDATE_INTERVAL 1 * IN_MILISECONDS // Server should send threat update to client periodically each second
+
+//==============================================================
+// Class to calculate the real threat based
+
+class ThreatCalcHelper
+{
+ public:
+ static float calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+};
+
+//==============================================================
+class HostileReference : public Reference<Unit, ThreatManager>
+{
+ public:
+ HostileReference(Unit* pUnit, ThreatManager *pThreatManager, float fThreat);
+
+ //=================================================
+ void addThreat(float fModThreat);
+
+ void setThreat(float fThreat) { addThreat(fThreat - getThreat()); }
+
+ void addThreatPercent(int32 pPercent)
+ {
+ float tmpThreat = iThreat;
+ tmpThreat = tmpThreat * (pPercent+100.0f) / 100.0f;
+ addThreat(tmpThreat-iThreat);
+ }
+
+ float getThreat() const { return iThreat; }
+
+ bool isOnline() const { return iOnline; }
+
+ // The Unit might be in water and the creature can not enter the water, but has range attack
+ // in this case online = true, but accessible = false
+ bool isAccessible() const { return iAccessible; }
+
+ // used for temporary setting a threat and reducting it later again.
+ // the threat modification is stored
+ void setTempThreat(float fThreat)
+ {
+ addTempThreat(fThreat - getThreat());
+ }
+
+ void addTempThreat(float fThreat)
+ {
+ iTempThreatModifier = fThreat;
+ 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 pIsOnline);
+
+ void setAccessibleState(bool pIsAccessible);
+ //=================================================
+
+ bool operator == (const HostileReference& pHostileReference) const { return pHostileReference.getUnitGuid() == getUnitGuid(); }
+
+ //=================================================
+
+ uint64 getUnitGuid() const { return iUnitGuid; }
+
+ //=================================================
+ // reference is not needed anymore. realy delete it !
+
+ void removeReference();
+
+ //=================================================
+
+ HostileReference* next() { return ((HostileReference*) Reference<Unit, ThreatManager>::next()); }
+
+ //=================================================
+
+ // Tell our refTo (target) object that we have a link
+ void targetObjectBuildLink();
+
+ // Tell our refTo (taget) object, that the link is cut
+ void targetObjectDestroyLink();
+
+ // Tell our refFrom (source) object, that the link is cut (Target destroyed)
+ void sourceObjectDestroyLink();
+ private:
+ // Inform the source, that the status of that reference was changed
+ void fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent);
+
+ Unit* getSourceUnit();
+ private:
+ float iThreat;
+ float iTempThreatModifier; // used for taunt
+ uint64 iUnitGuid;
+ bool iOnline;
+ bool iAccessible;
+};
+
+//==============================================================
+class ThreatManager;
+
+class ThreatContainer
+{
+ private:
+ std::list<HostileReference*> iThreatList;
+ bool iDirty;
+ protected:
+ friend class ThreatManager;
+
+ void remove(HostileReference* pRef) { iThreatList.remove(pRef); }
+ void addReference(HostileReference* pHostileReference) { iThreatList.push_back(pHostileReference); }
+ void clearReferences();
+ // Sort the list if necessary
+ void update();
+ public:
+ ThreatContainer() { iDirty = false; }
+ ~ThreatContainer() { clearReferences(); }
+
+ HostileReference* addThreat(Unit* pVictim, float fThreat);
+
+ void modifyThreatPercent(Unit *pVictim, int32 iPercent);
+
+ HostileReference* selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim);
+
+ void setDirty(bool pDirty) { iDirty = pDirty; }
+
+ bool isDirty() { return iDirty; }
+
+ bool empty() { return(iThreatList.empty()); }
+
+ HostileReference* getMostHated() { return iThreatList.empty() ? NULL : iThreatList.front(); }
+
+ HostileReference* getReferenceByTarget(Unit* pVictim);
+
+ std::list<HostileReference*>& getThreatList() { return iThreatList; }
+};
+
+//=================================================
+
+class ThreatManager
+{
+ public:
+ friend class HostileReference;
+
+ explicit ThreatManager(Unit *pOwner);
+
+ ~ThreatManager() { clearReferences(); }
+
+ void clearReferences();
+
+ void addThreat(Unit* pVictim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+ void modifyThreatPercent(Unit *pVictim, int32 iPercent);
+
+ float getThreat(Unit *pVictim, bool pAlsoSearchOfflineList = false);
+
+ bool isThreatListEmpty() { return iThreatContainer.empty(); }
+
+ void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent);
+
+ bool isNeedUpdateToClient(uint32 time);
+
+ HostileReference* getCurrentVictim() { return iCurrentVictim; }
+
+ Unit* getOwner() { return iOwner; }
+
+ Unit* getHostilTarget();
+
+ void tauntApply(Unit* pTaunter);
+ void tauntFadeOut(Unit *pTaunter);
+
+ void setCurrentVictim(HostileReference* pHostileReference);
+
+ void setDirty(bool bDirty) { iThreatContainer.setDirty(bDirty); }
+
+ // Reset all aggro without modifying the threadlist.
+ void resetAllAggro();
+
+ // Reset all aggro of unit in threadlist satisfying the predicate.
+ template<class PREDICATE> void resetAggro(PREDICATE predicate)
+ {
+ std::list<HostileReference*> &threatlist = getThreatList();
+ if (threatlist.empty())
+ return;
+
+ for (std::list<HostileReference*>::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
+ {
+ HostileReference* ref = (*itr);
+
+ 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.
+ std::list<HostileReference*>& getThreatList() { return iThreatContainer.getThreatList(); }
+ std::list<HostileReference*>& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); }
+ ThreatContainer& getOnlineContainer() { return iThreatContainer; }
+ ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; }
+ private:
+ void _addThreat(Unit *pVictim, float fThreat);
+
+ HostileReference* iCurrentVictim;
+ Unit* iOwner;
+ uint32 iUpdateTimer;
+ ThreatContainer iThreatContainer;
+ ThreatContainer iThreatOfflineContainer;
+};
+
+//=================================================
+
+namespace Trinity
+{
+ // Binary predicate for sorting HostileReferences based on threat value
+ class ThreatOrderPred
+ {
+ public:
+ ThreatOrderPred(bool ascending = false) : m_ascending(ascending) {}
+ bool operator() (const HostileReference *a, const HostileReference *b) const
+ {
+ return m_ascending ? a->getThreat() < b->getThreat() : a->getThreat() > b->getThreat();
+ }
+ private:
+ const bool m_ascending;
+ };
+}
+#endif
+