2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "WorldPacket.h"
21 #include "WorldSession.h"
25 #include "ObjectMgr.h"
28 #include "UpdateData.h"
29 #include "ObjectAccessor.h"
31 void WorldSession::HandleSplitItemOpcode( WorldPacket
& recv_data
)
33 CHECK_PACKET_SIZE(recv_data
,1+1+1+1+1);
35 //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
36 uint8 srcbag
, srcslot
, dstbag
, dstslot
, count
;
38 recv_data
>> srcbag
>> srcslot
>> dstbag
>> dstslot
>> count
;
39 //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
41 uint16 src
= ( (srcbag
<< 8) | srcslot
);
42 uint16 dst
= ( (dstbag
<< 8) | dstslot
);
48 return; //check count - if zero it's fake packet
50 if(!_player
->IsValidPos(srcbag
,srcslot
))
52 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
56 if(!_player
->IsValidPos(dstbag
,dstslot
))
58 _player
->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
, NULL
, NULL
);
62 _player
->SplitItem( src
, dst
, count
);
65 void WorldSession::HandleSwapInvItemOpcode( WorldPacket
& recv_data
)
67 CHECK_PACKET_SIZE(recv_data
,1+1);
69 //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM");
70 uint8 srcslot
, dstslot
;
72 recv_data
>> srcslot
>> dstslot
;
73 //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
75 // prevent attempt swap same item to current position generated by client at special checting sequence
79 if(!_player
->IsValidPos(INVENTORY_SLOT_BAG_0
,srcslot
))
81 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
85 if(!_player
->IsValidPos(INVENTORY_SLOT_BAG_0
,dstslot
))
87 _player
->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
, NULL
, NULL
);
91 uint16 src
= ( (INVENTORY_SLOT_BAG_0
<< 8) | srcslot
);
92 uint16 dst
= ( (INVENTORY_SLOT_BAG_0
<< 8) | dstslot
);
94 _player
->SwapItem( src
, dst
);
97 void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket
& recv_data
)
99 CHECK_PACKET_SIZE(recv_data
,8+1);
102 recv_data
>> itemguid
>> dstslot
;
104 // cheating attempt, client should never send opcode in that case
105 if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0
, dstslot
))
108 Item
* item
= _player
->GetItemByGuid(itemguid
);
109 uint16 dstpos
= dstslot
| (INVENTORY_SLOT_BAG_0
<< 8);
111 if(!item
|| item
->GetPos() == dstpos
)
114 _player
->SwapItem(item
->GetPos(), dstpos
);
117 void WorldSession::HandleSwapItem( WorldPacket
& recv_data
)
119 CHECK_PACKET_SIZE(recv_data
,1+1+1+1);
121 //sLog.outDebug("WORLD: CMSG_SWAP_ITEM");
122 uint8 dstbag
, dstslot
, srcbag
, srcslot
;
124 recv_data
>> dstbag
>> dstslot
>> srcbag
>> srcslot
;
125 //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
127 uint16 src
= ( (srcbag
<< 8) | srcslot
);
128 uint16 dst
= ( (dstbag
<< 8) | dstslot
);
130 // prevent attempt swap same item to current position generated by client at special checting sequence
134 if(!_player
->IsValidPos(srcbag
,srcslot
))
136 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
140 if(!_player
->IsValidPos(dstbag
,dstslot
))
142 _player
->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
, NULL
, NULL
);
146 _player
->SwapItem( src
, dst
);
149 void WorldSession::HandleAutoEquipItemOpcode( WorldPacket
& recv_data
)
151 CHECK_PACKET_SIZE(recv_data
,1+1);
153 //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM");
154 uint8 srcbag
, srcslot
;
156 recv_data
>> srcbag
>> srcslot
;
157 //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
159 Item
*pSrcItem
= _player
->GetItemByPos( srcbag
, srcslot
);
161 return; // only at cheat
163 if(pSrcItem
->m_lootGenerated
) // prevent swap looting item
165 //best error message found for attempting to swap while looting
166 _player
->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW
, pSrcItem
, NULL
);
171 uint8 msg
= _player
->CanEquipItem( NULL_SLOT
, dest
, pSrcItem
, !pSrcItem
->IsBag() );
172 if( msg
!= EQUIP_ERR_OK
)
174 _player
->SendEquipError( msg
, pSrcItem
, NULL
);
178 uint16 src
= pSrcItem
->GetPos();
179 if(dest
==src
) // prevent equip in same slot, only at cheat
182 Item
*pDstItem
= _player
->GetItemByPos( dest
);
183 if( !pDstItem
) // empty slot, simple case
185 _player
->RemoveItem( srcbag
, srcslot
, true );
186 _player
->EquipItem( dest
, pSrcItem
, true );
187 _player
->AutoUnequipOffhandIfNeed();
189 else // have currently equipped item, not simple case
191 uint8 dstbag
= pDstItem
->GetBagSlot();
192 uint8 dstslot
= pDstItem
->GetSlot();
194 msg
= _player
->CanUnequipItem( dest
, !pSrcItem
->IsBag() );
195 if( msg
!= EQUIP_ERR_OK
)
197 _player
->SendEquipError( msg
, pDstItem
, NULL
);
201 // check dest->src move possibility
202 ItemPosCountVec sSrc
;
204 if( _player
->IsInventoryPos( src
) )
206 msg
= _player
->CanStoreItem( srcbag
, srcslot
, sSrc
, pDstItem
, true );
207 if( msg
!= EQUIP_ERR_OK
)
208 msg
= _player
->CanStoreItem( srcbag
, NULL_SLOT
, sSrc
, pDstItem
, true );
209 if( msg
!= EQUIP_ERR_OK
)
210 msg
= _player
->CanStoreItem( NULL_BAG
, NULL_SLOT
, sSrc
, pDstItem
, true );
212 else if( _player
->IsBankPos( src
) )
214 msg
= _player
->CanBankItem( srcbag
, srcslot
, sSrc
, pDstItem
, true );
215 if( msg
!= EQUIP_ERR_OK
)
216 msg
= _player
->CanBankItem( srcbag
, NULL_SLOT
, sSrc
, pDstItem
, true );
217 if( msg
!= EQUIP_ERR_OK
)
218 msg
= _player
->CanBankItem( NULL_BAG
, NULL_SLOT
, sSrc
, pDstItem
, true );
220 else if( _player
->IsEquipmentPos( src
) )
222 msg
= _player
->CanEquipItem( srcslot
, eSrc
, pDstItem
, true);
223 if( msg
== EQUIP_ERR_OK
)
224 msg
= _player
->CanUnequipItem( eSrc
, true);
227 if( msg
!= EQUIP_ERR_OK
)
229 _player
->SendEquipError( msg
, pDstItem
, pSrcItem
);
233 // now do moves, remove...
234 _player
->RemoveItem(dstbag
, dstslot
, false);
235 _player
->RemoveItem(srcbag
, srcslot
, false);
238 _player
->EquipItem(dest
, pSrcItem
, true);
241 if( _player
->IsInventoryPos( src
) )
242 _player
->StoreItem(sSrc
, pDstItem
, true);
243 else if( _player
->IsBankPos( src
) )
244 _player
->BankItem(sSrc
, pDstItem
, true);
245 else if( _player
->IsEquipmentPos( src
) )
246 _player
->EquipItem(eSrc
, pDstItem
, true);
248 _player
->AutoUnequipOffhandIfNeed();
252 void WorldSession::HandleDestroyItemOpcode( WorldPacket
& recv_data
)
254 CHECK_PACKET_SIZE(recv_data
,1+1+1+1+1+1);
256 //sLog.outDebug("WORLD: CMSG_DESTROYITEM");
257 uint8 bag
, slot
, count
, data1
, data2
, data3
;
259 recv_data
>> bag
>> slot
>> count
>> data1
>> data2
>> data3
;
260 //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
262 uint16 pos
= (bag
<< 8) | slot
;
264 // prevent drop unequipable items (in combat, for example) and non-empty bags
265 if(_player
->IsEquipmentPos(pos
) || _player
->IsBagPos(pos
))
267 uint8 msg
= _player
->CanUnequipItem( pos
, false );
268 if( msg
!= EQUIP_ERR_OK
)
270 _player
->SendEquipError( msg
, _player
->GetItemByPos(pos
), NULL
);
275 Item
*pItem
= _player
->GetItemByPos( bag
, slot
);
278 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
284 uint32 i_count
= count
;
285 _player
->DestroyItemCount( pItem
, i_count
, true );
288 _player
->DestroyItem( bag
, slot
, true );
291 // Only _static_ data send in this packet !!!
292 void WorldSession::HandleItemQuerySingleOpcode( WorldPacket
& recv_data
)
294 CHECK_PACKET_SIZE(recv_data
, 4);
296 //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE");
300 sLog
.outDetail("STORAGE: Item Query = %u", item
);
302 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( item
);
305 std::string Name
= pProto
->Name1
;
306 std::string Description
= pProto
->Description
;
308 int loc_idx
= GetSessionDbLocaleIndex();
311 ItemLocale
const *il
= objmgr
.GetItemLocale(pProto
->ItemId
);
314 if (il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
315 Name
= il
->Name
[loc_idx
];
316 if (il
->Description
.size() > size_t(loc_idx
) && !il
->Description
[loc_idx
].empty())
317 Description
= il
->Description
[loc_idx
];
321 WorldPacket
data( SMSG_ITEM_QUERY_SINGLE_RESPONSE
, 600);
322 data
<< pProto
->ItemId
;
323 data
<< pProto
->Class
;
324 data
<< pProto
->SubClass
;
325 data
<< pProto
->Unk0
; // new 2.0.3, not exist in wdb cache?
327 data
<< uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
328 data
<< uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
329 data
<< uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
330 data
<< pProto
->DisplayInfoID
;
331 data
<< pProto
->Quality
;
332 data
<< pProto
->Flags
;
333 data
<< pProto
->BuyPrice
;
334 data
<< pProto
->SellPrice
;
335 data
<< pProto
->InventoryType
;
336 data
<< pProto
->AllowableClass
;
337 data
<< pProto
->AllowableRace
;
338 data
<< pProto
->ItemLevel
;
339 data
<< pProto
->RequiredLevel
;
340 data
<< pProto
->RequiredSkill
;
341 data
<< pProto
->RequiredSkillRank
;
342 data
<< pProto
->RequiredSpell
;
343 data
<< pProto
->RequiredHonorRank
;
344 data
<< pProto
->RequiredCityRank
;
345 data
<< pProto
->RequiredReputationFaction
;
346 data
<< pProto
->RequiredReputationRank
;
347 data
<< pProto
->MaxCount
;
348 data
<< pProto
->Stackable
;
349 data
<< pProto
->ContainerSlots
;
350 data
<< pProto
->StatsCount
; // item stats count
351 for(int i
= 0; i
< pProto
->StatsCount
; i
++)
353 data
<< pProto
->ItemStat
[i
].ItemStatType
;
354 data
<< pProto
->ItemStat
[i
].ItemStatValue
;
356 data
<< pProto
->ScalingStatDistribution
; // scaling stats distribution
357 data
<< pProto
->ScalingStatValue
; // some kind of flags used to determine stat values column
358 for(int i
= 0; i
< 5; i
++)
360 data
<< pProto
->Damage
[i
].DamageMin
;
361 data
<< pProto
->Damage
[i
].DamageMax
;
362 data
<< pProto
->Damage
[i
].DamageType
;
366 data
<< pProto
->Armor
;
367 data
<< pProto
->HolyRes
;
368 data
<< pProto
->FireRes
;
369 data
<< pProto
->NatureRes
;
370 data
<< pProto
->FrostRes
;
371 data
<< pProto
->ShadowRes
;
372 data
<< pProto
->ArcaneRes
;
374 data
<< pProto
->Delay
;
375 data
<< pProto
->AmmoType
;
376 data
<< pProto
->RangedModRange
;
378 for(int s
= 0; s
< 5; s
++)
380 // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
381 // use `item_template` or if not set then only use spell cooldowns
382 SpellEntry
const* spell
= sSpellStore
.LookupEntry(pProto
->Spells
[s
].SpellId
);
385 bool db_data
= pProto
->Spells
[s
].SpellCooldown
>= 0 || pProto
->Spells
[s
].SpellCategoryCooldown
>= 0;
387 data
<< pProto
->Spells
[s
].SpellId
;
388 data
<< pProto
->Spells
[s
].SpellTrigger
;
389 data
<< uint32(-abs(pProto
->Spells
[s
].SpellCharges
));
393 data
<< uint32(pProto
->Spells
[s
].SpellCooldown
);
394 data
<< uint32(pProto
->Spells
[s
].SpellCategory
);
395 data
<< uint32(pProto
->Spells
[s
].SpellCategoryCooldown
);
399 data
<< uint32(spell
->RecoveryTime
);
400 data
<< uint32(spell
->Category
);
401 data
<< uint32(spell
->CategoryRecoveryTime
);
414 data
<< pProto
->Bonding
;
416 data
<< pProto
->PageText
;
417 data
<< pProto
->LanguageID
;
418 data
<< pProto
->PageMaterial
;
419 data
<< pProto
->StartQuest
;
420 data
<< pProto
->LockID
;
421 data
<< pProto
->Material
;
422 data
<< pProto
->Sheath
;
423 data
<< pProto
->RandomProperty
;
424 data
<< pProto
->RandomSuffix
;
425 data
<< pProto
->Block
;
426 data
<< pProto
->ItemSet
;
427 data
<< pProto
->MaxDurability
;
428 data
<< pProto
->Area
;
429 data
<< pProto
->Map
; // Added in 1.12.x & 2.0.1 client branch
430 data
<< pProto
->BagFamily
;
431 data
<< pProto
->TotemCategory
;
432 for(int s
= 0; s
< 3; s
++)
434 data
<< pProto
->Socket
[s
].Color
;
435 data
<< pProto
->Socket
[s
].Content
;
437 data
<< pProto
->socketBonus
;
438 data
<< pProto
->GemProperties
;
439 data
<< pProto
->RequiredDisenchantSkill
;
440 data
<< pProto
->ArmorDamageModifier
;
441 data
<< pProto
->Duration
; // added in 2.4.2.8209, duration (seconds)
442 data
<< pProto
->ItemLimitCategory
; // WotLK, ItemLimitCategory
447 sLog
.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item
);
448 WorldPacket
data( SMSG_ITEM_QUERY_SINGLE_RESPONSE
, 4);
449 data
<< uint32(item
| 0x80000000);
454 void WorldSession::HandleReadItem( WorldPacket
& recv_data
)
456 CHECK_PACKET_SIZE(recv_data
,1+1);
458 //sLog.outDebug( "WORLD: CMSG_READ_ITEM");
461 recv_data
>> bag
>> slot
;
463 //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
464 Item
*pItem
= _player
->GetItemByPos( bag
, slot
);
466 if( pItem
&& pItem
->GetProto()->PageText
)
470 uint8 msg
= _player
->CanUseItem( pItem
);
471 if( msg
== EQUIP_ERR_OK
)
473 data
.Initialize (SMSG_READ_ITEM_OK
, 8);
474 sLog
.outDetail("STORAGE: Item page sent");
478 data
.Initialize( SMSG_READ_ITEM_FAILED
, 8 );
479 sLog
.outDetail("STORAGE: Unable to read item");
480 _player
->SendEquipError( msg
, pItem
, NULL
);
482 data
<< pItem
->GetGUID();
486 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
489 void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket
& recv_data
)
491 CHECK_PACKET_SIZE(recv_data
,4+8);
493 sLog
.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
498 recv_data
>> itemid
>> guid
;
500 sLog
.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u",
501 itemid
, GUID_LOPART(guid
), GUID_ENPART(guid
), GUID_HIPART(guid
));
504 void WorldSession::HandleSellItemOpcode( WorldPacket
& recv_data
)
506 CHECK_PACKET_SIZE(recv_data
,8+8+1);
508 sLog
.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
509 uint64 vendorguid
, itemguid
;
512 recv_data
>> vendorguid
>> itemguid
>> _count
;
514 // prevent possible overflow, as mangos uses uint32 for item count
515 uint32 count
= _count
;
520 Creature
*pCreature
= ObjectAccessor::GetNPCIfCanInteractWith(*_player
, vendorguid
,UNIT_NPC_FLAG_VENDOR
);
523 sLog
.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid
)) );
524 _player
->SendSellError( SELL_ERR_CANT_FIND_VENDOR
, NULL
, itemguid
, 0);
529 if(GetPlayer()->hasUnitState(UNIT_STAT_DIED
))
530 GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
532 Item
*pItem
= _player
->GetItemByGuid( itemguid
);
535 // prevent sell not owner item
536 if(_player
->GetGUID()!=pItem
->GetOwnerGUID())
538 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
542 // prevent sell non empty bag by drag-and-drop at vendor's item list
543 if(pItem
->IsBag() && !((Bag
*)pItem
)->IsEmpty())
545 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
549 // prevent sell currently looted item
550 if(_player
->GetLootGUID()==pItem
->GetGUID())
552 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
556 // special case at auto sell (sell all)
559 count
= pItem
->GetCount();
563 // prevent sell more items that exist in stack (possable only not from client)
564 if(count
> pItem
->GetCount())
566 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
571 ItemPrototype
const *pProto
= pItem
->GetProto();
574 if( pProto
->SellPrice
> 0 )
576 if(count
< pItem
->GetCount()) // need split items
578 Item
*pNewItem
= pItem
->CloneItem( count
, _player
);
581 sLog
.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem
->GetEntry(), count
);
582 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
586 pItem
->SetCount( pItem
->GetCount() - count
);
587 _player
->ItemRemovedQuestCheck( pItem
->GetEntry(), count
);
588 if( _player
->IsInWorld() )
589 pItem
->SendUpdateToPlayer( _player
);
590 pItem
->SetState(ITEM_CHANGED
, _player
);
592 _player
->AddItemToBuyBackSlot( pNewItem
);
593 if( _player
->IsInWorld() )
594 pNewItem
->SendUpdateToPlayer( _player
);
598 _player
->ItemRemovedQuestCheck( pItem
->GetEntry(), pItem
->GetCount());
599 _player
->RemoveItem( pItem
->GetBagSlot(), pItem
->GetSlot(), true);
600 pItem
->RemoveFromUpdateQueueOf(_player
);
601 _player
->AddItemToBuyBackSlot( pItem
);
604 _player
->ModifyMoney( pProto
->SellPrice
* count
);
607 _player
->SendSellError( SELL_ERR_CANT_SELL_ITEM
, pCreature
, itemguid
, 0);
611 _player
->SendSellError( SELL_ERR_CANT_FIND_ITEM
, pCreature
, itemguid
, 0);
615 void WorldSession::HandleBuybackItem(WorldPacket
& recv_data
)
617 CHECK_PACKET_SIZE(recv_data
,8+4);
619 sLog
.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
623 recv_data
>> vendorguid
>> slot
;
625 Creature
*pCreature
= ObjectAccessor::GetNPCIfCanInteractWith(*_player
, vendorguid
,UNIT_NPC_FLAG_VENDOR
);
628 sLog
.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid
)) );
629 _player
->SendSellError( SELL_ERR_CANT_FIND_VENDOR
, NULL
, 0, 0);
634 if(GetPlayer()->hasUnitState(UNIT_STAT_DIED
))
635 GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
637 Item
*pItem
= _player
->GetItemFromBuyBackSlot( slot
);
640 uint32 price
= _player
->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1
+ slot
- BUYBACK_SLOT_START
);
641 if( _player
->GetMoney() < price
)
643 _player
->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY
, pCreature
, pItem
->GetEntry(), 0);
647 ItemPosCountVec dest
;
648 uint8 msg
= _player
->CanStoreItem( NULL_BAG
, NULL_SLOT
, dest
, pItem
, false );
649 if( msg
== EQUIP_ERR_OK
)
651 _player
->ModifyMoney( -(int32
)price
);
652 _player
->RemoveItemFromBuyBackSlot( slot
, false );
653 _player
->ItemAddedQuestCheck( pItem
->GetEntry(), pItem
->GetCount());
654 _player
->StoreItem( dest
, pItem
, true );
657 _player
->SendEquipError( msg
, pItem
, NULL
);
661 _player
->SendBuyError( BUY_ERR_CANT_FIND_ITEM
, pCreature
, 0, 0);
664 void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket
& recv_data
)
666 CHECK_PACKET_SIZE(recv_data
,8+4+8+1+1);
668 sLog
.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
669 uint64 vendorguid
, bagguid
;
673 recv_data
>> vendorguid
>> item
>> bagguid
>> slot
>> count
;
675 GetPlayer()->BuyItemFromVendor(vendorguid
,item
,count
,bagguid
,slot
);
678 void WorldSession::HandleBuyItemOpcode( WorldPacket
& recv_data
)
680 CHECK_PACKET_SIZE(recv_data
,8+4+1+1);
682 sLog
.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
687 recv_data
>> vendorguid
>> item
>> count
>> unk1
;
689 GetPlayer()->BuyItemFromVendor(vendorguid
,item
,count
,NULL_BAG
,NULL_SLOT
);
692 void WorldSession::HandleListInventoryOpcode( WorldPacket
& recv_data
)
694 CHECK_PACKET_SIZE(recv_data
,8);
700 if(!GetPlayer()->isAlive())
703 sLog
.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" );
705 SendListInventory( guid
);
708 void WorldSession::SendListInventory( uint64 vendorguid
)
710 sLog
.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" );
712 Creature
*pCreature
= ObjectAccessor::GetNPCIfCanInteractWith(*_player
, vendorguid
,UNIT_NPC_FLAG_VENDOR
);
715 sLog
.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid
)) );
716 _player
->SendSellError( SELL_ERR_CANT_FIND_VENDOR
, NULL
, 0, 0);
721 if(GetPlayer()->hasUnitState(UNIT_STAT_DIED
))
722 GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
724 // Stop the npc if moving
725 pCreature
->StopMoving();
727 VendorItemData
const* vItems
= pCreature
->GetVendorItems();
730 _player
->SendSellError( SELL_ERR_CANT_FIND_VENDOR
, NULL
, 0, 0);
734 uint8 numitems
= vItems
->GetItemCount();
737 WorldPacket
data( SMSG_LIST_INVENTORY
, (8+1+numitems
*8*4) );
738 data
<< uint64(vendorguid
);
739 data
<< uint8(numitems
);
741 float discountMod
= _player
->GetReputationPriceDiscount(pCreature
);
743 for(int i
= 0; i
< numitems
; i
++ )
745 if(VendorItem
const* crItem
= vItems
->GetItem(i
))
747 if(ItemPrototype
const *pProto
= objmgr
.GetItemPrototype(crItem
->item
))
749 if((pProto
->AllowableClass
& _player
->getClassMask()) == 0 && pProto
->Bonding
== BIND_WHEN_PICKED_UP
&& !_player
->isGameMaster())
754 // reputation discount
755 uint32 price
= uint32(floor(pProto
->BuyPrice
* discountMod
));
757 data
<< uint32(count
);
758 data
<< uint32(crItem
->item
);
759 data
<< uint32(pProto
->DisplayInfoID
);
760 data
<< uint32(crItem
->maxcount
<= 0 ? 0xFFFFFFFF : pCreature
->GetVendorItemCurrentCount(crItem
));
761 data
<< uint32(price
);
762 data
<< uint32(pProto
->MaxDurability
);
763 data
<< uint32(pProto
->BuyCount
);
764 data
<< uint32(crItem
->ExtendedCost
);
769 if ( count
== 0 || data
.size() != 8 + 1 + size_t(count
) * 8 * 4 )
772 data
.put
<uint8
>(8, count
);
776 void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket
& recv_data
)
778 CHECK_PACKET_SIZE(recv_data
,1+1+1);
780 //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
781 uint8 srcbag
, srcslot
, dstbag
;
783 recv_data
>> srcbag
>> srcslot
>> dstbag
;
784 //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
786 Item
*pItem
= _player
->GetItemByPos( srcbag
, srcslot
);
790 if(!_player
->IsValidPos(dstbag
,NULL_SLOT
))
792 _player
->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT
, NULL
, NULL
);
796 uint16 src
= pItem
->GetPos();
798 // check unequip potability for equipped items and bank bags
799 if(_player
->IsEquipmentPos ( src
) || _player
->IsBagPos ( src
))
801 uint8 msg
= _player
->CanUnequipItem( src
, !_player
->IsBagPos ( src
));
802 if(msg
!= EQUIP_ERR_OK
)
804 _player
->SendEquipError( msg
, pItem
, NULL
);
809 ItemPosCountVec dest
;
810 uint8 msg
= _player
->CanStoreItem( dstbag
, NULL_SLOT
, dest
, pItem
, false );
811 if( msg
!= EQUIP_ERR_OK
)
813 _player
->SendEquipError( msg
, pItem
, NULL
);
817 // no-op: placed in same slot
818 if(dest
.size()==1 && dest
[0].pos
==src
)
820 // just remove grey item state
821 _player
->SendEquipError( EQUIP_ERR_NONE
, pItem
, NULL
);
825 _player
->RemoveItem(srcbag
, srcslot
, true );
826 _player
->StoreItem( dest
, pItem
, true );
829 void WorldSession::HandleBuyBankSlotOpcode(WorldPacket
& /*recvPacket*/)
831 sLog
.outDebug("WORLD: CMSG_BUY_BANK_SLOT");
833 uint32 slot
= _player
->GetByteValue(PLAYER_BYTES_2
, 2);
838 sLog
.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot
);
840 BankBagSlotPricesEntry
const* slotEntry
= sBankBagSlotPricesStore
.LookupEntry(slot
);
845 uint32 price
= slotEntry
->price
;
847 if (_player
->GetMoney() < price
)
850 _player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
, slot
);
851 _player
->SetByteValue(PLAYER_BYTES_2
, 2, slot
);
852 _player
->ModifyMoney(-int32(price
));
855 void WorldSession::HandleAutoBankItemOpcode(WorldPacket
& recvPacket
)
857 CHECK_PACKET_SIZE(recvPacket
,1+1);
859 sLog
.outDebug("WORLD: CMSG_AUTOBANK_ITEM");
860 uint8 srcbag
, srcslot
;
862 recvPacket
>> srcbag
>> srcslot
;
863 sLog
.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag
, srcslot
);
865 Item
*pItem
= _player
->GetItemByPos( srcbag
, srcslot
);
869 ItemPosCountVec dest
;
870 uint8 msg
= _player
->CanBankItem( NULL_BAG
, NULL_SLOT
, dest
, pItem
, false );
871 if( msg
!= EQUIP_ERR_OK
)
873 _player
->SendEquipError( msg
, pItem
, NULL
);
877 _player
->RemoveItem(srcbag
, srcslot
, true);
878 _player
->BankItem( dest
, pItem
, true );
881 void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket
& recvPacket
)
883 CHECK_PACKET_SIZE(recvPacket
,1+1);
885 sLog
.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
886 uint8 srcbag
, srcslot
;
888 recvPacket
>> srcbag
>> srcslot
;
889 sLog
.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag
, srcslot
);
891 Item
*pItem
= _player
->GetItemByPos( srcbag
, srcslot
);
895 if(_player
->IsBankPos(srcbag
, srcslot
)) // moving from bank to inventory
897 ItemPosCountVec dest
;
898 uint8 msg
= _player
->CanStoreItem( NULL_BAG
, NULL_SLOT
, dest
, pItem
, false );
899 if( msg
!= EQUIP_ERR_OK
)
901 _player
->SendEquipError( msg
, pItem
, NULL
);
905 _player
->RemoveItem(srcbag
, srcslot
, true);
906 _player
->StoreItem( dest
, pItem
, true );
908 else // moving from inventory to bank
910 ItemPosCountVec dest
;
911 uint8 msg
= _player
->CanBankItem( NULL_BAG
, NULL_SLOT
, dest
, pItem
, false );
912 if( msg
!= EQUIP_ERR_OK
)
914 _player
->SendEquipError( msg
, pItem
, NULL
);
918 _player
->RemoveItem(srcbag
, srcslot
, true);
919 _player
->BankItem( dest
, pItem
, true );
923 void WorldSession::HandleSetAmmoOpcode(WorldPacket
& recv_data
)
925 CHECK_PACKET_SIZE(recv_data
,4);
927 if(!GetPlayer()->isAlive())
929 GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD
, NULL
, NULL
);
933 sLog
.outDebug("WORLD: CMSG_SET_AMMO");
939 GetPlayer()->RemoveAmmo();
941 GetPlayer()->SetAmmo(item
);
944 void WorldSession::SendEnchantmentLog(uint64 Target
, uint64 Caster
,uint32 ItemID
,uint32 SpellID
)
946 WorldPacket
data(SMSG_ENCHANTMENTLOG
, (8+8+4+4+1)); // last check 2.0.10
955 void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid
, uint64 Itemguid
,uint32 slot
,uint32 Duration
)
958 WorldPacket
data(SMSG_ITEM_ENCHANT_TIME_UPDATE
, (8+4+4+8));
959 data
<< uint64(Itemguid
);
960 data
<< uint32(slot
);
961 data
<< uint32(Duration
);
962 data
<< uint64(Playerguid
);
966 void WorldSession::HandleItemNameQueryOpcode(WorldPacket
& recv_data
)
968 CHECK_PACKET_SIZE(recv_data
,4);
972 sLog
.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid
);
973 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( itemid
);
977 Name
= pProto
->Name1
;
979 int loc_idx
= GetSessionDbLocaleIndex();
982 ItemLocale
const *il
= objmgr
.GetItemLocale(pProto
->ItemId
);
985 if (il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
986 Name
= il
->Name
[loc_idx
];
990 WorldPacket
data(SMSG_ITEM_NAME_QUERY_RESPONSE
, (4+10));
991 data
<< uint32(pProto
->ItemId
);
993 data
<< uint32(pProto
->InventoryType
);
998 sLog
.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid
);
1001 void WorldSession::HandleWrapItemOpcode(WorldPacket
& recv_data
)
1003 CHECK_PACKET_SIZE(recv_data
,1+1+1+1);
1005 sLog
.outDebug("Received opcode CMSG_WRAP_ITEM");
1007 uint8 gift_bag
, gift_slot
, item_bag
, item_slot
;
1008 //recv_data.hexlike();
1010 recv_data
>> gift_bag
>> gift_slot
; // paper
1011 recv_data
>> item_bag
>> item_slot
; // item
1013 sLog
.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag
, gift_slot
, item_bag
, item_slot
);
1015 Item
*gift
= _player
->GetItemByPos( gift_bag
, gift_slot
);
1018 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, gift
, NULL
);
1022 if(!gift
->HasFlag(ITEM_FIELD_FLAGS
, ITEM_FLAGS_WRAPPER
))// cheating: non-wrapper wrapper
1024 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, gift
, NULL
);
1028 Item
*item
= _player
->GetItemByPos( item_bag
, item_slot
);
1032 _player
->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, item
, NULL
);
1036 if(item
==gift
) // not possable with pacjket from real client
1038 _player
->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED
, item
, NULL
);
1042 if(item
->IsEquipped())
1044 _player
->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED
, item
, NULL
);
1048 if(item
->GetUInt64Value(ITEM_FIELD_GIFTCREATOR
)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
1050 _player
->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED
, item
, NULL
);
1056 _player
->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED
, item
, NULL
);
1060 if(item
->IsSoulBound())
1062 _player
->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED
, item
, NULL
);
1066 if(item
->GetMaxStackCount() != 1)
1068 _player
->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED
, item
, NULL
);
1072 // maybe not correct check (it is better than nothing)
1073 if(item
->GetProto()->MaxCount
>0)
1075 _player
->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED
, item
, NULL
);
1079 CharacterDatabase
.BeginTransaction();
1080 CharacterDatabase
.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item
->GetOwnerGUID()), item
->GetGUIDLow(), item
->GetEntry(), item
->GetUInt32Value(ITEM_FIELD_FLAGS
));
1081 item
->SetEntry(gift
->GetEntry());
1083 switch (item
->GetEntry())
1085 case 5042: item
->SetEntry( 5043); break;
1086 case 5048: item
->SetEntry( 5044); break;
1087 case 17303: item
->SetEntry(17302); break;
1088 case 17304: item
->SetEntry(17305); break;
1089 case 17307: item
->SetEntry(17308); break;
1090 case 21830: item
->SetEntry(21831); break;
1092 item
->SetUInt64Value(ITEM_FIELD_GIFTCREATOR
, _player
->GetGUID());
1093 item
->SetUInt32Value(ITEM_FIELD_FLAGS
, ITEM_FLAGS_WRAPPED
);
1094 item
->SetState(ITEM_CHANGED
, _player
);
1096 if(item
->GetState()==ITEM_NEW
) // save new item, to have alway for `character_gifts` record in `item_instance`
1098 // after save it will be impossible to remove the item from the queue
1099 item
->RemoveFromUpdateQueueOf(_player
);
1100 item
->SaveToDB(); // item gave inventory record unchanged and can be save standalone
1102 CharacterDatabase
.CommitTransaction();
1105 _player
->DestroyItemCount(gift
, count
, true);
1108 void WorldSession::HandleSocketOpcode(WorldPacket
& recv_data
)
1110 sLog
.outDebug("WORLD: CMSG_SOCKET_GEMS");
1112 CHECK_PACKET_SIZE(recv_data
,8*4);
1115 uint32 GemEnchants
[3], OldEnchants
[3];
1117 bool SocketBonusActivated
, SocketBonusToBeActivated
;
1119 for(int i
= 0; i
< 4; i
++)
1120 recv_data
>> guids
[i
];
1125 //cheat -> tried to socket same gem multiple times
1126 if((guids
[1] && (guids
[1] == guids
[2] || guids
[1] == guids
[3])) || (guids
[2] && (guids
[2] == guids
[3])))
1129 Item
*itemTarget
= _player
->GetItemByGuid(guids
[0]);
1130 if(!itemTarget
) //missing item to socket
1133 //this slot is excepted when applying / removing meta gem bonus
1134 uint8 slot
= itemTarget
->IsEquipped() ? itemTarget
->GetSlot() : NULL_SLOT
;
1136 for(int i
= 0; i
< 3; i
++)
1137 Gems
[i
] = guids
[i
+ 1] ? _player
->GetItemByGuid(guids
[i
+ 1]) : NULL
;
1139 GemPropertiesEntry
const *GemProps
[3];
1140 for(int i
= 0; i
< 3; ++i
) //get geminfo from dbc storage
1142 GemProps
[i
] = (Gems
[i
]) ? sGemPropertiesStore
.LookupEntry(Gems
[i
]->GetProto()->GemProperties
) : NULL
;
1145 for(int i
= 0; i
< 3; ++i
) //check for hack maybe
1147 // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket
1148 // tried to put meta gem in normal socket
1149 if( GemProps
[i
] && ( !itemTarget
->GetProto()->Socket
[i
].Color
||
1150 itemTarget
->GetProto()->Socket
[i
].Color
== SOCKET_COLOR_META
&& GemProps
[i
]->color
!= SOCKET_COLOR_META
||
1151 itemTarget
->GetProto()->Socket
[i
].Color
!= SOCKET_COLOR_META
&& GemProps
[i
]->color
== SOCKET_COLOR_META
) )
1155 for(int i
= 0; i
< 3; ++i
) //get new and old enchantments
1157 GemEnchants
[i
] = (GemProps
[i
]) ? GemProps
[i
]->spellitemenchantement
: 0;
1158 OldEnchants
[i
] = itemTarget
->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT
+i
));
1161 // check unique-equipped conditions
1162 for(int i
= 0; i
< 3; ++i
)
1164 if (Gems
[i
] && (Gems
[i
]->GetProto()->Flags
& ITEM_FLAGS_UNIQUE_EQUIPPED
))
1166 // for equipped item check all equipment for duplicate equipped gems
1167 if(itemTarget
->IsEquipped())
1169 if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems
[i
]->GetEntry()))
1171 _player
->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE
, itemTarget
, NULL
);
1176 // continue check for case when attempt add 2 similar unique equipped gems in one item.
1177 for (int j
= 0; j
< 3; ++j
)
1179 if ((i
!= j
) && (Gems
[j
]) && (Gems
[i
]->GetProto()->ItemId
== Gems
[j
]->GetProto()->ItemId
))
1181 _player
->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED
, itemTarget
, NULL
);
1185 for (int j
= 0; j
< 3; ++j
)
1189 SpellItemEnchantmentEntry
const* enchantEntry
= sSpellItemEnchantmentStore
.LookupEntry(OldEnchants
[j
]);
1193 if ((enchantEntry
->GemID
== Gems
[i
]->GetProto()->ItemId
) && (i
!= j
))
1195 _player
->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED
, itemTarget
, NULL
);
1203 SocketBonusActivated
= itemTarget
->GemsFitSockets(); //save state of socketbonus
1204 _player
->ToggleMetaGemsActive(slot
, false); //turn off all metagems (except for the target item)
1206 //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
1208 //remove ALL enchants
1209 for(uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+3; ++enchant_slot
)
1210 _player
->ApplyEnchantment(itemTarget
,EnchantmentSlot(enchant_slot
),false);
1212 for(int i
= 0; i
< 3; ++i
)
1216 itemTarget
->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT
+i
), GemEnchants
[i
],0,0);
1217 if(Item
* guidItem
= _player
->GetItemByGuid(guids
[i
+ 1]))
1218 _player
->DestroyItem(guidItem
->GetBagSlot(), guidItem
->GetSlot(), true );
1222 for(uint32 enchant_slot
= SOCK_ENCHANTMENT_SLOT
; enchant_slot
< SOCK_ENCHANTMENT_SLOT
+3; ++enchant_slot
)
1223 _player
->ApplyEnchantment(itemTarget
,EnchantmentSlot(enchant_slot
),true);
1225 SocketBonusToBeActivated
= itemTarget
->GemsFitSockets();//current socketbonus state
1226 if(SocketBonusActivated
^ SocketBonusToBeActivated
) //if there was a change...
1228 _player
->ApplyEnchantment(itemTarget
,BONUS_ENCHANTMENT_SLOT
,false);
1229 itemTarget
->SetEnchantment(BONUS_ENCHANTMENT_SLOT
, (SocketBonusToBeActivated
? itemTarget
->GetProto()->socketBonus
: 0), 0, 0);
1230 _player
->ApplyEnchantment(itemTarget
,BONUS_ENCHANTMENT_SLOT
,true);
1231 //it is not displayed, client has an inbuilt system to determine if the bonus is activated
1234 _player
->ToggleMetaGemsActive(slot
, true); //turn on all metagems (except for target item)
1237 void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket
& recv_data
)
1239 sLog
.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
1241 CHECK_PACKET_SIZE(recv_data
,4);
1247 // apply only to equipped item
1248 if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0
,eslot
))
1251 Item
* item
= GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0
, eslot
);
1256 if(!item
->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT
))
1259 GetPlayer()->ApplyEnchantment(item
,TEMP_ENCHANTMENT_SLOT
,false);
1260 item
->ClearEnchantment(TEMP_ENCHANTMENT_SLOT
);