diff options
| author | Rat <none@none> | 2010-06-05 23:40:08 +0200 |
|---|---|---|
| committer | Rat <none@none> | 2010-06-05 23:40:08 +0200 |
| commit | 75b80d9f5b02a643c983b2fb1ededed79fd5d133 (patch) | |
| tree | ebd1c2cc12a2715909dd04c1ed147a260c6ceb14 /src/server/game/Combat | |
| parent | 6a9357b13d7ea6bd7d77dbfc6587af9028caa401 (diff) | |
rearranged core files
--HG--
branch : trunk
Diffstat (limited to 'src/server/game/Combat')
| -rw-r--r-- | src/server/game/Combat/CombatHandler.cpp | 91 | ||||
| -rw-r--r-- | src/server/game/Combat/HostileRefManager.cpp | 189 | ||||
| -rw-r--r-- | src/server/game/Combat/HostileRefManager.h | 74 | ||||
| -rw-r--r-- | src/server/game/Combat/ThreatManager.cpp | 553 | ||||
| -rw-r--r-- | src/server/game/Combat/ThreatManager.h | 279 |
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 + |
