mirror of
https://github.com/araxiaonline/TrinityCore2.git
synced 2026-06-16 21:09:50 -04:00
Backed out changeset 8fba4a7e4268
--HG-- branch : trunk
This commit is contained in:
12688
src/game/SpellAuras.cpp
12688
src/game/SpellAuras.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,496 +1,488 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
|
||||
*
|
||||
* Copyright (C) 2008 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 "Database/DBCStores.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "BattleGround.h"
|
||||
#include "MapManager.h"
|
||||
#include "ScriptCalls.h"
|
||||
#include "Totem.h"
|
||||
|
||||
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
// TODO: add targets.read() check
|
||||
CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8);
|
||||
|
||||
Player* pUser = _player;
|
||||
uint8 bagIndex, slot;
|
||||
uint8 spell_count; // number of spells at item, not used
|
||||
uint8 cast_count; // next cast if exists (single or not)
|
||||
uint64 item_guid;
|
||||
|
||||
recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid;
|
||||
|
||||
Item *pItem = pUser->GetItemByPos(bagIndex, slot);
|
||||
if(!pItem)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size());
|
||||
|
||||
ItemPrototype const *proto = pItem->GetProto();
|
||||
if(!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// some item classes can be used only in equipped state
|
||||
if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 msg = pUser->CanUseItem(pItem);
|
||||
if( msg != EQUIP_ERR_OK )
|
||||
{
|
||||
pUser->SendEquipError( msg, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
|
||||
if( proto->Class == ITEM_CLASS_CONSUMABLE &&
|
||||
!(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
|
||||
pUser->InArena())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pUser->isInCombat())
|
||||
{
|
||||
for(int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
|
||||
{
|
||||
if (IsNonCombatSpell(spellInfo))
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
|
||||
if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
|
||||
{
|
||||
if (!pItem->IsSoulBound())
|
||||
{
|
||||
pItem->SetState(ITEM_CHANGED, pUser);
|
||||
pItem->SetBinding( true );
|
||||
}
|
||||
}
|
||||
|
||||
SpellCastTargets targets;
|
||||
if(!targets.read(&recvPacket, pUser))
|
||||
return;
|
||||
|
||||
//Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
|
||||
if(!Script->ItemUse(pUser,pItem,targets))
|
||||
{
|
||||
// no script or script not process request by self
|
||||
|
||||
// special learning case
|
||||
if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN)
|
||||
{
|
||||
uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN);
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN);
|
||||
pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(pUser, spellInfo, false);
|
||||
spell->m_CastItem = pItem;
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->m_currentBasePoints[0] = learning_spell_id;
|
||||
spell->prepare(&targets);
|
||||
return;
|
||||
}
|
||||
|
||||
// use triggered flag only for items with many spell casts and for not first cast
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < 5; ++i)
|
||||
{
|
||||
_Spell const& spellData = pItem->GetProto()->Spells[i];
|
||||
|
||||
// no spell
|
||||
if(!spellData.SpellId)
|
||||
continue;
|
||||
|
||||
// wrong triggering type
|
||||
if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
|
||||
continue;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId);
|
||||
continue;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(pUser, spellInfo, (count > 0));
|
||||
spell->m_CastItem = pItem;
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->prepare(&targets);
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define OPEN_CHEST 11437
|
||||
#define OPEN_SAFE 11535
|
||||
#define OPEN_CAGE 11792
|
||||
#define OPEN_BOOTY_CHEST 5107
|
||||
#define OPEN_STRONGBOX 8517
|
||||
|
||||
void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,1+1);
|
||||
|
||||
sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size());
|
||||
|
||||
Player* pUser = _player;
|
||||
uint8 bagIndex, slot;
|
||||
|
||||
recvPacket >> bagIndex >> slot;
|
||||
|
||||
sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
|
||||
|
||||
Item *pItem = pUser->GetItemByPos(bagIndex, slot);
|
||||
if(!pItem)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPrototype const *proto = pItem->GetProto();
|
||||
if(!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// locked item
|
||||
uint32 lockId = proto->LockID;
|
||||
if(lockId)
|
||||
{
|
||||
LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
|
||||
|
||||
if (!lockInfo)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
|
||||
sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
|
||||
return;
|
||||
}
|
||||
|
||||
// required picklocking
|
||||
if(lockInfo->requiredlockskill || lockInfo->requiredminingskill)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
|
||||
{
|
||||
QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
||||
if (result)
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 entry = fields[0].GetUInt32();
|
||||
uint32 flags = fields[1].GetUInt32();
|
||||
|
||||
pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
|
||||
pItem->SetUInt32Value(OBJECT_FIELD_ENTRY, entry);
|
||||
pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
|
||||
pItem->SetState(ITEM_CHANGED, pUser);
|
||||
delete result;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
|
||||
pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||
return;
|
||||
}
|
||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
||||
}
|
||||
else
|
||||
pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
|
||||
{
|
||||
CHECK_PACKET_SIZE(recv_data,8);
|
||||
|
||||
uint64 guid;
|
||||
uint32 spellId = OPEN_CHEST;
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
|
||||
GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid);
|
||||
|
||||
if(!obj)
|
||||
return;
|
||||
|
||||
if (Script->GOHello(_player, obj))
|
||||
return;
|
||||
|
||||
obj->Use(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4+1+2);
|
||||
|
||||
uint32 spellId;
|
||||
uint8 cast_count;
|
||||
recvPacket >> spellId;
|
||||
recvPacket >> cast_count;
|
||||
|
||||
sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i",
|
||||
spellId, cast_count, recvPacket.size());
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
|
||||
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("WORLD: unknown spell id %u", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
// not have spell or spell passive and not casted by client
|
||||
if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) )
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
return;
|
||||
}
|
||||
|
||||
// can't use our own spells when we're in possession of another unit,
|
||||
if(_player->isPossessing())
|
||||
return;
|
||||
|
||||
// client provided targets
|
||||
SpellCastTargets targets;
|
||||
if(!targets.read(&recvPacket,_player))
|
||||
return;
|
||||
|
||||
// auto-selection buff level base at target level (in spellInfo)
|
||||
if(targets.getUnitTarget())
|
||||
{
|
||||
SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
|
||||
|
||||
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
|
||||
if(actualSpellInfo)
|
||||
spellInfo = actualSpellInfo;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(_player, spellInfo, false);
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->prepare(&targets);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4);
|
||||
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
//FIXME: hack, ignore unexpected client cancel Deadly Throw cast
|
||||
if(spellId==26679)
|
||||
return;
|
||||
|
||||
if(_player->IsNonMeleeSpellCasted(false))
|
||||
_player->InterruptNonMeleeSpells(false,spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4);
|
||||
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
// Remove possess/charm/sight aura from the possessed/charmed as well
|
||||
// TODO: Remove this once the ability to cancel aura sets at once is implemented
|
||||
if(_player->GetCharm() || _player->GetFarsightTarget())
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS_PET ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_CHARM ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_BIND_SIGHT)
|
||||
{
|
||||
_player->RemoveAurasDueToSpellByCancel(spellId);
|
||||
if (_player->GetCharm())
|
||||
_player->GetCharm()->RemoveAurasDueToSpellByCancel(spellId);
|
||||
else if (_player->GetFarsightTarget()->GetTypeId() != TYPEID_DYNAMICOBJECT)
|
||||
((Unit*)_player->GetFarsightTarget())->RemoveAurasDueToSpellByCancel(spellId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
|
||||
if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
|
||||
return;
|
||||
|
||||
// channeled spell case (it currently casted then)
|
||||
if(IsChanneledSpell(spellInfo))
|
||||
{
|
||||
if(Spell* spell = _player->m_currentSpells[CURRENT_CHANNELED_SPELL])
|
||||
{
|
||||
if(spell->m_spellInfo->Id==spellId)
|
||||
{
|
||||
spell->cancel();
|
||||
spell->SetReferencedFromCurrent(false);
|
||||
_player->m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// non channeled case
|
||||
_player->RemoveAurasDueToSpellByCancel(spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket, 8+4);
|
||||
|
||||
uint64 guid;
|
||||
uint32 spellId;
|
||||
|
||||
recvPacket >> guid;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("WORLD: unknown PET spell id %u", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
|
||||
|
||||
if(!pet)
|
||||
{
|
||||
sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
|
||||
return;
|
||||
}
|
||||
|
||||
if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
|
||||
{
|
||||
sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
|
||||
return;
|
||||
}
|
||||
|
||||
if(!pet->isAlive())
|
||||
{
|
||||
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
pet->RemoveAurasDueToSpell(spellId);
|
||||
|
||||
pet->AddCreatureSpellCooldown(spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// nothing do
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// may be better send SMSG_CANCEL_AUTO_REPEAT?
|
||||
// cancel and prepare for deleting
|
||||
_player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
|
||||
}
|
||||
|
||||
/// \todo Complete HandleCancelChanneling function
|
||||
void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
|
||||
{
|
||||
/*
|
||||
CHECK_PACKET_SIZE(recv_data, 4);
|
||||
|
||||
uint32 spellid;
|
||||
recv_data >> spellid;
|
||||
*/
|
||||
}
|
||||
|
||||
void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket, 1);
|
||||
|
||||
uint8 slotId;
|
||||
|
||||
recvPacket >> slotId;
|
||||
|
||||
if (slotId >= MAX_TOTEM)
|
||||
return;
|
||||
|
||||
if(!_player->m_TotemSlot[slotId])
|
||||
return;
|
||||
|
||||
Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]);
|
||||
// Don't unsummon sentry totem
|
||||
if(totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY)
|
||||
((Totem*)totem)->UnSummon();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
|
||||
{
|
||||
sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
|
||||
|
||||
if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
|
||||
{
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
|
||||
if(spellInfo)
|
||||
_player->CastSpell(_player,spellInfo,false,0);
|
||||
|
||||
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
|
||||
*
|
||||
* Copyright (C) 2008 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 "Database/DBCStores.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "BattleGround.h"
|
||||
#include "MapManager.h"
|
||||
#include "ScriptCalls.h"
|
||||
#include "Totem.h"
|
||||
|
||||
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
// TODO: add targets.read() check
|
||||
CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8);
|
||||
|
||||
Player* pUser = _player;
|
||||
uint8 bagIndex, slot;
|
||||
uint8 spell_count; // number of spells at item, not used
|
||||
uint8 cast_count; // next cast if exists (single or not)
|
||||
uint64 item_guid;
|
||||
|
||||
recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid;
|
||||
|
||||
Item *pItem = pUser->GetItemByPos(bagIndex, slot);
|
||||
if(!pItem)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size());
|
||||
|
||||
ItemPrototype const *proto = pItem->GetProto();
|
||||
if(!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// some item classes can be used only in equipped state
|
||||
if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 msg = pUser->CanUseItem(pItem);
|
||||
if( msg != EQUIP_ERR_OK )
|
||||
{
|
||||
pUser->SendEquipError( msg, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
|
||||
if( proto->Class == ITEM_CLASS_CONSUMABLE &&
|
||||
!(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
|
||||
pUser->InArena())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pUser->isInCombat())
|
||||
{
|
||||
for(int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
|
||||
{
|
||||
if (IsNonCombatSpell(spellInfo))
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
|
||||
if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
|
||||
{
|
||||
if (!pItem->IsSoulBound())
|
||||
{
|
||||
pItem->SetState(ITEM_CHANGED, pUser);
|
||||
pItem->SetBinding( true );
|
||||
}
|
||||
}
|
||||
|
||||
SpellCastTargets targets;
|
||||
if(!targets.read(&recvPacket, pUser))
|
||||
return;
|
||||
|
||||
//Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
|
||||
if(!Script->ItemUse(pUser,pItem,targets))
|
||||
{
|
||||
// no script or script not process request by self
|
||||
|
||||
// special learning case
|
||||
if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN)
|
||||
{
|
||||
uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN);
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN);
|
||||
pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(pUser, spellInfo, false);
|
||||
spell->m_CastItem = pItem;
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->m_currentBasePoints[0] = learning_spell_id;
|
||||
spell->prepare(&targets);
|
||||
return;
|
||||
}
|
||||
|
||||
// use triggered flag only for items with many spell casts and for not first cast
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < 5; ++i)
|
||||
{
|
||||
_Spell const& spellData = pItem->GetProto()->Spells[i];
|
||||
|
||||
// no spell
|
||||
if(!spellData.SpellId)
|
||||
continue;
|
||||
|
||||
// wrong triggering type
|
||||
if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
|
||||
continue;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId);
|
||||
continue;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(pUser, spellInfo, (count > 0));
|
||||
spell->m_CastItem = pItem;
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->prepare(&targets);
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define OPEN_CHEST 11437
|
||||
#define OPEN_SAFE 11535
|
||||
#define OPEN_CAGE 11792
|
||||
#define OPEN_BOOTY_CHEST 5107
|
||||
#define OPEN_STRONGBOX 8517
|
||||
|
||||
void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,1+1);
|
||||
|
||||
sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size());
|
||||
|
||||
Player* pUser = _player;
|
||||
uint8 bagIndex, slot;
|
||||
|
||||
recvPacket >> bagIndex >> slot;
|
||||
|
||||
sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
|
||||
|
||||
Item *pItem = pUser->GetItemByPos(bagIndex, slot);
|
||||
if(!pItem)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPrototype const *proto = pItem->GetProto();
|
||||
if(!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
// locked item
|
||||
uint32 lockId = proto->LockID;
|
||||
if(lockId)
|
||||
{
|
||||
LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
|
||||
|
||||
if (!lockInfo)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
|
||||
sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
|
||||
return;
|
||||
}
|
||||
|
||||
// required picklocking
|
||||
if(lockInfo->requiredlockskill || lockInfo->requiredminingskill)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
|
||||
{
|
||||
QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
||||
if (result)
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 entry = fields[0].GetUInt32();
|
||||
uint32 flags = fields[1].GetUInt32();
|
||||
|
||||
pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
|
||||
pItem->SetUInt32Value(OBJECT_FIELD_ENTRY, entry);
|
||||
pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
|
||||
pItem->SetState(ITEM_CHANGED, pUser);
|
||||
delete result;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
|
||||
pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||
return;
|
||||
}
|
||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
|
||||
}
|
||||
else
|
||||
pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
|
||||
{
|
||||
CHECK_PACKET_SIZE(recv_data,8);
|
||||
|
||||
uint64 guid;
|
||||
uint32 spellId = OPEN_CHEST;
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
|
||||
GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid);
|
||||
|
||||
if(!obj)
|
||||
return;
|
||||
|
||||
if (Script->GOHello(_player, obj))
|
||||
return;
|
||||
|
||||
obj->Use(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4+1+2);
|
||||
|
||||
uint32 spellId;
|
||||
uint8 cast_count;
|
||||
recvPacket >> spellId;
|
||||
recvPacket >> cast_count;
|
||||
|
||||
sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i",
|
||||
spellId, cast_count, recvPacket.size());
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
|
||||
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("WORLD: unknown spell id %u", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
// not have spell or spell passive and not casted by client
|
||||
if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) )
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
return;
|
||||
}
|
||||
|
||||
// can't use our own spells when we're in possession of another unit,
|
||||
if(_player->isPossessing())
|
||||
return;
|
||||
|
||||
// client provided targets
|
||||
SpellCastTargets targets;
|
||||
if(!targets.read(&recvPacket,_player))
|
||||
return;
|
||||
|
||||
// auto-selection buff level base at target level (in spellInfo)
|
||||
if(targets.getUnitTarget())
|
||||
{
|
||||
SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
|
||||
|
||||
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
|
||||
if(actualSpellInfo)
|
||||
spellInfo = actualSpellInfo;
|
||||
}
|
||||
|
||||
Spell *spell = new Spell(_player, spellInfo, false);
|
||||
spell->m_cast_count = cast_count; //set count of casts
|
||||
spell->prepare(&targets);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4);
|
||||
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
//FIXME: hack, ignore unexpected client cancel Deadly Throw cast
|
||||
if(spellId==26679)
|
||||
return;
|
||||
|
||||
if(_player->IsNonMeleeSpellCasted(false))
|
||||
_player->InterruptNonMeleeSpells(false,spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket,4);
|
||||
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
// Remove possess/charm/sight aura from the possessed/charmed as well
|
||||
// TODO: Remove this once the ability to cancel aura sets at once is implemented
|
||||
if(_player->GetCharm() || _player->GetFarsightTarget())
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS_PET ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_CHARM ||
|
||||
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_BIND_SIGHT)
|
||||
{
|
||||
_player->RemoveAurasDueToSpellByCancel(spellId);
|
||||
if (_player->GetCharm())
|
||||
_player->GetCharm()->RemoveAurasDueToSpellByCancel(spellId);
|
||||
else if (_player->GetFarsightTarget()->GetTypeId() != TYPEID_DYNAMICOBJECT)
|
||||
((Unit*)_player->GetFarsightTarget())->RemoveAurasDueToSpellByCancel(spellId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
|
||||
if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
|
||||
return;
|
||||
|
||||
_player->RemoveAurasDueToSpellByCancel(spellId);
|
||||
|
||||
if (spellId == 2584) // Waiting to resurrect spell cancel, we must remove player from resurrect queue
|
||||
{
|
||||
BattleGround *bg = _player->GetBattleGround();
|
||||
if(!bg)
|
||||
return;
|
||||
bg->RemovePlayerFromResurrectQueue(_player->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket, 8+4);
|
||||
|
||||
uint64 guid;
|
||||
uint32 spellId;
|
||||
|
||||
recvPacket >> guid;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
|
||||
if(!spellInfo)
|
||||
{
|
||||
sLog.outError("WORLD: unknown PET spell id %u", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
|
||||
|
||||
if(!pet)
|
||||
{
|
||||
sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
|
||||
return;
|
||||
}
|
||||
|
||||
if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
|
||||
{
|
||||
sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
|
||||
return;
|
||||
}
|
||||
|
||||
if(!pet->isAlive())
|
||||
{
|
||||
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
pet->RemoveAurasDueToSpell(spellId);
|
||||
|
||||
pet->AddCreatureSpellCooldown(spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// nothing do
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// may be better send SMSG_CANCEL_AUTO_REPEAT?
|
||||
// cancel and prepare for deleting
|
||||
_player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
|
||||
}
|
||||
|
||||
/// \todo Complete HandleCancelChanneling function
|
||||
void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
|
||||
{
|
||||
/*
|
||||
CHECK_PACKET_SIZE(recv_data, 4);
|
||||
|
||||
uint32 spellid;
|
||||
recv_data >> spellid;
|
||||
*/
|
||||
}
|
||||
|
||||
void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket)
|
||||
{
|
||||
CHECK_PACKET_SIZE(recvPacket, 1);
|
||||
|
||||
uint8 slotId;
|
||||
|
||||
recvPacket >> slotId;
|
||||
|
||||
if (slotId >= MAX_TOTEM)
|
||||
return;
|
||||
|
||||
if(!_player->m_TotemSlot[slotId])
|
||||
return;
|
||||
|
||||
Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]);
|
||||
// Don't unsummon sentry totem
|
||||
if(totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY)
|
||||
((Totem*)totem)->UnSummon();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
|
||||
{
|
||||
sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
|
||||
|
||||
if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
|
||||
{
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
|
||||
if(spellInfo)
|
||||
_player->CastSpell(_player,spellInfo,false,0);
|
||||
|
||||
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4094,17 +4094,12 @@ void Unit::RemoveNotOwnSingleTargetAuras()
|
||||
|
||||
void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
|
||||
{
|
||||
Aura* Aur = i->second;
|
||||
SpellEntry const* AurSpellInfo = Aur->GetSpellProto();
|
||||
|
||||
Unit* caster = NULL;
|
||||
if (IsSingleTargetSpell(AurSpellInfo))
|
||||
if (IsSingleTargetSpell((*i).second->GetSpellProto()))
|
||||
{
|
||||
caster = Aur->GetCaster();
|
||||
if(caster)
|
||||
if(Unit* caster = (*i).second->GetCaster())
|
||||
{
|
||||
AuraList& scAuras = caster->GetSingleCastAuras();
|
||||
scAuras.remove(Aur);
|
||||
scAuras.remove((*i).second);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4113,19 +4108,20 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
// remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
|
||||
if (Aur->GetModifier()->m_auraname < TOTAL_AURAS)
|
||||
if ((*i).second->GetModifier()->m_auraname < TOTAL_AURAS)
|
||||
{
|
||||
m_modAuras[Aur->GetModifier()->m_auraname].remove(Aur);
|
||||
if(Aur->GetSpellProto()->AuraInterruptFlags)
|
||||
m_modAuras[(*i).second->GetModifier()->m_auraname].remove((*i).second);
|
||||
if((*i).second->GetSpellProto()->AuraInterruptFlags)
|
||||
{
|
||||
m_interruptableAuras.remove(Aur);
|
||||
m_interruptableAuras.remove((*i).second);
|
||||
UpdateInterruptMask();
|
||||
}
|
||||
if(Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE)
|
||||
m_ccAuras.remove(Aur);
|
||||
if((*i).second->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE)
|
||||
m_ccAuras.remove((*i).second);
|
||||
}
|
||||
|
||||
// remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
|
||||
Aura* Aur = i->second;
|
||||
// Set remove mode
|
||||
Aur->SetRemoveMode(mode);
|
||||
// some ShapeshiftBoosts at remove trigger removing other auras including parent Shapeshift aura
|
||||
@@ -4133,22 +4129,12 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
|
||||
m_Auras.erase(i);
|
||||
++m_removedAuras; // internal count used by unit update
|
||||
|
||||
// Statue unsummoned at aura remove
|
||||
// Status unsummoned at aura remove
|
||||
Totem* statue = NULL;
|
||||
bool caster_channeled = false;
|
||||
if(IsChanneledSpell(AurSpellInfo))
|
||||
{
|
||||
if(!caster) // can be already located for IsSingleTargetSpell case
|
||||
caster = Aur->GetCaster();
|
||||
|
||||
if(caster)
|
||||
{
|
||||
if(IsChanneledSpell(Aur->GetSpellProto()))
|
||||
if(Unit* caster = Aur->GetCaster())
|
||||
if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE)
|
||||
statue = ((Totem*)caster);
|
||||
else
|
||||
caster_channeled = caster==this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(-(int32)Aur->GetSpellProto()->Id))
|
||||
@@ -4167,9 +4153,6 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
|
||||
Aur->_RemoveAura();
|
||||
delete Aur;
|
||||
|
||||
if(caster_channeled)
|
||||
RemoveAurasAtChanneledTarget (AurSpellInfo);
|
||||
|
||||
if(statue)
|
||||
statue->UnSummon();
|
||||
|
||||
@@ -4595,10 +4578,8 @@ void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchool
|
||||
ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType);
|
||||
}
|
||||
|
||||
bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * /*procSpell*/, uint32 /*procFlag*/, uint32 cooldown)
|
||||
bool Unit::HandleHasteAuraProc(Unit *pVictim, SpellEntry const *hasteSpell, uint32 /*effIndex*/, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 cooldown)
|
||||
{
|
||||
SpellEntry const *hasteSpell = triggeredByAura->GetSpellProto();
|
||||
|
||||
Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
|
||||
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
|
||||
|
||||
@@ -4658,11 +4639,8 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
|
||||
bool Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
|
||||
{
|
||||
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto ();
|
||||
uint32 effIndex = triggeredByAura->GetEffIndex ();
|
||||
|
||||
Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
|
||||
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
|
||||
|
||||
@@ -6480,10 +6458,8 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
|
||||
bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
|
||||
{
|
||||
int32 scriptId = triggeredByAura->GetModifier()->m_miscvalue;
|
||||
|
||||
if(!pVictim || !pVictim->isAlive())
|
||||
return false;
|
||||
|
||||
@@ -9947,15 +9923,17 @@ bool Unit::isFrozen() const
|
||||
|
||||
struct ProcTriggeredData
|
||||
{
|
||||
ProcTriggeredData(Aura* _triggeredByAura, uint32 _cooldown)
|
||||
: triggeredByAura(_triggeredByAura),
|
||||
ProcTriggeredData(SpellEntry const * _spellInfo, uint32 _spellParam, Aura* _triggeredByAura, uint32 _cooldown)
|
||||
: spellInfo(_spellInfo), spellParam(_spellParam), triggeredByAura(_triggeredByAura),
|
||||
triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())),
|
||||
cooldown(_cooldown)
|
||||
{}
|
||||
{}
|
||||
|
||||
Aura* triggeredByAura; // triggred aura, can be invalidate at triggered aura proccessing
|
||||
Unit::spellEffectPair triggeredByAura_SpellPair; // spell pair, used for re-find aura (by pointer comparison in range)
|
||||
uint32 cooldown; // possible hidden cooldown
|
||||
SpellEntry const * spellInfo;
|
||||
uint32 spellParam;
|
||||
Aura* triggeredByAura;
|
||||
Unit::spellEffectPair triggeredByAura_SpellPair;
|
||||
uint32 cooldown;
|
||||
};
|
||||
|
||||
typedef std::list< ProcTriggeredData > ProcTriggeredList;
|
||||
@@ -9972,13 +9950,87 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
|
||||
{
|
||||
next = i; ++next;
|
||||
|
||||
Aura* i_aura = *i;
|
||||
|
||||
uint32 cooldown; // returned at next line
|
||||
if(!IsTriggeredAtSpellProcEvent(i_aura->GetSpellProto(), procSpell, procFlag,attType,isVictim,cooldown))
|
||||
SpellEntry const *spellProto = (*i)->GetSpellProto();
|
||||
if(!spellProto)
|
||||
continue;
|
||||
|
||||
procTriggered.push_back( ProcTriggeredData(i_aura, cooldown) );
|
||||
SpellProcEventEntry const *spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
|
||||
if(!spellProcEvent)
|
||||
{
|
||||
// used to prevent spam in log about same non-handled spells
|
||||
static std::set<uint32> nonHandledSpellProcSet;
|
||||
|
||||
if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
|
||||
{
|
||||
sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
|
||||
nonHandledSpellProcSet.insert(spellProto->Id);
|
||||
}
|
||||
|
||||
// spell.dbc use totally different flags, that only can create problems if used.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check spellProcEvent data requirements
|
||||
if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
|
||||
continue;
|
||||
|
||||
// Check if current equipment allows aura to proc
|
||||
if(!isVictim && GetTypeId() == TYPEID_PLAYER )
|
||||
{
|
||||
if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
|
||||
|
||||
if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
|
||||
continue;
|
||||
}
|
||||
else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
|
||||
{
|
||||
// Check if player is wearing shield
|
||||
Item *item = ((Player*)this)->GetShield(true);
|
||||
if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float chance = (float)spellProto->procChance;
|
||||
|
||||
if(Player* modOwner = GetSpellModOwner())
|
||||
modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
|
||||
|
||||
if(!isVictim && spellProcEvent->ppmRate != 0)
|
||||
{
|
||||
uint32 WeaponSpeed = GetAttackTime(attType);
|
||||
chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
|
||||
}
|
||||
|
||||
if(roll_chance_f(chance))
|
||||
{
|
||||
uint32 cooldown = spellProcEvent->cooldown;
|
||||
|
||||
uint32 i_spell_eff = (*i)->GetEffIndex();
|
||||
|
||||
int32 i_spell_param;
|
||||
switch(*aur)
|
||||
{
|
||||
case SPELL_AURA_PROC_TRIGGER_SPELL:
|
||||
i_spell_param = procFlag;
|
||||
break;
|
||||
case SPELL_AURA_DUMMY:
|
||||
case SPELL_AURA_PRAYER_OF_MENDING:
|
||||
case SPELL_AURA_MOD_HASTE:
|
||||
i_spell_param = i_spell_eff;
|
||||
break;
|
||||
case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
|
||||
i_spell_param = (*i)->GetModifier()->m_miscvalue;
|
||||
break;
|
||||
default:
|
||||
i_spell_param = (*i)->GetModifier()->m_amount;
|
||||
break;
|
||||
}
|
||||
|
||||
procTriggered.push_back( ProcTriggeredData(spellProto,i_spell_param,*i, cooldown) );
|
||||
}
|
||||
}
|
||||
|
||||
// Handle effects proceed this time
|
||||
@@ -10011,66 +10063,106 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
|
||||
}
|
||||
}
|
||||
|
||||
// this is aura triggering code call
|
||||
Aura* triggeredByAura = i->triggeredByAura;
|
||||
|
||||
// save charges existence before processing to prevent crash at access to deleted triggered aura after
|
||||
bool triggeredByAuraWithCharges = triggeredByAura->m_procCharges > 0;
|
||||
bool triggeredByAuraWithCharges = i->triggeredByAura->m_procCharges > 0;
|
||||
|
||||
bool casted = false;
|
||||
switch(*aur)
|
||||
{
|
||||
case SPELL_AURA_PROC_TRIGGER_SPELL:
|
||||
{
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s proc aura of spell %u)",
|
||||
(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
casted = HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, attType, i->cooldown);
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
casted = HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell,i->spellParam,attType,i->cooldown);
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_PROC_TRIGGER_DAMAGE:
|
||||
{
|
||||
uint32 triggered_damage = triggeredByAura->GetModifier()->m_amount;
|
||||
sLog.outDebug("ProcDamageAndSpell: doing %u damage (triggered by %s aura of spell %u)",
|
||||
triggered_damage, (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
|
||||
SpellNonMeleeDamageLog(pTarget, triggeredByAura->GetId(), triggered_damage, true, true);
|
||||
sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", i->spellParam, i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
uint32 damage = i->spellParam;
|
||||
SpellNonMeleeDamageLog(pTarget, i->spellInfo->Id, damage, true, true);
|
||||
casted = true;
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_DUMMY:
|
||||
{
|
||||
uint32 effect = triggeredByAura->GetEffIndex();
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s dummy aura of spell %u)",
|
||||
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
|
||||
casted = HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag,i->cooldown);
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
casted = HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_PRAYER_OF_MENDING:
|
||||
{
|
||||
sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
|
||||
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
|
||||
sLog.outDebug("ProcDamageAndSpell(mending): casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
|
||||
casted = HandleMeandingAuraProc(triggeredByAura);
|
||||
// aura can be deleted at casts
|
||||
int32 heal = i->triggeredByAura->GetModifier()->m_amount;
|
||||
uint64 caster_guid = i->triggeredByAura->GetCasterGUID();
|
||||
|
||||
// jumps
|
||||
int32 jumps = i->triggeredByAura->m_procCharges-1;
|
||||
|
||||
// current aura expire
|
||||
i->triggeredByAura->m_procCharges = 1; // will removed at next charges decrease
|
||||
|
||||
// next target selection
|
||||
if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid))
|
||||
{
|
||||
Aura* aura = i->triggeredByAura;
|
||||
|
||||
SpellEntry const* spellProto = aura->GetSpellProto();
|
||||
uint32 effIdx = aura->GetEffIndex();
|
||||
|
||||
float radius;
|
||||
if (spellProto->EffectRadiusIndex[effIdx])
|
||||
radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
|
||||
else
|
||||
radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
|
||||
|
||||
if(Player* caster = ((Player*)aura->GetCaster()))
|
||||
{
|
||||
caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
|
||||
|
||||
if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius))
|
||||
{
|
||||
// aura will applied from caster, but spell casted from current aura holder
|
||||
SpellModifier *mod = new SpellModifier;
|
||||
mod->op = SPELLMOD_CHARGES;
|
||||
mod->value = jumps-5; // negative
|
||||
mod->type = SPELLMOD_FLAT;
|
||||
mod->spellId = spellProto->Id;
|
||||
mod->effectId = effIdx;
|
||||
mod->lastAffected = NULL;
|
||||
mod->mask = spellProto->SpellFamilyFlags;
|
||||
mod->charges = 0;
|
||||
|
||||
caster->AddSpellMod(mod, true);
|
||||
CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,aura,caster->GetGUID());
|
||||
caster->AddSpellMod(mod, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// heal
|
||||
CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
|
||||
casted = true;
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_MOD_HASTE:
|
||||
{
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s haste aura of spell %u)",
|
||||
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
|
||||
casted = HandleHasteAuraProc(pTarget, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
casted = HandleHasteAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
|
||||
{
|
||||
// nothing do, just charges counter
|
||||
// but count only in case appropriate school damage
|
||||
casted = triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask;
|
||||
casted = i->triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask;
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
|
||||
{
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s class script aura of spell %u)",
|
||||
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
|
||||
casted = HandleOverrideClassScriptAuraProc(pTarget, i->triggeredByAura, procSpell,i->cooldown);
|
||||
sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
|
||||
casted = HandleOverrideClassScriptAuraProc(pTarget, i->spellParam, damage, i->triggeredByAura, procSpell,i->cooldown);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -10718,132 +10810,3 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id)
|
||||
|
||||
return pet;
|
||||
}
|
||||
|
||||
bool Unit::IsTriggeredAtSpellProcEvent(SpellEntry const* spellProto, SpellEntry const* procSpell, uint32 procFlag, WeaponAttackType attType, bool isVictim, uint32& cooldown )
|
||||
{
|
||||
SpellProcEventEntry const * spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
|
||||
|
||||
if(!spellProcEvent)
|
||||
{
|
||||
// used to prevent spam in log about same non-handled spells
|
||||
static std::set<uint32> nonHandledSpellProcSet;
|
||||
|
||||
if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
|
||||
{
|
||||
sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
|
||||
nonHandledSpellProcSet.insert(spellProto->Id);
|
||||
}
|
||||
|
||||
// spell.dbc use totally different flags, that only can create problems if used.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check spellProcEvent data requirements
|
||||
if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
|
||||
return false;
|
||||
|
||||
// Check if current equipment allows aura to proc
|
||||
if(!isVictim && GetTypeId() == TYPEID_PLAYER )
|
||||
{
|
||||
if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
|
||||
|
||||
if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
|
||||
return false;
|
||||
}
|
||||
else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
|
||||
{
|
||||
// Check if player is wearing shield
|
||||
Item *item = ((Player*)this)->GetShield(true);
|
||||
if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float chance = (float)spellProto->procChance;
|
||||
|
||||
if(Player* modOwner = GetSpellModOwner())
|
||||
modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
|
||||
|
||||
if(!isVictim && spellProcEvent && spellProcEvent->ppmRate != 0)
|
||||
{
|
||||
uint32 WeaponSpeed = GetAttackTime(attType);
|
||||
chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
|
||||
}
|
||||
|
||||
cooldown = spellProcEvent ? spellProcEvent->cooldown : 0;
|
||||
return roll_chance_f(chance);
|
||||
}
|
||||
|
||||
bool Unit::HandleMeandingAuraProc( Aura* triggeredByAura )
|
||||
{
|
||||
// aura can be deleted at casts
|
||||
SpellEntry const* spellProto = triggeredByAura->GetSpellProto();
|
||||
uint32 effIdx = triggeredByAura->GetEffIndex();
|
||||
int32 heal = triggeredByAura->GetModifier()->m_amount;
|
||||
uint64 caster_guid = triggeredByAura->GetCasterGUID();
|
||||
|
||||
// jumps
|
||||
int32 jumps = triggeredByAura->m_procCharges-1;
|
||||
|
||||
// current aura expire
|
||||
triggeredByAura->m_procCharges = 1; // will removed at next charges decrease
|
||||
|
||||
// next target selection
|
||||
if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid))
|
||||
{
|
||||
float radius;
|
||||
if (spellProto->EffectRadiusIndex[effIdx])
|
||||
radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
|
||||
else
|
||||
radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
|
||||
|
||||
if(Player* caster = ((Player*)triggeredByAura->GetCaster()))
|
||||
{
|
||||
caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
|
||||
|
||||
if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius))
|
||||
{
|
||||
// aura will applied from caster, but spell casted from current aura holder
|
||||
SpellModifier *mod = new SpellModifier;
|
||||
mod->op = SPELLMOD_CHARGES;
|
||||
mod->value = jumps-5; // negative
|
||||
mod->type = SPELLMOD_FLAT;
|
||||
mod->spellId = spellProto->Id;
|
||||
mod->effectId = effIdx;
|
||||
mod->lastAffected = NULL;
|
||||
mod->mask = spellProto->SpellFamilyFlags;
|
||||
mod->charges = 0;
|
||||
|
||||
caster->AddSpellMod(mod, true);
|
||||
CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,triggeredByAura,caster->GetGUID());
|
||||
caster->AddSpellMod(mod, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// heal
|
||||
CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unit::RemoveAurasAtChanneledTarget(SpellEntry const* spellInfo)
|
||||
{
|
||||
uint64 target_guid = GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT);
|
||||
|
||||
if(!IS_UNIT_GUID(target_guid))
|
||||
return;
|
||||
|
||||
Unit* target = ObjectAccessor::GetUnit(*this, target_guid);
|
||||
if(!target)
|
||||
return;
|
||||
|
||||
for (AuraMap::iterator iter = target->GetAuras().begin(); iter != target->GetAuras().end(); )
|
||||
{
|
||||
if (iter->second->GetId() == spellInfo->Id && iter->second->GetCasterGUID()==GetGUID())
|
||||
target->RemoveAura(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
2791
src/game/Unit.h
2791
src/game/Unit.h
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user