server/src/game/ItemHandler.cpp
VladimirMangos 3e0cacbdaf [11743] Fixed auction crash in case missing localization for only some items
Source crash in missing locale strings array size check before access
to it in locale structure.

Also move repeating code for access to wide used localization string arrays to ObjectMgr functions.
2011-07-19 02:34:16 +04:00

1487 lines
50 KiB
C++

/*
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
*
* 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 "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "Item.h"
#include "UpdateData.h"
#include "Chat.h"
void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_SPLIT_ITEM");
uint8 srcbag, srcslot, dstbag, dstslot;
uint32 count;
recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count;
//DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
uint16 src = ( (srcbag << 8) | srcslot );
uint16 dst = ( (dstbag << 8) | dstslot );
if(src == dst)
return;
if (count == 0)
return; //check count - if zero it's fake packet
if(!_player->IsValidPos(srcbag, srcslot, true))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
if(!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
_player->SplitItem( src, dst, count );
}
void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_SWAP_INV_ITEM");
uint8 srcslot, dstslot;
recv_data >> dstslot >> srcslot;
//DEBUG_LOG("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
// prevent attempt swap same item to current position generated by client at special cheating sequence
if(srcslot == dstslot)
return;
if(!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
if(!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot );
uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot );
_player->SwapItem( src, dst );
}
void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data )
{
ObjectGuid itemGuid;
uint8 dstslot;
recv_data >> itemGuid >> dstslot;
// cheating attempt, client should never send opcode in that case
if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
return;
Item* item = _player->GetItemByGuid(itemGuid);
uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
if(!item || item->GetPos() == dstpos)
return;
_player->SwapItem(item->GetPos(), dstpos);
}
void WorldSession::HandleSwapItem( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_SWAP_ITEM");
uint8 dstbag, dstslot, srcbag, srcslot;
recv_data >> dstbag >> dstslot >> srcbag >> srcslot ;
//DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
uint16 src = ( (srcbag << 8) | srcslot );
uint16 dst = ( (dstbag << 8) | dstslot );
// prevent attempt swap same item to current position generated by client at special cheating sequence
if(src == dst)
return;
if(!_player->IsValidPos(srcbag, srcslot, true))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
if(!_player->IsValidPos(dstbag, dstslot, true))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
_player->SwapItem( src, dst );
}
void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_AUTOEQUIP_ITEM");
uint8 srcbag, srcslot;
recv_data >> srcbag >> srcslot;
//DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot );
if( !pSrcItem )
return; // only at cheat
uint16 dest;
InventoryResult msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pSrcItem, NULL );
return;
}
uint16 src = pSrcItem->GetPos();
if(dest == src) // prevent equip in same slot, only at cheat
return;
Item *pDstItem = _player->GetItemByPos( dest );
if( !pDstItem ) // empty slot, simple case
{
_player->RemoveItem( srcbag, srcslot, true );
_player->EquipItem( dest, pSrcItem, true );
_player->AutoUnequipOffhandIfNeed();
}
else // have currently equipped item, not simple case
{
uint8 dstbag = pDstItem->GetBagSlot();
uint8 dstslot = pDstItem->GetSlot();
msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pDstItem, NULL );
return;
}
// check dest->src move possibility
ItemPosCountVec sSrc;
uint16 eSrc = 0;
if( _player->IsInventoryPos( src ) )
{
msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true );
if( msg != EQUIP_ERR_OK )
msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
if( msg != EQUIP_ERR_OK )
msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
}
else if( _player->IsBankPos( src ) )
{
msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true );
if( msg != EQUIP_ERR_OK )
msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
if( msg != EQUIP_ERR_OK )
msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
}
else if( _player->IsEquipmentPos( src ) )
{
msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true);
if( msg == EQUIP_ERR_OK )
msg = _player->CanUnequipItem( eSrc, true);
}
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pDstItem, pSrcItem );
return;
}
// now do moves, remove...
_player->RemoveItem(dstbag, dstslot, false);
_player->RemoveItem(srcbag, srcslot, false);
// add to dest
_player->EquipItem(dest, pSrcItem, true);
// add to src
if( _player->IsInventoryPos( src ) )
_player->StoreItem(sSrc, pDstItem, true);
else if( _player->IsBankPos( src ) )
_player->BankItem(sSrc, pDstItem, true);
else if( _player->IsEquipmentPos( src ) )
_player->EquipItem(eSrc, pDstItem, true);
_player->AutoUnequipOffhandIfNeed();
}
}
void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_DESTROYITEM");
uint8 bag, slot, count, data1, data2, data3;
recv_data >> bag >> slot >> count >> data1 >> data2 >> data3;
//DEBUG_LOG("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
uint16 pos = (bag << 8) | slot;
// prevent drop unequipable items (in combat, for example) and non-empty bags
if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
{
InventoryResult msg = _player->CanUnequipItem( pos, false );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, _player->GetItemByPos(pos), NULL );
return;
}
}
Item *pItem = _player->GetItemByPos( bag, slot );
if(!pItem)
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
// checked at client side and not have server side appropriate error output
if (pItem->GetProto()->Flags & ITEM_FLAG_INDESTRUCTIBLE)
{
_player->SendEquipError( EQUIP_ERR_CANT_DROP_SOULBOUND, NULL, NULL );
return;
}
if(count)
{
uint32 i_count = count;
_player->DestroyItemCount( pItem, i_count, true );
}
else
_player->DestroyItem( bag, slot, true );
}
// Only _static_ data send in this packet !!!
void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_ITEM_QUERY_SINGLE");
uint32 item;
recv_data >> item;
DETAIL_LOG("STORAGE: Item Query = %u", item);
ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(item);
if (pProto)
{
int loc_idx = GetSessionDbLocaleIndex();
std::string name = pProto->Name1;
std::string description = pProto->Description;
sObjectMgr.GetItemLocaleStrings(pProto->ItemId, loc_idx, &name, &description);
// guess size
WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
data << pProto->ItemId;
data << pProto->Class;
data << pProto->SubClass;
data << int32(pProto->Unk0); // new 2.0.3, not exist in wdb cache?
data << name;
data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
data << pProto->DisplayInfoID;
data << pProto->Quality;
data << pProto->Flags;
data << pProto->Flags2; // new in 3.2
data << pProto->BuyPrice;
data << pProto->SellPrice;
data << pProto->InventoryType;
data << pProto->AllowableClass;
data << pProto->AllowableRace;
data << pProto->ItemLevel;
data << pProto->RequiredLevel;
data << pProto->RequiredSkill;
data << pProto->RequiredSkillRank;
data << pProto->RequiredSpell;
data << pProto->RequiredHonorRank;
data << pProto->RequiredCityRank;
data << pProto->RequiredReputationFaction;
data << pProto->RequiredReputationRank;
data << int32(pProto->MaxCount);
data << int32(pProto->Stackable);
data << pProto->ContainerSlots;
data << pProto->StatsCount; // item stats count
for(uint32 i = 0; i < pProto->StatsCount; ++i)
{
data << pProto->ItemStat[i].ItemStatType;
data << pProto->ItemStat[i].ItemStatValue;
}
data << pProto->ScalingStatDistribution; // scaling stats distribution
data << pProto->ScalingStatValue; // some kind of flags used to determine stat values column
for(int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
{
data << pProto->Damage[i].DamageMin;
data << pProto->Damage[i].DamageMax;
data << pProto->Damage[i].DamageType;
}
// resistances (7)
data << pProto->Armor;
data << pProto->HolyRes;
data << pProto->FireRes;
data << pProto->NatureRes;
data << pProto->FrostRes;
data << pProto->ShadowRes;
data << pProto->ArcaneRes;
data << pProto->Delay;
data << pProto->AmmoType;
data << pProto->RangedModRange;
for(int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s)
{
// send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
// use `item_template` or if not set then only use spell cooldowns
SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId);
if(spell)
{
bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
data << pProto->Spells[s].SpellId;
data << pProto->Spells[s].SpellTrigger;
data << uint32(-abs(pProto->Spells[s].SpellCharges));
if(db_data)
{
data << uint32(pProto->Spells[s].SpellCooldown);
data << uint32(pProto->Spells[s].SpellCategory);
data << uint32(pProto->Spells[s].SpellCategoryCooldown);
}
else
{
data << uint32(spell->RecoveryTime);
data << uint32(spell->Category);
data << uint32(spell->CategoryRecoveryTime);
}
}
else
{
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(-1);
data << uint32(0);
data << uint32(-1);
}
}
data << pProto->Bonding;
data << description;
data << pProto->PageText;
data << pProto->LanguageID;
data << pProto->PageMaterial;
data << pProto->StartQuest;
data << pProto->LockID;
data << int32(pProto->Material);
data << pProto->Sheath;
data << pProto->RandomProperty;
data << pProto->RandomSuffix;
data << pProto->Block;
data << pProto->ItemSet;
data << pProto->MaxDurability;
data << pProto->Area;
data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
data << pProto->BagFamily;
data << pProto->TotemCategory;
for(int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s)
{
data << pProto->Socket[s].Color;
data << pProto->Socket[s].Content;
}
data << uint32(pProto->socketBonus);
data << uint32(pProto->GemProperties);
data << int32(pProto->RequiredDisenchantSkill);
data << float(pProto->ArmorDamageModifier);
data << uint32(pProto->Duration); // added in 2.4.2.8209, duration (seconds)
data << uint32(pProto->ItemLimitCategory); // WotLK, ItemLimitCategory
data << uint32(pProto->HolidayId); // Holiday.dbc?
SendPacket( &data );
}
else
{
DEBUG_LOG( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item );
WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4);
data << uint32(item | 0x80000000);
SendPacket( &data );
}
}
void WorldSession::HandleReadItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG( "WORLD: CMSG_READ_ITEM");
uint8 bag, slot;
recv_data >> bag >> slot;
//sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
Item *pItem = _player->GetItemByPos( bag, slot );
if( pItem && pItem->GetProto()->PageText )
{
WorldPacket data;
InventoryResult msg = _player->CanUseItem( pItem );
if( msg == EQUIP_ERR_OK )
{
data.Initialize (SMSG_READ_ITEM_OK, 8);
DETAIL_LOG("STORAGE: Item page sent");
}
else
{
data.Initialize( SMSG_READ_ITEM_FAILED, 8 );
DETAIL_LOG("STORAGE: Unable to read item");
_player->SendEquipError( msg, pItem, NULL );
}
data << ObjectGuid(pItem->GetObjectGuid());
SendPacket(&data);
}
else
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
}
void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
{
DEBUG_LOG( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
uint32 itemid;
ObjectGuid guid;
recv_data >> itemid >> guid;
DETAIL_LOG("Packet Info: itemid: %u guid: %s", itemid, guid.GetString().c_str());
}
void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
{
DEBUG_LOG( "WORLD: Received CMSG_SELL_ITEM" );
ObjectGuid vendorGuid;
ObjectGuid itemGuid;
uint32 count;
recv_data >> vendorGuid;
recv_data >> itemGuid;
recv_data >> count;
if (!itemGuid)
return;
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
DEBUG_LOG("WORLD: HandleSellItemOpcode - %s not found or you can't interact with him.", vendorGuid.GetString().c_str());
_player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, itemGuid, 0);
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
Item *pItem = _player->GetItemByGuid(itemGuid);
if (pItem)
{
// prevent sell not owner item
if (_player->GetObjectGuid() != pItem->GetOwnerGuid())
{
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
// prevent sell non empty bag by drag-and-drop at vendor's item list
if (pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
{
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
// prevent sell currently looted item
if (_player->GetLootGuid() == pItem->GetObjectGuid())
{
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
// special case at auto sell (sell all)
if (count == 0)
{
count = pItem->GetCount();
}
else
{
// prevent sell more items that exist in stack (possible only not from client)
if (count > pItem->GetCount())
{
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
}
ItemPrototype const *pProto = pItem->GetProto();
if (pProto)
{
if (pProto->SellPrice > 0)
{
if (count < pItem->GetCount()) // need split items
{
Item *pNewItem = pItem->CloneItem( count, _player );
if (!pNewItem)
{
sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count );
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
pItem->SetCount(pItem->GetCount() - count);
_player->ItemRemovedQuestCheck(pItem->GetEntry(), count);
if (_player->IsInWorld())
pItem->SendCreateUpdateToPlayer(_player);
pItem->SetState(ITEM_CHANGED, _player);
_player->AddItemToBuyBackSlot(pNewItem);
if (_player->IsInWorld())
pNewItem->SendCreateUpdateToPlayer(_player);
}
else
{
_player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
_player->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
pItem->RemoveFromUpdateQueueOf(_player);
_player->AddItemToBuyBackSlot(pItem);
}
uint32 money = pProto->SellPrice * count;
_player->ModifyMoney(money);
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money);
}
else
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, pCreature, itemGuid, 0);
return;
}
}
_player->SendSellError(SELL_ERR_CANT_FIND_ITEM, pCreature, itemGuid, 0);
return;
}
void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
{
DEBUG_LOG( "WORLD: Received CMSG_BUYBACK_ITEM" );
ObjectGuid vendorGuid;
uint32 slot;
recv_data >> vendorGuid >> slot;
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
DEBUG_LOG("WORLD: HandleBuybackItem - %s not found or you can't interact with him.", vendorGuid.GetString().c_str());
_player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, ObjectGuid(), 0);
return;
}
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
Item *pItem = _player->GetItemFromBuyBackSlot( slot );
if (pItem)
{
uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START );
if (_player->GetMoney() < price)
{
_player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0);
return;
}
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
if (msg == EQUIP_ERR_OK)
{
_player->ModifyMoney( -(int32)price );
_player->RemoveItemFromBuyBackSlot( slot, false );
_player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount());
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
_player->StoreItem( dest, pItem, true );
}
else
_player->SendEquipError( msg, pItem, NULL );
return;
}
else
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
}
void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
{
DEBUG_LOG( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
ObjectGuid vendorGuid;
ObjectGuid bagGuid;
uint32 item, slot, count;
uint8 bagslot;
recv_data >> vendorGuid >> item >> slot >> bagGuid >> bagslot >> count;
// client side expected counting from 1, and we send to client vendorslot+1 already
if (slot > 0)
--slot;
else
return; // cheating
uint8 bag = NULL_BAG; // init for case invalid bagGUID
// find bag slot by bag guid
if (bagGuid == _player->GetObjectGuid())
bag = INVENTORY_SLOT_BAG_0;
else
{
for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;++i)
{
if (Bag *pBag = (Bag*)_player->GetItemByPos(INVENTORY_SLOT_BAG_0,i))
{
if (bagGuid == pBag->GetObjectGuid())
{
bag = i;
break;
}
}
}
}
// bag not found, cheating?
if (bag == NULL_BAG)
return;
GetPlayer()->BuyItemFromVendorSlot(vendorGuid, slot, item, count, bag, bagslot);
}
void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
{
DEBUG_LOG( "WORLD: Received CMSG_BUY_ITEM" );
ObjectGuid vendorGuid;
uint32 item, slot, count;
uint8 unk1;
recv_data >> vendorGuid >> item >> slot >> count >> unk1;
// client side expected counting from 1, and we send to client vendorslot+1 already
if (slot > 0)
--slot;
else
return; // cheating
GetPlayer()->BuyItemFromVendorSlot(vendorGuid, slot, item, count, NULL_BAG, NULL_SLOT);
}
void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
{
ObjectGuid guid;
recv_data >> guid;
if (!GetPlayer()->isAlive())
return;
DEBUG_LOG("WORLD: Recvd CMSG_LIST_INVENTORY");
SendListInventory( guid );
}
void WorldSession::SendListInventory(ObjectGuid vendorguid)
{
DEBUG_LOG("WORLD: Sent SMSG_LIST_INVENTORY");
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
DEBUG_LOG("WORLD: SendListInventory - %s not found or you can't interact with him.", vendorguid.GetString().c_str());
_player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, ObjectGuid(), 0);
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
// Stop the npc if moving
if (!pCreature->IsStopped())
pCreature->StopMoving();
VendorItemData const* vItems = pCreature->GetVendorItems();
VendorItemData const* tItems = pCreature->GetVendorTemplateItems();
if (!vItems && !tItems)
{
WorldPacket data( SMSG_LIST_INVENTORY, (8+1+1) );
data << ObjectGuid(vendorguid);
data << uint8(0); // count==0, next will be error code
data << uint8(0); // "Vendor has no inventory"
SendPacket(&data);
return;
}
uint8 customitems = vItems ? vItems->GetItemCount() : 0;
uint8 numitems = customitems + (tItems ? tItems->GetItemCount() : 0);
uint8 count = 0;
WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
data << ObjectGuid(vendorguid);
size_t count_pos = data.wpos();
data << uint8(count); // placeholder, client limit 150 items (as of 3.3.3)
float discountMod = _player->GetReputationPriceDiscount(pCreature);
for(uint8 vendorslot = 0; vendorslot < numitems; ++vendorslot )
{
VendorItem const* crItem = vendorslot < customitems ? vItems->GetItem(vendorslot) : tItems->GetItem(vendorslot - customitems);
if (crItem)
{
uint32 itemId = crItem->item;
ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(itemId);
if (pProto)
{
if (!_player->isGameMaster())
{
// class wrong item skip only for bindable case
if ((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP)
continue;
// race wrong item skip always
if ((pProto->Flags2 & ITEM_FLAG2_HORDE_ONLY) && _player->GetTeam() != HORDE)
continue;
if ((pProto->Flags2 & ITEM_FLAG2_ALLIANCE_ONLY) && _player->GetTeam() != ALLIANCE)
continue;
if ((pProto->AllowableRace & _player->getRaceMask()) == 0)
continue;
}
// possible item coverting for BoA case
if (pProto->Flags & ITEM_FLAG_BOA)
{
// convert if can use and then buy
if (pProto->RequiredReputationFaction && uint32(_player->GetReputationRank(pProto->RequiredReputationFaction)) >= pProto->RequiredReputationRank)
{
// checked at convert data loading as existed
if (uint32 newItemId = sObjectMgr.GetItemConvert(itemId, _player->getRaceMask()))
pProto = ObjectMgr::GetItemPrototype(newItemId);
}
}
++count;
// reputation discount
uint32 price = (crItem->ExtendedCost == 0 || pProto->Flags2 & ITEM_FLAG2_EXT_COST_REQUIRES_GOLD) ? uint32(floor(pProto->BuyPrice * discountMod)) : 0;
data << uint32(vendorslot +1); // client size expected counting from 1
data << uint32(itemId);
data << uint32(pProto->DisplayInfoID);
data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem));
data << uint32(price);
data << uint32(pProto->MaxDurability);
data << uint32(pProto->BuyCount);
data << uint32(crItem->ExtendedCost);
}
}
}
if (count == 0)
{
data << uint8(0); // "Vendor has no inventory"
SendPacket(&data);
return;
}
data.put<uint8>(count_pos, count);
SendPacket(&data);
}
void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
{
//DEBUG_LOG("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
uint8 srcbag, srcslot, dstbag;
recv_data >> srcbag >> srcslot >> dstbag;
//DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
Item *pItem = _player->GetItemByPos( srcbag, srcslot );
if( !pItem )
return;
if(!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
uint16 src = pItem->GetPos();
// check unequip potability for equipped items and bank bags
if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
{
InventoryResult msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src ));
if(msg != EQUIP_ERR_OK)
{
_player->SendEquipError( msg, pItem, NULL );
return;
}
}
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pItem, NULL );
return;
}
// no-op: placed in same slot
if(dest.size() == 1 && dest[0].pos == src)
{
// just remove gray item state
_player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
return;
}
_player->RemoveItem(srcbag, srcslot, true );
_player->StoreItem( dest, pItem, true );
}
bool WorldSession::CheckBanker(ObjectGuid guid)
{
// GM case
if (guid == GetPlayer()->GetObjectGuid())
{
// command case will return only if player have real access to command
if (!ChatHandler(GetPlayer()).FindCommand("bank"))
{
DEBUG_LOG("%s attempt open bank in cheating way.", guid.GetString().c_str());
return false;
}
}
// banker case
else
{
if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER))
{
DEBUG_LOG("Banker %s not found or you can't interact with him.", guid.GetString().c_str());
return false;
}
}
return true;
}
void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket)
{
DEBUG_LOG("WORLD: CMSG_BUY_BANK_SLOT");
ObjectGuid guid;
recvPacket >> guid;
if (!CheckBanker(guid))
return;
uint32 slot = _player->GetBankBagSlotCount();
// next slot
++slot;
DETAIL_LOG("PLAYER: Buy bank bag slot, slot number = %u", slot);
BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
WorldPacket data(SMSG_BUY_BANK_SLOT_RESULT, 4);
if(!slotEntry)
{
data << uint32(ERR_BANKSLOT_FAILED_TOO_MANY);
SendPacket(&data);
return;
}
uint32 price = slotEntry->price;
if (_player->GetMoney() < price)
{
data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS);
SendPacket(&data);
return;
}
_player->SetBankBagSlotCount(slot);
_player->ModifyMoney(-int32(price));
data << uint32(ERR_BANKSLOT_OK);
SendPacket(&data);
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
}
void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
{
DEBUG_LOG("WORLD: CMSG_AUTOBANK_ITEM");
uint8 srcbag, srcslot;
recvPacket >> srcbag >> srcslot;
DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
Item *pItem = _player->GetItemByPos( srcbag, srcslot );
if( !pItem )
return;
ItemPosCountVec dest;
InventoryResult msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pItem, NULL );
return;
}
// no-op: placed in same slot
if(dest.size() == 1 && dest[0].pos == pItem->GetPos())
{
// just remove gray item state
_player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
return;
}
_player->RemoveItem(srcbag, srcslot, true);
_player->BankItem( dest, pItem, true );
}
void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
{
DEBUG_LOG("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
uint8 srcbag, srcslot;
recvPacket >> srcbag >> srcslot;
DEBUG_LOG("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
Item *pItem = _player->GetItemByPos( srcbag, srcslot );
if( !pItem )
return;
if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
{
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pItem, NULL );
return;
}
_player->RemoveItem(srcbag, srcslot, true);
_player->StoreItem( dest, pItem, true );
}
else // moving from inventory to bank
{
ItemPosCountVec dest;
InventoryResult msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
if( msg != EQUIP_ERR_OK )
{
_player->SendEquipError( msg, pItem, NULL );
return;
}
_player->RemoveItem(srcbag, srcslot, true);
_player->BankItem( dest, pItem, true );
}
}
void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
{
if(!GetPlayer()->isAlive())
{
GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL );
return;
}
DEBUG_LOG("WORLD: CMSG_SET_AMMO");
uint32 item;
recv_data >> item;
if(!item)
GetPlayer()->RemoveAmmo();
else
GetPlayer()->SetAmmo(item);
}
void WorldSession::SendEnchantmentLog(ObjectGuid targetGuid, ObjectGuid casterGuid, uint32 itemId, uint32 spellId)
{
WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10
data << ObjectGuid(targetGuid);
data << ObjectGuid(casterGuid);
data << uint32(itemId);
data << uint32(spellId);
data << uint8(0);
SendPacket(&data);
}
void WorldSession::SendItemEnchantTimeUpdate(ObjectGuid playerGuid, ObjectGuid itemGuid, uint32 slot, uint32 duration)
{
// last check 2.0.10
WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
data << ObjectGuid(itemGuid);
data << uint32(slot);
data << uint32(duration);
data << ObjectGuid(playerGuid);
SendPacket(&data);
}
void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
{
uint32 itemid;
recv_data >> itemid;
recv_data.read_skip<uint64>(); // guid
DEBUG_LOG("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid);
if (ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(itemid))
{
int loc_idx = GetSessionDbLocaleIndex();
std::string name = pProto->Name1;
sObjectMgr.GetItemLocaleStrings(pProto->ItemId, loc_idx, &name);
// guess size
WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10));
data << uint32(pProto->ItemId);
data << name;
data << uint32(pProto->InventoryType);
SendPacket(&data);
return;
}
else
{
// listed in dbc or not expected to exist unknown item
if(sItemStore.LookupEntry(itemid))
sLog.outErrorDb("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (item listed in Item.dbc but not exist in DB)", itemid);
else
sLog.outError("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item, not listed in Item.dbc)", itemid);
}
}
void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
{
DEBUG_LOG("Received opcode CMSG_WRAP_ITEM");
uint8 gift_bag, gift_slot, item_bag, item_slot;
//recv_data.hexlike();
recv_data >> gift_bag >> gift_slot; // paper
recv_data >> item_bag >> item_slot; // item
DEBUG_LOG("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
Item *gift = _player->GetItemByPos( gift_bag, gift_slot );
if (!gift)
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
return;
}
// cheating: non-wrapper wrapper (all empty wrappers is stackable)
if (!(gift->GetProto()->Flags & ITEM_FLAG_WRAPPER) || gift->GetMaxStackCount() == 1)
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
return;
}
Item *item = _player->GetItemByPos( item_bag, item_slot );
if (!item)
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL );
return;
}
if (item == gift) // not possible with packet from real client
{
_player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
return;
}
if (item->IsEquipped())
{
_player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL );
return;
}
if (item->GetGuidValue(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED);
{
_player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
return;
}
if (item->IsBag())
{
_player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL );
return;
}
if (item->IsSoulBound())
{
_player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL );
return;
}
if (item->GetMaxStackCount() != 1)
{
_player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL );
return;
}
// maybe not correct check (it is better than nothing)
if (item->GetProto()->MaxCount > 0)
{
_player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL );
return;
}
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", item->GetOwnerGuid().GetCounter(), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS));
item->SetEntry(gift->GetEntry());
switch (item->GetEntry())
{
case 5042: item->SetEntry( 5043); break;
case 5048: item->SetEntry( 5044); break;
case 17303: item->SetEntry(17302); break;
case 17304: item->SetEntry(17305); break;
case 17307: item->SetEntry(17308); break;
case 21830: item->SetEntry(21831); break;
}
item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, _player->GetObjectGuid());
item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED);
item->SetState(ITEM_CHANGED, _player);
if(item->GetState() == ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
{
// after save it will be impossible to remove the item from the queue
item->RemoveFromUpdateQueueOf(_player);
item->SaveToDB(); // item gave inventory record unchanged and can be save standalone
}
CharacterDatabase.CommitTransaction();
uint32 count = 1;
_player->DestroyItemCount(gift, count, true);
}
void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
{
DEBUG_LOG("WORLD: CMSG_SOCKET_GEMS");
ObjectGuid itemGuid;
ObjectGuid gemGuids[MAX_GEM_SOCKETS];
recv_data >> itemGuid;
if (!itemGuid.IsItem())
return;
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
recv_data >> gemGuids[i];
//cheat -> tried to socket same gem multiple times
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
{
ObjectGuid gemGuid = gemGuids[i];
if (!gemGuid)
continue;
if (!gemGuid.IsItem())
return;
for(int j = i+1; j < MAX_GEM_SOCKETS; ++j)
if (gemGuids[j] == gemGuid)
return;
}
Item *itemTarget = _player->GetItemByGuid(itemGuid);
if(!itemTarget) //missing item to socket
return;
ItemPrototype const* itemProto = itemTarget->GetProto();
if(!itemProto)
return;
//this slot is excepted when applying / removing meta gem bonus
uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);
Item *Gems[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
Gems[i] = gemGuids[i] ? _player->GetItemByGuid(gemGuids[i]) : NULL;
GemPropertiesEntry const *GemProps[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage
GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe
{
if (!GemProps[i])
continue;
// tried to put gem in socket where no socket exists (take care about prismatic sockets)
if (!itemProto->Socket[i].Color)
{
// no prismatic socket
if(!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
return;
// not first not-colored (not normally used) socket
if(i != 0 && !itemProto->Socket[i - 1].Color && (i + 1 >= MAX_GEM_SOCKETS || itemProto->Socket[i + 1].Color))
return;
// ok, this is first not colored socket for item with prismatic socket
}
// tried to put normal gem in meta socket
if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META)
return;
// tried to put meta gem in normal socket
if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META)
return;
}
uint32 GemEnchants[MAX_GEM_SOCKETS];
uint32 OldEnchants[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get new and old enchantments
{
GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + i));
}
// check unique-equipped conditions
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
{
if(!Gems[i])
continue;
// continue check for case when attempt add 2 similar unique equipped gems in one item.
ItemPrototype const* iGemProto = Gems[i]->GetProto();
// unique item (for new and already placed bit removed enchantments
if (iGemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPED)
{
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
{
if(i == j) // skip self
continue;
if (Gems[j])
{
if (iGemProto->ItemId == Gems[j]->GetEntry())
{
_player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
return;
}
}
else if(OldEnchants[j])
{
if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
{
if (iGemProto->ItemId == enchantEntry->GemID)
{
_player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
return;
}
}
}
}
}
// unique limit type item
int32 limit_newcount = 0;
if (iGemProto->ItemLimitCategory)
{
if(ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory))
{
// NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
{
if (Gems[j])
{
// destroyed gem
if (OldEnchants[j])
{
if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
if(ItemPrototype const* jProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID))
if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
--limit_newcount;
}
// new gem
if (iGemProto->ItemLimitCategory == Gems[j]->GetProto()->ItemLimitCategory)
++limit_newcount;
}
// existing gem
else if(OldEnchants[j])
{
if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
if(ItemPrototype const* jProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID))
if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
++limit_newcount;
}
}
if(limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount)
{
_player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
return;
}
}
}
// for equipped item check all equipment for duplicate equipped gems
if(itemTarget->IsEquipped())
{
if(InventoryResult res = _player->CanEquipUniqueItem(Gems[i], slot, limit_newcount >= 0 ? limit_newcount : 0))
{
_player->SendEquipError( res, itemTarget, NULL );
return;
}
}
}
bool SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
_player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
//if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
//remove ALL enchants
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
_player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), false);
for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
{
if (GemEnchants[i])
{
itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + i), GemEnchants[i], 0, 0);
if (Item* guidItem = gemGuids[i] ? _player->GetItemByGuid(gemGuids[i]) : NULL)
_player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
}
}
for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
_player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), true);
bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();// current socketbonus state
if(SocketBonusActivated != SocketBonusToBeActivated) // if there was a change...
{
_player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT, false);
itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0);
_player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, true);
//it is not displayed, client has an inbuilt system to determine if the bonus is activated
}
_player->ToggleMetaGemsActive(slot, true); // turn on all metagems (except for target item)
}
void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recv_data)
{
DEBUG_LOG("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
uint32 eslot;
recv_data >> eslot;
// apply only to equipped item
if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, eslot))
return;
Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
if(!item)
return;
if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
return;
GetPlayer()->ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false);
item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
}
void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recv_data)
{
DEBUG_LOG("WORLD: CMSG_ITEM_REFUND_INFO_REQUEST");
ObjectGuid itemGuid;
recv_data >> itemGuid;
Item *item = _player->GetItemByGuid(itemGuid);
if(!item)
{
DEBUG_LOG("Item refund: item not found!");
return;
}
if(!(item->GetProto()->Flags & ITEM_FLAG_REFUNDABLE))
{
DEBUG_LOG("Item refund: item not refundable!");
return;
}
// item refund system not implemented yet
}
/**
* Handles the packet sent by the client when requesting information about item text.
*
* This function is called when player clicks on item which has some flag set
*/
void WorldSession::HandleItemTextQuery(WorldPacket & recv_data )
{
ObjectGuid itemGuid;
recv_data >> itemGuid;
DEBUG_LOG("CMSG_ITEM_TEXT_QUERY item guid: %u", itemGuid.GetCounter());
WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4+10));// guess size
if(Item *item = _player->GetItemByGuid(itemGuid))
{
data << uint8(0); // has text
data << ObjectGuid(itemGuid); // item guid
data << item->GetText();
}
else
{
data << uint8(1); // no text
}
SendPacket(&data);
}