mirror of
https://github.com/mangosfour/server.git
synced 2025-12-27 01:37:04 +00:00
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.
1487 lines
50 KiB
C++
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);
|
|
}
|