/* * Copyright (C) 2008 Trinity * * Thanks to the original authors: MaNGOS * * 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); 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; } // 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; // 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]); if(totem && totem->isTotem()) ((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); } }